From 2ba51878a6fcf14df4fc78ef3f550f79df9750c6 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 2 Mar 2021 18:46:24 +0000 Subject: [PATCH 001/137] build fixes for OCaml 4.12.0 --- xen.ocaml.4.12.fixes.patch | 33 +++++++++++++++++++++++++++++++++ xen.spec | 7 ++++++- 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 xen.ocaml.4.12.fixes.patch diff --git a/xen.ocaml.4.12.fixes.patch b/xen.ocaml.4.12.fixes.patch new file mode 100644 index 0000000..a203514 --- /dev/null +++ b/xen.ocaml.4.12.fixes.patch @@ -0,0 +1,33 @@ +--- xen-4.14.1/tools/ocaml/libs/xentoollog/xentoollog_stubs.c.orig 2020-12-17 16:47:25.000000000 +0000 ++++ xen-4.14.1/tools/ocaml/libs/xentoollog/xentoollog_stubs.c 2021-03-01 22:53:23.106205483 +0000 +@@ -53,8 +53,6 @@ + #include "_xtl_levels.inc" + + /* Option type support as per http://www.linux-nantes.org/~fmonnier/ocaml/ocaml-wrapping-c.php */ +-#define Val_none Val_int(0) +-#define Some_val(v) Field(v,0) + + static value Val_some(value v) + { +--- xen-4.14.1/tools/ocaml/libs/xl/xenlight_stubs.c.orig 2020-12-17 16:47:25.000000000 +0000 ++++ xen-4.14.1/tools/ocaml/libs/xl/xenlight_stubs.c 2021-03-01 23:01:24.229859714 +0000 +@@ -227,8 +227,6 @@ + } + + /* Option type support as per http://www.linux-nantes.org/~fmonnier/ocaml/ocaml-wrapping-c.php */ +-#define Val_none Val_int(0) +-#define Some_val(v) Field(v,0) + + static value Val_some(value v) + { +--- xen-4.14.1/tools/ocaml/libs/xc/xenctrl_stubs.c.orig 2020-12-17 16:47:25.000000000 +0000 ++++ xen-4.14.1/tools/ocaml/libs/xc/xenctrl_stubs.c 2021-03-01 23:24:45.477290290 +0000 +@@ -43,8 +43,6 @@ + #define _H(__h) ((xc_interface *)(__h)) + #define _D(__d) ((uint32_t)Int_val(__d)) + +-#define Val_none (Val_int(0)) +- + #define string_of_option_array(array, index) \ + ((Field(array, index) == Val_none) ? NULL : String_val(Field(Field(array, index), 0))) + diff --git a/xen.spec b/xen.spec index 349cb3a..8654a75 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.14.1 -Release: 5%{?dist} +Release: 6%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -120,6 +120,7 @@ Patch48: xen.git-d8099d94dfaa3573bd86ebfc457cbc8f70a3ecda.patch Patch49: xen.git-8169f82049efb5b2044b33aa482ba3a136b7804d.patch Patch50: xsa363.patch Patch51: xsa364.patch +Patch52: xen.ocaml.4.12.fixes.patch %if %build_qemutrad @@ -336,6 +337,7 @@ manage Xen virtual machines. %patch49 -p1 %patch50 -p1 %patch51 -p1 +%patch52 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -928,6 +930,9 @@ fi %endif %changelog +* Tue Mar 02 2021 Michael Young - 4.14.1-6 +- build fixes for OCaml 4.12.0 + * Tue Feb 16 2021 Michael Young - 4.14.1-5 - Linux: display frontend "be-alloc" mode is unsupported (comment only) [XSA-363, CVE-2021-26934] (#1929549) From d5e266a6ac8d3ff885f334643d262df523b38772 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 18 Mar 2021 20:23:16 +0000 Subject: [PATCH 002/137] HVM soft-reset crashes toolstack [XSA-368, CVE-2021-28687] (#1940610) --- xen.spec | 7 ++- xsa368-4.14.patch | 112 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 xsa368-4.14.patch diff --git a/xen.spec b/xen.spec index 349cb3a..b76f357 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.14.1 -Release: 5%{?dist} +Release: 7%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -120,6 +120,7 @@ Patch48: xen.git-d8099d94dfaa3573bd86ebfc457cbc8f70a3ecda.patch Patch49: xen.git-8169f82049efb5b2044b33aa482ba3a136b7804d.patch Patch50: xsa363.patch Patch51: xsa364.patch +Patch52: xsa368-4.14.patch %if %build_qemutrad @@ -336,6 +337,7 @@ manage Xen virtual machines. %patch49 -p1 %patch50 -p1 %patch51 -p1 +%patch52 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -928,6 +930,9 @@ fi %endif %changelog +* Thu Mar 18 2021 Michael Young - 4.14.1-7 +- HVM soft-reset crashes toolstack [XSA-368, CVE-2021-28687] (#1940610) + * Tue Feb 16 2021 Michael Young - 4.14.1-5 - Linux: display frontend "be-alloc" mode is unsupported (comment only) [XSA-363, CVE-2021-26934] (#1929549) diff --git a/xsa368-4.14.patch b/xsa368-4.14.patch new file mode 100644 index 0000000..815c756 --- /dev/null +++ b/xsa368-4.14.patch @@ -0,0 +1,112 @@ +From b1d5e033df1858edd6fa328abd126522947440aa Mon Sep 17 00:00:00 2001 +From: Anthony PERARD +Date: Wed, 24 Feb 2021 18:39:20 +0000 +Subject: [PATCH] libxl: Fix domain soft reset state handling + +In do_domain_soft_reset(), a `libxl__domain_suspend_state' is used +without been properly initialised and disposed of. This lead do a +abort() in libxl due to the `dsps.qmp' state been used before been +initialised: + libxl__ev_qmp_send: Assertion `ev->state == qmp_state_disconnected || ev->state == qmp_state_connected' failed. + +Once initialised, `dsps' also needs to be disposed of as the `qmp' +state might still be in the `Connected' state in the callback for +libxl__domain_suspend_device_model(). So this patch adds +libxl__domain_suspend_dispose() which can be called from the two +places where we need to dispose of `dsps'. + +Reported-by: Olaf Hering +Signed-off-by: Anthony PERARD +Reviewed-by: Ian Jackson +Tested-by: Olaf Hering +--- + tools/libxl/libxl_create.c | 11 ++++++++--- + tools/libxl/libxl_dom_suspend.c | 15 +++++++++++---- + tools/libxl/libxl_internal.h | 2 ++ + 3 files changed, 21 insertions(+), 7 deletions(-) + +diff --git a/tools/libxl/libxl_create.c b/tools/libxl/libxl_create.c +index 2814818e34..83b0eb00bf 100644 +--- a/tools/libxl/libxl_create.c ++++ b/tools/libxl/libxl_create.c +@@ -2174,9 +2174,7 @@ static int do_domain_soft_reset(libxl_ctx *ctx, + state->console_tty = libxl__strdup(gc, console_tty); + + dss->ao = ao; +- dss->domid = dss->dsps.domid = domid; +- dss->dsps.dm_savefile = GCSPRINTF(LIBXL_DEVICE_MODEL_SAVE_FILE".%d", +- domid); ++ dss->domid = domid; + + rc = libxl__save_emulator_xenstore_data(dss, &srs->toolstack_buf, + &srs->toolstack_len); +@@ -2186,6 +2184,11 @@ static int do_domain_soft_reset(libxl_ctx *ctx, + } + + dss->dsps.ao = ao; ++ dss->dsps.domid = domid; ++ dss->dsps.live = false; ++ rc = libxl__domain_suspend_init(egc, &dss->dsps, d_config->b_info.type); ++ if (rc) ++ goto out; + dss->dsps.callback_device_model_done = soft_reset_dm_suspended; + libxl__domain_suspend_device_model(egc, &dss->dsps); /* must be last */ + +@@ -2204,6 +2207,8 @@ static void soft_reset_dm_suspended(libxl__egc *egc, + CONTAINER_OF(dsps, *srs, dss.dsps); + libxl__app_domain_create_state *cdcs = &srs->cdcs; + ++ libxl__domain_suspend_dispose(gc, dsps); ++ + /* + * Ask all backends to disconnect by removing the domain from + * xenstore. On the creation path the domain will be introduced to +diff --git a/tools/libxl/libxl_dom_suspend.c b/tools/libxl/libxl_dom_suspend.c +index 25d1571895..2a280f69a1 100644 +--- a/tools/libxl/libxl_dom_suspend.c ++++ b/tools/libxl/libxl_dom_suspend.c +@@ -67,6 +67,16 @@ out: + return rc; + } + ++void libxl__domain_suspend_dispose(libxl__gc *gc, ++ libxl__domain_suspend_state *dsps) ++{ ++ libxl__xswait_stop(gc, &dsps->pvcontrol); ++ libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn); ++ libxl__ev_xswatch_deregister(gc, &dsps->guest_watch); ++ libxl__ev_time_deregister(gc, &dsps->guest_timeout); ++ libxl__ev_qmp_dispose(gc, &dsps->qmp); ++} ++ + /*----- callbacks, called by xc_domain_save -----*/ + + void libxl__domain_suspend_device_model(libxl__egc *egc, +@@ -388,10 +398,7 @@ static void domain_suspend_common_done(libxl__egc *egc, + { + EGC_GC; + assert(!libxl__xswait_inuse(&dsps->pvcontrol)); +- libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn); +- libxl__ev_xswatch_deregister(gc, &dsps->guest_watch); +- libxl__ev_time_deregister(gc, &dsps->guest_timeout); +- libxl__ev_qmp_dispose(gc, &dsps->qmp); ++ libxl__domain_suspend_dispose(gc, dsps); + dsps->callback_common_done(egc, dsps, rc); + } + +diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h +index 94a23179d3..3bc3bbcf84 100644 +--- a/tools/libxl/libxl_internal.h ++++ b/tools/libxl/libxl_internal.h +@@ -3615,6 +3615,8 @@ struct libxl__domain_suspend_state { + int libxl__domain_suspend_init(libxl__egc *egc, + libxl__domain_suspend_state *dsps, + libxl_domain_type type); ++void libxl__domain_suspend_dispose(libxl__gc *gc, ++ libxl__domain_suspend_state *dsps); + + /* calls dsps->callback_device_model_done when done + * may synchronously calls this callback */ +-- +2.30.1 + From 520a08e99146ea6834d59b1dc46734313b67520d Mon Sep 17 00:00:00 2001 From: Michael Young Date: Fri, 19 Mar 2021 22:19:01 +0000 Subject: [PATCH 003/137] HVM soft-reset crashes toolstack [XSA-368, CVE-2021-28687] (#1940610) adjust efi test to stop build failing --- xen.efi.build.fix.patch | 11 ++++ xen.spec | 13 ++++- xsa368-4.14.patch | 112 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 xen.efi.build.fix.patch create mode 100644 xsa368-4.14.patch diff --git a/xen.efi.build.fix.patch b/xen.efi.build.fix.patch new file mode 100644 index 0000000..1ff1923 --- /dev/null +++ b/xen.efi.build.fix.patch @@ -0,0 +1,11 @@ +--- xen-4.14.1/xen/arch/x86/Makefile.orig 2020-12-17 16:47:25.000000000 +0000 ++++ xen-4.14.1/xen/arch/x86/Makefile 2021-03-19 19:48:03.688449791 +0000 +@@ -176,7 +176,7 @@ + # Check if the compiler supports the MS ABI. + export XEN_BUILD_EFI := $(shell $(CC) $(XEN_CFLAGS) -c efi/check.c -o efi/check.o 2>/dev/null && echo y) + # Check if the linker supports PE. +-XEN_BUILD_PE := $(if $(XEN_BUILD_EFI),$(shell $(LD) -mi386pep --subsystem=10 -o efi/check.efi efi/check.o 2>/dev/null && echo y)) ++XEN_BUILD_PE := $(if $(XEN_BUILD_EFI),$(shell $(LD) -mi386pep --subsystem=10 --strip-debug -o efi/check.efi efi/check.o 2>/dev/null && echo y)) + CFLAGS-$(XEN_BUILD_EFI) += -DXEN_BUILD_EFI + + $(TARGET).efi: VIRT_BASE = 0x$(shell $(NM) efi/relocs-dummy.o | sed -n 's, A VIRT_START$$,,p') diff --git a/xen.spec b/xen.spec index 8654a75..89d63c9 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.14.1 -Release: 6%{?dist} +Release: 8%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -120,8 +120,10 @@ Patch48: xen.git-d8099d94dfaa3573bd86ebfc457cbc8f70a3ecda.patch Patch49: xen.git-8169f82049efb5b2044b33aa482ba3a136b7804d.patch Patch50: xsa363.patch Patch51: xsa364.patch -Patch52: xen.ocaml.4.12.fixes.patch +Patch52: xsa368-4.14.patch +Patch100: xen.ocaml.4.12.fixes.patch +Patch101: xen.efi.build.fix.patch %if %build_qemutrad BuildRequires: libidn-devel zlib-devel SDL-devel curl-devel @@ -339,6 +341,9 @@ manage Xen virtual machines. %patch51 -p1 %patch52 -p1 +%patch100 -p1 +%patch101 -p1 + # qemu-xen-traditional patches pushd tools/qemu-xen-traditional %patch27 -p1 @@ -930,6 +935,10 @@ fi %endif %changelog +* Fri Mar 19 2021 Michael Young - 4.14.1-8 +- HVM soft-reset crashes toolstack [XSA-368, CVE-2021-28687] (#1940610) +- adjust efi test to stop build failing + * Tue Mar 02 2021 Michael Young - 4.14.1-6 - build fixes for OCaml 4.12.0 diff --git a/xsa368-4.14.patch b/xsa368-4.14.patch new file mode 100644 index 0000000..815c756 --- /dev/null +++ b/xsa368-4.14.patch @@ -0,0 +1,112 @@ +From b1d5e033df1858edd6fa328abd126522947440aa Mon Sep 17 00:00:00 2001 +From: Anthony PERARD +Date: Wed, 24 Feb 2021 18:39:20 +0000 +Subject: [PATCH] libxl: Fix domain soft reset state handling + +In do_domain_soft_reset(), a `libxl__domain_suspend_state' is used +without been properly initialised and disposed of. This lead do a +abort() in libxl due to the `dsps.qmp' state been used before been +initialised: + libxl__ev_qmp_send: Assertion `ev->state == qmp_state_disconnected || ev->state == qmp_state_connected' failed. + +Once initialised, `dsps' also needs to be disposed of as the `qmp' +state might still be in the `Connected' state in the callback for +libxl__domain_suspend_device_model(). So this patch adds +libxl__domain_suspend_dispose() which can be called from the two +places where we need to dispose of `dsps'. + +Reported-by: Olaf Hering +Signed-off-by: Anthony PERARD +Reviewed-by: Ian Jackson +Tested-by: Olaf Hering +--- + tools/libxl/libxl_create.c | 11 ++++++++--- + tools/libxl/libxl_dom_suspend.c | 15 +++++++++++---- + tools/libxl/libxl_internal.h | 2 ++ + 3 files changed, 21 insertions(+), 7 deletions(-) + +diff --git a/tools/libxl/libxl_create.c b/tools/libxl/libxl_create.c +index 2814818e34..83b0eb00bf 100644 +--- a/tools/libxl/libxl_create.c ++++ b/tools/libxl/libxl_create.c +@@ -2174,9 +2174,7 @@ static int do_domain_soft_reset(libxl_ctx *ctx, + state->console_tty = libxl__strdup(gc, console_tty); + + dss->ao = ao; +- dss->domid = dss->dsps.domid = domid; +- dss->dsps.dm_savefile = GCSPRINTF(LIBXL_DEVICE_MODEL_SAVE_FILE".%d", +- domid); ++ dss->domid = domid; + + rc = libxl__save_emulator_xenstore_data(dss, &srs->toolstack_buf, + &srs->toolstack_len); +@@ -2186,6 +2184,11 @@ static int do_domain_soft_reset(libxl_ctx *ctx, + } + + dss->dsps.ao = ao; ++ dss->dsps.domid = domid; ++ dss->dsps.live = false; ++ rc = libxl__domain_suspend_init(egc, &dss->dsps, d_config->b_info.type); ++ if (rc) ++ goto out; + dss->dsps.callback_device_model_done = soft_reset_dm_suspended; + libxl__domain_suspend_device_model(egc, &dss->dsps); /* must be last */ + +@@ -2204,6 +2207,8 @@ static void soft_reset_dm_suspended(libxl__egc *egc, + CONTAINER_OF(dsps, *srs, dss.dsps); + libxl__app_domain_create_state *cdcs = &srs->cdcs; + ++ libxl__domain_suspend_dispose(gc, dsps); ++ + /* + * Ask all backends to disconnect by removing the domain from + * xenstore. On the creation path the domain will be introduced to +diff --git a/tools/libxl/libxl_dom_suspend.c b/tools/libxl/libxl_dom_suspend.c +index 25d1571895..2a280f69a1 100644 +--- a/tools/libxl/libxl_dom_suspend.c ++++ b/tools/libxl/libxl_dom_suspend.c +@@ -67,6 +67,16 @@ out: + return rc; + } + ++void libxl__domain_suspend_dispose(libxl__gc *gc, ++ libxl__domain_suspend_state *dsps) ++{ ++ libxl__xswait_stop(gc, &dsps->pvcontrol); ++ libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn); ++ libxl__ev_xswatch_deregister(gc, &dsps->guest_watch); ++ libxl__ev_time_deregister(gc, &dsps->guest_timeout); ++ libxl__ev_qmp_dispose(gc, &dsps->qmp); ++} ++ + /*----- callbacks, called by xc_domain_save -----*/ + + void libxl__domain_suspend_device_model(libxl__egc *egc, +@@ -388,10 +398,7 @@ static void domain_suspend_common_done(libxl__egc *egc, + { + EGC_GC; + assert(!libxl__xswait_inuse(&dsps->pvcontrol)); +- libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn); +- libxl__ev_xswatch_deregister(gc, &dsps->guest_watch); +- libxl__ev_time_deregister(gc, &dsps->guest_timeout); +- libxl__ev_qmp_dispose(gc, &dsps->qmp); ++ libxl__domain_suspend_dispose(gc, dsps); + dsps->callback_common_done(egc, dsps, rc); + } + +diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h +index 94a23179d3..3bc3bbcf84 100644 +--- a/tools/libxl/libxl_internal.h ++++ b/tools/libxl/libxl_internal.h +@@ -3615,6 +3615,8 @@ struct libxl__domain_suspend_state { + int libxl__domain_suspend_init(libxl__egc *egc, + libxl__domain_suspend_state *dsps, + libxl_domain_type type); ++void libxl__domain_suspend_dispose(libxl__gc *gc, ++ libxl__domain_suspend_state *dsps); + + /* calls dsps->callback_device_model_done when done + * may synchronously calls this callback */ +-- +2.30.1 + From 5e6740fccc2ac34c2a27242dc00a7952298caa7c Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 4 May 2021 22:06:08 +0100 Subject: [PATCH 004/137] update to xen-4.14.2 remove or adjust patch content now included or superceded upstream re-copy grub modules if they have changed --- .gitignore | 2 +- sources | 2 +- xen.gcc11.fixes.patch | 12 ----- xen.spec | 28 +++++------ xsa360-4.14.patch | 97 ------------------------------------ xsa364.patch | 69 -------------------------- xsa368-4.14.patch | 112 ------------------------------------------ 7 files changed, 16 insertions(+), 306 deletions(-) delete mode 100644 xsa360-4.14.patch delete mode 100644 xsa364.patch delete mode 100644 xsa368-4.14.patch diff --git a/.gitignore b/.gitignore index 155f9cb..a217f2a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.14.1.tar.gz +/xen-4.14.2.tar.gz diff --git a/sources b/sources index e3f8b84..b24adf4 100644 --- a/sources +++ b/sources @@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.14.1.tar.gz) = c75cbec82793435f5a7026626ffdb2e9a2166b42d2be4b2f1194240e0312458124f0ebd53eeb02ce7330c22afe402a28a96b32f8af66e41e9416fe94535724c9 +SHA512 (xen-4.14.2.tar.gz) = 83c9333b70dbee3e29c6bf08e5ad030676e6c4a32b976f3f5e6a8f8d0dd9e4898bac88dd8e1c9d2ad3509cebb5d212e1745f9392a469d7afeb841d79801ccf39 diff --git a/xen.gcc11.fixes.patch b/xen.gcc11.fixes.patch index fd8fe15..a075a6f 100644 --- a/xen.gcc11.fixes.patch +++ b/xen.gcc11.fixes.patch @@ -1,15 +1,3 @@ ---- xen-4.14.0/xen/include/crypto/rijndael.h.orig 2020-07-23 16:07:51.000000000 +0100 -+++ xen-4.14.0/xen/include/crypto/rijndael.h 2020-10-24 14:59:34.349318594 +0100 -@@ -52,7 +52,7 @@ - - int rijndaelKeySetupEnc(unsigned int [], const unsigned char [], int); - int rijndaelKeySetupDec(unsigned int [], const unsigned char [], int); --void rijndaelEncrypt(const unsigned int [], int, const unsigned char [], -- unsigned char []); -+void rijndaelEncrypt(const unsigned int [], int, const unsigned char [16], -+ unsigned char [16]); - - #endif /* __RIJNDAEL_H */ --- xen-4.14.0/xen/include/crypto/vmac.h.orig 2020-07-23 16:07:51.000000000 +0100 +++ xen-4.14.0/xen/include/crypto/vmac.h 2020-10-24 15:45:49.246467465 +0100 @@ -142,7 +142,7 @@ diff --git a/xen.spec b/xen.spec index b76f357..c4773ac 100644 --- a/xen.spec +++ b/xen.spec @@ -57,8 +57,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.14.1 -Release: 7%{?dist} +Version: 4.14.2 +Release: 1%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -112,15 +112,12 @@ Patch40: xen.drop.brctl.patch Patch41: xen.gcc9.fixes.patch Patch42: xen.gcc10.fixes.patch Patch43: xen.gcc11.fixes.patch -Patch44: xsa360-4.14.patch Patch45: xen.git-7c9f81687ad611515474b1c17afc2f79f19faef5.patch Patch46: xen.git-35d2960ae65f28106fdc5c2130f5f08fadca0e4c.patch Patch47: xen.git-d6627cf1b63ce57a6a7e2c1800dbc50eed742c32.patch Patch48: xen.git-d8099d94dfaa3573bd86ebfc457cbc8f70a3ecda.patch Patch49: xen.git-8169f82049efb5b2044b33aa482ba3a136b7804d.patch Patch50: xsa363.patch -Patch51: xsa364.patch -Patch52: xsa368-4.14.patch %if %build_qemutrad @@ -329,15 +326,12 @@ manage Xen virtual machines. %patch41 -p1 %patch42 -p1 %patch43 -p1 -%patch44 -p1 %patch45 -p1 %patch46 -p1 %patch47 -p1 %patch48 -p1 %patch49 -p1 %patch50 -p1 -%patch51 -p1 -%patch52 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -614,12 +608,13 @@ do_it() { if [ ! -d $TARGET ]; then mkdir $TARGET fi - if [ -f $DIR/relocator.mod -a ! -f $TARGET/relocator.mod ]; then - cp -p $DIR/relocator.mod $TARGET/relocator.mod - fi - if [ -f $DIR/multiboot2.mod -a ! -f $TARGET/multiboot2.mod ]; then - cp -p $DIR/multiboot2.mod $TARGET/multiboot2.mod - fi + for m in relocator.mod multiboot2.mod; do + if [ -f $DIR/$m ]; then + if [ ! -f $TARGET/$m ] || ! cmp -s $DIR/$m $TARGET/$m; then + cp -p $DIR/$m $TARGET/$m + fi + fi + done fi } if [ $1 == 1 -a -f /sbin/grub2-mkconfig ]; then @@ -930,6 +925,11 @@ fi %endif %changelog +* Tue May 04 2021 Michael Young - 4.14.2-1 +- update to 4.14.2 + remove or adjust patch content now included or superceded upstream +- re-copy grub modules if they have changed + * Thu Mar 18 2021 Michael Young - 4.14.1-7 - HVM soft-reset crashes toolstack [XSA-368, CVE-2021-28687] (#1940610) diff --git a/xsa360-4.14.patch b/xsa360-4.14.patch deleted file mode 100644 index 1bc185b..0000000 --- a/xsa360-4.14.patch +++ /dev/null @@ -1,97 +0,0 @@ -From: Roger Pau Monne -Subject: x86/dpci: do not remove pirqs from domain tree on unbind - -A fix for a previous issue removed the pirqs from the domain tree when -they are unbound in order to prevent shared pirqs from triggering a -BUG_ON in __pirq_guest_unbind if they are unbound multiple times. That -caused free_domain_pirqs to no longer unmap the pirqs because they -are gone from the domain pirq tree, thus leaving stale unbound pirqs -after domain destruction if the domain had mapped dpci pirqs after -shutdown. - -Take a different approach to fix the original issue, instead of -removing the pirq from d->pirq_tree clear the flags of the dpci pirq -struct to signal that the pirq is now unbound. This prevents calling -pirq_guest_unbind multiple times for the same pirq without having to -remove it from the domain pirq tree. - -This is XSA-360. - -Fixes: 5b58dad089 ('x86/pass-through: avoid double IRQ unbind during domain cleanup') -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich - ---- a/xen/arch/x86/irq.c -+++ b/xen/arch/x86/irq.c -@@ -1331,7 +1331,7 @@ void (pirq_cleanup_check)(struct pirq *p - } - - if ( radix_tree_delete(&d->pirq_tree, pirq->pirq) != pirq ) -- BUG_ON(!d->is_dying); -+ BUG(); - } - - /* Flush all ready EOIs from the top of this CPU's pending-EOI stack. */ ---- a/xen/drivers/passthrough/pci.c -+++ b/xen/drivers/passthrough/pci.c -@@ -862,6 +862,10 @@ static int pci_clean_dpci_irq(struct dom - { - struct dev_intx_gsi_link *digl, *tmp; - -+ if ( !pirq_dpci->flags ) -+ /* Already processed. */ -+ return 0; -+ - pirq_guest_unbind(d, dpci_pirq(pirq_dpci)); - - if ( pt_irq_need_timer(pirq_dpci->flags) ) -@@ -872,15 +876,10 @@ static int pci_clean_dpci_irq(struct dom - list_del(&digl->list); - xfree(digl); - } -+ /* Note the pirq is now unbound. */ -+ pirq_dpci->flags = 0; - -- radix_tree_delete(&d->pirq_tree, dpci_pirq(pirq_dpci)->pirq); -- -- if ( !pt_pirq_softirq_active(pirq_dpci) ) -- return 0; -- -- domain_get_irq_dpci(d)->pending_pirq_dpci = pirq_dpci; -- -- return -ERESTART; -+ return pt_pirq_softirq_active(pirq_dpci) ? -ERESTART : 0; - } - - static int pci_clean_dpci_irqs(struct domain *d) -@@ -897,18 +896,8 @@ static int pci_clean_dpci_irqs(struct do - hvm_irq_dpci = domain_get_irq_dpci(d); - if ( hvm_irq_dpci != NULL ) - { -- int ret = 0; -- -- if ( hvm_irq_dpci->pending_pirq_dpci ) -- { -- if ( pt_pirq_softirq_active(hvm_irq_dpci->pending_pirq_dpci) ) -- ret = -ERESTART; -- else -- hvm_irq_dpci->pending_pirq_dpci = NULL; -- } -+ int ret = pt_pirq_iterate(d, pci_clean_dpci_irq, NULL); - -- if ( !ret ) -- ret = pt_pirq_iterate(d, pci_clean_dpci_irq, NULL); - if ( ret ) - { - spin_unlock(&d->event_lock); ---- a/xen/include/asm-x86/hvm/irq.h -+++ b/xen/include/asm-x86/hvm/irq.h -@@ -160,8 +160,6 @@ struct hvm_irq_dpci { - DECLARE_BITMAP(isairq_map, NR_ISAIRQS); - /* Record of mapped Links */ - uint8_t link_cnt[NR_LINK]; -- /* Clean up: Entry with a softirq invocation pending / in progress. */ -- struct hvm_pirq_dpci *pending_pirq_dpci; - }; - - /* Machine IRQ to guest device/intx mapping. */ diff --git a/xsa364.patch b/xsa364.patch deleted file mode 100644 index 2d4b057..0000000 --- a/xsa364.patch +++ /dev/null @@ -1,69 +0,0 @@ -From dadb5b4b21c904ce59024c686eb1c55be8f46c52 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Thu, 21 Jan 2021 10:16:08 +0000 -Subject: [PATCH] xen/page_alloc: Only flush the page to RAM once we know they - are scrubbed - -At the moment, each page are flushed to RAM just after the allocator -found some free pages. However, this is happening before check if the -page was scrubbed. - -As a consequence, on Arm, a guest may be able to access the old content -of the scrubbed pages if it has cache disabled (default at boot) and -the content didn't reach the Point of Coherency. - -The flush is now moved after we know the content of the page will not -change. This also has the benefit to reduce the amount of work happening -with the heap_lock held. - -This is XSA-364. - -Fixes: 307c3be3ccb2 ("mm: Don't scrub pages while holding heap lock in alloc_heap_pages()") -Signed-off-by: Julien Grall -Reviewed-by: Jan Beulich ---- - xen/common/page_alloc.c | 14 +++++++++----- - 1 file changed, 9 insertions(+), 5 deletions(-) - -diff --git a/xen/common/page_alloc.c b/xen/common/page_alloc.c -index 02ac1fa613e7..1744e6faa5c4 100644 ---- a/xen/common/page_alloc.c -+++ b/xen/common/page_alloc.c -@@ -924,6 +924,7 @@ static struct page_info *alloc_heap_pages( - bool need_tlbflush = false; - uint32_t tlbflush_timestamp = 0; - unsigned int dirty_cnt = 0; -+ mfn_t mfn; - - /* Make sure there are enough bits in memflags for nodeID. */ - BUILD_BUG_ON((_MEMF_bits - _MEMF_node) < (8 * sizeof(nodeid_t))); -@@ -1022,11 +1023,6 @@ static struct page_info *alloc_heap_pages( - pg[i].u.inuse.type_info = 0; - page_set_owner(&pg[i], NULL); - -- /* Ensure cache and RAM are consistent for platforms where the -- * guest can control its own visibility of/through the cache. -- */ -- flush_page_to_ram(mfn_x(page_to_mfn(&pg[i])), -- !(memflags & MEMF_no_icache_flush)); - } - - spin_unlock(&heap_lock); -@@ -1062,6 +1058,14 @@ static struct page_info *alloc_heap_pages( - if ( need_tlbflush ) - filtered_flush_tlb_mask(tlbflush_timestamp); - -+ /* -+ * Ensure cache and RAM are consistent for platforms where the guest -+ * can control its own visibility of/through the cache. -+ */ -+ mfn = page_to_mfn(pg); -+ for ( i = 0; i < (1U << order); i++ ) -+ flush_page_to_ram(mfn_x(mfn) + i, !(memflags & MEMF_no_icache_flush)); -+ - return pg; - } - --- -2.17.1 - diff --git a/xsa368-4.14.patch b/xsa368-4.14.patch deleted file mode 100644 index 815c756..0000000 --- a/xsa368-4.14.patch +++ /dev/null @@ -1,112 +0,0 @@ -From b1d5e033df1858edd6fa328abd126522947440aa Mon Sep 17 00:00:00 2001 -From: Anthony PERARD -Date: Wed, 24 Feb 2021 18:39:20 +0000 -Subject: [PATCH] libxl: Fix domain soft reset state handling - -In do_domain_soft_reset(), a `libxl__domain_suspend_state' is used -without been properly initialised and disposed of. This lead do a -abort() in libxl due to the `dsps.qmp' state been used before been -initialised: - libxl__ev_qmp_send: Assertion `ev->state == qmp_state_disconnected || ev->state == qmp_state_connected' failed. - -Once initialised, `dsps' also needs to be disposed of as the `qmp' -state might still be in the `Connected' state in the callback for -libxl__domain_suspend_device_model(). So this patch adds -libxl__domain_suspend_dispose() which can be called from the two -places where we need to dispose of `dsps'. - -Reported-by: Olaf Hering -Signed-off-by: Anthony PERARD -Reviewed-by: Ian Jackson -Tested-by: Olaf Hering ---- - tools/libxl/libxl_create.c | 11 ++++++++--- - tools/libxl/libxl_dom_suspend.c | 15 +++++++++++---- - tools/libxl/libxl_internal.h | 2 ++ - 3 files changed, 21 insertions(+), 7 deletions(-) - -diff --git a/tools/libxl/libxl_create.c b/tools/libxl/libxl_create.c -index 2814818e34..83b0eb00bf 100644 ---- a/tools/libxl/libxl_create.c -+++ b/tools/libxl/libxl_create.c -@@ -2174,9 +2174,7 @@ static int do_domain_soft_reset(libxl_ctx *ctx, - state->console_tty = libxl__strdup(gc, console_tty); - - dss->ao = ao; -- dss->domid = dss->dsps.domid = domid; -- dss->dsps.dm_savefile = GCSPRINTF(LIBXL_DEVICE_MODEL_SAVE_FILE".%d", -- domid); -+ dss->domid = domid; - - rc = libxl__save_emulator_xenstore_data(dss, &srs->toolstack_buf, - &srs->toolstack_len); -@@ -2186,6 +2184,11 @@ static int do_domain_soft_reset(libxl_ctx *ctx, - } - - dss->dsps.ao = ao; -+ dss->dsps.domid = domid; -+ dss->dsps.live = false; -+ rc = libxl__domain_suspend_init(egc, &dss->dsps, d_config->b_info.type); -+ if (rc) -+ goto out; - dss->dsps.callback_device_model_done = soft_reset_dm_suspended; - libxl__domain_suspend_device_model(egc, &dss->dsps); /* must be last */ - -@@ -2204,6 +2207,8 @@ static void soft_reset_dm_suspended(libxl__egc *egc, - CONTAINER_OF(dsps, *srs, dss.dsps); - libxl__app_domain_create_state *cdcs = &srs->cdcs; - -+ libxl__domain_suspend_dispose(gc, dsps); -+ - /* - * Ask all backends to disconnect by removing the domain from - * xenstore. On the creation path the domain will be introduced to -diff --git a/tools/libxl/libxl_dom_suspend.c b/tools/libxl/libxl_dom_suspend.c -index 25d1571895..2a280f69a1 100644 ---- a/tools/libxl/libxl_dom_suspend.c -+++ b/tools/libxl/libxl_dom_suspend.c -@@ -67,6 +67,16 @@ out: - return rc; - } - -+void libxl__domain_suspend_dispose(libxl__gc *gc, -+ libxl__domain_suspend_state *dsps) -+{ -+ libxl__xswait_stop(gc, &dsps->pvcontrol); -+ libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn); -+ libxl__ev_xswatch_deregister(gc, &dsps->guest_watch); -+ libxl__ev_time_deregister(gc, &dsps->guest_timeout); -+ libxl__ev_qmp_dispose(gc, &dsps->qmp); -+} -+ - /*----- callbacks, called by xc_domain_save -----*/ - - void libxl__domain_suspend_device_model(libxl__egc *egc, -@@ -388,10 +398,7 @@ static void domain_suspend_common_done(libxl__egc *egc, - { - EGC_GC; - assert(!libxl__xswait_inuse(&dsps->pvcontrol)); -- libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn); -- libxl__ev_xswatch_deregister(gc, &dsps->guest_watch); -- libxl__ev_time_deregister(gc, &dsps->guest_timeout); -- libxl__ev_qmp_dispose(gc, &dsps->qmp); -+ libxl__domain_suspend_dispose(gc, dsps); - dsps->callback_common_done(egc, dsps, rc); - } - -diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h -index 94a23179d3..3bc3bbcf84 100644 ---- a/tools/libxl/libxl_internal.h -+++ b/tools/libxl/libxl_internal.h -@@ -3615,6 +3615,8 @@ struct libxl__domain_suspend_state { - int libxl__domain_suspend_init(libxl__egc *egc, - libxl__domain_suspend_state *dsps, - libxl_domain_type type); -+void libxl__domain_suspend_dispose(libxl__gc *gc, -+ libxl__domain_suspend_state *dsps); - - /* calls dsps->callback_device_model_done when done - * may synchronously calls this callback */ --- -2.30.1 - From 0736a4dbf197a2ebfbee42fae66d0537fafaa271 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 5 May 2021 22:40:31 +0100 Subject: [PATCH 005/137] update to xen-4.15.0 adjust xen.ocaml.4.12.fixes.patch to work with earlier ocaml re-copy grub modules if they have changed --- .gitignore | 2 +- sources | 2 +- xen.canonicalize.patch | 24 +- xen.efi.build.fix.patch | 11 - xen.gcc11.fixes.patch | 12 - ...960ae65f28106fdc5c2130f5f08fadca0e4c.patch | 865 --- ...81687ad611515474b1c17afc2f79f19faef5.patch | 280 - ...f82049efb5b2044b33aa482ba3a136b7804d.patch | 821 --- ...7cf1b63ce57a6a7e2c1800dbc50eed742c32.patch | 6399 ----------------- ...9d94dfaa3573bd86ebfc457cbc8f70a3ecda.patch | 105 - xen.ocaml.4.12.fixes.patch | 63 +- xen.spec | 66 +- xsa360-4.14.patch | 97 - xsa363.patch | 22 - xsa364.patch | 69 - xsa368-4.14.patch | 112 - 16 files changed, 81 insertions(+), 8869 deletions(-) delete mode 100644 xen.efi.build.fix.patch delete mode 100644 xen.git-35d2960ae65f28106fdc5c2130f5f08fadca0e4c.patch delete mode 100644 xen.git-7c9f81687ad611515474b1c17afc2f79f19faef5.patch delete mode 100644 xen.git-8169f82049efb5b2044b33aa482ba3a136b7804d.patch delete mode 100644 xen.git-d6627cf1b63ce57a6a7e2c1800dbc50eed742c32.patch delete mode 100644 xen.git-d8099d94dfaa3573bd86ebfc457cbc8f70a3ecda.patch delete mode 100644 xsa360-4.14.patch delete mode 100644 xsa363.patch delete mode 100644 xsa364.patch delete mode 100644 xsa368-4.14.patch diff --git a/.gitignore b/.gitignore index 155f9cb..794101c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.14.1.tar.gz +/xen-4.15.0.tar.gz diff --git a/sources b/sources index e3f8b84..875f44f 100644 --- a/sources +++ b/sources @@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.14.1.tar.gz) = c75cbec82793435f5a7026626ffdb2e9a2166b42d2be4b2f1194240e0312458124f0ebd53eeb02ce7330c22afe402a28a96b32f8af66e41e9416fe94535724c9 +SHA512 (xen-4.15.0.tar.gz) = 93683b8a97387ca5f003c635a11d163e61c87dbdc9a03081f9155fe87b49f1dfa74ce243fcd5e04dc009353a36e2375b786f1ebde828b5951a094cd64197b4c7 diff --git a/xen.canonicalize.patch b/xen.canonicalize.patch index 500cc72..c8caca8 100644 --- a/xen.canonicalize.patch +++ b/xen.canonicalize.patch @@ -1,15 +1,15 @@ --- xen-4.9.0-rc1.2/tools/xenstore/xenstored_watch.c.orig 2017-04-12 16:18:57.000000000 +0100 +++ xen-4.9.0-rc1.2/tools/xenstore/xenstored_watch.c 2017-04-13 21:17:12.255231094 +0100 -@@ -166,7 +166,7 @@ - /* check if valid event */ +@@ -215,7 +215,7 @@ + goto inval; } else { - relative = !strstarts(vec[0], "/"); -- vec[0] = canonicalize(conn, in, vec[0]); -+ vec[0] = xenstore_canonicalize(conn, in, vec[0]); - if (!vec[0]) - return ENOMEM; - if (!is_valid_nodename(vec[0])) -@@ -219,7 +219,7 @@ + *relative = !strstarts(*path, "/"); +- *path = canonicalize(conn, ctx, *path); ++ *path = xenstore_canonicalize(conn, ctx, *path); + if (!*path) + return errno; + if (!is_valid_nodename(*path)) +@@ -305,7 +305,7 @@ if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) return EINVAL; @@ -29,15 +29,15 @@ { const char *prefix; -@@ -799,7 +799,7 @@ +@@ -874,7 +874,7 @@ if (!canonical_name) canonical_name = &tmp_name; - *canonical_name = canonicalize(conn, ctx, name); + *canonical_name = xenstore_canonicalize(conn, ctx, name); + if (!*canonical_name) + return NULL; return get_node(conn, ctx, *canonical_name, perm); - } - --- xen-4.8.0/tools/console/testsuite/console-dom0.c.orig 2016-12-05 12:03:27.000000000 +0000 +++ xen-4.8.0/tools/console/testsuite/console-dom0.c 2017-02-26 21:52:24.554678631 +0000 @@ -18,7 +18,7 @@ diff --git a/xen.efi.build.fix.patch b/xen.efi.build.fix.patch deleted file mode 100644 index 1ff1923..0000000 --- a/xen.efi.build.fix.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- xen-4.14.1/xen/arch/x86/Makefile.orig 2020-12-17 16:47:25.000000000 +0000 -+++ xen-4.14.1/xen/arch/x86/Makefile 2021-03-19 19:48:03.688449791 +0000 -@@ -176,7 +176,7 @@ - # Check if the compiler supports the MS ABI. - export XEN_BUILD_EFI := $(shell $(CC) $(XEN_CFLAGS) -c efi/check.c -o efi/check.o 2>/dev/null && echo y) - # Check if the linker supports PE. --XEN_BUILD_PE := $(if $(XEN_BUILD_EFI),$(shell $(LD) -mi386pep --subsystem=10 -o efi/check.efi efi/check.o 2>/dev/null && echo y)) -+XEN_BUILD_PE := $(if $(XEN_BUILD_EFI),$(shell $(LD) -mi386pep --subsystem=10 --strip-debug -o efi/check.efi efi/check.o 2>/dev/null && echo y)) - CFLAGS-$(XEN_BUILD_EFI) += -DXEN_BUILD_EFI - - $(TARGET).efi: VIRT_BASE = 0x$(shell $(NM) efi/relocs-dummy.o | sed -n 's, A VIRT_START$$,,p') diff --git a/xen.gcc11.fixes.patch b/xen.gcc11.fixes.patch index fd8fe15..a075a6f 100644 --- a/xen.gcc11.fixes.patch +++ b/xen.gcc11.fixes.patch @@ -1,15 +1,3 @@ ---- xen-4.14.0/xen/include/crypto/rijndael.h.orig 2020-07-23 16:07:51.000000000 +0100 -+++ xen-4.14.0/xen/include/crypto/rijndael.h 2020-10-24 14:59:34.349318594 +0100 -@@ -52,7 +52,7 @@ - - int rijndaelKeySetupEnc(unsigned int [], const unsigned char [], int); - int rijndaelKeySetupDec(unsigned int [], const unsigned char [], int); --void rijndaelEncrypt(const unsigned int [], int, const unsigned char [], -- unsigned char []); -+void rijndaelEncrypt(const unsigned int [], int, const unsigned char [16], -+ unsigned char [16]); - - #endif /* __RIJNDAEL_H */ --- xen-4.14.0/xen/include/crypto/vmac.h.orig 2020-07-23 16:07:51.000000000 +0100 +++ xen-4.14.0/xen/include/crypto/vmac.h 2020-10-24 15:45:49.246467465 +0100 @@ -142,7 +142,7 @@ diff --git a/xen.git-35d2960ae65f28106fdc5c2130f5f08fadca0e4c.patch b/xen.git-35d2960ae65f28106fdc5c2130f5f08fadca0e4c.patch deleted file mode 100644 index 181a9ba..0000000 --- a/xen.git-35d2960ae65f28106fdc5c2130f5f08fadca0e4c.patch +++ /dev/null @@ -1,865 +0,0 @@ -From 35d2960ae65f28106fdc5c2130f5f08fadca0e4c Mon Sep 17 00:00:00 2001 -From: Jan Beulich -Date: Mon, 18 Jan 2021 12:10:34 +0100 -Subject: [PATCH] lib: introduce xxhash - -Taken from Linux at commit d89775fc929c ("lib/: replace HTTP links with -HTTPS ones"), but split into separate 32-bit and 64-bit sources, since -the immediate consumer (zstd) will need only the latter. - -Note that the building of this code is restricted to x86 for now because -of the need to sort asm/unaligned.h for Arm. - -Signed-off-by: Jan Beulich -Acked-by: Andrew Cooper ---- - xen/include/xen/xxhash.h | 259 ++++++++++++++++++++++++++++++++++ - xen/lib/Makefile | 2 + - xen/lib/xxhash32.c | 259 ++++++++++++++++++++++++++++++++++ - xen/lib/xxhash64.c | 294 +++++++++++++++++++++++++++++++++++++++ - 4 files changed, 814 insertions(+) - create mode 100644 xen/include/xen/xxhash.h - create mode 100644 xen/lib/xxhash32.c - create mode 100644 xen/lib/xxhash64.c - -diff --git a/xen/include/xen/xxhash.h b/xen/include/xen/xxhash.h -new file mode 100644 -index 0000000000..6f2237cbcf ---- /dev/null -+++ b/xen/include/xen/xxhash.h -@@ -0,0 +1,259 @@ -+/* -+ * xxHash - Extremely Fast Hash algorithm -+ * Copyright (C) 2012-2016, Yann Collet. -+ * -+ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are -+ * met: -+ * -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above -+ * copyright notice, this list of conditions and the following disclaimer -+ * in the documentation and/or other materials provided with the -+ * distribution. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ * -+ * This program is free software; you can redistribute it and/or modify it under -+ * the terms of the GNU General Public License version 2 as published by the -+ * Free Software Foundation. This program is dual-licensed; you may select -+ * either version 2 of the GNU General Public License ("GPL") or BSD license -+ * ("BSD"). -+ * -+ * You can contact the author at: -+ * - xxHash homepage: https://cyan4973.github.io/xxHash/ -+ * - xxHash source repository: https://github.com/Cyan4973/xxHash -+ */ -+ -+/* -+ * Notice extracted from xxHash homepage: -+ * -+ * xxHash is an extremely fast Hash algorithm, running at RAM speed limits. -+ * It also successfully passes all tests from the SMHasher suite. -+ * -+ * Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 -+ * Duo @3GHz) -+ * -+ * Name Speed Q.Score Author -+ * xxHash 5.4 GB/s 10 -+ * CrapWow 3.2 GB/s 2 Andrew -+ * MumurHash 3a 2.7 GB/s 10 Austin Appleby -+ * SpookyHash 2.0 GB/s 10 Bob Jenkins -+ * SBox 1.4 GB/s 9 Bret Mulvey -+ * Lookup3 1.2 GB/s 9 Bob Jenkins -+ * SuperFastHash 1.2 GB/s 1 Paul Hsieh -+ * CityHash64 1.05 GB/s 10 Pike & Alakuijala -+ * FNV 0.55 GB/s 5 Fowler, Noll, Vo -+ * CRC32 0.43 GB/s 9 -+ * MD5-32 0.33 GB/s 10 Ronald L. Rivest -+ * SHA1-32 0.28 GB/s 10 -+ * -+ * Q.Score is a measure of quality of the hash function. -+ * It depends on successfully passing SMHasher test set. -+ * 10 is a perfect score. -+ * -+ * A 64-bits version, named xxh64 offers much better speed, -+ * but for 64-bits applications only. -+ * Name Speed on 64 bits Speed on 32 bits -+ * xxh64 13.8 GB/s 1.9 GB/s -+ * xxh32 6.8 GB/s 6.0 GB/s -+ */ -+ -+#ifndef __XENXXHASH_H__ -+#define __XENXXHASH_H__ -+ -+#include -+ -+/*-**************************** -+ * Simple Hash Functions -+ *****************************/ -+ -+/** -+ * xxh32() - calculate the 32-bit hash of the input with a given seed. -+ * -+ * @input: The data to hash. -+ * @length: The length of the data to hash. -+ * @seed: The seed can be used to alter the result predictably. -+ * -+ * Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s -+ * -+ * Return: The 32-bit hash of the data. -+ */ -+uint32_t xxh32(const void *input, size_t length, uint32_t seed); -+ -+/** -+ * xxh64() - calculate the 64-bit hash of the input with a given seed. -+ * -+ * @input: The data to hash. -+ * @length: The length of the data to hash. -+ * @seed: The seed can be used to alter the result predictably. -+ * -+ * This function runs 2x faster on 64-bit systems, but slower on 32-bit systems. -+ * -+ * Return: The 64-bit hash of the data. -+ */ -+uint64_t xxh64(const void *input, size_t length, uint64_t seed); -+ -+/** -+ * xxhash() - calculate wordsize hash of the input with a given seed -+ * @input: The data to hash. -+ * @length: The length of the data to hash. -+ * @seed: The seed can be used to alter the result predictably. -+ * -+ * If the hash does not need to be comparable between machines with -+ * different word sizes, this function will call whichever of xxh32() -+ * or xxh64() is faster. -+ * -+ * Return: wordsize hash of the data. -+ */ -+ -+static inline unsigned long xxhash(const void *input, size_t length, -+ uint64_t seed) -+{ -+#if BITS_PER_LONG == 64 -+ return xxh64(input, length, seed); -+#else -+ return xxh32(input, length, seed); -+#endif -+} -+ -+/*-**************************** -+ * Streaming Hash Functions -+ *****************************/ -+ -+/* -+ * These definitions are only meant to allow allocation of XXH state -+ * statically, on stack, or in a struct for example. -+ * Do not use members directly. -+ */ -+ -+/** -+ * struct xxh32_state - private xxh32 state, do not use members directly -+ */ -+struct xxh32_state { -+ uint32_t total_len_32; -+ uint32_t large_len; -+ uint32_t v1; -+ uint32_t v2; -+ uint32_t v3; -+ uint32_t v4; -+ uint32_t mem32[4]; -+ uint32_t memsize; -+}; -+ -+/** -+ * struct xxh32_state - private xxh64 state, do not use members directly -+ */ -+struct xxh64_state { -+ uint64_t total_len; -+ uint64_t v1; -+ uint64_t v2; -+ uint64_t v3; -+ uint64_t v4; -+ uint64_t mem64[4]; -+ uint32_t memsize; -+}; -+ -+/** -+ * xxh32_reset() - reset the xxh32 state to start a new hashing operation -+ * -+ * @state: The xxh32 state to reset. -+ * @seed: Initialize the hash state with this seed. -+ * -+ * Call this function on any xxh32_state to prepare for a new hashing operation. -+ */ -+void xxh32_reset(struct xxh32_state *state, uint32_t seed); -+ -+/** -+ * xxh32_update() - hash the data given and update the xxh32 state -+ * -+ * @state: The xxh32 state to update. -+ * @input: The data to hash. -+ * @length: The length of the data to hash. -+ * -+ * After calling xxh32_reset() call xxh32_update() as many times as necessary. -+ * -+ * Return: Zero on success, otherwise an error code. -+ */ -+int xxh32_update(struct xxh32_state *state, const void *input, size_t length); -+ -+/** -+ * xxh32_digest() - produce the current xxh32 hash -+ * -+ * @state: Produce the current xxh32 hash of this state. -+ * -+ * A hash value can be produced at any time. It is still possible to continue -+ * inserting input into the hash state after a call to xxh32_digest(), and -+ * generate new hashes later on, by calling xxh32_digest() again. -+ * -+ * Return: The xxh32 hash stored in the state. -+ */ -+uint32_t xxh32_digest(const struct xxh32_state *state); -+ -+/** -+ * xxh64_reset() - reset the xxh64 state to start a new hashing operation -+ * -+ * @state: The xxh64 state to reset. -+ * @seed: Initialize the hash state with this seed. -+ */ -+void xxh64_reset(struct xxh64_state *state, uint64_t seed); -+ -+/** -+ * xxh64_update() - hash the data given and update the xxh64 state -+ * @state: The xxh64 state to update. -+ * @input: The data to hash. -+ * @length: The length of the data to hash. -+ * -+ * After calling xxh64_reset() call xxh64_update() as many times as necessary. -+ * -+ * Return: Zero on success, otherwise an error code. -+ */ -+int xxh64_update(struct xxh64_state *state, const void *input, size_t length); -+ -+/** -+ * xxh64_digest() - produce the current xxh64 hash -+ * -+ * @state: Produce the current xxh64 hash of this state. -+ * -+ * A hash value can be produced at any time. It is still possible to continue -+ * inserting input into the hash state after a call to xxh64_digest(), and -+ * generate new hashes later on, by calling xxh64_digest() again. -+ * -+ * Return: The xxh64 hash stored in the state. -+ */ -+uint64_t xxh64_digest(const struct xxh64_state *state); -+ -+/*-************************** -+ * Utils -+ ***************************/ -+ -+/** -+ * xxh32_copy_state() - copy the source state into the destination state -+ * -+ * @src: The source xxh32 state. -+ * @dst: The destination xxh32 state. -+ */ -+void xxh32_copy_state(struct xxh32_state *dst, const struct xxh32_state *src); -+ -+/** -+ * xxh64_copy_state() - copy the source state into the destination state -+ * -+ * @src: The source xxh64 state. -+ * @dst: The destination xxh64 state. -+ */ -+void xxh64_copy_state(struct xxh64_state *dst, const struct xxh64_state *src); -+ -+#endif /* __XENXXHASH_H__ */ -diff --git a/xen/lib/Makefile b/xen/lib/Makefile -index 42cf7a1164..1c2227cbfc 100644 ---- a/xen/lib/Makefile -+++ b/xen/lib/Makefile -@@ -1,1 +1,3 @@ lib-y += list-sort.o - obj-$(CONFIG_X86) += x86/ -+obj-$(CONFIG_X86) += xxhash32.o -+obj-$(CONFIG_X86) += xxhash64.o -diff --git a/xen/lib/xxhash32.c b/xen/lib/xxhash32.c -new file mode 100644 -index 0000000000..e8d403e5ce ---- /dev/null -+++ b/xen/lib/xxhash32.c -@@ -0,0 +1,259 @@ -+/* -+ * xxHash - Extremely Fast Hash algorithm -+ * Copyright (C) 2012-2016, Yann Collet. -+ * -+ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are -+ * met: -+ * -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above -+ * copyright notice, this list of conditions and the following disclaimer -+ * in the documentation and/or other materials provided with the -+ * distribution. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ * -+ * This program is free software; you can redistribute it and/or modify it under -+ * the terms of the GNU General Public License version 2 as published by the -+ * Free Software Foundation. This program is dual-licensed; you may select -+ * either version 2 of the GNU General Public License ("GPL") or BSD license -+ * ("BSD"). -+ * -+ * You can contact the author at: -+ * - xxHash homepage: https://cyan4973.github.io/xxHash/ -+ * - xxHash source repository: https://github.com/Cyan4973/xxHash -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+ -+/*-************************************* -+ * Macros -+ **************************************/ -+#define xxh_rotl32(x, r) ((x << r) | (x >> (32 - r))) -+ -+#ifdef __LITTLE_ENDIAN -+# define XXH_CPU_LITTLE_ENDIAN 1 -+#else -+# define XXH_CPU_LITTLE_ENDIAN 0 -+#endif -+ -+/*-************************************* -+ * Constants -+ **************************************/ -+static const uint32_t PRIME32_1 = 2654435761U; -+static const uint32_t PRIME32_2 = 2246822519U; -+static const uint32_t PRIME32_3 = 3266489917U; -+static const uint32_t PRIME32_4 = 668265263U; -+static const uint32_t PRIME32_5 = 374761393U; -+ -+/*-************************** -+ * Utils -+ ***************************/ -+void xxh32_copy_state(struct xxh32_state *dst, const struct xxh32_state *src) -+{ -+ memcpy(dst, src, sizeof(*dst)); -+} -+ -+/*-*************************** -+ * Simple Hash Functions -+ ****************************/ -+static uint32_t xxh32_round(uint32_t seed, const uint32_t input) -+{ -+ seed += input * PRIME32_2; -+ seed = xxh_rotl32(seed, 13); -+ seed *= PRIME32_1; -+ return seed; -+} -+ -+uint32_t xxh32(const void *input, const size_t len, const uint32_t seed) -+{ -+ const uint8_t *p = (const uint8_t *)input; -+ const uint8_t *b_end = p + len; -+ uint32_t h32; -+ -+ if (len >= 16) { -+ const uint8_t *const limit = b_end - 16; -+ uint32_t v1 = seed + PRIME32_1 + PRIME32_2; -+ uint32_t v2 = seed + PRIME32_2; -+ uint32_t v3 = seed + 0; -+ uint32_t v4 = seed - PRIME32_1; -+ -+ do { -+ v1 = xxh32_round(v1, get_unaligned_le32(p)); -+ p += 4; -+ v2 = xxh32_round(v2, get_unaligned_le32(p)); -+ p += 4; -+ v3 = xxh32_round(v3, get_unaligned_le32(p)); -+ p += 4; -+ v4 = xxh32_round(v4, get_unaligned_le32(p)); -+ p += 4; -+ } while (p <= limit); -+ -+ h32 = xxh_rotl32(v1, 1) + xxh_rotl32(v2, 7) + -+ xxh_rotl32(v3, 12) + xxh_rotl32(v4, 18); -+ } else { -+ h32 = seed + PRIME32_5; -+ } -+ -+ h32 += (uint32_t)len; -+ -+ while (p + 4 <= b_end) { -+ h32 += get_unaligned_le32(p) * PRIME32_3; -+ h32 = xxh_rotl32(h32, 17) * PRIME32_4; -+ p += 4; -+ } -+ -+ while (p < b_end) { -+ h32 += (*p) * PRIME32_5; -+ h32 = xxh_rotl32(h32, 11) * PRIME32_1; -+ p++; -+ } -+ -+ h32 ^= h32 >> 15; -+ h32 *= PRIME32_2; -+ h32 ^= h32 >> 13; -+ h32 *= PRIME32_3; -+ h32 ^= h32 >> 16; -+ -+ return h32; -+} -+ -+/*-************************************************** -+ * Advanced Hash Functions -+ ***************************************************/ -+void xxh32_reset(struct xxh32_state *statePtr, const uint32_t seed) -+{ -+ /* use a local state for memcpy() to avoid strict-aliasing warnings */ -+ struct xxh32_state state; -+ -+ memset(&state, 0, sizeof(state)); -+ state.v1 = seed + PRIME32_1 + PRIME32_2; -+ state.v2 = seed + PRIME32_2; -+ state.v3 = seed + 0; -+ state.v4 = seed - PRIME32_1; -+ memcpy(statePtr, &state, sizeof(state)); -+} -+ -+int xxh32_update(struct xxh32_state *state, const void *input, const size_t len) -+{ -+ const uint8_t *p = (const uint8_t *)input; -+ const uint8_t *const b_end = p + len; -+ -+ if (input == NULL) -+ return -EINVAL; -+ -+ state->total_len_32 += (uint32_t)len; -+ state->large_len |= (len >= 16) | (state->total_len_32 >= 16); -+ -+ if (state->memsize + len < 16) { /* fill in tmp buffer */ -+ memcpy((uint8_t *)(state->mem32) + state->memsize, input, len); -+ state->memsize += (uint32_t)len; -+ return 0; -+ } -+ -+ if (state->memsize) { /* some data left from previous update */ -+ const uint32_t *p32 = state->mem32; -+ -+ memcpy((uint8_t *)(state->mem32) + state->memsize, input, -+ 16 - state->memsize); -+ -+ state->v1 = xxh32_round(state->v1, get_unaligned_le32(p32)); -+ p32++; -+ state->v2 = xxh32_round(state->v2, get_unaligned_le32(p32)); -+ p32++; -+ state->v3 = xxh32_round(state->v3, get_unaligned_le32(p32)); -+ p32++; -+ state->v4 = xxh32_round(state->v4, get_unaligned_le32(p32)); -+ p32++; -+ -+ p += 16-state->memsize; -+ state->memsize = 0; -+ } -+ -+ if (p <= b_end - 16) { -+ const uint8_t *const limit = b_end - 16; -+ uint32_t v1 = state->v1; -+ uint32_t v2 = state->v2; -+ uint32_t v3 = state->v3; -+ uint32_t v4 = state->v4; -+ -+ do { -+ v1 = xxh32_round(v1, get_unaligned_le32(p)); -+ p += 4; -+ v2 = xxh32_round(v2, get_unaligned_le32(p)); -+ p += 4; -+ v3 = xxh32_round(v3, get_unaligned_le32(p)); -+ p += 4; -+ v4 = xxh32_round(v4, get_unaligned_le32(p)); -+ p += 4; -+ } while (p <= limit); -+ -+ state->v1 = v1; -+ state->v2 = v2; -+ state->v3 = v3; -+ state->v4 = v4; -+ } -+ -+ if (p < b_end) { -+ memcpy(state->mem32, p, (size_t)(b_end-p)); -+ state->memsize = (uint32_t)(b_end-p); -+ } -+ -+ return 0; -+} -+ -+uint32_t xxh32_digest(const struct xxh32_state *state) -+{ -+ const uint8_t *p = (const uint8_t *)state->mem32; -+ const uint8_t *const b_end = (const uint8_t *)(state->mem32) + -+ state->memsize; -+ uint32_t h32; -+ -+ if (state->large_len) { -+ h32 = xxh_rotl32(state->v1, 1) + xxh_rotl32(state->v2, 7) + -+ xxh_rotl32(state->v3, 12) + xxh_rotl32(state->v4, 18); -+ } else { -+ h32 = state->v3 /* == seed */ + PRIME32_5; -+ } -+ -+ h32 += state->total_len_32; -+ -+ while (p + 4 <= b_end) { -+ h32 += get_unaligned_le32(p) * PRIME32_3; -+ h32 = xxh_rotl32(h32, 17) * PRIME32_4; -+ p += 4; -+ } -+ -+ while (p < b_end) { -+ h32 += (*p) * PRIME32_5; -+ h32 = xxh_rotl32(h32, 11) * PRIME32_1; -+ p++; -+ } -+ -+ h32 ^= h32 >> 15; -+ h32 *= PRIME32_2; -+ h32 ^= h32 >> 13; -+ h32 *= PRIME32_3; -+ h32 ^= h32 >> 16; -+ -+ return h32; -+} -+ -diff --git a/xen/lib/xxhash64.c b/xen/lib/xxhash64.c -new file mode 100644 -index 0000000000..ba6bcf152d ---- /dev/null -+++ b/xen/lib/xxhash64.c -@@ -0,0 +1,294 @@ -+/* -+ * xxHash - Extremely Fast Hash algorithm -+ * Copyright (C) 2012-2016, Yann Collet. -+ * -+ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are -+ * met: -+ * -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above -+ * copyright notice, this list of conditions and the following disclaimer -+ * in the documentation and/or other materials provided with the -+ * distribution. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ * -+ * This program is free software; you can redistribute it and/or modify it under -+ * the terms of the GNU General Public License version 2 as published by the -+ * Free Software Foundation. This program is dual-licensed; you may select -+ * either version 2 of the GNU General Public License ("GPL") or BSD license -+ * ("BSD"). -+ * -+ * You can contact the author at: -+ * - xxHash homepage: https://cyan4973.github.io/xxHash/ -+ * - xxHash source repository: https://github.com/Cyan4973/xxHash -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+ -+/*-************************************* -+ * Macros -+ **************************************/ -+#define xxh_rotl64(x, r) ((x << r) | (x >> (64 - r))) -+ -+#ifdef __LITTLE_ENDIAN -+# define XXH_CPU_LITTLE_ENDIAN 1 -+#else -+# define XXH_CPU_LITTLE_ENDIAN 0 -+#endif -+ -+/*-************************************* -+ * Constants -+ **************************************/ -+static const uint64_t PRIME64_1 = 11400714785074694791ULL; -+static const uint64_t PRIME64_2 = 14029467366897019727ULL; -+static const uint64_t PRIME64_3 = 1609587929392839161ULL; -+static const uint64_t PRIME64_4 = 9650029242287828579ULL; -+static const uint64_t PRIME64_5 = 2870177450012600261ULL; -+ -+/*-************************** -+ * Utils -+ ***************************/ -+void xxh64_copy_state(struct xxh64_state *dst, const struct xxh64_state *src) -+{ -+ memcpy(dst, src, sizeof(*dst)); -+} -+ -+/*-*************************** -+ * Simple Hash Functions -+ ****************************/ -+static uint64_t xxh64_round(uint64_t acc, const uint64_t input) -+{ -+ acc += input * PRIME64_2; -+ acc = xxh_rotl64(acc, 31); -+ acc *= PRIME64_1; -+ return acc; -+} -+ -+static uint64_t xxh64_merge_round(uint64_t acc, uint64_t val) -+{ -+ val = xxh64_round(0, val); -+ acc ^= val; -+ acc = acc * PRIME64_1 + PRIME64_4; -+ return acc; -+} -+ -+uint64_t xxh64(const void *input, const size_t len, const uint64_t seed) -+{ -+ const uint8_t *p = (const uint8_t *)input; -+ const uint8_t *const b_end = p + len; -+ uint64_t h64; -+ -+ if (len >= 32) { -+ const uint8_t *const limit = b_end - 32; -+ uint64_t v1 = seed + PRIME64_1 + PRIME64_2; -+ uint64_t v2 = seed + PRIME64_2; -+ uint64_t v3 = seed + 0; -+ uint64_t v4 = seed - PRIME64_1; -+ -+ do { -+ v1 = xxh64_round(v1, get_unaligned_le64(p)); -+ p += 8; -+ v2 = xxh64_round(v2, get_unaligned_le64(p)); -+ p += 8; -+ v3 = xxh64_round(v3, get_unaligned_le64(p)); -+ p += 8; -+ v4 = xxh64_round(v4, get_unaligned_le64(p)); -+ p += 8; -+ } while (p <= limit); -+ -+ h64 = xxh_rotl64(v1, 1) + xxh_rotl64(v2, 7) + -+ xxh_rotl64(v3, 12) + xxh_rotl64(v4, 18); -+ h64 = xxh64_merge_round(h64, v1); -+ h64 = xxh64_merge_round(h64, v2); -+ h64 = xxh64_merge_round(h64, v3); -+ h64 = xxh64_merge_round(h64, v4); -+ -+ } else { -+ h64 = seed + PRIME64_5; -+ } -+ -+ h64 += (uint64_t)len; -+ -+ while (p + 8 <= b_end) { -+ const uint64_t k1 = xxh64_round(0, get_unaligned_le64(p)); -+ -+ h64 ^= k1; -+ h64 = xxh_rotl64(h64, 27) * PRIME64_1 + PRIME64_4; -+ p += 8; -+ } -+ -+ if (p + 4 <= b_end) { -+ h64 ^= (uint64_t)(get_unaligned_le32(p)) * PRIME64_1; -+ h64 = xxh_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; -+ p += 4; -+ } -+ -+ while (p < b_end) { -+ h64 ^= (*p) * PRIME64_5; -+ h64 = xxh_rotl64(h64, 11) * PRIME64_1; -+ p++; -+ } -+ -+ h64 ^= h64 >> 33; -+ h64 *= PRIME64_2; -+ h64 ^= h64 >> 29; -+ h64 *= PRIME64_3; -+ h64 ^= h64 >> 32; -+ -+ return h64; -+} -+ -+/*-************************************************** -+ * Advanced Hash Functions -+ ***************************************************/ -+void xxh64_reset(struct xxh64_state *statePtr, const uint64_t seed) -+{ -+ /* use a local state for memcpy() to avoid strict-aliasing warnings */ -+ struct xxh64_state state; -+ -+ memset(&state, 0, sizeof(state)); -+ state.v1 = seed + PRIME64_1 + PRIME64_2; -+ state.v2 = seed + PRIME64_2; -+ state.v3 = seed + 0; -+ state.v4 = seed - PRIME64_1; -+ memcpy(statePtr, &state, sizeof(state)); -+} -+ -+int xxh64_update(struct xxh64_state *state, const void *input, const size_t len) -+{ -+ const uint8_t *p = (const uint8_t *)input; -+ const uint8_t *const b_end = p + len; -+ -+ if (input == NULL) -+ return -EINVAL; -+ -+ state->total_len += len; -+ -+ if (state->memsize + len < 32) { /* fill in tmp buffer */ -+ memcpy(((uint8_t *)state->mem64) + state->memsize, input, len); -+ state->memsize += (uint32_t)len; -+ return 0; -+ } -+ -+ if (state->memsize) { /* tmp buffer is full */ -+ uint64_t *p64 = state->mem64; -+ -+ memcpy(((uint8_t *)p64) + state->memsize, input, -+ 32 - state->memsize); -+ -+ state->v1 = xxh64_round(state->v1, get_unaligned_le64(p64)); -+ p64++; -+ state->v2 = xxh64_round(state->v2, get_unaligned_le64(p64)); -+ p64++; -+ state->v3 = xxh64_round(state->v3, get_unaligned_le64(p64)); -+ p64++; -+ state->v4 = xxh64_round(state->v4, get_unaligned_le64(p64)); -+ -+ p += 32 - state->memsize; -+ state->memsize = 0; -+ } -+ -+ if (p + 32 <= b_end) { -+ const uint8_t *const limit = b_end - 32; -+ uint64_t v1 = state->v1; -+ uint64_t v2 = state->v2; -+ uint64_t v3 = state->v3; -+ uint64_t v4 = state->v4; -+ -+ do { -+ v1 = xxh64_round(v1, get_unaligned_le64(p)); -+ p += 8; -+ v2 = xxh64_round(v2, get_unaligned_le64(p)); -+ p += 8; -+ v3 = xxh64_round(v3, get_unaligned_le64(p)); -+ p += 8; -+ v4 = xxh64_round(v4, get_unaligned_le64(p)); -+ p += 8; -+ } while (p <= limit); -+ -+ state->v1 = v1; -+ state->v2 = v2; -+ state->v3 = v3; -+ state->v4 = v4; -+ } -+ -+ if (p < b_end) { -+ memcpy(state->mem64, p, (size_t)(b_end-p)); -+ state->memsize = (uint32_t)(b_end - p); -+ } -+ -+ return 0; -+} -+ -+uint64_t xxh64_digest(const struct xxh64_state *state) -+{ -+ const uint8_t *p = (const uint8_t *)state->mem64; -+ const uint8_t *const b_end = (const uint8_t *)state->mem64 + -+ state->memsize; -+ uint64_t h64; -+ -+ if (state->total_len >= 32) { -+ const uint64_t v1 = state->v1; -+ const uint64_t v2 = state->v2; -+ const uint64_t v3 = state->v3; -+ const uint64_t v4 = state->v4; -+ -+ h64 = xxh_rotl64(v1, 1) + xxh_rotl64(v2, 7) + -+ xxh_rotl64(v3, 12) + xxh_rotl64(v4, 18); -+ h64 = xxh64_merge_round(h64, v1); -+ h64 = xxh64_merge_round(h64, v2); -+ h64 = xxh64_merge_round(h64, v3); -+ h64 = xxh64_merge_round(h64, v4); -+ } else { -+ h64 = state->v3 + PRIME64_5; -+ } -+ -+ h64 += (uint64_t)state->total_len; -+ -+ while (p + 8 <= b_end) { -+ const uint64_t k1 = xxh64_round(0, get_unaligned_le64(p)); -+ -+ h64 ^= k1; -+ h64 = xxh_rotl64(h64, 27) * PRIME64_1 + PRIME64_4; -+ p += 8; -+ } -+ -+ if (p + 4 <= b_end) { -+ h64 ^= (uint64_t)(get_unaligned_le32(p)) * PRIME64_1; -+ h64 = xxh_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; -+ p += 4; -+ } -+ -+ while (p < b_end) { -+ h64 ^= (*p) * PRIME64_5; -+ h64 = xxh_rotl64(h64, 11) * PRIME64_1; -+ p++; -+ } -+ -+ h64 ^= h64 >> 33; -+ h64 *= PRIME64_2; -+ h64 ^= h64 >> 29; -+ h64 *= PRIME64_3; -+ h64 ^= h64 >> 32; -+ -+ return h64; -+} --- -2.20.1 - diff --git a/xen.git-7c9f81687ad611515474b1c17afc2f79f19faef5.patch b/xen.git-7c9f81687ad611515474b1c17afc2f79f19faef5.patch deleted file mode 100644 index 42ee5bd..0000000 --- a/xen.git-7c9f81687ad611515474b1c17afc2f79f19faef5.patch +++ /dev/null @@ -1,280 +0,0 @@ -From 7c9f81687ad611515474b1c17afc2f79f19faef5 Mon Sep 17 00:00:00 2001 -From: Jan Beulich -Date: Mon, 18 Jan 2021 12:09:13 +0100 -Subject: [PATCH] introduce unaligned.h - -Rather than open-coding commonly used constructs in yet more places when -pulling in zstd decompression support (and its xxhash prereq), pull out -the custom bits into a commonly used header (for the hypervisor build; -the tool stack and stubdom builds of libxenguest will still remain in -need of similarly taking care of). For now this is limited to x86, where -custom logic isn't needed (considering this is going to be used in init -code only, even using alternatives patching to use MOVBE doesn't seem -worthwhile). - -For Arm64 with CONFIG_ACPI=y (due to efi-dom0.c's re-use of xz/crc32.c) -drop the not really necessary inclusion of xz's private.h. - -No change in generated code. - -Signed-off-by: Jan Beulich -Acked-by: Andrew Cooper ---- - xen/common/lz4/defs.h | 9 ++-- - xen/common/lzo.c | 7 ++- - xen/common/unlzo.c | 19 ++------ - xen/common/xz/crc32.c | 2 - - xen/common/xz/private.h | 23 +++------- - xen/include/asm-x86/unaligned.h | 6 +++ - xen/include/xen/unaligned.h | 79 +++++++++++++++++++++++++++++++++ - 7 files changed, 104 insertions(+), 41 deletions(-) - create mode 100644 xen/include/asm-x86/unaligned.h - create mode 100644 xen/include/xen/unaligned.h - -diff --git a/xen/common/lz4/defs.h b/xen/common/lz4/defs.h -index d886a4e122..4fbea2ac3d 100644 ---- a/xen/common/lz4/defs.h -+++ b/xen/common/lz4/defs.h -@@ -10,18 +10,21 @@ - - #ifdef __XEN__ - #include --#endif -+#include -+#else - --static inline u16 INIT get_unaligned_le16(const void *p) -+static inline u16 get_unaligned_le16(const void *p) - { - return le16_to_cpup(p); - } - --static inline u32 INIT get_unaligned_le32(const void *p) -+static inline u32 get_unaligned_le32(const void *p) - { - return le32_to_cpup(p); - } - -+#endif -+ - /* - * Detects 64 bits mode - */ -diff --git a/xen/common/lzo.c b/xen/common/lzo.c -index f4c0ad8530..e4841794f4 100644 ---- a/xen/common/lzo.c -+++ b/xen/common/lzo.c -@@ -97,13 +97,12 @@ - #ifdef __XEN__ - #include - #include -+#include -+#else -+#define get_unaligned_le16(_p) (*(u16 *)(_p)) - #endif - - #include --#define get_unaligned(_p) (*(_p)) --#define put_unaligned(_val,_p) (*(_p)=_val) --#define get_unaligned_le16(_p) (*(u16 *)(_p)) --#define get_unaligned_le32(_p) (*(u32 *)(_p)) - - #include "decompress.h" - -diff --git a/xen/common/unlzo.c b/xen/common/unlzo.c -index 5ae6cf911e..11f64fcf3b 100644 ---- a/xen/common/unlzo.c -+++ b/xen/common/unlzo.c -@@ -34,30 +34,19 @@ - - #ifdef __XEN__ - #include --#endif -+#include -+#else - --#if 1 /* ndef CONFIG_??? */ --static inline u16 INIT get_unaligned_be16(void *p) -+static inline u16 get_unaligned_be16(const void *p) - { - return be16_to_cpup(p); - } - --static inline u32 INIT get_unaligned_be32(void *p) -+static inline u32 get_unaligned_be32(const void *p) - { - return be32_to_cpup(p); - } --#else --#include -- --static inline u16 INIT get_unaligned_be16(void *p) --{ -- return be16_to_cpu(__get_unaligned(p, 2)); --} - --static inline u32 INIT get_unaligned_be32(void *p) --{ -- return be32_to_cpu(__get_unaligned(p, 4)); --} - #endif - - static const unsigned char lzop_magic[] = { -diff --git a/xen/common/xz/crc32.c b/xen/common/xz/crc32.c -index af08ae2cf6..0708b61638 100644 ---- a/xen/common/xz/crc32.c -+++ b/xen/common/xz/crc32.c -@@ -15,8 +15,6 @@ - * but they are bigger and use more memory for the lookup table. - */ - --#include "private.h" -- - XZ_EXTERN uint32_t INITDATA xz_crc32_table[256]; - - XZ_EXTERN void INIT xz_crc32_init(void) -diff --git a/xen/common/xz/private.h b/xen/common/xz/private.h -index 7ea2489229..511343fcc2 100644 ---- a/xen/common/xz/private.h -+++ b/xen/common/xz/private.h -@@ -13,34 +13,23 @@ - #ifdef __XEN__ - #include - #include --#endif -- --#define get_le32(p) le32_to_cpup((const uint32_t *)(p)) -+#include -+#else - --#if 1 /* ndef CONFIG_??? */ --static inline u32 INIT get_unaligned_le32(void *p) -+static inline u32 get_unaligned_le32(const void *p) - { - return le32_to_cpup(p); - } - --static inline void INIT put_unaligned_le32(u32 val, void *p) -+static inline void put_unaligned_le32(u32 val, void *p) - { - *(__force __le32*)p = cpu_to_le32(val); - } --#else --#include -- --static inline u32 INIT get_unaligned_le32(void *p) --{ -- return le32_to_cpu(__get_unaligned(p, 4)); --} - --static inline void INIT put_unaligned_le32(u32 val, void *p) --{ -- __put_unaligned(cpu_to_le32(val), p, 4); --} - #endif - -+#define get_le32(p) le32_to_cpup((const uint32_t *)(p)) -+ - #define false 0 - #define true 1 - -diff --git a/xen/include/asm-x86/unaligned.h b/xen/include/asm-x86/unaligned.h -new file mode 100644 -index 0000000000..6070801d4a ---- /dev/null -+++ b/xen/include/asm-x86/unaligned.h -@@ -0,0 +1,6 @@ -+#ifndef __ASM_UNALIGNED_H__ -+#define __ASM_UNALIGNED_H__ -+ -+#include -+ -+#endif /* __ASM_UNALIGNED_H__ */ -diff --git a/xen/include/xen/unaligned.h b/xen/include/xen/unaligned.h -new file mode 100644 -index 0000000000..eef7ec73b6 ---- /dev/null -+++ b/xen/include/xen/unaligned.h -@@ -0,0 +1,79 @@ -+/* -+ * This header can be used by architectures where unaligned accesses work -+ * without faulting, and at least reasonably efficiently. Other architectures -+ * will need to have a custom asm/unaligned.h. -+ */ -+#ifndef __ASM_UNALIGNED_H__ -+#error "xen/unaligned.h should not be included directly - include asm/unaligned.h instead" -+#endif -+ -+#ifndef __XEN_UNALIGNED_H__ -+#define __XEN_UNALIGNED_H__ -+ -+#include -+#include -+ -+#define get_unaligned(p) (*(p)) -+#define put_unaligned(val, p) (*(p) = (val)) -+ -+static inline uint16_t get_unaligned_be16(const void *p) -+{ -+ return be16_to_cpup(p); -+} -+ -+static inline void put_unaligned_be16(uint16_t val, void *p) -+{ -+ *(__force __be16*)p = cpu_to_be16(val); -+} -+ -+static inline uint32_t get_unaligned_be32(const void *p) -+{ -+ return be32_to_cpup(p); -+} -+ -+static inline void put_unaligned_be32(uint32_t val, void *p) -+{ -+ *(__force __be32*)p = cpu_to_be32(val); -+} -+ -+static inline uint64_t get_unaligned_be64(const void *p) -+{ -+ return be64_to_cpup(p); -+} -+ -+static inline void put_unaligned_be64(uint64_t val, void *p) -+{ -+ *(__force __be64*)p = cpu_to_be64(val); -+} -+ -+static inline uint16_t get_unaligned_le16(const void *p) -+{ -+ return le16_to_cpup(p); -+} -+ -+static inline void put_unaligned_le16(uint16_t val, void *p) -+{ -+ *(__force __le16*)p = cpu_to_le16(val); -+} -+ -+static inline uint32_t get_unaligned_le32(const void *p) -+{ -+ return le32_to_cpup(p); -+} -+ -+static inline void put_unaligned_le32(uint32_t val, void *p) -+{ -+ *(__force __le32*)p = cpu_to_le32(val); -+} -+ -+static inline uint64_t get_unaligned_le64(const void *p) -+{ -+ return le64_to_cpup(p); -+} -+ -+static inline void put_unaligned_le64(uint64_t val, void *p) -+{ -+ *(__force __le64*)p = cpu_to_le64(val); -+} -+ -+#endif /* __XEN_UNALIGNED_H__ */ --- -2.20.1 - diff --git a/xen.git-8169f82049efb5b2044b33aa482ba3a136b7804d.patch b/xen.git-8169f82049efb5b2044b33aa482ba3a136b7804d.patch deleted file mode 100644 index 4d4f5b3..0000000 --- a/xen.git-8169f82049efb5b2044b33aa482ba3a136b7804d.patch +++ /dev/null @@ -1,821 +0,0 @@ -From 8169f82049efb5b2044b33aa482ba3a136b7804d Mon Sep 17 00:00:00 2001 -From: Jan Beulich -Date: Tue, 26 Jan 2021 14:16:34 +0100 -Subject: [PATCH] libxenguest: support zstd compressed kernels - -This follows the logic used for other decompression methods utilizing an -external library, albeit here we can't ignore the 32-bit size field -appended to the compressed image - its presence causes decompression to -fail. Leverage the field instead to allocate the output buffer in one -go, i.e. without incrementally realloc()ing. - -As far as configure.ac goes, I'm pretty sure there is a better (more -"standard") way of using PKG_CHECK_MODULES(). The construct also gets -put next to the other decompression library checks, albeit I think they -all ought to be x86-specific (e.g. placed in the existing case block a -few lines down). - -Note that, where possible, instead of #ifdef-ing xen/*.h inclusions, -they get removed. - -Signed-off-by: Jan Beulich -Acked-by: Wei Liu -Reviewed-by: Ian Jackson -Release-Acked-by: Ian Jackson ---- - README | 2 + - tools/configure | 79 ++++++++++++++++ - tools/configure.ac | 2 + - tools/libs/guest/Makefile | 1 + - tools/libs/guest/xg_dom_bzimageloader.c | 90 +++++++++++++++++++ - tools/libs/guest/xg_dom_decompress_unsafe.h | 2 + - .../guest/xg_dom_decompress_unsafe_zstd.c | 45 ++++++++++ - xen/common/zstd/decompress.c | 67 +++++++++----- - xen/common/zstd/error_private.h | 5 -- - xen/common/zstd/fse.h | 5 -- - xen/common/zstd/fse_decompress.c | 2 - - xen/common/zstd/huf.h | 3 - - xen/common/zstd/huf_decompress.c | 2 - - xen/common/zstd/mem.h | 2 + - xen/common/zstd/zstd_internal.h | 4 + - xen/include/xen/unaligned.h | 2 + - xen/lib/xxhash64.c | 2 + - 17 files changed, 276 insertions(+), 39 deletions(-) - create mode 100644 tools/libs/guest/xg_dom_decompress_unsafe_zstd.c - -diff --git a/README b/README -index 0e4787c1a6..33cdf6b826 100644 ---- a/README -+++ b/README -@@ -84,6 +84,8 @@ disabled at compile time: - * 16-bit x86 assembler, loader and compiler for qemu-traditional / rombios - (dev86 rpm or bin86 & bcc debs) - * Development install of liblzma for rombios -+ * Development install of libbz2, liblzma, liblzo2, and libzstd for DomU -+ kernel decompression. - - Second, you need to acquire a suitable kernel for use in domain 0. If - possible you should use a kernel provided by your OS distributor. If -diff --git a/tools/configure b/tools/configure -index 131112c41e..42c0d05bc1 100755 ---- a/tools/configure -+++ b/tools/configure -@@ -643,6 +643,8 @@ PTHREAD_CFLAGS - EXTFS_LIBS - system_aio - zlib -+libzstd_LIBS -+libzstd_CFLAGS - FETCHER - FTP - FALSE -@@ -857,6 +859,8 @@ glib_CFLAGS - glib_LIBS - pixman_CFLAGS - pixman_LIBS -+libzstd_CFLAGS -+libzstd_LIBS - LIBNL3_CFLAGS - LIBNL3_LIBS - SYSTEMD_CFLAGS -@@ -1605,6 +1609,10 @@ Some influential environment variables: - pixman_CFLAGS - C compiler flags for pixman, overriding pkg-config - pixman_LIBS linker flags for pixman, overriding pkg-config -+ libzstd_CFLAGS -+ C compiler flags for libzstd, overriding pkg-config -+ libzstd_LIBS -+ linker flags for libzstd, overriding pkg-config - LIBNL3_CFLAGS - C compiler flags for LIBNL3, overriding pkg-config - LIBNL3_LIBS linker flags for LIBNL3, overriding pkg-config -@@ -8744,6 +8752,77 @@ fi - - - -+pkg_failed=no -+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libzstd" >&5 -+$as_echo_n "checking for libzstd... " >&6; } -+ -+if test -n "$libzstd_CFLAGS"; then -+ pkg_cv_libzstd_CFLAGS="$libzstd_CFLAGS" -+ elif test -n "$PKG_CONFIG"; then -+ if test -n "$PKG_CONFIG" && \ -+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libzstd\""; } >&5 -+ ($PKG_CONFIG --exists --print-errors "libzstd") 2>&5 -+ ac_status=$? -+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 -+ test $ac_status = 0; }; then -+ pkg_cv_libzstd_CFLAGS=`$PKG_CONFIG --cflags "libzstd" 2>/dev/null` -+ test "x$?" != "x0" && pkg_failed=yes -+else -+ pkg_failed=yes -+fi -+ else -+ pkg_failed=untried -+fi -+if test -n "$libzstd_LIBS"; then -+ pkg_cv_libzstd_LIBS="$libzstd_LIBS" -+ elif test -n "$PKG_CONFIG"; then -+ if test -n "$PKG_CONFIG" && \ -+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libzstd\""; } >&5 -+ ($PKG_CONFIG --exists --print-errors "libzstd") 2>&5 -+ ac_status=$? -+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 -+ test $ac_status = 0; }; then -+ pkg_cv_libzstd_LIBS=`$PKG_CONFIG --libs "libzstd" 2>/dev/null` -+ test "x$?" != "x0" && pkg_failed=yes -+else -+ pkg_failed=yes -+fi -+ else -+ pkg_failed=untried -+fi -+ -+ -+ -+if test $pkg_failed = yes; then -+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -+$as_echo "no" >&6; } -+ -+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then -+ _pkg_short_errors_supported=yes -+else -+ _pkg_short_errors_supported=no -+fi -+ if test $_pkg_short_errors_supported = yes; then -+ libzstd_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libzstd" 2>&1` -+ else -+ libzstd_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libzstd" 2>&1` -+ fi -+ # Put the nasty error message in config.log where it belongs -+ echo "$libzstd_PKG_ERRORS" >&5 -+ -+ true -+elif test $pkg_failed = untried; then -+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -+$as_echo "no" >&6; } -+ true -+else -+ libzstd_CFLAGS=$pkg_cv_libzstd_CFLAGS -+ libzstd_LIBS=$pkg_cv_libzstd_LIBS -+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -+$as_echo "yes" >&6; } -+ zlib="$zlib -DHAVE_ZSTD $libzstd_CFLAGS $libzstd_LIBS" -+fi -+ - - - ac_fn_c_check_header_mongrel "$LINENO" "ext2fs/ext2fs.h" "ac_cv_header_ext2fs_ext2fs_h" "$ac_includes_default" -diff --git a/tools/configure.ac b/tools/configure.ac -index ee8ba5ff24..5b328700e0 100644 ---- a/tools/configure.ac -+++ b/tools/configure.ac -@@ -414,6 +414,8 @@ AC_CHECK_LIB([lzma], [lzma_stream_decoder], [zlib="$zlib -DHAVE_LZMA -llzma"]) - AC_CHECK_HEADER([lzo/lzo1x.h], [ - AC_CHECK_LIB([lzo2], [lzo1x_decompress], [zlib="$zlib -DHAVE_LZO1X -llzo2"]) - ]) -+PKG_CHECK_MODULES([libzstd], [libzstd], -+ [zlib="$zlib -DHAVE_ZSTD $libzstd_CFLAGS $libzstd_LIBS"], [true]) - AC_SUBST(zlib) - AC_SUBST(system_aio) - AX_CHECK_EXTFS -diff --git a/tools/libs/guest/Makefile b/tools/libs/guest/Makefile -index 1c729040b3..58c50250df 100644 ---- a/tools/libxc/Makefile -+++ b/tools/libxc/Makefile -@@ -64,6 +64,7 @@ SRCS-y += xg_dom_decompress_unsafe_bzip2.c - GUEST_SRCS-y += xc_dom_decompress_unsafe_lzma.c - GUEST_SRCS-y += xc_dom_decompress_unsafe_lzo1x.c - GUEST_SRCS-y += xc_dom_decompress_unsafe_xz.c -+GUEST_SRCS-y += xc_dom_decompress_unsafe_zstd.c - endif - - -include $(XEN_TARGET_ARCH)/Makefile -diff --git a/tools/libs/guest/xg_dom_bzimageloader.c b/tools/libs/guest/xg_dom_bzimageloader.c -index f959a77602..668881458c 100644 ---- a/tools/libxc/xc_dom_bzimageloader.c -+++ b/tools/libxc/xc_dom_bzimageloader.c -@@ -589,6 +589,85 @@ static int xc_try_lzo1x_decode( - - #endif - -+#if defined(HAVE_ZSTD) -+ -+#include -+ -+static int xc_try_zstd_decode( -+ struct xc_dom_image *dom, void **blob, size_t *size) -+{ -+ size_t outsize, insize, actual; -+ unsigned char *outbuf; -+ -+ /* Magic, descriptor byte, and trailing size field. */ -+ if ( *size <= 9 ) -+ { -+ DOMPRINTF("ZSTD: insufficient input data"); -+ return -1; -+ } -+ -+ insize = *size - 4; -+ outsize = get_unaligned_le32(*blob + insize); -+ -+ if ( xc_dom_kernel_check_size(dom, outsize) ) -+ { -+ DOMPRINTF("ZSTD: output too large"); -+ return -1; -+ } -+ -+ outbuf = malloc(outsize); -+ if ( !outbuf ) -+ { -+ DOMPRINTF("ZSTD: failed to alloc memory"); -+ return -1; -+ } -+ -+ actual = ZSTD_decompress(outbuf, outsize, *blob, insize); -+ -+ if ( ZSTD_isError(actual) ) -+ { -+ DOMPRINTF("ZSTD: error: %s", ZSTD_getErrorName(actual)); -+ free(outbuf); -+ return -1; -+ } -+ -+ if ( actual != outsize ) -+ { -+ DOMPRINTF("ZSTD: got 0x%zx bytes instead of 0x%zx", -+ actual, outsize); -+ free(outbuf); -+ return -1; -+ } -+ -+ if ( xc_dom_register_external(dom, outbuf, outsize) ) -+ { -+ DOMPRINTF("ZSTD: error registering stream output"); -+ free(outbuf); -+ return -1; -+ } -+ -+ DOMPRINTF("%s: ZSTD decompress OK, 0x%zx -> 0x%zx", -+ __FUNCTION__, insize, outsize); -+ -+ *blob = outbuf; -+ *size = outsize; -+ -+ return 0; -+} -+ -+#else /* !defined(HAVE_ZSTD) */ -+ -+static int xc_try_zstd_decode( -+ struct xc_dom_image *dom, void **blob, size_t *size) -+{ -+ xc_dom_panic(dom->xch, XC_INTERNAL_ERROR, -+ "%s: ZSTD decompress support unavailable\n", -+ __FUNCTION__); -+ return -1; -+} -+ -+#endif -+ - #else /* __MINIOS__ */ - - int xc_try_bzip2_decode(struct xc_dom_image *dom, void **blob, size_t *size); -@@ -736,6 +815,17 @@ static int xc_dom_probe_bzimage_kernel(struct xc_dom_image *dom) - return -EINVAL; - } - } -+ else if ( check_magic(dom, "\x28\xb5\x2f\xfd", 4) ) -+ { -+ ret = xc_try_zstd_decode(dom, &dom->kernel_blob, &dom->kernel_size); -+ if ( ret < 0 ) -+ { -+ xc_dom_panic(dom->xch, XC_INVALID_KERNEL, -+ "%s unable to ZSTD decompress kernel", -+ __FUNCTION__); -+ return -EINVAL; -+ } -+ } - else if ( check_magic(dom, "\135\000", 2) ) - { - ret = xc_try_lzma_decode(dom, &dom->kernel_blob, &dom->kernel_size); -diff --git a/tools/libs/guest/xg_dom_decompress_unsafe.h b/tools/libs/guest/xg_dom_decompress_unsafe.h -index 5dc18c896e..969846cb32 100644 ---- a/tools/libxc/xc_dom_decompress_unsafe.h -+++ b/tools/libxc/xc_dom_decompress_unsafe.h -@@ -16,3 +16,5 @@ int xc_try_lzo1x_decode(struct xc_dom_image *dom, void **blob, size_t *size) - __attribute__((visibility("internal"))); - int xc_try_xz_decode(struct xc_dom_image *dom, void **blob, size_t *size) - __attribute__((visibility("internal"))); -+int xc_try_zstd_decode(struct xc_dom_image *dom, void **blob, size_t *size) -+ __attribute__((visibility("internal"))); -diff --git a/tools/libs/guest/xg_dom_decompress_unsafe_zstd.c b/tools/libs/guest/xg_dom_decompress_unsafe_zstd.c -new file mode 100644 -index 0000000000..52558d2ffc ---- /dev/null -+++ b/tools/libxc/xc_dom_decompress_unsafe_zstd.c -@@ -0,0 +1,45 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "xg_private.h" -+#include "xc_dom_decompress_unsafe.h" -+ -+typedef uint8_t u8; -+ -+typedef uint16_t __u16; -+typedef uint32_t __u32; -+typedef uint64_t __u64; -+ -+typedef uint16_t __le16; -+typedef uint32_t __le32; -+typedef uint64_t __le64; -+ -+typedef uint16_t __be16; -+typedef uint32_t __be32; -+typedef uint64_t __be64; -+ -+#define __attribute_const__ -+#define __force -+#define always_inline -+#define noinline -+ -+#undef ERROR -+ -+#define __BYTEORDER_HAS_U64__ -+#define __TYPES_H__ /* xen/types.h guard */ -+#include "../../xen/include/xen/byteorder/little_endian.h" -+#define __ASM_UNALIGNED_H__ /* asm/unaligned.h guard */ -+#include "../../xen/include/xen/unaligned.h" -+#include "../../xen/include/xen/xxhash.h" -+#include "../../xen/lib/xxhash64.c" -+#include "../../xen/common/unzstd.c" -+ -+int xc_try_zstd_decode( -+ struct xc_dom_image *dom, void **blob, size_t *size) -+{ -+ return xc_dom_decompress_unsafe(unzstd, dom, blob, size); -+} -diff --git a/xen/common/zstd/decompress.c b/xen/common/zstd/decompress.c -index 3d3ef136e5..b024910814 100644 ---- a/xen/common/zstd/decompress.c -+++ b/xen/common/zstd/decompress.c -@@ -33,7 +33,6 @@ - #include "huf.h" - #include "mem.h" /* low level memory routines */ - #include "zstd_internal.h" --#include /* memcpy, memmove, memset */ - - #define ZSTD_PREFETCH(ptr) __builtin_prefetch(ptr, 0, 0) - -@@ -99,9 +98,12 @@ struct ZSTD_DCtx_s { - BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX]; - }; /* typedef'd to ZSTD_DCtx within "zstd.h" */ - --size_t INIT ZSTD_DCtxWorkspaceBound(void) { return ZSTD_ALIGN(sizeof(ZSTD_stack)) + ZSTD_ALIGN(sizeof(ZSTD_DCtx)); } -+STATIC size_t INIT ZSTD_DCtxWorkspaceBound(void) -+{ -+ return ZSTD_ALIGN(sizeof(ZSTD_stack)) + ZSTD_ALIGN(sizeof(ZSTD_DCtx)); -+} - --size_t INIT ZSTD_decompressBegin(ZSTD_DCtx *dctx) -+STATIC size_t INIT ZSTD_decompressBegin(ZSTD_DCtx *dctx) - { - dctx->expected = ZSTD_frameHeaderSize_prefix; - dctx->stage = ZSTDds_getFrameHeaderSize; -@@ -121,7 +123,7 @@ size_t INIT ZSTD_decompressBegin(ZSTD_DCtx *dctx) - return 0; - } - --ZSTD_DCtx *INIT ZSTD_createDCtx_advanced(ZSTD_customMem customMem) -+STATIC ZSTD_DCtx *INIT ZSTD_createDCtx_advanced(ZSTD_customMem customMem) - { - ZSTD_DCtx *dctx; - -@@ -136,7 +138,7 @@ ZSTD_DCtx *INIT ZSTD_createDCtx_advanced(ZSTD_customMem customMem) - return dctx; - } - --ZSTD_DCtx *INIT ZSTD_initDCtx(void *workspace, size_t workspaceSize) -+STATIC ZSTD_DCtx *INIT ZSTD_initDCtx(void *workspace, size_t workspaceSize) - { - ZSTD_customMem const stackMem = ZSTD_initStack(workspace, workspaceSize); - return ZSTD_createDCtx_advanced(stackMem); -@@ -150,11 +152,13 @@ size_t INIT ZSTD_freeDCtx(ZSTD_DCtx *dctx) - return 0; /* reserved as a potential error code in the future */ - } - -+#ifdef BUILD_DEAD_CODE - void INIT ZSTD_copyDCtx(ZSTD_DCtx *dstDCtx, const ZSTD_DCtx *srcDCtx) - { - size_t const workSpaceSize = (ZSTD_BLOCKSIZE_ABSOLUTEMAX + WILDCOPY_OVERLENGTH) + ZSTD_frameHeaderSize_max; - memcpy(dstDCtx, srcDCtx, sizeof(ZSTD_DCtx) - workSpaceSize); /* no need to copy workspace */ - } -+#endif - - STATIC size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize); - STATIC size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx *dctx, const void *dict, -@@ -166,6 +170,7 @@ static void ZSTD_refDDict(ZSTD_DCtx *dstDCtx, const ZSTD_DDict *ddict); - * Decompression section - ***************************************************************/ - -+#ifdef BUILD_DEAD_CODE - /*! ZSTD_isFrame() : - * Tells if the content of `buffer` starts with a valid Frame Identifier. - * Note : Frame Identifier is 4 bytes. If `size < 4`, @return will always be 0. -@@ -184,6 +189,7 @@ unsigned INIT ZSTD_isFrame(const void *buffer, size_t size) - } - return 0; - } -+#endif - - /** ZSTD_frameHeaderSize() : - * srcSize must be >= ZSTD_frameHeaderSize_prefix. -@@ -206,7 +212,7 @@ static size_t INIT ZSTD_frameHeaderSize(const void *src, size_t srcSize) - * @return : 0, `fparamsPtr` is correctly filled, - * >0, `srcSize` is too small, result is expected `srcSize`, - * or an error code, which can be tested using ZSTD_isError() */ --size_t INIT ZSTD_getFrameParams(ZSTD_frameParams *fparamsPtr, const void *src, size_t srcSize) -+STATIC size_t INIT ZSTD_getFrameParams(ZSTD_frameParams *fparamsPtr, const void *src, size_t srcSize) - { - const BYTE *ip = (const BYTE *)src; - -@@ -291,6 +297,7 @@ size_t INIT ZSTD_getFrameParams(ZSTD_frameParams *fparamsPtr, const void *src, s - return 0; - } - -+#ifdef BUILD_DEAD_CODE - /** ZSTD_getFrameContentSize() : - * compatible with legacy mode - * @return : decompressed size of the single frame pointed to be `src` if known, otherwise -@@ -367,6 +374,7 @@ unsigned long long INIT ZSTD_findDecompressedSize(const void *src, size_t srcSiz - return totalDstSize; - } - } -+#endif /* BUILD_DEAD_CODE */ - - /** ZSTD_decodeFrameHeader() : - * `headerSize` must be the size provided by ZSTD_frameHeaderSize(). -@@ -393,7 +401,7 @@ typedef struct { - - /*! ZSTD_getcBlockSize() : - * Provides the size of compressed block from block header `src` */ --size_t INIT ZSTD_getcBlockSize(const void *src, size_t srcSize, blockProperties_t *bpPtr) -+STATIC size_t INIT ZSTD_getcBlockSize(const void *src, size_t srcSize, blockProperties_t *bpPtr) - { - if (srcSize < ZSTD_blockHeaderSize) - return ERROR(srcSize_wrong); -@@ -431,7 +439,7 @@ static size_t INIT ZSTD_setRleBlock(void *dst, size_t dstCapacity, const void *s - - /*! ZSTD_decodeLiteralsBlock() : - @return : nb of bytes read from src (< srcSize ) */ --size_t INIT ZSTD_decodeLiteralsBlock(ZSTD_DCtx *dctx, const void *src, size_t srcSize) /* note : srcSize < BLOCKSIZE */ -+STATIC size_t INIT ZSTD_decodeLiteralsBlock(ZSTD_DCtx *dctx, const void *src, size_t srcSize) /* note : srcSize < BLOCKSIZE */ - { - if (srcSize < MIN_CBLOCK_SIZE) - return ERROR(corruption_detected); -@@ -795,7 +803,7 @@ static size_t INIT ZSTD_buildSeqTable(FSE_DTable *DTableSpace, const FSE_DTable - } - } - --size_t INIT ZSTD_decodeSeqHeaders(ZSTD_DCtx *dctx, int *nbSeqPtr, const void *src, size_t srcSize) -+STATIC size_t INIT ZSTD_decodeSeqHeaders(ZSTD_DCtx *dctx, int *nbSeqPtr, const void *src, size_t srcSize) - { - const BYTE *const istart = (const BYTE *const)src; - const BYTE *const iend = istart + srcSize; -@@ -1481,6 +1489,7 @@ static void INIT ZSTD_checkContinuity(ZSTD_DCtx *dctx, const void *dst) - } - } - -+#ifdef BUILD_DEAD_CODE - size_t INIT ZSTD_decompressBlock(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize) - { - size_t dSize; -@@ -1498,8 +1507,9 @@ size_t INIT ZSTD_insertBlock(ZSTD_DCtx *dctx, const void *blockStart, size_t blo - dctx->previousDstEnd = (const char *)blockStart + blockSize; - return blockSize; - } -+#endif /* BUILD_DEAD_CODE */ - --size_t INIT ZSTD_generateNxBytes(void *dst, size_t dstCapacity, BYTE byte, size_t length) -+STATIC size_t INIT ZSTD_generateNxBytes(void *dst, size_t dstCapacity, BYTE byte, size_t length) - { - if (length > dstCapacity) - return ERROR(dstSize_tooSmall); -@@ -1512,7 +1522,7 @@ size_t INIT ZSTD_generateNxBytes(void *dst, size_t dstCapacity, BYTE byte, size_ - * `src` must point to the start of a ZSTD frame, ZSTD legacy frame, or skippable frame - * `srcSize` must be at least as large as the frame contained - * @return : the compressed size of the frame starting at `src` */ --size_t INIT ZSTD_findFrameCompressedSize(const void *src, size_t srcSize) -+STATIC size_t INIT ZSTD_findFrameCompressedSize(const void *src, size_t srcSize) - { - if (srcSize >= ZSTD_skippableHeaderSize && (ZSTD_readLE32(src) & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) { - return ZSTD_skippableHeaderSize + ZSTD_readLE32((const BYTE *)src + 4); -@@ -1709,12 +1719,12 @@ static size_t INIT ZSTD_decompressMultiFrame(ZSTD_DCtx *dctx, void *dst, size_t - return (BYTE *)dst - (BYTE *)dststart; - } - --size_t INIT ZSTD_decompress_usingDict(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize, const void *dict, size_t dictSize) -+STATIC size_t INIT ZSTD_decompress_usingDict(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize, const void *dict, size_t dictSize) - { - return ZSTD_decompressMultiFrame(dctx, dst, dstCapacity, src, srcSize, dict, dictSize, NULL); - } - --size_t INIT ZSTD_decompressDCtx(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize) -+STATIC size_t INIT ZSTD_decompressDCtx(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize) - { - return ZSTD_decompress_usingDict(dctx, dst, dstCapacity, src, srcSize, NULL, 0); - } -@@ -1723,9 +1733,12 @@ size_t INIT ZSTD_decompressDCtx(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, - * Advanced Streaming Decompression API - * Bufferless and synchronous - ****************************************/ --size_t INIT ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx *dctx) { return dctx->expected; } -+STATIC size_t INIT ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx *dctx) -+{ -+ return dctx->expected; -+} - --ZSTD_nextInputType_e INIT ZSTD_nextInputType(ZSTD_DCtx *dctx) -+STATIC ZSTD_nextInputType_e INIT ZSTD_nextInputType(ZSTD_DCtx *dctx) - { - switch (dctx->stage) { - default: /* should not happen */ -@@ -1745,7 +1758,7 @@ int INIT ZSTD_isSkipFrame(ZSTD_DCtx *dctx) { return dctx->stage == ZSTDds_skipFr - /** ZSTD_decompressContinue() : - * @return : nb of bytes generated into `dst` (necessarily <= `dstCapacity) - * or an error code, which can be tested using ZSTD_isError() */ --size_t INIT ZSTD_decompressContinue(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize) -+STATIC size_t INIT ZSTD_decompressContinue(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize) - { - /* Sanity check */ - if (srcSize != dctx->expected) -@@ -1971,7 +1984,7 @@ static size_t INIT ZSTD_decompress_insertDictionary(ZSTD_DCtx *dctx, const void - return ZSTD_refDictContent(dctx, dict, dictSize); - } - --size_t INIT ZSTD_decompressBegin_usingDict(ZSTD_DCtx *dctx, const void *dict, size_t dictSize) -+STATIC size_t INIT ZSTD_decompressBegin_usingDict(ZSTD_DCtx *dctx, const void *dict, size_t dictSize) - { - CHECK_F(ZSTD_decompressBegin(dctx)); - if (dict && dictSize) -@@ -1991,7 +2004,9 @@ struct ZSTD_DDict_s { - ZSTD_customMem cMem; - }; /* typedef'd to ZSTD_DDict within "zstd.h" */ - -+#ifdef BUILD_DEAD_CODE - size_t INIT ZSTD_DDictWorkspaceBound(void) { return ZSTD_ALIGN(sizeof(ZSTD_stack)) + ZSTD_ALIGN(sizeof(ZSTD_DDict)); } -+#endif - - static const void *INIT ZSTD_DDictDictContent(const ZSTD_DDict *ddict) { return ddict->dictContent; } - -@@ -2023,6 +2038,7 @@ static void INIT ZSTD_refDDict(ZSTD_DCtx *dstDCtx, const ZSTD_DDict *ddict) - } - } - -+#ifdef BUILD_DEAD_CODE - static size_t INIT ZSTD_loadEntropy_inDDict(ZSTD_DDict *ddict) - { - ddict->dictID = 0; -@@ -2090,6 +2106,7 @@ ZSTD_DDict *INIT ZSTD_initDDict(const void *dict, size_t dictSize, void *workspa - ZSTD_customMem const stackMem = ZSTD_initStack(workspace, workspaceSize); - return ZSTD_createDDict_advanced(dict, dictSize, 1, stackMem); - } -+#endif /* BUILD_DEAD_CODE */ - - size_t INIT ZSTD_freeDDict(ZSTD_DDict *ddict) - { -@@ -2103,6 +2120,7 @@ size_t INIT ZSTD_freeDDict(ZSTD_DDict *ddict) - } - } - -+#ifdef BUILD_DEAD_CODE - /*! ZSTD_getDictID_fromDict() : - * Provides the dictID stored within dictionary. - * if @return == 0, the dictionary is not conformant with Zstandard specification. -@@ -2145,11 +2163,12 @@ unsigned INIT ZSTD_getDictID_fromFrame(const void *src, size_t srcSize) - return 0; - return zfp.dictID; - } -+#endif /* BUILD_DEAD_CODE */ - - /*! ZSTD_decompress_usingDDict() : - * Decompression using a pre-digested Dictionary - * Use dictionary without significant overhead. */ --size_t INIT ZSTD_decompress_usingDDict(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize, const ZSTD_DDict *ddict) -+STATIC size_t INIT ZSTD_decompress_usingDDict(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize, const ZSTD_DDict *ddict) - { - /* pass content and size in case legacy frames are encountered */ - return ZSTD_decompressMultiFrame(dctx, dst, dstCapacity, src, srcSize, NULL, 0, ddict); -@@ -2186,7 +2205,7 @@ struct ZSTD_DStream_s { - U32 hostageByte; - }; /* typedef'd to ZSTD_DStream within "zstd.h" */ - --size_t INIT ZSTD_DStreamWorkspaceBound(size_t maxWindowSize) -+STATIC size_t INIT ZSTD_DStreamWorkspaceBound(size_t maxWindowSize) - { - size_t const blockSize = MIN(maxWindowSize, ZSTD_BLOCKSIZE_ABSOLUTEMAX); - size_t const inBuffSize = blockSize; -@@ -2216,7 +2235,7 @@ static ZSTD_DStream *INIT ZSTD_createDStream_advanced(ZSTD_customMem customMem) - return zds; - } - --ZSTD_DStream *INIT ZSTD_initDStream(size_t maxWindowSize, void *workspace, size_t workspaceSize) -+STATIC ZSTD_DStream *INIT ZSTD_initDStream(size_t maxWindowSize, void *workspace, size_t workspaceSize) - { - ZSTD_customMem const stackMem = ZSTD_initStack(workspace, workspaceSize); - ZSTD_DStream *zds = ZSTD_createDStream_advanced(stackMem); -@@ -2249,6 +2268,7 @@ ZSTD_DStream *INIT ZSTD_initDStream(size_t maxWindowSize, void *workspace, size_ - return zds; - } - -+#ifdef BUILD_DEAD_CODE - ZSTD_DStream *INIT ZSTD_initDStream_usingDDict(size_t maxWindowSize, const ZSTD_DDict *ddict, void *workspace, size_t workspaceSize) - { - ZSTD_DStream *zds = ZSTD_initDStream(maxWindowSize, workspace, workspaceSize); -@@ -2257,6 +2277,7 @@ ZSTD_DStream *INIT ZSTD_initDStream_usingDDict(size_t maxWindowSize, const ZSTD_ - } - return zds; - } -+#endif - - size_t INIT ZSTD_freeDStream(ZSTD_DStream *zds) - { -@@ -2279,10 +2300,12 @@ size_t INIT ZSTD_freeDStream(ZSTD_DStream *zds) - - /* *** Initialization *** */ - -+#ifdef BUILD_DEAD_CODE - size_t INIT ZSTD_DStreamInSize(void) { return ZSTD_BLOCKSIZE_ABSOLUTEMAX + ZSTD_blockHeaderSize; } - size_t INIT ZSTD_DStreamOutSize(void) { return ZSTD_BLOCKSIZE_ABSOLUTEMAX; } -+#endif - --size_t INIT ZSTD_resetDStream(ZSTD_DStream *zds) -+STATIC size_t INIT ZSTD_resetDStream(ZSTD_DStream *zds) - { - zds->stage = zdss_loadHeader; - zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0; -@@ -2300,7 +2323,7 @@ ZSTD_STATIC size_t INIT ZSTD_limitCopy(void *dst, size_t dstCapacity, const void - return length; - } - --size_t INIT ZSTD_decompressStream(ZSTD_DStream *zds, ZSTD_outBuffer *output, ZSTD_inBuffer *input) -+STATIC size_t INIT ZSTD_decompressStream(ZSTD_DStream *zds, ZSTD_outBuffer *output, ZSTD_inBuffer *input) - { - const char *const istart = (const char *)(input->src) + input->pos; - const char *const iend = (const char *)(input->src) + input->size; -diff --git a/xen/common/zstd/error_private.h b/xen/common/zstd/error_private.h -index d07bf3cb9b..906d537e08 100644 ---- a/xen/common/zstd/error_private.h -+++ b/xen/common/zstd/error_private.h -@@ -19,11 +19,6 @@ - #ifndef ERROR_H_MODULE - #define ERROR_H_MODULE - --/* **************************************** --* Dependencies --******************************************/ --#include /* size_t */ -- - /** - * enum ZSTD_ErrorCode - zstd error codes - * -diff --git a/xen/common/zstd/fse.h b/xen/common/zstd/fse.h -index b86717c34d..5761e09f17 100644 ---- a/xen/common/zstd/fse.h -+++ b/xen/common/zstd/fse.h -@@ -40,11 +40,6 @@ - #ifndef FSE_H - #define FSE_H - --/*-***************************************** --* Dependencies --******************************************/ --#include /* size_t, ptrdiff_t */ -- - /*-***************************************** - * FSE_PUBLIC_API : control library symbols visibility - ******************************************/ -diff --git a/xen/common/zstd/fse_decompress.c b/xen/common/zstd/fse_decompress.c -index cc51206df6..6c61e9002e 100644 ---- a/xen/common/zstd/fse_decompress.c -+++ b/xen/common/zstd/fse_decompress.c -@@ -48,8 +48,6 @@ - #include "bitstream.h" - #include "fse.h" - #include "zstd_internal.h" --#include --#include /* memcpy, memset */ - - /* ************************************************************** - * Error Management -diff --git a/xen/common/zstd/huf.h b/xen/common/zstd/huf.h -index a9d522c7bb..a498e0de28 100644 ---- a/xen/common/zstd/huf.h -+++ b/xen/common/zstd/huf.h -@@ -40,9 +40,6 @@ - #ifndef HUF_H_298734234 - #define HUF_H_298734234 - --/* *** Dependencies *** */ --#include /* size_t */ -- - /* *** Tool functions *** */ - #define HUF_BLOCKSIZE_MAX (128 * 1024) /**< maximum input size for a single block compressed with HUF_compress */ - size_t HUF_compressBound(size_t size); /**< maximum compressed size (worst case) */ -diff --git a/xen/common/zstd/huf_decompress.c b/xen/common/zstd/huf_decompress.c -index 341619e642..f6aca709a6 100644 ---- a/xen/common/zstd/huf_decompress.c -+++ b/xen/common/zstd/huf_decompress.c -@@ -48,8 +48,6 @@ - #include "bitstream.h" /* BIT_* */ - #include "fse.h" /* header compression */ - #include "huf.h" --#include --#include /* memcpy, memset */ - - /* ************************************************************** - * Error Management -diff --git a/xen/common/zstd/mem.h b/xen/common/zstd/mem.h -index 2883200696..2acae6a8ed 100644 ---- a/xen/common/zstd/mem.h -+++ b/xen/common/zstd/mem.h -@@ -20,9 +20,11 @@ - /*-**************************************** - * Dependencies - ******************************************/ -+#ifdef __XEN__ - #include /* memcpy */ - #include /* size_t, ptrdiff_t */ - #include -+#endif - - /*-**************************************** - * Compiler specifics -diff --git a/xen/common/zstd/zstd_internal.h b/xen/common/zstd/zstd_internal.h -index 7f8e5529eb..caa7aab406 100644 ---- a/xen/common/zstd/zstd_internal.h -+++ b/xen/common/zstd/zstd_internal.h -@@ -28,8 +28,10 @@ - ***************************************/ - #include "error_private.h" - #include "mem.h" -+#ifdef __XEN__ - #include - #include -+#endif - - #define ALIGN(x, a) ((x + (a) - 1) & ~((a) - 1)) - #define PTR_ALIGN(p, a) ((typeof(p))ALIGN((unsigned long)(p), (a))) -@@ -95,8 +97,10 @@ typedef struct ZSTD_DStream_s ZSTD_DStream; - /*-************************************* - * shared macros - ***************************************/ -+#ifndef MIN - #define MIN(a, b) ((a) < (b) ? (a) : (b)) - #define MAX(a, b) ((a) > (b) ? (a) : (b)) -+#endif - #define CHECK_F(f) \ - { \ - size_t const errcod = f; \ -diff --git a/xen/include/xen/unaligned.h b/xen/include/xen/unaligned.h -index eef7ec73b6..0a2b16d05d 100644 ---- a/xen/include/xen/unaligned.h -+++ b/xen/include/xen/unaligned.h -@@ -10,8 +10,10 @@ - #ifndef __XEN_UNALIGNED_H__ - #define __XEN_UNALIGNED_H__ - -+#ifdef __XEN__ - #include - #include -+#endif - - #define get_unaligned(p) (*(p)) - #define put_unaligned(val, p) (*(p) = (val)) -diff --git a/xen/lib/xxhash64.c b/xen/lib/xxhash64.c -index ba6bcf152d..481e76fbcf 100644 ---- a/xen/lib/xxhash64.c -+++ b/xen/lib/xxhash64.c -@@ -38,11 +38,13 @@ - * - xxHash source repository: https://github.com/Cyan4973/xxHash - */ - -+#ifdef __XEN__ - #include - #include - #include - #include - #include -+#endif - - /*-************************************* - * Macros --- -2.20.1 - diff --git a/xen.git-d6627cf1b63ce57a6a7e2c1800dbc50eed742c32.patch b/xen.git-d6627cf1b63ce57a6a7e2c1800dbc50eed742c32.patch deleted file mode 100644 index c8643cf..0000000 --- a/xen.git-d6627cf1b63ce57a6a7e2c1800dbc50eed742c32.patch +++ /dev/null @@ -1,6399 +0,0 @@ -From d6627cf1b63ce57a6a7e2c1800dbc50eed742c32 Mon Sep 17 00:00:00 2001 -From: Jan Beulich -Date: Mon, 18 Jan 2021 12:12:23 +0100 -Subject: [PATCH] x86/Dom0: support zstd compressed kernels - -Taken from Linux at commit 1c4dd334df3a ("lib: decompress_unzstd: Limit -output size") for unzstd.c (renamed from decompress_unzstd.c) and -36f9ff9e03de ("lib: Fix fall-through warnings for Clang") for zstd/, -with bits from linux/zstd.h merged into suitable other headers. - -To limit the editing necessary, introduce ptrdiff_t. - -Signed-off-by: Jan Beulich -Acked-by: Andrew Cooper ---- - xen/common/Makefile | 2 +- - xen/common/decompress.c | 3 + - xen/common/unzstd.c | 308 ++++ - xen/common/zstd/bitstream.h | 380 +++++ - xen/common/zstd/decompress.c | 2496 ++++++++++++++++++++++++++++++ - xen/common/zstd/entropy_common.c | 243 +++ - xen/common/zstd/error_private.h | 110 ++ - xen/common/zstd/fse.h | 575 +++++++ - xen/common/zstd/fse_decompress.c | 324 ++++ - xen/common/zstd/huf.h | 212 +++ - xen/common/zstd/huf_decompress.c | 960 ++++++++++++ - xen/common/zstd/mem.h | 151 ++ - xen/common/zstd/zstd_common.c | 74 + - xen/common/zstd/zstd_internal.h | 372 +++++ - xen/include/asm-arm/types.h | 6 + - xen/include/asm-x86/types.h | 6 + - xen/include/xen/decompress.h | 2 +- - 17 files changed, 6222 insertions(+), 2 deletions(-) - create mode 100644 xen/common/unzstd.c - create mode 100644 xen/common/zstd/bitstream.h - create mode 100644 xen/common/zstd/decompress.c - create mode 100644 xen/common/zstd/entropy_common.c - create mode 100644 xen/common/zstd/error_private.h - create mode 100644 xen/common/zstd/fse.h - create mode 100644 xen/common/zstd/fse_decompress.c - create mode 100644 xen/common/zstd/huf.h - create mode 100644 xen/common/zstd/huf_decompress.c - create mode 100644 xen/common/zstd/mem.h - create mode 100644 xen/common/zstd/zstd_common.c - create mode 100644 xen/common/zstd/zstd_internal.h - -diff --git a/xen/common/Makefile b/xen/common/Makefile -index 7a4e652b57..d751315a87 100644 ---- a/xen/common/Makefile -+++ b/xen/common/Makefile -@@ -55,7 +55,7 @@ obj-bin-y += warning.init.o - obj-$(CONFIG_XENOPROF) += xenoprof.o - obj-y += xmalloc_tlsf.o - --obj-bin-$(CONFIG_X86) += $(foreach n,decompress bunzip2 unxz unlzma lzo unlzo unlz4 earlycpio,$(n).init.o) -+obj-bin-$(CONFIG_X86) += $(foreach n,decompress bunzip2 unxz unlzma lzo unlzo unlz4 unzstd earlycpio,$(n).init.o) - - obj-$(CONFIG_COMPAT) += $(addprefix compat/,domain.o kernel.o memory.o multicall.o xlat.o) - -diff --git a/xen/common/decompress.c b/xen/common/decompress.c -index 9d6e0c4ab0..79e60f4802 100644 ---- a/xen/common/decompress.c -+++ b/xen/common/decompress.c -@@ -31,5 +31,8 @@ int __init decompress(void *inbuf, unsigned int len, void *outbuf) - if ( len >= 2 && !memcmp(inbuf, "\x02\x21", 2) ) - return unlz4(inbuf, len, NULL, NULL, outbuf, NULL, error); - -+ if ( len >= 4 && !memcmp(inbuf, "\x28\xb5\x2f\xfd", 4) ) -+ return unzstd(inbuf, len, NULL, NULL, outbuf, NULL, error); -+ - return 1; - } -diff --git a/xen/common/unzstd.c b/xen/common/unzstd.c -new file mode 100644 -index 0000000000..a107616427 ---- /dev/null -+++ b/xen/common/unzstd.c -@@ -0,0 +1,308 @@ -+// SPDX-License-Identifier: GPL-2.0 -+ -+/* -+ * Important notes about in-place decompression -+ * -+ * At least on x86, the kernel is decompressed in place: the compressed data -+ * is placed to the end of the output buffer, and the decompressor overwrites -+ * most of the compressed data. There must be enough safety margin to -+ * guarantee that the write position is always behind the read position. -+ * -+ * The safety margin for ZSTD with a 128 KB block size is calculated below. -+ * Note that the margin with ZSTD is bigger than with GZIP or XZ! -+ * -+ * The worst case for in-place decompression is that the beginning of -+ * the file is compressed extremely well, and the rest of the file is -+ * uncompressible. Thus, we must look for worst-case expansion when the -+ * compressor is encoding uncompressible data. -+ * -+ * The structure of the .zst file in case of a compresed kernel is as follows. -+ * Maximum sizes (as bytes) of the fields are in parenthesis. -+ * -+ * Frame Header: (18) -+ * Blocks: (N) -+ * Checksum: (4) -+ * -+ * The frame header and checksum overhead is at most 22 bytes. -+ * -+ * ZSTD stores the data in blocks. Each block has a header whose size is -+ * a 3 bytes. After the block header, there is up to 128 KB of payload. -+ * The maximum uncompressed size of the payload is 128 KB. The minimum -+ * uncompressed size of the payload is never less than the payload size -+ * (excluding the block header). -+ * -+ * The assumption, that the uncompressed size of the payload is never -+ * smaller than the payload itself, is valid only when talking about -+ * the payload as a whole. It is possible that the payload has parts where -+ * the decompressor consumes more input than it produces output. Calculating -+ * the worst case for this would be tricky. Instead of trying to do that, -+ * let's simply make sure that the decompressor never overwrites any bytes -+ * of the payload which it is currently reading. -+ * -+ * Now we have enough information to calculate the safety margin. We need -+ * - 22 bytes for the .zst file format headers; -+ * - 3 bytes per every 128 KiB of uncompressed size (one block header per -+ * block); and -+ * - 128 KiB (biggest possible zstd block size) to make sure that the -+ * decompressor never overwrites anything from the block it is currently -+ * reading. -+ * -+ * We get the following formula: -+ * -+ * safety_margin = 22 + uncompressed_size * 3 / 131072 + 131072 -+ * <= 22 + (uncompressed_size >> 15) + 131072 -+ */ -+ -+#include "decompress.h" -+ -+#include "zstd/entropy_common.c" -+#include "zstd/fse_decompress.c" -+#include "zstd/huf_decompress.c" -+#include "zstd/zstd_common.c" -+#include "zstd/decompress.c" -+ -+/* 128MB is the maximum window size supported by zstd. */ -+#define ZSTD_WINDOWSIZE_MAX (1 << ZSTD_WINDOWLOG_MAX) -+/* -+ * Size of the input and output buffers in multi-call mode. -+ * Pick a larger size because it isn't used during kernel decompression, -+ * since that is single pass, and we have to allocate a large buffer for -+ * zstd's window anyway. The larger size speeds up initramfs decompression. -+ */ -+#define ZSTD_IOBUF_SIZE (1 << 17) -+ -+static int INIT handle_zstd_error(size_t ret, void (*error)(const char *x)) -+{ -+ const int err = ZSTD_getErrorCode(ret); -+ -+ if (!ZSTD_isError(ret)) -+ return 0; -+ -+ switch (err) { -+ case ZSTD_error_memory_allocation: -+ error("ZSTD decompressor ran out of memory"); -+ break; -+ case ZSTD_error_prefix_unknown: -+ error("Input is not in the ZSTD format (wrong magic bytes)"); -+ break; -+ case ZSTD_error_dstSize_tooSmall: -+ case ZSTD_error_corruption_detected: -+ case ZSTD_error_checksum_wrong: -+ error("ZSTD-compressed data is corrupt"); -+ break; -+ default: -+ error("ZSTD-compressed data is probably corrupt"); -+ break; -+ } -+ return -1; -+} -+ -+/* -+ * Handle the case where we have the entire input and output in one segment. -+ * We can allocate less memory (no circular buffer for the sliding window), -+ * and avoid some memcpy() calls. -+ */ -+static int INIT decompress_single(const u8 *in_buf, long in_len, u8 *out_buf, -+ long out_len, unsigned int *in_pos, -+ void (*error)(const char *x)) -+{ -+ const size_t wksp_size = ZSTD_DCtxWorkspaceBound(); -+ void *wksp = large_malloc(wksp_size); -+ ZSTD_DCtx *dctx = ZSTD_initDCtx(wksp, wksp_size); -+ int err; -+ size_t ret; -+ -+ if (dctx == NULL) { -+ error("Out of memory while allocating ZSTD_DCtx"); -+ err = -1; -+ goto out; -+ } -+ /* -+ * Find out how large the frame actually is, there may be junk at -+ * the end of the frame that ZSTD_decompressDCtx() can't handle. -+ */ -+ ret = ZSTD_findFrameCompressedSize(in_buf, in_len); -+ err = handle_zstd_error(ret, error); -+ if (err) -+ goto out; -+ in_len = (long)ret; -+ -+ ret = ZSTD_decompressDCtx(dctx, out_buf, out_len, in_buf, in_len); -+ err = handle_zstd_error(ret, error); -+ if (err) -+ goto out; -+ -+ if (in_pos != NULL) -+ *in_pos = in_len; -+ -+ err = 0; -+out: -+ if (wksp != NULL) -+ large_free(wksp); -+ return err; -+} -+ -+STATIC int INIT unzstd(unsigned char *in_buf, unsigned int in_len, -+ int (*fill)(void*, unsigned int), -+ int (*flush)(void*, unsigned int), -+ unsigned char *out_buf, -+ unsigned int *in_pos, -+ void (*error)(const char *x)) -+{ -+ ZSTD_inBuffer in; -+ ZSTD_outBuffer out; -+ ZSTD_frameParams params; -+ void *in_allocated = NULL; -+ void *out_allocated = NULL; -+ void *wksp = NULL; -+ size_t wksp_size; -+ ZSTD_DStream *dstream; -+ int err; -+ size_t ret; -+ /* -+ * ZSTD decompression code won't be happy if the buffer size is so big -+ * that its end address overflows. When the size is not provided, make -+ * it as big as possible without having the end address overflow. -+ */ -+ unsigned long out_len = ULONG_MAX - (unsigned long)out_buf; -+ -+ if (fill == NULL && flush == NULL) -+ /* -+ * We can decompress faster and with less memory when we have a -+ * single chunk. -+ */ -+ return decompress_single(in_buf, in_len, out_buf, out_len, -+ in_pos, error); -+ -+ /* -+ * If in_buf is not provided, we must be using fill(), so allocate -+ * a large enough buffer. If it is provided, it must be at least -+ * ZSTD_IOBUF_SIZE large. -+ */ -+ if (in_buf == NULL) { -+ in_allocated = large_malloc(ZSTD_IOBUF_SIZE); -+ if (in_allocated == NULL) { -+ error("Out of memory while allocating input buffer"); -+ err = -1; -+ goto out; -+ } -+ in_buf = in_allocated; -+ in_len = 0; -+ } -+ /* Read the first chunk, since we need to decode the frame header. */ -+ if (fill != NULL) -+ in_len = fill(in_buf, ZSTD_IOBUF_SIZE); -+ if ((int)in_len < 0) { -+ error("ZSTD-compressed data is truncated"); -+ err = -1; -+ goto out; -+ } -+ /* Set the first non-empty input buffer. */ -+ in.src = in_buf; -+ in.pos = 0; -+ in.size = in_len; -+ /* Allocate the output buffer if we are using flush(). */ -+ if (flush != NULL) { -+ out_allocated = large_malloc(ZSTD_IOBUF_SIZE); -+ if (out_allocated == NULL) { -+ error("Out of memory while allocating output buffer"); -+ err = -1; -+ goto out; -+ } -+ out_buf = out_allocated; -+ out_len = ZSTD_IOBUF_SIZE; -+ } -+ /* Set the output buffer. */ -+ out.dst = out_buf; -+ out.pos = 0; -+ out.size = out_len; -+ -+ /* -+ * We need to know the window size to allocate the ZSTD_DStream. -+ * Since we are streaming, we need to allocate a buffer for the sliding -+ * window. The window size varies from 1 KB to ZSTD_WINDOWSIZE_MAX -+ * (8 MB), so it is important to use the actual value so as not to -+ * waste memory when it is smaller. -+ */ -+ ret = ZSTD_getFrameParams(¶ms, in.src, in.size); -+ err = handle_zstd_error(ret, error); -+ if (err) -+ goto out; -+ if (ret != 0) { -+ error("ZSTD-compressed data has an incomplete frame header"); -+ err = -1; -+ goto out; -+ } -+ if (params.windowSize > ZSTD_WINDOWSIZE_MAX) { -+ error("ZSTD-compressed data has too large a window size"); -+ err = -1; -+ goto out; -+ } -+ -+ /* -+ * Allocate the ZSTD_DStream now that we know how much memory is -+ * required. -+ */ -+ wksp_size = ZSTD_DStreamWorkspaceBound(params.windowSize); -+ wksp = large_malloc(wksp_size); -+ dstream = ZSTD_initDStream(params.windowSize, wksp, wksp_size); -+ if (dstream == NULL) { -+ error("Out of memory while allocating ZSTD_DStream"); -+ err = -1; -+ goto out; -+ } -+ -+ /* -+ * Decompression loop: -+ * Read more data if necessary (error if no more data can be read). -+ * Call the decompression function, which returns 0 when finished. -+ * Flush any data produced if using flush(). -+ */ -+ if (in_pos != NULL) -+ *in_pos = 0; -+ do { -+ /* -+ * If we need to reload data, either we have fill() and can -+ * try to get more data, or we don't and the input is truncated. -+ */ -+ if (in.pos == in.size) { -+ if (in_pos != NULL) -+ *in_pos += in.pos; -+ in_len = fill ? fill(in_buf, ZSTD_IOBUF_SIZE) : -1; -+ if ((int)in_len < 0) { -+ error("ZSTD-compressed data is truncated"); -+ err = -1; -+ goto out; -+ } -+ in.pos = 0; -+ in.size = in_len; -+ } -+ /* Returns zero when the frame is complete. */ -+ ret = ZSTD_decompressStream(dstream, &out, &in); -+ err = handle_zstd_error(ret, error); -+ if (err) -+ goto out; -+ /* Flush all of the data produced if using flush(). */ -+ if (flush != NULL && out.pos > 0) { -+ if (out.pos != flush(out.dst, out.pos)) { -+ error("Failed to flush()"); -+ err = -1; -+ goto out; -+ } -+ out.pos = 0; -+ } -+ } while (ret != 0); -+ -+ if (in_pos != NULL) -+ *in_pos += in.pos; -+ -+ err = 0; -+out: -+ if (in_allocated != NULL) -+ large_free(in_allocated); -+ if (out_allocated != NULL) -+ large_free(out_allocated); -+ if (wksp != NULL) -+ large_free(wksp); -+ return err; -+} -diff --git a/xen/common/zstd/bitstream.h b/xen/common/zstd/bitstream.h -new file mode 100644 -index 0000000000..2b06d4551f ---- /dev/null -+++ b/xen/common/zstd/bitstream.h -@@ -0,0 +1,380 @@ -+/* -+ * bitstream -+ * Part of FSE library -+ * header file (to include) -+ * Copyright (C) 2013-2016, Yann Collet. -+ * -+ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are -+ * met: -+ * -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above -+ * copyright notice, this list of conditions and the following disclaimer -+ * in the documentation and/or other materials provided with the -+ * distribution. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ * -+ * This program is free software; you can redistribute it and/or modify it under -+ * the terms of the GNU General Public License version 2 as published by the -+ * Free Software Foundation. This program is dual-licensed; you may select -+ * either version 2 of the GNU General Public License ("GPL") or BSD license -+ * ("BSD"). -+ * -+ * You can contact the author at : -+ * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy -+ */ -+#ifndef BITSTREAM_H_MODULE -+#define BITSTREAM_H_MODULE -+ -+/* -+* This API consists of small unitary functions, which must be inlined for best performance. -+* Since link-time-optimization is not available for all compilers, -+* these functions are defined into a .h to be included. -+*/ -+ -+/*-**************************************** -+* Dependencies -+******************************************/ -+#include "error_private.h" /* error codes and messages */ -+#include "mem.h" /* unaligned access routines */ -+ -+/*========================================= -+* Target specific -+=========================================*/ -+#define STREAM_ACCUMULATOR_MIN_32 25 -+#define STREAM_ACCUMULATOR_MIN_64 57 -+#define STREAM_ACCUMULATOR_MIN ((U32)(ZSTD_32bits() ? STREAM_ACCUMULATOR_MIN_32 : STREAM_ACCUMULATOR_MIN_64)) -+ -+/*-****************************************** -+* bitStream encoding API (write forward) -+********************************************/ -+/* bitStream can mix input from multiple sources. -+* A critical property of these streams is that they encode and decode in **reverse** direction. -+* So the first bit sequence you add will be the last to be read, like a LIFO stack. -+*/ -+typedef struct { -+ size_t bitContainer; -+ int bitPos; -+ char *startPtr; -+ char *ptr; -+ char *endPtr; -+} BIT_CStream_t; -+ -+ZSTD_STATIC size_t BIT_initCStream(BIT_CStream_t *bitC, void *dstBuffer, size_t dstCapacity); -+ZSTD_STATIC void BIT_addBits(BIT_CStream_t *bitC, size_t value, unsigned nbBits); -+ZSTD_STATIC void BIT_flushBits(BIT_CStream_t *bitC); -+ZSTD_STATIC size_t BIT_closeCStream(BIT_CStream_t *bitC); -+ -+/* Start with initCStream, providing the size of buffer to write into. -+* bitStream will never write outside of this buffer. -+* `dstCapacity` must be >= sizeof(bitD->bitContainer), otherwise @return will be an error code. -+* -+* bits are first added to a local register. -+* Local register is size_t, hence 64-bits on 64-bits systems, or 32-bits on 32-bits systems. -+* Writing data into memory is an explicit operation, performed by the flushBits function. -+* Hence keep track how many bits are potentially stored into local register to avoid register overflow. -+* After a flushBits, a maximum of 7 bits might still be stored into local register. -+* -+* Avoid storing elements of more than 24 bits if you want compatibility with 32-bits bitstream readers. -+* -+* Last operation is to close the bitStream. -+* The function returns the final size of CStream in bytes. -+* If data couldn't fit into `dstBuffer`, it will return a 0 ( == not storable) -+*/ -+ -+/*-******************************************** -+* bitStream decoding API (read backward) -+**********************************************/ -+typedef struct { -+ size_t bitContainer; -+ unsigned bitsConsumed; -+ const char *ptr; -+ const char *start; -+} BIT_DStream_t; -+ -+typedef enum { -+ BIT_DStream_unfinished = 0, -+ BIT_DStream_endOfBuffer = 1, -+ BIT_DStream_completed = 2, -+ BIT_DStream_overflow = 3 -+} BIT_DStream_status; /* result of BIT_reloadDStream() */ -+/* 1,2,4,8 would be better for bitmap combinations, but slows down performance a bit ... :( */ -+ -+ZSTD_STATIC size_t BIT_initDStream(BIT_DStream_t *bitD, const void *srcBuffer, size_t srcSize); -+ZSTD_STATIC size_t BIT_readBits(BIT_DStream_t *bitD, unsigned nbBits); -+ZSTD_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t *bitD); -+ZSTD_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t *bitD); -+ -+/* Start by invoking BIT_initDStream(). -+* A chunk of the bitStream is then stored into a local register. -+* Local register size is 64-bits on 64-bits systems, 32-bits on 32-bits systems (size_t). -+* You can then retrieve bitFields stored into the local register, **in reverse order**. -+* Local register is explicitly reloaded from memory by the BIT_reloadDStream() method. -+* A reload guarantee a minimum of ((8*sizeof(bitD->bitContainer))-7) bits when its result is BIT_DStream_unfinished. -+* Otherwise, it can be less than that, so proceed accordingly. -+* Checking if DStream has reached its end can be performed with BIT_endOfDStream(). -+*/ -+ -+/*-**************************************** -+* unsafe API -+******************************************/ -+ZSTD_STATIC void BIT_addBitsFast(BIT_CStream_t *bitC, size_t value, unsigned nbBits); -+/* faster, but works only if value is "clean", meaning all high bits above nbBits are 0 */ -+ -+ZSTD_STATIC void BIT_flushBitsFast(BIT_CStream_t *bitC); -+/* unsafe version; does not check buffer overflow */ -+ -+ZSTD_STATIC size_t BIT_readBitsFast(BIT_DStream_t *bitD, unsigned nbBits); -+/* faster, but works only if nbBits >= 1 */ -+ -+/*-************************************************************** -+* Internal functions -+****************************************************************/ -+ZSTD_STATIC unsigned BIT_highbit32(register U32 val) { return 31 - __builtin_clz(val); } -+ -+/*===== Local Constants =====*/ -+static const unsigned BIT_mask[] = {0, 1, 3, 7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, -+ 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0x1FFFF, -+ 0x3FFFF, 0x7FFFF, 0xFFFFF, 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF, 0x1FFFFFF, 0x3FFFFFF}; /* up to 26 bits */ -+ -+/*-************************************************************** -+* bitStream encoding -+****************************************************************/ -+/*! BIT_initCStream() : -+ * `dstCapacity` must be > sizeof(void*) -+ * @return : 0 if success, -+ otherwise an error code (can be tested using ERR_isError() ) */ -+ZSTD_STATIC size_t BIT_initCStream(BIT_CStream_t *bitC, void *startPtr, size_t dstCapacity) -+{ -+ bitC->bitContainer = 0; -+ bitC->bitPos = 0; -+ bitC->startPtr = (char *)startPtr; -+ bitC->ptr = bitC->startPtr; -+ bitC->endPtr = bitC->startPtr + dstCapacity - sizeof(bitC->ptr); -+ if (dstCapacity <= sizeof(bitC->ptr)) -+ return ERROR(dstSize_tooSmall); -+ return 0; -+} -+ -+/*! BIT_addBits() : -+ can add up to 26 bits into `bitC`. -+ Does not check for register overflow ! */ -+ZSTD_STATIC void BIT_addBits(BIT_CStream_t *bitC, size_t value, unsigned nbBits) -+{ -+ bitC->bitContainer |= (value & BIT_mask[nbBits]) << bitC->bitPos; -+ bitC->bitPos += nbBits; -+} -+ -+/*! BIT_addBitsFast() : -+ * works only if `value` is _clean_, meaning all high bits above nbBits are 0 */ -+ZSTD_STATIC void BIT_addBitsFast(BIT_CStream_t *bitC, size_t value, unsigned nbBits) -+{ -+ bitC->bitContainer |= value << bitC->bitPos; -+ bitC->bitPos += nbBits; -+} -+ -+/*! BIT_flushBitsFast() : -+ * unsafe version; does not check buffer overflow */ -+ZSTD_STATIC void BIT_flushBitsFast(BIT_CStream_t *bitC) -+{ -+ size_t const nbBytes = bitC->bitPos >> 3; -+ ZSTD_writeLEST(bitC->ptr, bitC->bitContainer); -+ bitC->ptr += nbBytes; -+ bitC->bitPos &= 7; -+ bitC->bitContainer >>= nbBytes * 8; /* if bitPos >= sizeof(bitContainer)*8 --> undefined behavior */ -+} -+ -+/*! BIT_flushBits() : -+ * safe version; check for buffer overflow, and prevents it. -+ * note : does not signal buffer overflow. This will be revealed later on using BIT_closeCStream() */ -+ZSTD_STATIC void BIT_flushBits(BIT_CStream_t *bitC) -+{ -+ size_t const nbBytes = bitC->bitPos >> 3; -+ ZSTD_writeLEST(bitC->ptr, bitC->bitContainer); -+ bitC->ptr += nbBytes; -+ if (bitC->ptr > bitC->endPtr) -+ bitC->ptr = bitC->endPtr; -+ bitC->bitPos &= 7; -+ bitC->bitContainer >>= nbBytes * 8; /* if bitPos >= sizeof(bitContainer)*8 --> undefined behavior */ -+} -+ -+/*! BIT_closeCStream() : -+ * @return : size of CStream, in bytes, -+ or 0 if it could not fit into dstBuffer */ -+ZSTD_STATIC size_t BIT_closeCStream(BIT_CStream_t *bitC) -+{ -+ BIT_addBitsFast(bitC, 1, 1); /* endMark */ -+ BIT_flushBits(bitC); -+ -+ if (bitC->ptr >= bitC->endPtr) -+ return 0; /* doesn't fit within authorized budget : cancel */ -+ -+ return (bitC->ptr - bitC->startPtr) + (bitC->bitPos > 0); -+} -+ -+/*-******************************************************** -+* bitStream decoding -+**********************************************************/ -+/*! BIT_initDStream() : -+* Initialize a BIT_DStream_t. -+* `bitD` : a pointer to an already allocated BIT_DStream_t structure. -+* `srcSize` must be the *exact* size of the bitStream, in bytes. -+* @return : size of stream (== srcSize) or an errorCode if a problem is detected -+*/ -+ZSTD_STATIC size_t BIT_initDStream(BIT_DStream_t *bitD, const void *srcBuffer, size_t srcSize) -+{ -+ if (srcSize < 1) { -+ memset(bitD, 0, sizeof(*bitD)); -+ return ERROR(srcSize_wrong); -+ } -+ -+ if (srcSize >= sizeof(bitD->bitContainer)) { /* normal case */ -+ bitD->start = (const char *)srcBuffer; -+ bitD->ptr = (const char *)srcBuffer + srcSize - sizeof(bitD->bitContainer); -+ bitD->bitContainer = ZSTD_readLEST(bitD->ptr); -+ { -+ BYTE const lastByte = ((const BYTE *)srcBuffer)[srcSize - 1]; -+ bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0; /* ensures bitsConsumed is always set */ -+ if (lastByte == 0) -+ return ERROR(GENERIC); /* endMark not present */ -+ } -+ } else { -+ bitD->start = (const char *)srcBuffer; -+ bitD->ptr = bitD->start; -+ bitD->bitContainer = *(const BYTE *)(bitD->start); -+ switch (srcSize) { -+ case 7: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[6]) << (sizeof(bitD->bitContainer) * 8 - 16); -+ /* fallthrough */ -+ case 6: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[5]) << (sizeof(bitD->bitContainer) * 8 - 24); -+ /* fallthrough */ -+ case 5: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[4]) << (sizeof(bitD->bitContainer) * 8 - 32); -+ /* fallthrough */ -+ case 4: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[3]) << 24; -+ /* fallthrough */ -+ case 3: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[2]) << 16; -+ /* fallthrough */ -+ case 2: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[1]) << 8; -+ /* fallthrough */ -+ default:; -+ } -+ { -+ BYTE const lastByte = ((const BYTE *)srcBuffer)[srcSize - 1]; -+ bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0; -+ if (lastByte == 0) -+ return ERROR(GENERIC); /* endMark not present */ -+ } -+ bitD->bitsConsumed += (U32)(sizeof(bitD->bitContainer) - srcSize) * 8; -+ } -+ -+ return srcSize; -+} -+ -+ZSTD_STATIC size_t BIT_getUpperBits(size_t bitContainer, U32 const start) { return bitContainer >> start; } -+ -+ZSTD_STATIC size_t BIT_getMiddleBits(size_t bitContainer, U32 const start, U32 const nbBits) { return (bitContainer >> start) & BIT_mask[nbBits]; } -+ -+ZSTD_STATIC size_t BIT_getLowerBits(size_t bitContainer, U32 const nbBits) { return bitContainer & BIT_mask[nbBits]; } -+ -+/*! BIT_lookBits() : -+ * Provides next n bits from local register. -+ * local register is not modified. -+ * On 32-bits, maxNbBits==24. -+ * On 64-bits, maxNbBits==56. -+ * @return : value extracted -+ */ -+ZSTD_STATIC size_t BIT_lookBits(const BIT_DStream_t *bitD, U32 nbBits) -+{ -+ U32 const bitMask = sizeof(bitD->bitContainer) * 8 - 1; -+ return ((bitD->bitContainer << (bitD->bitsConsumed & bitMask)) >> 1) >> ((bitMask - nbBits) & bitMask); -+} -+ -+/*! BIT_lookBitsFast() : -+* unsafe version; only works only if nbBits >= 1 */ -+ZSTD_STATIC size_t BIT_lookBitsFast(const BIT_DStream_t *bitD, U32 nbBits) -+{ -+ U32 const bitMask = sizeof(bitD->bitContainer) * 8 - 1; -+ return (bitD->bitContainer << (bitD->bitsConsumed & bitMask)) >> (((bitMask + 1) - nbBits) & bitMask); -+} -+ -+ZSTD_STATIC void BIT_skipBits(BIT_DStream_t *bitD, U32 nbBits) { bitD->bitsConsumed += nbBits; } -+ -+/*! BIT_readBits() : -+ * Read (consume) next n bits from local register and update. -+ * Pay attention to not read more than nbBits contained into local register. -+ * @return : extracted value. -+ */ -+ZSTD_STATIC size_t BIT_readBits(BIT_DStream_t *bitD, U32 nbBits) -+{ -+ size_t const value = BIT_lookBits(bitD, nbBits); -+ BIT_skipBits(bitD, nbBits); -+ return value; -+} -+ -+/*! BIT_readBitsFast() : -+* unsafe version; only works only if nbBits >= 1 */ -+ZSTD_STATIC size_t BIT_readBitsFast(BIT_DStream_t *bitD, U32 nbBits) -+{ -+ size_t const value = BIT_lookBitsFast(bitD, nbBits); -+ BIT_skipBits(bitD, nbBits); -+ return value; -+} -+ -+/*! BIT_reloadDStream() : -+* Refill `bitD` from buffer previously set in BIT_initDStream() . -+* This function is safe, it guarantees it will not read beyond src buffer. -+* @return : status of `BIT_DStream_t` internal register. -+ if status == BIT_DStream_unfinished, internal register is filled with >= (sizeof(bitD->bitContainer)*8 - 7) bits */ -+ZSTD_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t *bitD) -+{ -+ if (bitD->bitsConsumed > (sizeof(bitD->bitContainer) * 8)) /* should not happen => corruption detected */ -+ return BIT_DStream_overflow; -+ -+ if (bitD->ptr >= bitD->start + sizeof(bitD->bitContainer)) { -+ bitD->ptr -= bitD->bitsConsumed >> 3; -+ bitD->bitsConsumed &= 7; -+ bitD->bitContainer = ZSTD_readLEST(bitD->ptr); -+ return BIT_DStream_unfinished; -+ } -+ if (bitD->ptr == bitD->start) { -+ if (bitD->bitsConsumed < sizeof(bitD->bitContainer) * 8) -+ return BIT_DStream_endOfBuffer; -+ return BIT_DStream_completed; -+ } -+ { -+ U32 nbBytes = bitD->bitsConsumed >> 3; -+ BIT_DStream_status result = BIT_DStream_unfinished; -+ if (bitD->ptr - nbBytes < bitD->start) { -+ nbBytes = (U32)(bitD->ptr - bitD->start); /* ptr > start */ -+ result = BIT_DStream_endOfBuffer; -+ } -+ bitD->ptr -= nbBytes; -+ bitD->bitsConsumed -= nbBytes * 8; -+ bitD->bitContainer = ZSTD_readLEST(bitD->ptr); /* reminder : srcSize > sizeof(bitD) */ -+ return result; -+ } -+} -+ -+/*! BIT_endOfDStream() : -+* @return Tells if DStream has exactly reached its end (all bits consumed). -+*/ -+ZSTD_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t *DStream) -+{ -+ return ((DStream->ptr == DStream->start) && (DStream->bitsConsumed == sizeof(DStream->bitContainer) * 8)); -+} -+ -+#endif /* BITSTREAM_H_MODULE */ -diff --git a/xen/common/zstd/decompress.c b/xen/common/zstd/decompress.c -new file mode 100644 -index 0000000000..3d3ef136e5 ---- /dev/null -+++ b/xen/common/zstd/decompress.c -@@ -0,0 +1,2496 @@ -+/** -+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. -+ * All rights reserved. -+ * -+ * This source code is licensed under the BSD-style license found in the -+ * LICENSE file in the root directory of https://github.com/facebook/zstd. -+ * An additional grant of patent rights can be found in the PATENTS file in the -+ * same directory. -+ * -+ * This program is free software; you can redistribute it and/or modify it under -+ * the terms of the GNU General Public License version 2 as published by the -+ * Free Software Foundation. This program is dual-licensed; you may select -+ * either version 2 of the GNU General Public License ("GPL") or BSD license -+ * ("BSD"). -+ */ -+ -+/* *************************************************************** -+* Tuning parameters -+*****************************************************************/ -+/*! -+* MAXWINDOWSIZE_DEFAULT : -+* maximum window size accepted by DStream, by default. -+* Frames requiring more memory will be rejected. -+*/ -+#ifndef ZSTD_MAXWINDOWSIZE_DEFAULT -+#define ZSTD_MAXWINDOWSIZE_DEFAULT ((1 << ZSTD_WINDOWLOG_MAX) + 1) /* defined within zstd.h */ -+#endif -+ -+/*-******************************************************* -+* Dependencies -+*********************************************************/ -+#include "fse.h" -+#include "huf.h" -+#include "mem.h" /* low level memory routines */ -+#include "zstd_internal.h" -+#include /* memcpy, memmove, memset */ -+ -+#define ZSTD_PREFETCH(ptr) __builtin_prefetch(ptr, 0, 0) -+ -+/*-************************************* -+* Macros -+***************************************/ -+#define ZSTD_isError ERR_isError /* for inlining */ -+#define FSE_isError ERR_isError -+#define HUF_isError ERR_isError -+ -+/*_******************************************************* -+* Memory operations -+**********************************************************/ -+static void INIT ZSTD_copy4(void *dst, const void *src) { memcpy(dst, src, 4); } -+ -+/*-************************************************************* -+* Context management -+***************************************************************/ -+typedef enum { -+ ZSTDds_getFrameHeaderSize, -+ ZSTDds_decodeFrameHeader, -+ ZSTDds_decodeBlockHeader, -+ ZSTDds_decompressBlock, -+ ZSTDds_decompressLastBlock, -+ ZSTDds_checkChecksum, -+ ZSTDds_decodeSkippableHeader, -+ ZSTDds_skipFrame -+} ZSTD_dStage; -+ -+typedef struct { -+ FSE_DTable LLTable[FSE_DTABLE_SIZE_U32(LLFSELog)]; -+ FSE_DTable OFTable[FSE_DTABLE_SIZE_U32(OffFSELog)]; -+ FSE_DTable MLTable[FSE_DTABLE_SIZE_U32(MLFSELog)]; -+ HUF_DTable hufTable[HUF_DTABLE_SIZE(HufLog)]; /* can accommodate HUF_decompress4X */ -+ U64 workspace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32 / 2]; -+ U32 rep[ZSTD_REP_NUM]; -+} ZSTD_entropyTables_t; -+ -+struct ZSTD_DCtx_s { -+ const FSE_DTable *LLTptr; -+ const FSE_DTable *MLTptr; -+ const FSE_DTable *OFTptr; -+ const HUF_DTable *HUFptr; -+ ZSTD_entropyTables_t entropy; -+ const void *previousDstEnd; /* detect continuity */ -+ const void *base; /* start of curr segment */ -+ const void *vBase; /* virtual start of previous segment if it was just before curr one */ -+ const void *dictEnd; /* end of previous segment */ -+ size_t expected; -+ ZSTD_frameParams fParams; -+ blockType_e bType; /* used in ZSTD_decompressContinue(), to transfer blockType between header decoding and block decoding stages */ -+ ZSTD_dStage stage; -+ U32 litEntropy; -+ U32 fseEntropy; -+ struct xxh64_state xxhState; -+ size_t headerSize; -+ U32 dictID; -+ const BYTE *litPtr; -+ ZSTD_customMem customMem; -+ size_t litSize; -+ size_t rleSize; -+ BYTE litBuffer[ZSTD_BLOCKSIZE_ABSOLUTEMAX + WILDCOPY_OVERLENGTH]; -+ BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX]; -+}; /* typedef'd to ZSTD_DCtx within "zstd.h" */ -+ -+size_t INIT ZSTD_DCtxWorkspaceBound(void) { return ZSTD_ALIGN(sizeof(ZSTD_stack)) + ZSTD_ALIGN(sizeof(ZSTD_DCtx)); } -+ -+size_t INIT ZSTD_decompressBegin(ZSTD_DCtx *dctx) -+{ -+ dctx->expected = ZSTD_frameHeaderSize_prefix; -+ dctx->stage = ZSTDds_getFrameHeaderSize; -+ dctx->previousDstEnd = NULL; -+ dctx->base = NULL; -+ dctx->vBase = NULL; -+ dctx->dictEnd = NULL; -+ dctx->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001); /* cover both little and big endian */ -+ dctx->litEntropy = dctx->fseEntropy = 0; -+ dctx->dictID = 0; -+ ZSTD_STATIC_ASSERT(sizeof(dctx->entropy.rep) == sizeof(repStartValue)); -+ memcpy(dctx->entropy.rep, repStartValue, sizeof(repStartValue)); /* initial repcodes */ -+ dctx->LLTptr = dctx->entropy.LLTable; -+ dctx->MLTptr = dctx->entropy.MLTable; -+ dctx->OFTptr = dctx->entropy.OFTable; -+ dctx->HUFptr = dctx->entropy.hufTable; -+ return 0; -+} -+ -+ZSTD_DCtx *INIT ZSTD_createDCtx_advanced(ZSTD_customMem customMem) -+{ -+ ZSTD_DCtx *dctx; -+ -+ if (!customMem.customAlloc || !customMem.customFree) -+ return NULL; -+ -+ dctx = (ZSTD_DCtx *)ZSTD_malloc(sizeof(ZSTD_DCtx), customMem); -+ if (!dctx) -+ return NULL; -+ memcpy(&dctx->customMem, &customMem, sizeof(customMem)); -+ ZSTD_decompressBegin(dctx); -+ return dctx; -+} -+ -+ZSTD_DCtx *INIT ZSTD_initDCtx(void *workspace, size_t workspaceSize) -+{ -+ ZSTD_customMem const stackMem = ZSTD_initStack(workspace, workspaceSize); -+ return ZSTD_createDCtx_advanced(stackMem); -+} -+ -+size_t INIT ZSTD_freeDCtx(ZSTD_DCtx *dctx) -+{ -+ if (dctx == NULL) -+ return 0; /* support free on NULL */ -+ ZSTD_free(dctx, dctx->customMem); -+ return 0; /* reserved as a potential error code in the future */ -+} -+ -+void INIT ZSTD_copyDCtx(ZSTD_DCtx *dstDCtx, const ZSTD_DCtx *srcDCtx) -+{ -+ size_t const workSpaceSize = (ZSTD_BLOCKSIZE_ABSOLUTEMAX + WILDCOPY_OVERLENGTH) + ZSTD_frameHeaderSize_max; -+ memcpy(dstDCtx, srcDCtx, sizeof(ZSTD_DCtx) - workSpaceSize); /* no need to copy workspace */ -+} -+ -+STATIC size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize); -+STATIC size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx *dctx, const void *dict, -+ size_t dictSize); -+ -+static void ZSTD_refDDict(ZSTD_DCtx *dstDCtx, const ZSTD_DDict *ddict); -+ -+/*-************************************************************* -+* Decompression section -+***************************************************************/ -+ -+/*! ZSTD_isFrame() : -+ * Tells if the content of `buffer` starts with a valid Frame Identifier. -+ * Note : Frame Identifier is 4 bytes. If `size < 4`, @return will always be 0. -+ * Note 2 : Legacy Frame Identifiers are considered valid only if Legacy Support is enabled. -+ * Note 3 : Skippable Frame Identifiers are considered valid. */ -+unsigned INIT ZSTD_isFrame(const void *buffer, size_t size) -+{ -+ if (size < 4) -+ return 0; -+ { -+ U32 const magic = ZSTD_readLE32(buffer); -+ if (magic == ZSTD_MAGICNUMBER) -+ return 1; -+ if ((magic & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) -+ return 1; -+ } -+ return 0; -+} -+ -+/** ZSTD_frameHeaderSize() : -+* srcSize must be >= ZSTD_frameHeaderSize_prefix. -+* @return : size of the Frame Header */ -+static size_t INIT ZSTD_frameHeaderSize(const void *src, size_t srcSize) -+{ -+ if (srcSize < ZSTD_frameHeaderSize_prefix) -+ return ERROR(srcSize_wrong); -+ { -+ BYTE const fhd = ((const BYTE *)src)[4]; -+ U32 const dictID = fhd & 3; -+ U32 const singleSegment = (fhd >> 5) & 1; -+ U32 const fcsId = fhd >> 6; -+ return ZSTD_frameHeaderSize_prefix + !singleSegment + ZSTD_did_fieldSize[dictID] + ZSTD_fcs_fieldSize[fcsId] + (singleSegment && !fcsId); -+ } -+} -+ -+/** ZSTD_getFrameParams() : -+* decode Frame Header, or require larger `srcSize`. -+* @return : 0, `fparamsPtr` is correctly filled, -+* >0, `srcSize` is too small, result is expected `srcSize`, -+* or an error code, which can be tested using ZSTD_isError() */ -+size_t INIT ZSTD_getFrameParams(ZSTD_frameParams *fparamsPtr, const void *src, size_t srcSize) -+{ -+ const BYTE *ip = (const BYTE *)src; -+ -+ if (srcSize < ZSTD_frameHeaderSize_prefix) -+ return ZSTD_frameHeaderSize_prefix; -+ if (ZSTD_readLE32(src) != ZSTD_MAGICNUMBER) { -+ if ((ZSTD_readLE32(src) & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) { -+ if (srcSize < ZSTD_skippableHeaderSize) -+ return ZSTD_skippableHeaderSize; /* magic number + skippable frame length */ -+ memset(fparamsPtr, 0, sizeof(*fparamsPtr)); -+ fparamsPtr->frameContentSize = ZSTD_readLE32((const char *)src + 4); -+ fparamsPtr->windowSize = 0; /* windowSize==0 means a frame is skippable */ -+ return 0; -+ } -+ return ERROR(prefix_unknown); -+ } -+ -+ /* ensure there is enough `srcSize` to fully read/decode frame header */ -+ { -+ size_t const fhsize = ZSTD_frameHeaderSize(src, srcSize); -+ if (srcSize < fhsize) -+ return fhsize; -+ } -+ -+ { -+ BYTE const fhdByte = ip[4]; -+ size_t pos = 5; -+ U32 const dictIDSizeCode = fhdByte & 3; -+ U32 const checksumFlag = (fhdByte >> 2) & 1; -+ U32 const singleSegment = (fhdByte >> 5) & 1; -+ U32 const fcsID = fhdByte >> 6; -+ U32 const windowSizeMax = 1U << ZSTD_WINDOWLOG_MAX; -+ U32 windowSize = 0; -+ U32 dictID = 0; -+ U64 frameContentSize = 0; -+ if ((fhdByte & 0x08) != 0) -+ return ERROR(frameParameter_unsupported); /* reserved bits, which must be zero */ -+ if (!singleSegment) { -+ BYTE const wlByte = ip[pos++]; -+ U32 const windowLog = (wlByte >> 3) + ZSTD_WINDOWLOG_ABSOLUTEMIN; -+ if (windowLog > ZSTD_WINDOWLOG_MAX) -+ return ERROR(frameParameter_windowTooLarge); /* avoids issue with 1 << windowLog */ -+ windowSize = (1U << windowLog); -+ windowSize += (windowSize >> 3) * (wlByte & 7); -+ } -+ -+ switch (dictIDSizeCode) { -+ default: /* impossible */ -+ case 0: break; -+ case 1: -+ dictID = ip[pos]; -+ pos++; -+ break; -+ case 2: -+ dictID = ZSTD_readLE16(ip + pos); -+ pos += 2; -+ break; -+ case 3: -+ dictID = ZSTD_readLE32(ip + pos); -+ pos += 4; -+ break; -+ } -+ switch (fcsID) { -+ default: /* impossible */ -+ case 0: -+ if (singleSegment) -+ frameContentSize = ip[pos]; -+ break; -+ case 1: frameContentSize = ZSTD_readLE16(ip + pos) + 256; break; -+ case 2: frameContentSize = ZSTD_readLE32(ip + pos); break; -+ case 3: frameContentSize = ZSTD_readLE64(ip + pos); break; -+ } -+ if (!windowSize) -+ windowSize = (U32)frameContentSize; -+ if (windowSize > windowSizeMax) -+ return ERROR(frameParameter_windowTooLarge); -+ fparamsPtr->frameContentSize = frameContentSize; -+ fparamsPtr->windowSize = windowSize; -+ fparamsPtr->dictID = dictID; -+ fparamsPtr->checksumFlag = checksumFlag; -+ } -+ return 0; -+} -+ -+/** ZSTD_getFrameContentSize() : -+* compatible with legacy mode -+* @return : decompressed size of the single frame pointed to be `src` if known, otherwise -+* - ZSTD_CONTENTSIZE_UNKNOWN if the size cannot be determined -+* - ZSTD_CONTENTSIZE_ERROR if an error occurred (e.g. invalid magic number, srcSize too small) */ -+unsigned long long INIT ZSTD_getFrameContentSize(const void *src, size_t srcSize) -+{ -+ { -+ ZSTD_frameParams fParams; -+ if (ZSTD_getFrameParams(&fParams, src, srcSize) != 0) -+ return ZSTD_CONTENTSIZE_ERROR; -+ if (fParams.windowSize == 0) { -+ /* Either skippable or empty frame, size == 0 either way */ -+ return 0; -+ } else if (fParams.frameContentSize != 0) { -+ return fParams.frameContentSize; -+ } else { -+ return ZSTD_CONTENTSIZE_UNKNOWN; -+ } -+ } -+} -+ -+/** ZSTD_findDecompressedSize() : -+ * compatible with legacy mode -+ * `srcSize` must be the exact length of some number of ZSTD compressed and/or -+ * skippable frames -+ * @return : decompressed size of the frames contained */ -+unsigned long long INIT ZSTD_findDecompressedSize(const void *src, size_t srcSize) -+{ -+ { -+ unsigned long long totalDstSize = 0; -+ while (srcSize >= ZSTD_frameHeaderSize_prefix) { -+ const U32 magicNumber = ZSTD_readLE32(src); -+ -+ if ((magicNumber & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) { -+ size_t skippableSize; -+ if (srcSize < ZSTD_skippableHeaderSize) -+ return ERROR(srcSize_wrong); -+ skippableSize = ZSTD_readLE32((const BYTE *)src + 4) + ZSTD_skippableHeaderSize; -+ if (srcSize < skippableSize) { -+ return ZSTD_CONTENTSIZE_ERROR; -+ } -+ -+ src = (const BYTE *)src + skippableSize; -+ srcSize -= skippableSize; -+ continue; -+ } -+ -+ { -+ unsigned long long const ret = ZSTD_getFrameContentSize(src, srcSize); -+ if (ret >= ZSTD_CONTENTSIZE_ERROR) -+ return ret; -+ -+ /* check for overflow */ -+ if (totalDstSize + ret < totalDstSize) -+ return ZSTD_CONTENTSIZE_ERROR; -+ totalDstSize += ret; -+ } -+ { -+ size_t const frameSrcSize = ZSTD_findFrameCompressedSize(src, srcSize); -+ if (ZSTD_isError(frameSrcSize)) { -+ return ZSTD_CONTENTSIZE_ERROR; -+ } -+ -+ src = (const BYTE *)src + frameSrcSize; -+ srcSize -= frameSrcSize; -+ } -+ } -+ -+ if (srcSize) { -+ return ZSTD_CONTENTSIZE_ERROR; -+ } -+ -+ return totalDstSize; -+ } -+} -+ -+/** ZSTD_decodeFrameHeader() : -+* `headerSize` must be the size provided by ZSTD_frameHeaderSize(). -+* @return : 0 if success, or an error code, which can be tested using ZSTD_isError() */ -+static size_t INIT ZSTD_decodeFrameHeader(ZSTD_DCtx *dctx, const void *src, size_t headerSize) -+{ -+ size_t const result = ZSTD_getFrameParams(&(dctx->fParams), src, headerSize); -+ if (ZSTD_isError(result)) -+ return result; /* invalid header */ -+ if (result > 0) -+ return ERROR(srcSize_wrong); /* headerSize too small */ -+ if (dctx->fParams.dictID && (dctx->dictID != dctx->fParams.dictID)) -+ return ERROR(dictionary_wrong); -+ if (dctx->fParams.checksumFlag) -+ xxh64_reset(&dctx->xxhState, 0); -+ return 0; -+} -+ -+typedef struct { -+ blockType_e blockType; -+ U32 lastBlock; -+ U32 origSize; -+} blockProperties_t; -+ -+/*! ZSTD_getcBlockSize() : -+* Provides the size of compressed block from block header `src` */ -+size_t INIT ZSTD_getcBlockSize(const void *src, size_t srcSize, blockProperties_t *bpPtr) -+{ -+ if (srcSize < ZSTD_blockHeaderSize) -+ return ERROR(srcSize_wrong); -+ { -+ U32 const cBlockHeader = ZSTD_readLE24(src); -+ U32 const cSize = cBlockHeader >> 3; -+ bpPtr->lastBlock = cBlockHeader & 1; -+ bpPtr->blockType = (blockType_e)((cBlockHeader >> 1) & 3); -+ bpPtr->origSize = cSize; /* only useful for RLE */ -+ if (bpPtr->blockType == bt_rle) -+ return 1; -+ if (bpPtr->blockType == bt_reserved) -+ return ERROR(corruption_detected); -+ return cSize; -+ } -+} -+ -+static size_t INIT ZSTD_copyRawBlock(void *dst, size_t dstCapacity, const void *src, size_t srcSize) -+{ -+ if (srcSize > dstCapacity) -+ return ERROR(dstSize_tooSmall); -+ memcpy(dst, src, srcSize); -+ return srcSize; -+} -+ -+static size_t INIT ZSTD_setRleBlock(void *dst, size_t dstCapacity, const void *src, size_t srcSize, size_t regenSize) -+{ -+ if (srcSize != 1) -+ return ERROR(srcSize_wrong); -+ if (regenSize > dstCapacity) -+ return ERROR(dstSize_tooSmall); -+ memset(dst, *(const BYTE *)src, regenSize); -+ return regenSize; -+} -+ -+/*! ZSTD_decodeLiteralsBlock() : -+ @return : nb of bytes read from src (< srcSize ) */ -+size_t INIT ZSTD_decodeLiteralsBlock(ZSTD_DCtx *dctx, const void *src, size_t srcSize) /* note : srcSize < BLOCKSIZE */ -+{ -+ if (srcSize < MIN_CBLOCK_SIZE) -+ return ERROR(corruption_detected); -+ -+ { -+ const BYTE *const istart = (const BYTE *)src; -+ symbolEncodingType_e const litEncType = (symbolEncodingType_e)(istart[0] & 3); -+ -+ switch (litEncType) { -+ case set_repeat: -+ if (dctx->litEntropy == 0) -+ return ERROR(dictionary_corrupted); -+ /* fallthrough */ -+ case set_compressed: -+ if (srcSize < 5) -+ return ERROR(corruption_detected); /* srcSize >= MIN_CBLOCK_SIZE == 3; here we need up to 5 for case 3 */ -+ { -+ size_t lhSize, litSize, litCSize; -+ U32 singleStream = 0; -+ U32 const lhlCode = (istart[0] >> 2) & 3; -+ U32 const lhc = ZSTD_readLE32(istart); -+ switch (lhlCode) { -+ case 0: -+ case 1: -+ default: /* note : default is impossible, since lhlCode into [0..3] */ -+ /* 2 - 2 - 10 - 10 */ -+ singleStream = !lhlCode; -+ lhSize = 3; -+ litSize = (lhc >> 4) & 0x3FF; -+ litCSize = (lhc >> 14) & 0x3FF; -+ break; -+ case 2: -+ /* 2 - 2 - 14 - 14 */ -+ lhSize = 4; -+ litSize = (lhc >> 4) & 0x3FFF; -+ litCSize = lhc >> 18; -+ break; -+ case 3: -+ /* 2 - 2 - 18 - 18 */ -+ lhSize = 5; -+ litSize = (lhc >> 4) & 0x3FFFF; -+ litCSize = (lhc >> 22) + (istart[4] << 10); -+ break; -+ } -+ if (litSize > ZSTD_BLOCKSIZE_ABSOLUTEMAX) -+ return ERROR(corruption_detected); -+ if (litCSize + lhSize > srcSize) -+ return ERROR(corruption_detected); -+ -+ if (HUF_isError( -+ (litEncType == set_repeat) -+ ? (singleStream ? HUF_decompress1X_usingDTable(dctx->litBuffer, litSize, istart + lhSize, litCSize, dctx->HUFptr) -+ : HUF_decompress4X_usingDTable(dctx->litBuffer, litSize, istart + lhSize, litCSize, dctx->HUFptr)) -+ : (singleStream -+ ? HUF_decompress1X2_DCtx_wksp(dctx->entropy.hufTable, dctx->litBuffer, litSize, istart + lhSize, litCSize, -+ dctx->entropy.workspace, sizeof(dctx->entropy.workspace)) -+ : HUF_decompress4X_hufOnly_wksp(dctx->entropy.hufTable, dctx->litBuffer, litSize, istart + lhSize, litCSize, -+ dctx->entropy.workspace, sizeof(dctx->entropy.workspace))))) -+ return ERROR(corruption_detected); -+ -+ dctx->litPtr = dctx->litBuffer; -+ dctx->litSize = litSize; -+ dctx->litEntropy = 1; -+ if (litEncType == set_compressed) -+ dctx->HUFptr = dctx->entropy.hufTable; -+ memset(dctx->litBuffer + dctx->litSize, 0, WILDCOPY_OVERLENGTH); -+ return litCSize + lhSize; -+ } -+ -+ case set_basic: { -+ size_t litSize, lhSize; -+ U32 const lhlCode = ((istart[0]) >> 2) & 3; -+ switch (lhlCode) { -+ case 0: -+ case 2: -+ default: /* note : default is impossible, since lhlCode into [0..3] */ -+ lhSize = 1; -+ litSize = istart[0] >> 3; -+ break; -+ case 1: -+ lhSize = 2; -+ litSize = ZSTD_readLE16(istart) >> 4; -+ break; -+ case 3: -+ lhSize = 3; -+ litSize = ZSTD_readLE24(istart) >> 4; -+ break; -+ } -+ -+ if (lhSize + litSize + WILDCOPY_OVERLENGTH > srcSize) { /* risk reading beyond src buffer with wildcopy */ -+ if (litSize + lhSize > srcSize) -+ return ERROR(corruption_detected); -+ memcpy(dctx->litBuffer, istart + lhSize, litSize); -+ dctx->litPtr = dctx->litBuffer; -+ dctx->litSize = litSize; -+ memset(dctx->litBuffer + dctx->litSize, 0, WILDCOPY_OVERLENGTH); -+ return lhSize + litSize; -+ } -+ /* direct reference into compressed stream */ -+ dctx->litPtr = istart + lhSize; -+ dctx->litSize = litSize; -+ return lhSize + litSize; -+ } -+ -+ case set_rle: { -+ U32 const lhlCode = ((istart[0]) >> 2) & 3; -+ size_t litSize, lhSize; -+ switch (lhlCode) { -+ case 0: -+ case 2: -+ default: /* note : default is impossible, since lhlCode into [0..3] */ -+ lhSize = 1; -+ litSize = istart[0] >> 3; -+ break; -+ case 1: -+ lhSize = 2; -+ litSize = ZSTD_readLE16(istart) >> 4; -+ break; -+ case 3: -+ lhSize = 3; -+ litSize = ZSTD_readLE24(istart) >> 4; -+ if (srcSize < 4) -+ return ERROR(corruption_detected); /* srcSize >= MIN_CBLOCK_SIZE == 3; here we need lhSize+1 = 4 */ -+ break; -+ } -+ if (litSize > ZSTD_BLOCKSIZE_ABSOLUTEMAX) -+ return ERROR(corruption_detected); -+ memset(dctx->litBuffer, istart[lhSize], litSize + WILDCOPY_OVERLENGTH); -+ dctx->litPtr = dctx->litBuffer; -+ dctx->litSize = litSize; -+ return lhSize + 1; -+ } -+ default: -+ return ERROR(corruption_detected); /* impossible */ -+ } -+ } -+} -+ -+typedef union { -+ FSE_decode_t realData; -+ U32 alignedBy4; -+} FSE_decode_t4; -+ -+static const FSE_decode_t4 LL_defaultDTable[(1 << LL_DEFAULTNORMLOG) + 1] = { -+ {{LL_DEFAULTNORMLOG, 1, 1}}, /* header : tableLog, fastMode, fastMode */ -+ {{0, 0, 4}}, /* 0 : base, symbol, bits */ -+ {{16, 0, 4}}, -+ {{32, 1, 5}}, -+ {{0, 3, 5}}, -+ {{0, 4, 5}}, -+ {{0, 6, 5}}, -+ {{0, 7, 5}}, -+ {{0, 9, 5}}, -+ {{0, 10, 5}}, -+ {{0, 12, 5}}, -+ {{0, 14, 6}}, -+ {{0, 16, 5}}, -+ {{0, 18, 5}}, -+ {{0, 19, 5}}, -+ {{0, 21, 5}}, -+ {{0, 22, 5}}, -+ {{0, 24, 5}}, -+ {{32, 25, 5}}, -+ {{0, 26, 5}}, -+ {{0, 27, 6}}, -+ {{0, 29, 6}}, -+ {{0, 31, 6}}, -+ {{32, 0, 4}}, -+ {{0, 1, 4}}, -+ {{0, 2, 5}}, -+ {{32, 4, 5}}, -+ {{0, 5, 5}}, -+ {{32, 7, 5}}, -+ {{0, 8, 5}}, -+ {{32, 10, 5}}, -+ {{0, 11, 5}}, -+ {{0, 13, 6}}, -+ {{32, 16, 5}}, -+ {{0, 17, 5}}, -+ {{32, 19, 5}}, -+ {{0, 20, 5}}, -+ {{32, 22, 5}}, -+ {{0, 23, 5}}, -+ {{0, 25, 4}}, -+ {{16, 25, 4}}, -+ {{32, 26, 5}}, -+ {{0, 28, 6}}, -+ {{0, 30, 6}}, -+ {{48, 0, 4}}, -+ {{16, 1, 4}}, -+ {{32, 2, 5}}, -+ {{32, 3, 5}}, -+ {{32, 5, 5}}, -+ {{32, 6, 5}}, -+ {{32, 8, 5}}, -+ {{32, 9, 5}}, -+ {{32, 11, 5}}, -+ {{32, 12, 5}}, -+ {{0, 15, 6}}, -+ {{32, 17, 5}}, -+ {{32, 18, 5}}, -+ {{32, 20, 5}}, -+ {{32, 21, 5}}, -+ {{32, 23, 5}}, -+ {{32, 24, 5}}, -+ {{0, 35, 6}}, -+ {{0, 34, 6}}, -+ {{0, 33, 6}}, -+ {{0, 32, 6}}, -+}; /* LL_defaultDTable */ -+ -+static const FSE_decode_t4 ML_defaultDTable[(1 << ML_DEFAULTNORMLOG) + 1] = { -+ {{ML_DEFAULTNORMLOG, 1, 1}}, /* header : tableLog, fastMode, fastMode */ -+ {{0, 0, 6}}, /* 0 : base, symbol, bits */ -+ {{0, 1, 4}}, -+ {{32, 2, 5}}, -+ {{0, 3, 5}}, -+ {{0, 5, 5}}, -+ {{0, 6, 5}}, -+ {{0, 8, 5}}, -+ {{0, 10, 6}}, -+ {{0, 13, 6}}, -+ {{0, 16, 6}}, -+ {{0, 19, 6}}, -+ {{0, 22, 6}}, -+ {{0, 25, 6}}, -+ {{0, 28, 6}}, -+ {{0, 31, 6}}, -+ {{0, 33, 6}}, -+ {{0, 35, 6}}, -+ {{0, 37, 6}}, -+ {{0, 39, 6}}, -+ {{0, 41, 6}}, -+ {{0, 43, 6}}, -+ {{0, 45, 6}}, -+ {{16, 1, 4}}, -+ {{0, 2, 4}}, -+ {{32, 3, 5}}, -+ {{0, 4, 5}}, -+ {{32, 6, 5}}, -+ {{0, 7, 5}}, -+ {{0, 9, 6}}, -+ {{0, 12, 6}}, -+ {{0, 15, 6}}, -+ {{0, 18, 6}}, -+ {{0, 21, 6}}, -+ {{0, 24, 6}}, -+ {{0, 27, 6}}, -+ {{0, 30, 6}}, -+ {{0, 32, 6}}, -+ {{0, 34, 6}}, -+ {{0, 36, 6}}, -+ {{0, 38, 6}}, -+ {{0, 40, 6}}, -+ {{0, 42, 6}}, -+ {{0, 44, 6}}, -+ {{32, 1, 4}}, -+ {{48, 1, 4}}, -+ {{16, 2, 4}}, -+ {{32, 4, 5}}, -+ {{32, 5, 5}}, -+ {{32, 7, 5}}, -+ {{32, 8, 5}}, -+ {{0, 11, 6}}, -+ {{0, 14, 6}}, -+ {{0, 17, 6}}, -+ {{0, 20, 6}}, -+ {{0, 23, 6}}, -+ {{0, 26, 6}}, -+ {{0, 29, 6}}, -+ {{0, 52, 6}}, -+ {{0, 51, 6}}, -+ {{0, 50, 6}}, -+ {{0, 49, 6}}, -+ {{0, 48, 6}}, -+ {{0, 47, 6}}, -+ {{0, 46, 6}}, -+}; /* ML_defaultDTable */ -+ -+static const FSE_decode_t4 OF_defaultDTable[(1 << OF_DEFAULTNORMLOG) + 1] = { -+ {{OF_DEFAULTNORMLOG, 1, 1}}, /* header : tableLog, fastMode, fastMode */ -+ {{0, 0, 5}}, /* 0 : base, symbol, bits */ -+ {{0, 6, 4}}, -+ {{0, 9, 5}}, -+ {{0, 15, 5}}, -+ {{0, 21, 5}}, -+ {{0, 3, 5}}, -+ {{0, 7, 4}}, -+ {{0, 12, 5}}, -+ {{0, 18, 5}}, -+ {{0, 23, 5}}, -+ {{0, 5, 5}}, -+ {{0, 8, 4}}, -+ {{0, 14, 5}}, -+ {{0, 20, 5}}, -+ {{0, 2, 5}}, -+ {{16, 7, 4}}, -+ {{0, 11, 5}}, -+ {{0, 17, 5}}, -+ {{0, 22, 5}}, -+ {{0, 4, 5}}, -+ {{16, 8, 4}}, -+ {{0, 13, 5}}, -+ {{0, 19, 5}}, -+ {{0, 1, 5}}, -+ {{16, 6, 4}}, -+ {{0, 10, 5}}, -+ {{0, 16, 5}}, -+ {{0, 28, 5}}, -+ {{0, 27, 5}}, -+ {{0, 26, 5}}, -+ {{0, 25, 5}}, -+ {{0, 24, 5}}, -+}; /* OF_defaultDTable */ -+ -+/*! ZSTD_buildSeqTable() : -+ @return : nb bytes read from src, -+ or an error code if it fails, testable with ZSTD_isError() -+*/ -+static size_t INIT ZSTD_buildSeqTable(FSE_DTable *DTableSpace, const FSE_DTable **DTablePtr, -+ symbolEncodingType_e type, U32 max, U32 maxLog, const void *src, -+ size_t srcSize, const FSE_decode_t4 *defaultTable, -+ U32 flagRepeatTable, void *workspace, size_t workspaceSize) -+{ -+ const void *const tmpPtr = defaultTable; /* bypass strict aliasing */ -+ switch (type) { -+ case set_rle: -+ if (!srcSize) -+ return ERROR(srcSize_wrong); -+ if ((*(const BYTE *)src) > max) -+ return ERROR(corruption_detected); -+ FSE_buildDTable_rle(DTableSpace, *(const BYTE *)src); -+ *DTablePtr = DTableSpace; -+ return 1; -+ case set_basic: *DTablePtr = (const FSE_DTable *)tmpPtr; return 0; -+ case set_repeat: -+ if (!flagRepeatTable) -+ return ERROR(corruption_detected); -+ return 0; -+ default: /* impossible */ -+ case set_compressed: { -+ U32 tableLog; -+ S16 *norm = (S16 *)workspace; -+ size_t const spaceUsed32 = ALIGN(sizeof(S16) * (MaxSeq + 1), sizeof(U32)) >> 2; -+ -+ if ((spaceUsed32 << 2) > workspaceSize) -+ return ERROR(GENERIC); -+ workspace = (U32 *)workspace + spaceUsed32; -+ workspaceSize -= (spaceUsed32 << 2); -+ { -+ size_t const headerSize = FSE_readNCount(norm, &max, &tableLog, src, srcSize); -+ if (FSE_isError(headerSize)) -+ return ERROR(corruption_detected); -+ if (tableLog > maxLog) -+ return ERROR(corruption_detected); -+ FSE_buildDTable_wksp(DTableSpace, norm, max, tableLog, workspace, workspaceSize); -+ *DTablePtr = DTableSpace; -+ return headerSize; -+ } -+ } -+ } -+} -+ -+size_t INIT ZSTD_decodeSeqHeaders(ZSTD_DCtx *dctx, int *nbSeqPtr, const void *src, size_t srcSize) -+{ -+ const BYTE *const istart = (const BYTE *const)src; -+ const BYTE *const iend = istart + srcSize; -+ const BYTE *ip = istart; -+ -+ /* check */ -+ if (srcSize < MIN_SEQUENCES_SIZE) -+ return ERROR(srcSize_wrong); -+ -+ /* SeqHead */ -+ { -+ int nbSeq = *ip++; -+ if (!nbSeq) { -+ *nbSeqPtr = 0; -+ return 1; -+ } -+ if (nbSeq > 0x7F) { -+ if (nbSeq == 0xFF) { -+ if (ip + 2 > iend) -+ return ERROR(srcSize_wrong); -+ nbSeq = ZSTD_readLE16(ip) + LONGNBSEQ, ip += 2; -+ } else { -+ if (ip >= iend) -+ return ERROR(srcSize_wrong); -+ nbSeq = ((nbSeq - 0x80) << 8) + *ip++; -+ } -+ } -+ *nbSeqPtr = nbSeq; -+ } -+ -+ /* FSE table descriptors */ -+ if (ip + 4 > iend) -+ return ERROR(srcSize_wrong); /* minimum possible size */ -+ { -+ symbolEncodingType_e const LLtype = (symbolEncodingType_e)(*ip >> 6); -+ symbolEncodingType_e const OFtype = (symbolEncodingType_e)((*ip >> 4) & 3); -+ symbolEncodingType_e const MLtype = (symbolEncodingType_e)((*ip >> 2) & 3); -+ ip++; -+ -+ /* Build DTables */ -+ { -+ size_t const llhSize = ZSTD_buildSeqTable(dctx->entropy.LLTable, &dctx->LLTptr, LLtype, MaxLL, LLFSELog, ip, iend - ip, -+ LL_defaultDTable, dctx->fseEntropy, dctx->entropy.workspace, sizeof(dctx->entropy.workspace)); -+ if (ZSTD_isError(llhSize)) -+ return ERROR(corruption_detected); -+ ip += llhSize; -+ } -+ { -+ size_t const ofhSize = ZSTD_buildSeqTable(dctx->entropy.OFTable, &dctx->OFTptr, OFtype, MaxOff, OffFSELog, ip, iend - ip, -+ OF_defaultDTable, dctx->fseEntropy, dctx->entropy.workspace, sizeof(dctx->entropy.workspace)); -+ if (ZSTD_isError(ofhSize)) -+ return ERROR(corruption_detected); -+ ip += ofhSize; -+ } -+ { -+ size_t const mlhSize = ZSTD_buildSeqTable(dctx->entropy.MLTable, &dctx->MLTptr, MLtype, MaxML, MLFSELog, ip, iend - ip, -+ ML_defaultDTable, dctx->fseEntropy, dctx->entropy.workspace, sizeof(dctx->entropy.workspace)); -+ if (ZSTD_isError(mlhSize)) -+ return ERROR(corruption_detected); -+ ip += mlhSize; -+ } -+ } -+ -+ return ip - istart; -+} -+ -+typedef struct { -+ size_t litLength; -+ size_t matchLength; -+ size_t offset; -+ const BYTE *match; -+} seq_t; -+ -+typedef struct { -+ BIT_DStream_t DStream; -+ FSE_DState_t stateLL; -+ FSE_DState_t stateOffb; -+ FSE_DState_t stateML; -+ size_t prevOffset[ZSTD_REP_NUM]; -+ const BYTE *base; -+ size_t pos; -+ uPtrDiff gotoDict; -+} seqState_t; -+ -+FORCE_NOINLINE -+size_t ZSTD_execSequenceLast7(BYTE *op, BYTE *const oend, seq_t sequence, const BYTE **litPtr, const BYTE *const litLimit, const BYTE *const base, -+ const BYTE *const vBase, const BYTE *const dictEnd) -+{ -+ BYTE *const oLitEnd = op + sequence.litLength; -+ size_t const sequenceLength = sequence.litLength + sequence.matchLength; -+ BYTE *const oMatchEnd = op + sequenceLength; /* risk : address space overflow (32-bits) */ -+ BYTE *const oend_w = oend - WILDCOPY_OVERLENGTH; -+ const BYTE *const iLitEnd = *litPtr + sequence.litLength; -+ const BYTE *match = oLitEnd - sequence.offset; -+ -+ /* check */ -+ if (oMatchEnd > oend) -+ return ERROR(dstSize_tooSmall); /* last match must start at a minimum distance of WILDCOPY_OVERLENGTH from oend */ -+ if (iLitEnd > litLimit) -+ return ERROR(corruption_detected); /* over-read beyond lit buffer */ -+ if (oLitEnd <= oend_w) -+ return ERROR(GENERIC); /* Precondition */ -+ -+ /* copy literals */ -+ if (op < oend_w) { -+ ZSTD_wildcopy(op, *litPtr, oend_w - op); -+ *litPtr += oend_w - op; -+ op = oend_w; -+ } -+ while (op < oLitEnd) -+ *op++ = *(*litPtr)++; -+ -+ /* copy Match */ -+ if (sequence.offset > (size_t)(oLitEnd - base)) { -+ /* offset beyond prefix */ -+ if (sequence.offset > (size_t)(oLitEnd - vBase)) -+ return ERROR(corruption_detected); -+ match = dictEnd - (base - match); -+ if (match + sequence.matchLength <= dictEnd) { -+ memmove(oLitEnd, match, sequence.matchLength); -+ return sequenceLength; -+ } -+ /* span extDict & currPrefixSegment */ -+ { -+ size_t const length1 = dictEnd - match; -+ memmove(oLitEnd, match, length1); -+ op = oLitEnd + length1; -+ sequence.matchLength -= length1; -+ match = base; -+ } -+ } -+ while (op < oMatchEnd) -+ *op++ = *match++; -+ return sequenceLength; -+} -+ -+static seq_t INIT ZSTD_decodeSequence(seqState_t *seqState) -+{ -+ seq_t seq; -+ -+ U32 const llCode = FSE_peekSymbol(&seqState->stateLL); -+ U32 const mlCode = FSE_peekSymbol(&seqState->stateML); -+ U32 const ofCode = FSE_peekSymbol(&seqState->stateOffb); /* <= maxOff, by table construction */ -+ -+ U32 const llBits = LL_bits[llCode]; -+ U32 const mlBits = ML_bits[mlCode]; -+ U32 const ofBits = ofCode; -+ U32 const totalBits = llBits + mlBits + ofBits; -+ -+ static const U32 LL_base[MaxLL + 1] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, -+ 20, 22, 24, 28, 32, 40, 48, 64, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000, 0x10000}; -+ -+ static const U32 ML_base[MaxML + 1] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, -+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 37, 39, 41, -+ 43, 47, 51, 59, 67, 83, 99, 0x83, 0x103, 0x203, 0x403, 0x803, 0x1003, 0x2003, 0x4003, 0x8003, 0x10003}; -+ -+ static const U32 OF_base[MaxOff + 1] = {0, 1, 1, 5, 0xD, 0x1D, 0x3D, 0x7D, 0xFD, 0x1FD, -+ 0x3FD, 0x7FD, 0xFFD, 0x1FFD, 0x3FFD, 0x7FFD, 0xFFFD, 0x1FFFD, 0x3FFFD, 0x7FFFD, -+ 0xFFFFD, 0x1FFFFD, 0x3FFFFD, 0x7FFFFD, 0xFFFFFD, 0x1FFFFFD, 0x3FFFFFD, 0x7FFFFFD, 0xFFFFFFD}; -+ -+ /* sequence */ -+ { -+ size_t offset; -+ if (!ofCode) -+ offset = 0; -+ else { -+ offset = OF_base[ofCode] + BIT_readBitsFast(&seqState->DStream, ofBits); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ -+ if (ZSTD_32bits()) -+ BIT_reloadDStream(&seqState->DStream); -+ } -+ -+ if (ofCode <= 1) { -+ offset += (llCode == 0); -+ if (offset) { -+ size_t temp = (offset == 3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset]; -+ temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */ -+ if (offset != 1) -+ seqState->prevOffset[2] = seqState->prevOffset[1]; -+ seqState->prevOffset[1] = seqState->prevOffset[0]; -+ seqState->prevOffset[0] = offset = temp; -+ } else { -+ offset = seqState->prevOffset[0]; -+ } -+ } else { -+ seqState->prevOffset[2] = seqState->prevOffset[1]; -+ seqState->prevOffset[1] = seqState->prevOffset[0]; -+ seqState->prevOffset[0] = offset; -+ } -+ seq.offset = offset; -+ } -+ -+ seq.matchLength = ML_base[mlCode] + ((mlCode > 31) ? BIT_readBitsFast(&seqState->DStream, mlBits) : 0); /* <= 16 bits */ -+ if (ZSTD_32bits() && (mlBits + llBits > 24)) -+ BIT_reloadDStream(&seqState->DStream); -+ -+ seq.litLength = LL_base[llCode] + ((llCode > 15) ? BIT_readBitsFast(&seqState->DStream, llBits) : 0); /* <= 16 bits */ -+ if (ZSTD_32bits() || (totalBits > 64 - 7 - (LLFSELog + MLFSELog + OffFSELog))) -+ BIT_reloadDStream(&seqState->DStream); -+ -+ /* ANS state update */ -+ FSE_updateState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */ -+ FSE_updateState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */ -+ if (ZSTD_32bits()) -+ BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */ -+ FSE_updateState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */ -+ -+ seq.match = NULL; -+ -+ return seq; -+} -+ -+FORCE_INLINE -+size_t ZSTD_execSequence(BYTE *op, BYTE *const oend, seq_t sequence, const BYTE **litPtr, const BYTE *const litLimit, const BYTE *const base, -+ const BYTE *const vBase, const BYTE *const dictEnd) -+{ -+ BYTE *const oLitEnd = op + sequence.litLength; -+ size_t const sequenceLength = sequence.litLength + sequence.matchLength; -+ BYTE *const oMatchEnd = op + sequenceLength; /* risk : address space overflow (32-bits) */ -+ BYTE *const oend_w = oend - WILDCOPY_OVERLENGTH; -+ const BYTE *const iLitEnd = *litPtr + sequence.litLength; -+ const BYTE *match = oLitEnd - sequence.offset; -+ -+ /* check */ -+ if (oMatchEnd > oend) -+ return ERROR(dstSize_tooSmall); /* last match must start at a minimum distance of WILDCOPY_OVERLENGTH from oend */ -+ if (iLitEnd > litLimit) -+ return ERROR(corruption_detected); /* over-read beyond lit buffer */ -+ if (oLitEnd > oend_w) -+ return ZSTD_execSequenceLast7(op, oend, sequence, litPtr, litLimit, base, vBase, dictEnd); -+ -+ /* copy Literals */ -+ ZSTD_copy8(op, *litPtr); -+ if (sequence.litLength > 8) -+ ZSTD_wildcopy(op + 8, (*litPtr) + 8, -+ sequence.litLength - 8); /* note : since oLitEnd <= oend-WILDCOPY_OVERLENGTH, no risk of overwrite beyond oend */ -+ op = oLitEnd; -+ *litPtr = iLitEnd; /* update for next sequence */ -+ -+ /* copy Match */ -+ if (sequence.offset > (size_t)(oLitEnd - base)) { -+ /* offset beyond prefix */ -+ if (sequence.offset > (size_t)(oLitEnd - vBase)) -+ return ERROR(corruption_detected); -+ match = dictEnd + (match - base); -+ if (match + sequence.matchLength <= dictEnd) { -+ memmove(oLitEnd, match, sequence.matchLength); -+ return sequenceLength; -+ } -+ /* span extDict & currPrefixSegment */ -+ { -+ size_t const length1 = dictEnd - match; -+ memmove(oLitEnd, match, length1); -+ op = oLitEnd + length1; -+ sequence.matchLength -= length1; -+ match = base; -+ if (op > oend_w || sequence.matchLength < MINMATCH) { -+ U32 i; -+ for (i = 0; i < sequence.matchLength; ++i) -+ op[i] = match[i]; -+ return sequenceLength; -+ } -+ } -+ } -+ /* Requirement: op <= oend_w && sequence.matchLength >= MINMATCH */ -+ -+ /* match within prefix */ -+ if (sequence.offset < 8) { -+ /* close range match, overlap */ -+ static const U32 dec32table[] = {0, 1, 2, 1, 4, 4, 4, 4}; /* added */ -+ static const int dec64table[] = {8, 8, 8, 7, 8, 9, 10, 11}; /* subtracted */ -+ int const sub2 = dec64table[sequence.offset]; -+ op[0] = match[0]; -+ op[1] = match[1]; -+ op[2] = match[2]; -+ op[3] = match[3]; -+ match += dec32table[sequence.offset]; -+ ZSTD_copy4(op + 4, match); -+ match -= sub2; -+ } else { -+ ZSTD_copy8(op, match); -+ } -+ op += 8; -+ match += 8; -+ -+ if (oMatchEnd > oend - (16 - MINMATCH)) { -+ if (op < oend_w) { -+ ZSTD_wildcopy(op, match, oend_w - op); -+ match += oend_w - op; -+ op = oend_w; -+ } -+ while (op < oMatchEnd) -+ *op++ = *match++; -+ } else { -+ ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength - 8); /* works even if matchLength < 8 */ -+ } -+ return sequenceLength; -+} -+ -+static size_t INIT ZSTD_decompressSequences(ZSTD_DCtx *dctx, void *dst, size_t maxDstSize, const void *seqStart, size_t seqSize) -+{ -+ const BYTE *ip = (const BYTE *)seqStart; -+ const BYTE *const iend = ip + seqSize; -+ BYTE *const ostart = (BYTE * const)dst; -+ BYTE *const oend = ostart + maxDstSize; -+ BYTE *op = ostart; -+ const BYTE *litPtr = dctx->litPtr; -+ const BYTE *const litEnd = litPtr + dctx->litSize; -+ const BYTE *const base = (const BYTE *)(dctx->base); -+ const BYTE *const vBase = (const BYTE *)(dctx->vBase); -+ const BYTE *const dictEnd = (const BYTE *)(dctx->dictEnd); -+ int nbSeq; -+ -+ /* Build Decoding Tables */ -+ { -+ size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, seqSize); -+ if (ZSTD_isError(seqHSize)) -+ return seqHSize; -+ ip += seqHSize; -+ } -+ -+ /* Regen sequences */ -+ if (nbSeq) { -+ seqState_t seqState; -+ dctx->fseEntropy = 1; -+ { -+ U32 i; -+ for (i = 0; i < ZSTD_REP_NUM; i++) -+ seqState.prevOffset[i] = dctx->entropy.rep[i]; -+ } -+ CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend - ip), corruption_detected); -+ FSE_initDState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); -+ FSE_initDState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); -+ FSE_initDState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); -+ -+ for (; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && nbSeq;) { -+ nbSeq--; -+ { -+ seq_t const sequence = ZSTD_decodeSequence(&seqState); -+ size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequence, &litPtr, litEnd, base, vBase, dictEnd); -+ if (ZSTD_isError(oneSeqSize)) -+ return oneSeqSize; -+ op += oneSeqSize; -+ } -+ } -+ -+ /* check if reached exact end */ -+ if (nbSeq) -+ return ERROR(corruption_detected); -+ /* save reps for next block */ -+ { -+ U32 i; -+ for (i = 0; i < ZSTD_REP_NUM; i++) -+ dctx->entropy.rep[i] = (U32)(seqState.prevOffset[i]); -+ } -+ } -+ -+ /* last literal segment */ -+ { -+ size_t const lastLLSize = litEnd - litPtr; -+ if (lastLLSize > (size_t)(oend - op)) -+ return ERROR(dstSize_tooSmall); -+ memcpy(op, litPtr, lastLLSize); -+ op += lastLLSize; -+ } -+ -+ return op - ostart; -+} -+ -+FORCE_INLINE seq_t ZSTD_decodeSequenceLong_generic(seqState_t *seqState, int const longOffsets) -+{ -+ seq_t seq; -+ -+ U32 const llCode = FSE_peekSymbol(&seqState->stateLL); -+ U32 const mlCode = FSE_peekSymbol(&seqState->stateML); -+ U32 const ofCode = FSE_peekSymbol(&seqState->stateOffb); /* <= maxOff, by table construction */ -+ -+ U32 const llBits = LL_bits[llCode]; -+ U32 const mlBits = ML_bits[mlCode]; -+ U32 const ofBits = ofCode; -+ U32 const totalBits = llBits + mlBits + ofBits; -+ -+ static const U32 LL_base[MaxLL + 1] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, -+ 20, 22, 24, 28, 32, 40, 48, 64, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000, 0x10000}; -+ -+ static const U32 ML_base[MaxML + 1] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, -+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 37, 39, 41, -+ 43, 47, 51, 59, 67, 83, 99, 0x83, 0x103, 0x203, 0x403, 0x803, 0x1003, 0x2003, 0x4003, 0x8003, 0x10003}; -+ -+ static const U32 OF_base[MaxOff + 1] = {0, 1, 1, 5, 0xD, 0x1D, 0x3D, 0x7D, 0xFD, 0x1FD, -+ 0x3FD, 0x7FD, 0xFFD, 0x1FFD, 0x3FFD, 0x7FFD, 0xFFFD, 0x1FFFD, 0x3FFFD, 0x7FFFD, -+ 0xFFFFD, 0x1FFFFD, 0x3FFFFD, 0x7FFFFD, 0xFFFFFD, 0x1FFFFFD, 0x3FFFFFD, 0x7FFFFFD, 0xFFFFFFD}; -+ -+ /* sequence */ -+ { -+ size_t offset; -+ if (!ofCode) -+ offset = 0; -+ else { -+ if (longOffsets) { -+ int const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN); -+ offset = OF_base[ofCode] + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits); -+ if (ZSTD_32bits() || extraBits) -+ BIT_reloadDStream(&seqState->DStream); -+ if (extraBits) -+ offset += BIT_readBitsFast(&seqState->DStream, extraBits); -+ } else { -+ offset = OF_base[ofCode] + BIT_readBitsFast(&seqState->DStream, ofBits); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ -+ if (ZSTD_32bits()) -+ BIT_reloadDStream(&seqState->DStream); -+ } -+ } -+ -+ if (ofCode <= 1) { -+ offset += (llCode == 0); -+ if (offset) { -+ size_t temp = (offset == 3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset]; -+ temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */ -+ if (offset != 1) -+ seqState->prevOffset[2] = seqState->prevOffset[1]; -+ seqState->prevOffset[1] = seqState->prevOffset[0]; -+ seqState->prevOffset[0] = offset = temp; -+ } else { -+ offset = seqState->prevOffset[0]; -+ } -+ } else { -+ seqState->prevOffset[2] = seqState->prevOffset[1]; -+ seqState->prevOffset[1] = seqState->prevOffset[0]; -+ seqState->prevOffset[0] = offset; -+ } -+ seq.offset = offset; -+ } -+ -+ seq.matchLength = ML_base[mlCode] + ((mlCode > 31) ? BIT_readBitsFast(&seqState->DStream, mlBits) : 0); /* <= 16 bits */ -+ if (ZSTD_32bits() && (mlBits + llBits > 24)) -+ BIT_reloadDStream(&seqState->DStream); -+ -+ seq.litLength = LL_base[llCode] + ((llCode > 15) ? BIT_readBitsFast(&seqState->DStream, llBits) : 0); /* <= 16 bits */ -+ if (ZSTD_32bits() || (totalBits > 64 - 7 - (LLFSELog + MLFSELog + OffFSELog))) -+ BIT_reloadDStream(&seqState->DStream); -+ -+ { -+ size_t const pos = seqState->pos + seq.litLength; -+ seq.match = seqState->base + pos - seq.offset; /* single memory segment */ -+ if (seq.offset > pos) -+ seq.match += seqState->gotoDict; /* separate memory segment */ -+ seqState->pos = pos + seq.matchLength; -+ } -+ -+ /* ANS state update */ -+ FSE_updateState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */ -+ FSE_updateState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */ -+ if (ZSTD_32bits()) -+ BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */ -+ FSE_updateState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */ -+ -+ return seq; -+} -+ -+static seq_t INIT ZSTD_decodeSequenceLong(seqState_t *seqState, unsigned const windowSize) -+{ -+ if (ZSTD_highbit32(windowSize) > STREAM_ACCUMULATOR_MIN) { -+ return ZSTD_decodeSequenceLong_generic(seqState, 1); -+ } else { -+ return ZSTD_decodeSequenceLong_generic(seqState, 0); -+ } -+} -+ -+FORCE_INLINE -+size_t INIT ZSTD_execSequenceLong(BYTE *op, BYTE *const oend, seq_t sequence, const BYTE **litPtr, -+ const BYTE *const litLimit, const BYTE *const base, -+ const BYTE *const vBase, const BYTE *const dictEnd) -+{ -+ BYTE *const oLitEnd = op + sequence.litLength; -+ size_t const sequenceLength = sequence.litLength + sequence.matchLength; -+ BYTE *const oMatchEnd = op + sequenceLength; /* risk : address space overflow (32-bits) */ -+ BYTE *const oend_w = oend - WILDCOPY_OVERLENGTH; -+ const BYTE *const iLitEnd = *litPtr + sequence.litLength; -+ const BYTE *match = sequence.match; -+ -+ /* check */ -+ if (oMatchEnd > oend) -+ return ERROR(dstSize_tooSmall); /* last match must start at a minimum distance of WILDCOPY_OVERLENGTH from oend */ -+ if (iLitEnd > litLimit) -+ return ERROR(corruption_detected); /* over-read beyond lit buffer */ -+ if (oLitEnd > oend_w) -+ return ZSTD_execSequenceLast7(op, oend, sequence, litPtr, litLimit, base, vBase, dictEnd); -+ -+ /* copy Literals */ -+ ZSTD_copy8(op, *litPtr); -+ if (sequence.litLength > 8) -+ ZSTD_wildcopy(op + 8, (*litPtr) + 8, -+ sequence.litLength - 8); /* note : since oLitEnd <= oend-WILDCOPY_OVERLENGTH, no risk of overwrite beyond oend */ -+ op = oLitEnd; -+ *litPtr = iLitEnd; /* update for next sequence */ -+ -+ /* copy Match */ -+ if (sequence.offset > (size_t)(oLitEnd - base)) { -+ /* offset beyond prefix */ -+ if (sequence.offset > (size_t)(oLitEnd - vBase)) -+ return ERROR(corruption_detected); -+ if (match + sequence.matchLength <= dictEnd) { -+ memmove(oLitEnd, match, sequence.matchLength); -+ return sequenceLength; -+ } -+ /* span extDict & currPrefixSegment */ -+ { -+ size_t const length1 = dictEnd - match; -+ memmove(oLitEnd, match, length1); -+ op = oLitEnd + length1; -+ sequence.matchLength -= length1; -+ match = base; -+ if (op > oend_w || sequence.matchLength < MINMATCH) { -+ U32 i; -+ for (i = 0; i < sequence.matchLength; ++i) -+ op[i] = match[i]; -+ return sequenceLength; -+ } -+ } -+ } -+ /* Requirement: op <= oend_w && sequence.matchLength >= MINMATCH */ -+ -+ /* match within prefix */ -+ if (sequence.offset < 8) { -+ /* close range match, overlap */ -+ static const U32 dec32table[] = {0, 1, 2, 1, 4, 4, 4, 4}; /* added */ -+ static const int dec64table[] = {8, 8, 8, 7, 8, 9, 10, 11}; /* subtracted */ -+ int const sub2 = dec64table[sequence.offset]; -+ op[0] = match[0]; -+ op[1] = match[1]; -+ op[2] = match[2]; -+ op[3] = match[3]; -+ match += dec32table[sequence.offset]; -+ ZSTD_copy4(op + 4, match); -+ match -= sub2; -+ } else { -+ ZSTD_copy8(op, match); -+ } -+ op += 8; -+ match += 8; -+ -+ if (oMatchEnd > oend - (16 - MINMATCH)) { -+ if (op < oend_w) { -+ ZSTD_wildcopy(op, match, oend_w - op); -+ match += oend_w - op; -+ op = oend_w; -+ } -+ while (op < oMatchEnd) -+ *op++ = *match++; -+ } else { -+ ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength - 8); /* works even if matchLength < 8 */ -+ } -+ return sequenceLength; -+} -+ -+static size_t INIT ZSTD_decompressSequencesLong(ZSTD_DCtx *dctx, void *dst, size_t maxDstSize, const void *seqStart, size_t seqSize) -+{ -+ const BYTE *ip = (const BYTE *)seqStart; -+ const BYTE *const iend = ip + seqSize; -+ BYTE *const ostart = (BYTE * const)dst; -+ BYTE *const oend = ostart + maxDstSize; -+ BYTE *op = ostart; -+ const BYTE *litPtr = dctx->litPtr; -+ const BYTE *const litEnd = litPtr + dctx->litSize; -+ const BYTE *const base = (const BYTE *)(dctx->base); -+ const BYTE *const vBase = (const BYTE *)(dctx->vBase); -+ const BYTE *const dictEnd = (const BYTE *)(dctx->dictEnd); -+ unsigned const windowSize = dctx->fParams.windowSize; -+ int nbSeq; -+ -+ /* Build Decoding Tables */ -+ { -+ size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, seqSize); -+ if (ZSTD_isError(seqHSize)) -+ return seqHSize; -+ ip += seqHSize; -+ } -+ -+ /* Regen sequences */ -+ if (nbSeq) { -+#define STORED_SEQS 4 -+#define STOSEQ_MASK (STORED_SEQS - 1) -+#define ADVANCED_SEQS 4 -+ seq_t *sequences = (seq_t *)dctx->entropy.workspace; -+ int const seqAdvance = MIN(nbSeq, ADVANCED_SEQS); -+ seqState_t seqState; -+ int seqNb; -+ ZSTD_STATIC_ASSERT(sizeof(dctx->entropy.workspace) >= sizeof(seq_t) * STORED_SEQS); -+ dctx->fseEntropy = 1; -+ { -+ U32 i; -+ for (i = 0; i < ZSTD_REP_NUM; i++) -+ seqState.prevOffset[i] = dctx->entropy.rep[i]; -+ } -+ seqState.base = base; -+ seqState.pos = (size_t)(op - base); -+ seqState.gotoDict = (uPtrDiff)dictEnd - (uPtrDiff)base; /* cast to avoid undefined behaviour */ -+ CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend - ip), corruption_detected); -+ FSE_initDState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); -+ FSE_initDState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); -+ FSE_initDState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); -+ -+ /* prepare in advance */ -+ for (seqNb = 0; (BIT_reloadDStream(&seqState.DStream) <= BIT_DStream_completed) && seqNb < seqAdvance; seqNb++) { -+ sequences[seqNb] = ZSTD_decodeSequenceLong(&seqState, windowSize); -+ } -+ if (seqNb < seqAdvance) -+ return ERROR(corruption_detected); -+ -+ /* decode and decompress */ -+ for (; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && seqNb < nbSeq; seqNb++) { -+ seq_t const sequence = ZSTD_decodeSequenceLong(&seqState, windowSize); -+ size_t const oneSeqSize = -+ ZSTD_execSequenceLong(op, oend, sequences[(seqNb - ADVANCED_SEQS) & STOSEQ_MASK], &litPtr, litEnd, base, vBase, dictEnd); -+ if (ZSTD_isError(oneSeqSize)) -+ return oneSeqSize; -+ ZSTD_PREFETCH(sequence.match); -+ sequences[seqNb & STOSEQ_MASK] = sequence; -+ op += oneSeqSize; -+ } -+ if (seqNb < nbSeq) -+ return ERROR(corruption_detected); -+ -+ /* finish queue */ -+ seqNb -= seqAdvance; -+ for (; seqNb < nbSeq; seqNb++) { -+ size_t const oneSeqSize = ZSTD_execSequenceLong(op, oend, sequences[seqNb & STOSEQ_MASK], &litPtr, litEnd, base, vBase, dictEnd); -+ if (ZSTD_isError(oneSeqSize)) -+ return oneSeqSize; -+ op += oneSeqSize; -+ } -+ -+ /* save reps for next block */ -+ { -+ U32 i; -+ for (i = 0; i < ZSTD_REP_NUM; i++) -+ dctx->entropy.rep[i] = (U32)(seqState.prevOffset[i]); -+ } -+ } -+ -+ /* last literal segment */ -+ { -+ size_t const lastLLSize = litEnd - litPtr; -+ if (lastLLSize > (size_t)(oend - op)) -+ return ERROR(dstSize_tooSmall); -+ memcpy(op, litPtr, lastLLSize); -+ op += lastLLSize; -+ } -+ -+ return op - ostart; -+} -+ -+static size_t INIT ZSTD_decompressBlock_internal(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize) -+{ /* blockType == blockCompressed */ -+ const BYTE *ip = (const BYTE *)src; -+ -+ if (srcSize >= ZSTD_BLOCKSIZE_ABSOLUTEMAX) -+ return ERROR(srcSize_wrong); -+ -+ /* Decode literals section */ -+ { -+ size_t const litCSize = ZSTD_decodeLiteralsBlock(dctx, src, srcSize); -+ if (ZSTD_isError(litCSize)) -+ return litCSize; -+ ip += litCSize; -+ srcSize -= litCSize; -+ } -+ if (sizeof(size_t) > 4) /* do not enable prefetching on 32-bits x86, as it's performance detrimental */ -+ /* likely because of register pressure */ -+ /* if that's the correct cause, then 32-bits ARM should be affected differently */ -+ /* it would be good to test this on ARM real hardware, to see if prefetch version improves speed */ -+ if (dctx->fParams.windowSize > (1 << 23)) -+ return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize); -+ return ZSTD_decompressSequences(dctx, dst, dstCapacity, ip, srcSize); -+} -+ -+static void INIT ZSTD_checkContinuity(ZSTD_DCtx *dctx, const void *dst) -+{ -+ if (dst != dctx->previousDstEnd) { /* not contiguous */ -+ dctx->dictEnd = dctx->previousDstEnd; -+ dctx->vBase = (const char *)dst - ((const char *)(dctx->previousDstEnd) - (const char *)(dctx->base)); -+ dctx->base = dst; -+ dctx->previousDstEnd = dst; -+ } -+} -+ -+size_t INIT ZSTD_decompressBlock(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize) -+{ -+ size_t dSize; -+ ZSTD_checkContinuity(dctx, dst); -+ dSize = ZSTD_decompressBlock_internal(dctx, dst, dstCapacity, src, srcSize); -+ dctx->previousDstEnd = (char *)dst + dSize; -+ return dSize; -+} -+ -+/** ZSTD_insertBlock() : -+ insert `src` block into `dctx` history. Useful to track uncompressed blocks. */ -+size_t INIT ZSTD_insertBlock(ZSTD_DCtx *dctx, const void *blockStart, size_t blockSize) -+{ -+ ZSTD_checkContinuity(dctx, blockStart); -+ dctx->previousDstEnd = (const char *)blockStart + blockSize; -+ return blockSize; -+} -+ -+size_t INIT ZSTD_generateNxBytes(void *dst, size_t dstCapacity, BYTE byte, size_t length) -+{ -+ if (length > dstCapacity) -+ return ERROR(dstSize_tooSmall); -+ memset(dst, byte, length); -+ return length; -+} -+ -+/** ZSTD_findFrameCompressedSize() : -+ * compatible with legacy mode -+ * `src` must point to the start of a ZSTD frame, ZSTD legacy frame, or skippable frame -+ * `srcSize` must be at least as large as the frame contained -+ * @return : the compressed size of the frame starting at `src` */ -+size_t INIT ZSTD_findFrameCompressedSize(const void *src, size_t srcSize) -+{ -+ if (srcSize >= ZSTD_skippableHeaderSize && (ZSTD_readLE32(src) & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) { -+ return ZSTD_skippableHeaderSize + ZSTD_readLE32((const BYTE *)src + 4); -+ } else { -+ const BYTE *ip = (const BYTE *)src; -+ const BYTE *const ipstart = ip; -+ size_t remainingSize = srcSize; -+ ZSTD_frameParams fParams; -+ -+ size_t const headerSize = ZSTD_frameHeaderSize(ip, remainingSize); -+ if (ZSTD_isError(headerSize)) -+ return headerSize; -+ -+ /* Frame Header */ -+ { -+ size_t const ret = ZSTD_getFrameParams(&fParams, ip, remainingSize); -+ if (ZSTD_isError(ret)) -+ return ret; -+ if (ret > 0) -+ return ERROR(srcSize_wrong); -+ } -+ -+ ip += headerSize; -+ remainingSize -= headerSize; -+ -+ /* Loop on each block */ -+ while (1) { -+ blockProperties_t blockProperties; -+ size_t const cBlockSize = ZSTD_getcBlockSize(ip, remainingSize, &blockProperties); -+ if (ZSTD_isError(cBlockSize)) -+ return cBlockSize; -+ -+ if (ZSTD_blockHeaderSize + cBlockSize > remainingSize) -+ return ERROR(srcSize_wrong); -+ -+ ip += ZSTD_blockHeaderSize + cBlockSize; -+ remainingSize -= ZSTD_blockHeaderSize + cBlockSize; -+ -+ if (blockProperties.lastBlock) -+ break; -+ } -+ -+ if (fParams.checksumFlag) { /* Frame content checksum */ -+ if (remainingSize < 4) -+ return ERROR(srcSize_wrong); -+ ip += 4; -+ remainingSize -= 4; -+ } -+ -+ return ip - ipstart; -+ } -+} -+ -+/*! ZSTD_decompressFrame() : -+* @dctx must be properly initialized */ -+static size_t INIT ZSTD_decompressFrame(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void **srcPtr, size_t *srcSizePtr) -+{ -+ const BYTE *ip = (const BYTE *)(*srcPtr); -+ BYTE *const ostart = (BYTE * const)dst; -+ BYTE *const oend = ostart + dstCapacity; -+ BYTE *op = ostart; -+ size_t remainingSize = *srcSizePtr; -+ -+ /* check */ -+ if (remainingSize < ZSTD_frameHeaderSize_min + ZSTD_blockHeaderSize) -+ return ERROR(srcSize_wrong); -+ -+ /* Frame Header */ -+ { -+ size_t const frameHeaderSize = ZSTD_frameHeaderSize(ip, ZSTD_frameHeaderSize_prefix); -+ if (ZSTD_isError(frameHeaderSize)) -+ return frameHeaderSize; -+ if (remainingSize < frameHeaderSize + ZSTD_blockHeaderSize) -+ return ERROR(srcSize_wrong); -+ CHECK_F(ZSTD_decodeFrameHeader(dctx, ip, frameHeaderSize)); -+ ip += frameHeaderSize; -+ remainingSize -= frameHeaderSize; -+ } -+ -+ /* Loop on each block */ -+ while (1) { -+ size_t decodedSize; -+ blockProperties_t blockProperties; -+ size_t const cBlockSize = ZSTD_getcBlockSize(ip, remainingSize, &blockProperties); -+ if (ZSTD_isError(cBlockSize)) -+ return cBlockSize; -+ -+ ip += ZSTD_blockHeaderSize; -+ remainingSize -= ZSTD_blockHeaderSize; -+ if (cBlockSize > remainingSize) -+ return ERROR(srcSize_wrong); -+ -+ switch (blockProperties.blockType) { -+ case bt_compressed: decodedSize = ZSTD_decompressBlock_internal(dctx, op, oend - op, ip, cBlockSize); break; -+ case bt_raw: decodedSize = ZSTD_copyRawBlock(op, oend - op, ip, cBlockSize); break; -+ case bt_rle: decodedSize = ZSTD_generateNxBytes(op, oend - op, *ip, blockProperties.origSize); break; -+ case bt_reserved: -+ default: return ERROR(corruption_detected); -+ } -+ -+ if (ZSTD_isError(decodedSize)) -+ return decodedSize; -+ if (dctx->fParams.checksumFlag) -+ xxh64_update(&dctx->xxhState, op, decodedSize); -+ op += decodedSize; -+ ip += cBlockSize; -+ remainingSize -= cBlockSize; -+ if (blockProperties.lastBlock) -+ break; -+ } -+ -+ if (dctx->fParams.checksumFlag) { /* Frame content checksum verification */ -+ U32 const checkCalc = (U32)xxh64_digest(&dctx->xxhState); -+ U32 checkRead; -+ if (remainingSize < 4) -+ return ERROR(checksum_wrong); -+ checkRead = ZSTD_readLE32(ip); -+ if (checkRead != checkCalc) -+ return ERROR(checksum_wrong); -+ ip += 4; -+ remainingSize -= 4; -+ } -+ -+ /* Allow caller to get size read */ -+ *srcPtr = ip; -+ *srcSizePtr = remainingSize; -+ return op - ostart; -+} -+ -+static const void *ZSTD_DDictDictContent(const ZSTD_DDict *ddict); -+static size_t ZSTD_DDictDictSize(const ZSTD_DDict *ddict); -+ -+static size_t INIT ZSTD_decompressMultiFrame(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize, const void *dict, size_t dictSize, -+ const ZSTD_DDict *ddict) -+{ -+ void *const dststart = dst; -+ -+ if (ddict) { -+ if (dict) { -+ /* programmer error, these two cases should be mutually exclusive */ -+ return ERROR(GENERIC); -+ } -+ -+ dict = ZSTD_DDictDictContent(ddict); -+ dictSize = ZSTD_DDictDictSize(ddict); -+ } -+ -+ while (srcSize >= ZSTD_frameHeaderSize_prefix) { -+ U32 magicNumber; -+ -+ magicNumber = ZSTD_readLE32(src); -+ if (magicNumber != ZSTD_MAGICNUMBER) { -+ if ((magicNumber & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) { -+ size_t skippableSize; -+ if (srcSize < ZSTD_skippableHeaderSize) -+ return ERROR(srcSize_wrong); -+ skippableSize = ZSTD_readLE32((const BYTE *)src + 4) + ZSTD_skippableHeaderSize; -+ if (srcSize < skippableSize) { -+ return ERROR(srcSize_wrong); -+ } -+ -+ src = (const BYTE *)src + skippableSize; -+ srcSize -= skippableSize; -+ continue; -+ } else { -+ return ERROR(prefix_unknown); -+ } -+ } -+ -+ if (ddict) { -+ /* we were called from ZSTD_decompress_usingDDict */ -+ ZSTD_refDDict(dctx, ddict); -+ } else { -+ /* this will initialize correctly with no dict if dict == NULL, so -+ * use this in all cases but ddict */ -+ CHECK_F(ZSTD_decompressBegin_usingDict(dctx, dict, dictSize)); -+ } -+ ZSTD_checkContinuity(dctx, dst); -+ -+ { -+ const size_t res = ZSTD_decompressFrame(dctx, dst, dstCapacity, &src, &srcSize); -+ if (ZSTD_isError(res)) -+ return res; -+ /* don't need to bounds check this, ZSTD_decompressFrame will have -+ * already */ -+ dst = (BYTE *)dst + res; -+ dstCapacity -= res; -+ } -+ } -+ -+ if (srcSize) -+ return ERROR(srcSize_wrong); /* input not entirely consumed */ -+ -+ return (BYTE *)dst - (BYTE *)dststart; -+} -+ -+size_t INIT ZSTD_decompress_usingDict(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize, const void *dict, size_t dictSize) -+{ -+ return ZSTD_decompressMultiFrame(dctx, dst, dstCapacity, src, srcSize, dict, dictSize, NULL); -+} -+ -+size_t INIT ZSTD_decompressDCtx(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize) -+{ -+ return ZSTD_decompress_usingDict(dctx, dst, dstCapacity, src, srcSize, NULL, 0); -+} -+ -+/*-************************************** -+* Advanced Streaming Decompression API -+* Bufferless and synchronous -+****************************************/ -+size_t INIT ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx *dctx) { return dctx->expected; } -+ -+ZSTD_nextInputType_e INIT ZSTD_nextInputType(ZSTD_DCtx *dctx) -+{ -+ switch (dctx->stage) { -+ default: /* should not happen */ -+ case ZSTDds_getFrameHeaderSize: -+ case ZSTDds_decodeFrameHeader: return ZSTDnit_frameHeader; -+ case ZSTDds_decodeBlockHeader: return ZSTDnit_blockHeader; -+ case ZSTDds_decompressBlock: return ZSTDnit_block; -+ case ZSTDds_decompressLastBlock: return ZSTDnit_lastBlock; -+ case ZSTDds_checkChecksum: return ZSTDnit_checksum; -+ case ZSTDds_decodeSkippableHeader: -+ case ZSTDds_skipFrame: return ZSTDnit_skippableFrame; -+ } -+} -+ -+int INIT ZSTD_isSkipFrame(ZSTD_DCtx *dctx) { return dctx->stage == ZSTDds_skipFrame; } /* for zbuff */ -+ -+/** ZSTD_decompressContinue() : -+* @return : nb of bytes generated into `dst` (necessarily <= `dstCapacity) -+* or an error code, which can be tested using ZSTD_isError() */ -+size_t INIT ZSTD_decompressContinue(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize) -+{ -+ /* Sanity check */ -+ if (srcSize != dctx->expected) -+ return ERROR(srcSize_wrong); -+ if (dstCapacity) -+ ZSTD_checkContinuity(dctx, dst); -+ -+ switch (dctx->stage) { -+ case ZSTDds_getFrameHeaderSize: -+ if (srcSize != ZSTD_frameHeaderSize_prefix) -+ return ERROR(srcSize_wrong); /* impossible */ -+ if ((ZSTD_readLE32(src) & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) { /* skippable frame */ -+ memcpy(dctx->headerBuffer, src, ZSTD_frameHeaderSize_prefix); -+ dctx->expected = ZSTD_skippableHeaderSize - ZSTD_frameHeaderSize_prefix; /* magic number + skippable frame length */ -+ dctx->stage = ZSTDds_decodeSkippableHeader; -+ return 0; -+ } -+ dctx->headerSize = ZSTD_frameHeaderSize(src, ZSTD_frameHeaderSize_prefix); -+ if (ZSTD_isError(dctx->headerSize)) -+ return dctx->headerSize; -+ memcpy(dctx->headerBuffer, src, ZSTD_frameHeaderSize_prefix); -+ if (dctx->headerSize > ZSTD_frameHeaderSize_prefix) { -+ dctx->expected = dctx->headerSize - ZSTD_frameHeaderSize_prefix; -+ dctx->stage = ZSTDds_decodeFrameHeader; -+ return 0; -+ } -+ dctx->expected = 0; /* not necessary to copy more */ -+ /* fallthrough */ -+ -+ case ZSTDds_decodeFrameHeader: -+ memcpy(dctx->headerBuffer + ZSTD_frameHeaderSize_prefix, src, dctx->expected); -+ CHECK_F(ZSTD_decodeFrameHeader(dctx, dctx->headerBuffer, dctx->headerSize)); -+ dctx->expected = ZSTD_blockHeaderSize; -+ dctx->stage = ZSTDds_decodeBlockHeader; -+ return 0; -+ -+ case ZSTDds_decodeBlockHeader: { -+ blockProperties_t bp; -+ size_t const cBlockSize = ZSTD_getcBlockSize(src, ZSTD_blockHeaderSize, &bp); -+ if (ZSTD_isError(cBlockSize)) -+ return cBlockSize; -+ dctx->expected = cBlockSize; -+ dctx->bType = bp.blockType; -+ dctx->rleSize = bp.origSize; -+ if (cBlockSize) { -+ dctx->stage = bp.lastBlock ? ZSTDds_decompressLastBlock : ZSTDds_decompressBlock; -+ return 0; -+ } -+ /* empty block */ -+ if (bp.lastBlock) { -+ if (dctx->fParams.checksumFlag) { -+ dctx->expected = 4; -+ dctx->stage = ZSTDds_checkChecksum; -+ } else { -+ dctx->expected = 0; /* end of frame */ -+ dctx->stage = ZSTDds_getFrameHeaderSize; -+ } -+ } else { -+ dctx->expected = 3; /* go directly to next header */ -+ dctx->stage = ZSTDds_decodeBlockHeader; -+ } -+ return 0; -+ } -+ case ZSTDds_decompressLastBlock: -+ case ZSTDds_decompressBlock: { -+ size_t rSize; -+ switch (dctx->bType) { -+ case bt_compressed: rSize = ZSTD_decompressBlock_internal(dctx, dst, dstCapacity, src, srcSize); break; -+ case bt_raw: rSize = ZSTD_copyRawBlock(dst, dstCapacity, src, srcSize); break; -+ case bt_rle: rSize = ZSTD_setRleBlock(dst, dstCapacity, src, srcSize, dctx->rleSize); break; -+ case bt_reserved: /* should never happen */ -+ default: return ERROR(corruption_detected); -+ } -+ if (ZSTD_isError(rSize)) -+ return rSize; -+ if (dctx->fParams.checksumFlag) -+ xxh64_update(&dctx->xxhState, dst, rSize); -+ -+ if (dctx->stage == ZSTDds_decompressLastBlock) { /* end of frame */ -+ if (dctx->fParams.checksumFlag) { /* another round for frame checksum */ -+ dctx->expected = 4; -+ dctx->stage = ZSTDds_checkChecksum; -+ } else { -+ dctx->expected = 0; /* ends here */ -+ dctx->stage = ZSTDds_getFrameHeaderSize; -+ } -+ } else { -+ dctx->stage = ZSTDds_decodeBlockHeader; -+ dctx->expected = ZSTD_blockHeaderSize; -+ dctx->previousDstEnd = (char *)dst + rSize; -+ } -+ return rSize; -+ } -+ case ZSTDds_checkChecksum: { -+ U32 const h32 = (U32)xxh64_digest(&dctx->xxhState); -+ U32 const check32 = ZSTD_readLE32(src); /* srcSize == 4, guaranteed by dctx->expected */ -+ if (check32 != h32) -+ return ERROR(checksum_wrong); -+ dctx->expected = 0; -+ dctx->stage = ZSTDds_getFrameHeaderSize; -+ return 0; -+ } -+ case ZSTDds_decodeSkippableHeader: { -+ memcpy(dctx->headerBuffer + ZSTD_frameHeaderSize_prefix, src, dctx->expected); -+ dctx->expected = ZSTD_readLE32(dctx->headerBuffer + 4); -+ dctx->stage = ZSTDds_skipFrame; -+ return 0; -+ } -+ case ZSTDds_skipFrame: { -+ dctx->expected = 0; -+ dctx->stage = ZSTDds_getFrameHeaderSize; -+ return 0; -+ } -+ default: -+ return ERROR(GENERIC); /* impossible */ -+ } -+} -+ -+static size_t INIT ZSTD_refDictContent(ZSTD_DCtx *dctx, const void *dict, size_t dictSize) -+{ -+ dctx->dictEnd = dctx->previousDstEnd; -+ dctx->vBase = (const char *)dict - ((const char *)(dctx->previousDstEnd) - (const char *)(dctx->base)); -+ dctx->base = dict; -+ dctx->previousDstEnd = (const char *)dict + dictSize; -+ return 0; -+} -+ -+/* ZSTD_loadEntropy() : -+ * dict : must point at beginning of a valid zstd dictionary -+ * @return : size of entropy tables read */ -+static size_t INIT ZSTD_loadEntropy(ZSTD_entropyTables_t *entropy, const void *const dict, size_t const dictSize) -+{ -+ const BYTE *dictPtr = (const BYTE *)dict; -+ const BYTE *const dictEnd = dictPtr + dictSize; -+ -+ if (dictSize <= 8) -+ return ERROR(dictionary_corrupted); -+ dictPtr += 8; /* skip header = magic + dictID */ -+ -+ { -+ size_t const hSize = HUF_readDTableX4_wksp(entropy->hufTable, dictPtr, dictEnd - dictPtr, entropy->workspace, sizeof(entropy->workspace)); -+ if (HUF_isError(hSize)) -+ return ERROR(dictionary_corrupted); -+ dictPtr += hSize; -+ } -+ -+ { -+ short offcodeNCount[MaxOff + 1]; -+ U32 offcodeMaxValue = MaxOff, offcodeLog; -+ size_t const offcodeHeaderSize = FSE_readNCount(offcodeNCount, &offcodeMaxValue, &offcodeLog, dictPtr, dictEnd - dictPtr); -+ if (FSE_isError(offcodeHeaderSize)) -+ return ERROR(dictionary_corrupted); -+ if (offcodeLog > OffFSELog) -+ return ERROR(dictionary_corrupted); -+ CHECK_E(FSE_buildDTable_wksp(entropy->OFTable, offcodeNCount, offcodeMaxValue, offcodeLog, entropy->workspace, sizeof(entropy->workspace)), dictionary_corrupted); -+ dictPtr += offcodeHeaderSize; -+ } -+ -+ { -+ short matchlengthNCount[MaxML + 1]; -+ unsigned matchlengthMaxValue = MaxML, matchlengthLog; -+ size_t const matchlengthHeaderSize = FSE_readNCount(matchlengthNCount, &matchlengthMaxValue, &matchlengthLog, dictPtr, dictEnd - dictPtr); -+ if (FSE_isError(matchlengthHeaderSize)) -+ return ERROR(dictionary_corrupted); -+ if (matchlengthLog > MLFSELog) -+ return ERROR(dictionary_corrupted); -+ CHECK_E(FSE_buildDTable_wksp(entropy->MLTable, matchlengthNCount, matchlengthMaxValue, matchlengthLog, entropy->workspace, sizeof(entropy->workspace)), dictionary_corrupted); -+ dictPtr += matchlengthHeaderSize; -+ } -+ -+ { -+ short litlengthNCount[MaxLL + 1]; -+ unsigned litlengthMaxValue = MaxLL, litlengthLog; -+ size_t const litlengthHeaderSize = FSE_readNCount(litlengthNCount, &litlengthMaxValue, &litlengthLog, dictPtr, dictEnd - dictPtr); -+ if (FSE_isError(litlengthHeaderSize)) -+ return ERROR(dictionary_corrupted); -+ if (litlengthLog > LLFSELog) -+ return ERROR(dictionary_corrupted); -+ CHECK_E(FSE_buildDTable_wksp(entropy->LLTable, litlengthNCount, litlengthMaxValue, litlengthLog, entropy->workspace, sizeof(entropy->workspace)), dictionary_corrupted); -+ dictPtr += litlengthHeaderSize; -+ } -+ -+ if (dictPtr + 12 > dictEnd) -+ return ERROR(dictionary_corrupted); -+ { -+ int i; -+ size_t const dictContentSize = (size_t)(dictEnd - (dictPtr + 12)); -+ for (i = 0; i < 3; i++) { -+ U32 const rep = ZSTD_readLE32(dictPtr); -+ dictPtr += 4; -+ if (rep == 0 || rep >= dictContentSize) -+ return ERROR(dictionary_corrupted); -+ entropy->rep[i] = rep; -+ } -+ } -+ -+ return dictPtr - (const BYTE *)dict; -+} -+ -+static size_t INIT ZSTD_decompress_insertDictionary(ZSTD_DCtx *dctx, const void *dict, size_t dictSize) -+{ -+ if (dictSize < 8) -+ return ZSTD_refDictContent(dctx, dict, dictSize); -+ { -+ U32 const magic = ZSTD_readLE32(dict); -+ if (magic != ZSTD_DICT_MAGIC) { -+ return ZSTD_refDictContent(dctx, dict, dictSize); /* pure content mode */ -+ } -+ } -+ dctx->dictID = ZSTD_readLE32((const char *)dict + 4); -+ -+ /* load entropy tables */ -+ { -+ size_t const eSize = ZSTD_loadEntropy(&dctx->entropy, dict, dictSize); -+ if (ZSTD_isError(eSize)) -+ return ERROR(dictionary_corrupted); -+ dict = (const char *)dict + eSize; -+ dictSize -= eSize; -+ } -+ dctx->litEntropy = dctx->fseEntropy = 1; -+ -+ /* reference dictionary content */ -+ return ZSTD_refDictContent(dctx, dict, dictSize); -+} -+ -+size_t INIT ZSTD_decompressBegin_usingDict(ZSTD_DCtx *dctx, const void *dict, size_t dictSize) -+{ -+ CHECK_F(ZSTD_decompressBegin(dctx)); -+ if (dict && dictSize) -+ CHECK_E(ZSTD_decompress_insertDictionary(dctx, dict, dictSize), dictionary_corrupted); -+ return 0; -+} -+ -+/* ====== ZSTD_DDict ====== */ -+ -+struct ZSTD_DDict_s { -+ void *dictBuffer; -+ const void *dictContent; -+ size_t dictSize; -+ ZSTD_entropyTables_t entropy; -+ U32 dictID; -+ U32 entropyPresent; -+ ZSTD_customMem cMem; -+}; /* typedef'd to ZSTD_DDict within "zstd.h" */ -+ -+size_t INIT ZSTD_DDictWorkspaceBound(void) { return ZSTD_ALIGN(sizeof(ZSTD_stack)) + ZSTD_ALIGN(sizeof(ZSTD_DDict)); } -+ -+static const void *INIT ZSTD_DDictDictContent(const ZSTD_DDict *ddict) { return ddict->dictContent; } -+ -+static size_t INIT ZSTD_DDictDictSize(const ZSTD_DDict *ddict) { return ddict->dictSize; } -+ -+static void INIT ZSTD_refDDict(ZSTD_DCtx *dstDCtx, const ZSTD_DDict *ddict) -+{ -+ ZSTD_decompressBegin(dstDCtx); /* init */ -+ if (ddict) { /* support refDDict on NULL */ -+ dstDCtx->dictID = ddict->dictID; -+ dstDCtx->base = ddict->dictContent; -+ dstDCtx->vBase = ddict->dictContent; -+ dstDCtx->dictEnd = (const BYTE *)ddict->dictContent + ddict->dictSize; -+ dstDCtx->previousDstEnd = dstDCtx->dictEnd; -+ if (ddict->entropyPresent) { -+ dstDCtx->litEntropy = 1; -+ dstDCtx->fseEntropy = 1; -+ dstDCtx->LLTptr = ddict->entropy.LLTable; -+ dstDCtx->MLTptr = ddict->entropy.MLTable; -+ dstDCtx->OFTptr = ddict->entropy.OFTable; -+ dstDCtx->HUFptr = ddict->entropy.hufTable; -+ dstDCtx->entropy.rep[0] = ddict->entropy.rep[0]; -+ dstDCtx->entropy.rep[1] = ddict->entropy.rep[1]; -+ dstDCtx->entropy.rep[2] = ddict->entropy.rep[2]; -+ } else { -+ dstDCtx->litEntropy = 0; -+ dstDCtx->fseEntropy = 0; -+ } -+ } -+} -+ -+static size_t INIT ZSTD_loadEntropy_inDDict(ZSTD_DDict *ddict) -+{ -+ ddict->dictID = 0; -+ ddict->entropyPresent = 0; -+ if (ddict->dictSize < 8) -+ return 0; -+ { -+ U32 const magic = ZSTD_readLE32(ddict->dictContent); -+ if (magic != ZSTD_DICT_MAGIC) -+ return 0; /* pure content mode */ -+ } -+ ddict->dictID = ZSTD_readLE32((const char *)ddict->dictContent + 4); -+ -+ /* load entropy tables */ -+ CHECK_E(ZSTD_loadEntropy(&ddict->entropy, ddict->dictContent, ddict->dictSize), dictionary_corrupted); -+ ddict->entropyPresent = 1; -+ return 0; -+} -+ -+static ZSTD_DDict *INIT ZSTD_createDDict_advanced(const void *dict, size_t dictSize, unsigned byReference, ZSTD_customMem customMem) -+{ -+ if (!customMem.customAlloc || !customMem.customFree) -+ return NULL; -+ -+ { -+ ZSTD_DDict *const ddict = (ZSTD_DDict *)ZSTD_malloc(sizeof(ZSTD_DDict), customMem); -+ if (!ddict) -+ return NULL; -+ ddict->cMem = customMem; -+ -+ if ((byReference) || (!dict) || (!dictSize)) { -+ ddict->dictBuffer = NULL; -+ ddict->dictContent = dict; -+ } else { -+ void *const internalBuffer = ZSTD_malloc(dictSize, customMem); -+ if (!internalBuffer) { -+ ZSTD_freeDDict(ddict); -+ return NULL; -+ } -+ memcpy(internalBuffer, dict, dictSize); -+ ddict->dictBuffer = internalBuffer; -+ ddict->dictContent = internalBuffer; -+ } -+ ddict->dictSize = dictSize; -+ ddict->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001); /* cover both little and big endian */ -+ /* parse dictionary content */ -+ { -+ size_t const errorCode = ZSTD_loadEntropy_inDDict(ddict); -+ if (ZSTD_isError(errorCode)) { -+ ZSTD_freeDDict(ddict); -+ return NULL; -+ } -+ } -+ -+ return ddict; -+ } -+} -+ -+/*! ZSTD_initDDict() : -+* Create a digested dictionary, to start decompression without startup delay. -+* `dict` content is copied inside DDict. -+* Consequently, `dict` can be released after `ZSTD_DDict` creation */ -+ZSTD_DDict *INIT ZSTD_initDDict(const void *dict, size_t dictSize, void *workspace, size_t workspaceSize) -+{ -+ ZSTD_customMem const stackMem = ZSTD_initStack(workspace, workspaceSize); -+ return ZSTD_createDDict_advanced(dict, dictSize, 1, stackMem); -+} -+ -+size_t INIT ZSTD_freeDDict(ZSTD_DDict *ddict) -+{ -+ if (ddict == NULL) -+ return 0; /* support free on NULL */ -+ { -+ ZSTD_customMem const cMem = ddict->cMem; -+ ZSTD_free(ddict->dictBuffer, cMem); -+ ZSTD_free(ddict, cMem); -+ return 0; -+ } -+} -+ -+/*! ZSTD_getDictID_fromDict() : -+ * Provides the dictID stored within dictionary. -+ * if @return == 0, the dictionary is not conformant with Zstandard specification. -+ * It can still be loaded, but as a content-only dictionary. */ -+unsigned INIT ZSTD_getDictID_fromDict(const void *dict, size_t dictSize) -+{ -+ if (dictSize < 8) -+ return 0; -+ if (ZSTD_readLE32(dict) != ZSTD_DICT_MAGIC) -+ return 0; -+ return ZSTD_readLE32((const char *)dict + 4); -+} -+ -+/*! ZSTD_getDictID_fromDDict() : -+ * Provides the dictID of the dictionary loaded into `ddict`. -+ * If @return == 0, the dictionary is not conformant to Zstandard specification, or empty. -+ * Non-conformant dictionaries can still be loaded, but as content-only dictionaries. */ -+unsigned INIT ZSTD_getDictID_fromDDict(const ZSTD_DDict *ddict) -+{ -+ if (ddict == NULL) -+ return 0; -+ return ZSTD_getDictID_fromDict(ddict->dictContent, ddict->dictSize); -+} -+ -+/*! ZSTD_getDictID_fromFrame() : -+ * Provides the dictID required to decompressed the frame stored within `src`. -+ * If @return == 0, the dictID could not be decoded. -+ * This could for one of the following reasons : -+ * - The frame does not require a dictionary to be decoded (most common case). -+ * - The frame was built with dictID intentionally removed. Whatever dictionary is necessary is a hidden information. -+ * Note : this use case also happens when using a non-conformant dictionary. -+ * - `srcSize` is too small, and as a result, the frame header could not be decoded (only possible if `srcSize < ZSTD_FRAMEHEADERSIZE_MAX`). -+ * - This is not a Zstandard frame. -+ * When identifying the exact failure cause, it's possible to used ZSTD_getFrameParams(), which will provide a more precise error code. */ -+unsigned INIT ZSTD_getDictID_fromFrame(const void *src, size_t srcSize) -+{ -+ ZSTD_frameParams zfp = {0, 0, 0, 0}; -+ size_t const hError = ZSTD_getFrameParams(&zfp, src, srcSize); -+ if (ZSTD_isError(hError)) -+ return 0; -+ return zfp.dictID; -+} -+ -+/*! ZSTD_decompress_usingDDict() : -+* Decompression using a pre-digested Dictionary -+* Use dictionary without significant overhead. */ -+size_t INIT ZSTD_decompress_usingDDict(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize, const ZSTD_DDict *ddict) -+{ -+ /* pass content and size in case legacy frames are encountered */ -+ return ZSTD_decompressMultiFrame(dctx, dst, dstCapacity, src, srcSize, NULL, 0, ddict); -+} -+ -+/*===================================== -+* Streaming decompression -+*====================================*/ -+ -+typedef enum { zdss_init, zdss_loadHeader, zdss_read, zdss_load, zdss_flush } ZSTD_dStreamStage; -+ -+/* *** Resource management *** */ -+struct ZSTD_DStream_s { -+ ZSTD_DCtx *dctx; -+ ZSTD_DDict *ddictLocal; -+ const ZSTD_DDict *ddict; -+ ZSTD_frameParams fParams; -+ ZSTD_dStreamStage stage; -+ char *inBuff; -+ size_t inBuffSize; -+ size_t inPos; -+ size_t maxWindowSize; -+ char *outBuff; -+ size_t outBuffSize; -+ size_t outStart; -+ size_t outEnd; -+ size_t blockSize; -+ BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX]; /* tmp buffer to store frame header */ -+ size_t lhSize; -+ ZSTD_customMem customMem; -+ void *legacyContext; -+ U32 previousLegacyVersion; -+ U32 legacyVersion; -+ U32 hostageByte; -+}; /* typedef'd to ZSTD_DStream within "zstd.h" */ -+ -+size_t INIT ZSTD_DStreamWorkspaceBound(size_t maxWindowSize) -+{ -+ size_t const blockSize = MIN(maxWindowSize, ZSTD_BLOCKSIZE_ABSOLUTEMAX); -+ size_t const inBuffSize = blockSize; -+ size_t const outBuffSize = maxWindowSize + blockSize + WILDCOPY_OVERLENGTH * 2; -+ return ZSTD_DCtxWorkspaceBound() + ZSTD_ALIGN(sizeof(ZSTD_DStream)) + ZSTD_ALIGN(inBuffSize) + ZSTD_ALIGN(outBuffSize); -+} -+ -+static ZSTD_DStream *INIT ZSTD_createDStream_advanced(ZSTD_customMem customMem) -+{ -+ ZSTD_DStream *zds; -+ -+ if (!customMem.customAlloc || !customMem.customFree) -+ return NULL; -+ -+ zds = (ZSTD_DStream *)ZSTD_malloc(sizeof(ZSTD_DStream), customMem); -+ if (zds == NULL) -+ return NULL; -+ memset(zds, 0, sizeof(ZSTD_DStream)); -+ memcpy(&zds->customMem, &customMem, sizeof(ZSTD_customMem)); -+ zds->dctx = ZSTD_createDCtx_advanced(customMem); -+ if (zds->dctx == NULL) { -+ ZSTD_freeDStream(zds); -+ return NULL; -+ } -+ zds->stage = zdss_init; -+ zds->maxWindowSize = ZSTD_MAXWINDOWSIZE_DEFAULT; -+ return zds; -+} -+ -+ZSTD_DStream *INIT ZSTD_initDStream(size_t maxWindowSize, void *workspace, size_t workspaceSize) -+{ -+ ZSTD_customMem const stackMem = ZSTD_initStack(workspace, workspaceSize); -+ ZSTD_DStream *zds = ZSTD_createDStream_advanced(stackMem); -+ if (!zds) { -+ return NULL; -+ } -+ -+ zds->maxWindowSize = maxWindowSize; -+ zds->stage = zdss_loadHeader; -+ zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0; -+ ZSTD_freeDDict(zds->ddictLocal); -+ zds->ddictLocal = NULL; -+ zds->ddict = zds->ddictLocal; -+ zds->legacyVersion = 0; -+ zds->hostageByte = 0; -+ -+ { -+ size_t const blockSize = MIN(zds->maxWindowSize, ZSTD_BLOCKSIZE_ABSOLUTEMAX); -+ size_t const neededOutSize = zds->maxWindowSize + blockSize + WILDCOPY_OVERLENGTH * 2; -+ -+ zds->inBuff = (char *)ZSTD_malloc(blockSize, zds->customMem); -+ zds->inBuffSize = blockSize; -+ zds->outBuff = (char *)ZSTD_malloc(neededOutSize, zds->customMem); -+ zds->outBuffSize = neededOutSize; -+ if (zds->inBuff == NULL || zds->outBuff == NULL) { -+ ZSTD_freeDStream(zds); -+ return NULL; -+ } -+ } -+ return zds; -+} -+ -+ZSTD_DStream *INIT ZSTD_initDStream_usingDDict(size_t maxWindowSize, const ZSTD_DDict *ddict, void *workspace, size_t workspaceSize) -+{ -+ ZSTD_DStream *zds = ZSTD_initDStream(maxWindowSize, workspace, workspaceSize); -+ if (zds) { -+ zds->ddict = ddict; -+ } -+ return zds; -+} -+ -+size_t INIT ZSTD_freeDStream(ZSTD_DStream *zds) -+{ -+ if (zds == NULL) -+ return 0; /* support free on null */ -+ { -+ ZSTD_customMem const cMem = zds->customMem; -+ ZSTD_freeDCtx(zds->dctx); -+ zds->dctx = NULL; -+ ZSTD_freeDDict(zds->ddictLocal); -+ zds->ddictLocal = NULL; -+ ZSTD_free(zds->inBuff, cMem); -+ zds->inBuff = NULL; -+ ZSTD_free(zds->outBuff, cMem); -+ zds->outBuff = NULL; -+ ZSTD_free(zds, cMem); -+ return 0; -+ } -+} -+ -+/* *** Initialization *** */ -+ -+size_t INIT ZSTD_DStreamInSize(void) { return ZSTD_BLOCKSIZE_ABSOLUTEMAX + ZSTD_blockHeaderSize; } -+size_t INIT ZSTD_DStreamOutSize(void) { return ZSTD_BLOCKSIZE_ABSOLUTEMAX; } -+ -+size_t INIT ZSTD_resetDStream(ZSTD_DStream *zds) -+{ -+ zds->stage = zdss_loadHeader; -+ zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0; -+ zds->legacyVersion = 0; -+ zds->hostageByte = 0; -+ return ZSTD_frameHeaderSize_prefix; -+} -+ -+/* ***** Decompression ***** */ -+ -+ZSTD_STATIC size_t INIT ZSTD_limitCopy(void *dst, size_t dstCapacity, const void *src, size_t srcSize) -+{ -+ size_t const length = MIN(dstCapacity, srcSize); -+ memcpy(dst, src, length); -+ return length; -+} -+ -+size_t INIT ZSTD_decompressStream(ZSTD_DStream *zds, ZSTD_outBuffer *output, ZSTD_inBuffer *input) -+{ -+ const char *const istart = (const char *)(input->src) + input->pos; -+ const char *const iend = (const char *)(input->src) + input->size; -+ const char *ip = istart; -+ char *const ostart = (char *)(output->dst) + output->pos; -+ char *const oend = (char *)(output->dst) + output->size; -+ char *op = ostart; -+ U32 someMoreWork = 1; -+ -+ while (someMoreWork) { -+ switch (zds->stage) { -+ case zdss_init: -+ ZSTD_resetDStream(zds); /* transparent reset on starting decoding a new frame */ -+ /* fallthrough */ -+ -+ case zdss_loadHeader: { -+ size_t const hSize = ZSTD_getFrameParams(&zds->fParams, zds->headerBuffer, zds->lhSize); -+ if (ZSTD_isError(hSize)) -+ return hSize; -+ if (hSize != 0) { /* need more input */ -+ size_t const toLoad = hSize - zds->lhSize; /* if hSize!=0, hSize > zds->lhSize */ -+ if (toLoad > (size_t)(iend - ip)) { /* not enough input to load full header */ -+ memcpy(zds->headerBuffer + zds->lhSize, ip, iend - ip); -+ zds->lhSize += iend - ip; -+ input->pos = input->size; -+ return (MAX(ZSTD_frameHeaderSize_min, hSize) - zds->lhSize) + -+ ZSTD_blockHeaderSize; /* remaining header bytes + next block header */ -+ } -+ memcpy(zds->headerBuffer + zds->lhSize, ip, toLoad); -+ zds->lhSize = hSize; -+ ip += toLoad; -+ break; -+ } -+ -+ /* check for single-pass mode opportunity */ -+ if (zds->fParams.frameContentSize && zds->fParams.windowSize /* skippable frame if == 0 */ -+ && (U64)(size_t)(oend - op) >= zds->fParams.frameContentSize) { -+ size_t const cSize = ZSTD_findFrameCompressedSize(istart, iend - istart); -+ if (cSize <= (size_t)(iend - istart)) { -+ size_t const decompressedSize = ZSTD_decompress_usingDDict(zds->dctx, op, oend - op, istart, cSize, zds->ddict); -+ if (ZSTD_isError(decompressedSize)) -+ return decompressedSize; -+ ip = istart + cSize; -+ op += decompressedSize; -+ zds->dctx->expected = 0; -+ zds->stage = zdss_init; -+ someMoreWork = 0; -+ break; -+ } -+ } -+ -+ /* Consume header */ -+ ZSTD_refDDict(zds->dctx, zds->ddict); -+ { -+ size_t const h1Size = ZSTD_nextSrcSizeToDecompress(zds->dctx); /* == ZSTD_frameHeaderSize_prefix */ -+ CHECK_F(ZSTD_decompressContinue(zds->dctx, NULL, 0, zds->headerBuffer, h1Size)); -+ { -+ size_t const h2Size = ZSTD_nextSrcSizeToDecompress(zds->dctx); -+ CHECK_F(ZSTD_decompressContinue(zds->dctx, NULL, 0, zds->headerBuffer + h1Size, h2Size)); -+ } -+ } -+ -+ zds->fParams.windowSize = MAX(zds->fParams.windowSize, 1U << ZSTD_WINDOWLOG_ABSOLUTEMIN); -+ if (zds->fParams.windowSize > zds->maxWindowSize) -+ return ERROR(frameParameter_windowTooLarge); -+ -+ /* Buffers are preallocated, but double check */ -+ { -+ size_t const blockSize = MIN(zds->maxWindowSize, ZSTD_BLOCKSIZE_ABSOLUTEMAX); -+ size_t const neededOutSize = zds->maxWindowSize + blockSize + WILDCOPY_OVERLENGTH * 2; -+ if (zds->inBuffSize < blockSize) { -+ return ERROR(GENERIC); -+ } -+ if (zds->outBuffSize < neededOutSize) { -+ return ERROR(GENERIC); -+ } -+ zds->blockSize = blockSize; -+ } -+ zds->stage = zdss_read; -+ } -+ /* fallthrough */ -+ -+ case zdss_read: { -+ size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zds->dctx); -+ if (neededInSize == 0) { /* end of frame */ -+ zds->stage = zdss_init; -+ someMoreWork = 0; -+ break; -+ } -+ if ((size_t)(iend - ip) >= neededInSize) { /* decode directly from src */ -+ const int isSkipFrame = ZSTD_isSkipFrame(zds->dctx); -+ size_t const decodedSize = ZSTD_decompressContinue(zds->dctx, zds->outBuff + zds->outStart, -+ (isSkipFrame ? 0 : zds->outBuffSize - zds->outStart), ip, neededInSize); -+ if (ZSTD_isError(decodedSize)) -+ return decodedSize; -+ ip += neededInSize; -+ if (!decodedSize && !isSkipFrame) -+ break; /* this was just a header */ -+ zds->outEnd = zds->outStart + decodedSize; -+ zds->stage = zdss_flush; -+ break; -+ } -+ if (ip == iend) { -+ someMoreWork = 0; -+ break; -+ } /* no more input */ -+ zds->stage = zdss_load; -+ /* pass-through */ -+ } -+ /* fallthrough */ -+ -+ case zdss_load: { -+ size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zds->dctx); -+ size_t const toLoad = neededInSize - zds->inPos; /* should always be <= remaining space within inBuff */ -+ size_t loadedSize; -+ if (toLoad > zds->inBuffSize - zds->inPos) -+ return ERROR(corruption_detected); /* should never happen */ -+ loadedSize = ZSTD_limitCopy(zds->inBuff + zds->inPos, toLoad, ip, iend - ip); -+ ip += loadedSize; -+ zds->inPos += loadedSize; -+ if (loadedSize < toLoad) { -+ someMoreWork = 0; -+ break; -+ } /* not enough input, wait for more */ -+ -+ /* decode loaded input */ -+ { -+ const int isSkipFrame = ZSTD_isSkipFrame(zds->dctx); -+ size_t const decodedSize = ZSTD_decompressContinue(zds->dctx, zds->outBuff + zds->outStart, zds->outBuffSize - zds->outStart, -+ zds->inBuff, neededInSize); -+ if (ZSTD_isError(decodedSize)) -+ return decodedSize; -+ zds->inPos = 0; /* input is consumed */ -+ if (!decodedSize && !isSkipFrame) { -+ zds->stage = zdss_read; -+ break; -+ } /* this was just a header */ -+ zds->outEnd = zds->outStart + decodedSize; -+ zds->stage = zdss_flush; -+ /* pass-through */ -+ } -+ } -+ /* fallthrough */ -+ -+ case zdss_flush: { -+ size_t const toFlushSize = zds->outEnd - zds->outStart; -+ size_t const flushedSize = ZSTD_limitCopy(op, oend - op, zds->outBuff + zds->outStart, toFlushSize); -+ op += flushedSize; -+ zds->outStart += flushedSize; -+ if (flushedSize == toFlushSize) { /* flush completed */ -+ zds->stage = zdss_read; -+ if (zds->outStart + zds->blockSize > zds->outBuffSize) -+ zds->outStart = zds->outEnd = 0; -+ break; -+ } -+ /* cannot complete flush */ -+ someMoreWork = 0; -+ break; -+ } -+ default: -+ return ERROR(GENERIC); /* impossible */ -+ } -+ } -+ -+ /* result */ -+ input->pos += (size_t)(ip - istart); -+ output->pos += (size_t)(op - ostart); -+ { -+ size_t nextSrcSizeHint = ZSTD_nextSrcSizeToDecompress(zds->dctx); -+ if (!nextSrcSizeHint) { /* frame fully decoded */ -+ if (zds->outEnd == zds->outStart) { /* output fully flushed */ -+ if (zds->hostageByte) { -+ if (input->pos >= input->size) { -+ zds->stage = zdss_read; -+ return 1; -+ } /* can't release hostage (not present) */ -+ input->pos++; /* release hostage */ -+ } -+ return 0; -+ } -+ if (!zds->hostageByte) { /* output not fully flushed; keep last byte as hostage; will be released when all output is flushed */ -+ input->pos--; /* note : pos > 0, otherwise, impossible to finish reading last block */ -+ zds->hostageByte = 1; -+ } -+ return 1; -+ } -+ nextSrcSizeHint += ZSTD_blockHeaderSize * (ZSTD_nextInputType(zds->dctx) == ZSTDnit_block); /* preload header of next block */ -+ if (zds->inPos > nextSrcSizeHint) -+ return ERROR(GENERIC); /* should never happen */ -+ nextSrcSizeHint -= zds->inPos; /* already loaded*/ -+ return nextSrcSizeHint; -+ } -+} -diff --git a/xen/common/zstd/entropy_common.c b/xen/common/zstd/entropy_common.c -new file mode 100644 -index 0000000000..bcdb57982b ---- /dev/null -+++ b/xen/common/zstd/entropy_common.c -@@ -0,0 +1,243 @@ -+/* -+ * Common functions of New Generation Entropy library -+ * Copyright (C) 2016, Yann Collet. -+ * -+ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are -+ * met: -+ * -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above -+ * copyright notice, this list of conditions and the following disclaimer -+ * in the documentation and/or other materials provided with the -+ * distribution. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ * -+ * This program is free software; you can redistribute it and/or modify it under -+ * the terms of the GNU General Public License version 2 as published by the -+ * Free Software Foundation. This program is dual-licensed; you may select -+ * either version 2 of the GNU General Public License ("GPL") or BSD license -+ * ("BSD"). -+ * -+ * You can contact the author at : -+ * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy -+ */ -+ -+/* ************************************* -+* Dependencies -+***************************************/ -+#include "error_private.h" /* ERR_*, ERROR */ -+#include "fse.h" -+#include "huf.h" -+#include "mem.h" -+ -+/*=== Version ===*/ -+unsigned INIT FSE_versionNumber(void) { return FSE_VERSION_NUMBER; } -+ -+/*=== Error Management ===*/ -+unsigned INIT FSE_isError(size_t code) { return ERR_isError(code); } -+ -+unsigned INIT HUF_isError(size_t code) { return ERR_isError(code); } -+ -+/*-************************************************************** -+* FSE NCount encoding-decoding -+****************************************************************/ -+size_t INIT FSE_readNCount(short *normalizedCounter, unsigned *maxSVPtr, unsigned *tableLogPtr, const void *headerBuffer, size_t hbSize) -+{ -+ const BYTE *const istart = (const BYTE *)headerBuffer; -+ const BYTE *const iend = istart + hbSize; -+ const BYTE *ip = istart; -+ int nbBits; -+ int remaining; -+ int threshold; -+ U32 bitStream; -+ int bitCount; -+ unsigned charnum = 0; -+ int previous0 = 0; -+ -+ if (hbSize < 4) -+ return ERROR(srcSize_wrong); -+ bitStream = ZSTD_readLE32(ip); -+ nbBits = (bitStream & 0xF) + FSE_MIN_TABLELOG; /* extract tableLog */ -+ if (nbBits > FSE_TABLELOG_ABSOLUTE_MAX) -+ return ERROR(tableLog_tooLarge); -+ bitStream >>= 4; -+ bitCount = 4; -+ *tableLogPtr = nbBits; -+ remaining = (1 << nbBits) + 1; -+ threshold = 1 << nbBits; -+ nbBits++; -+ -+ while ((remaining > 1) & (charnum <= *maxSVPtr)) { -+ if (previous0) { -+ unsigned n0 = charnum; -+ while ((bitStream & 0xFFFF) == 0xFFFF) { -+ n0 += 24; -+ if (ip < iend - 5) { -+ ip += 2; -+ bitStream = ZSTD_readLE32(ip) >> bitCount; -+ } else { -+ bitStream >>= 16; -+ bitCount += 16; -+ } -+ } -+ while ((bitStream & 3) == 3) { -+ n0 += 3; -+ bitStream >>= 2; -+ bitCount += 2; -+ } -+ n0 += bitStream & 3; -+ bitCount += 2; -+ if (n0 > *maxSVPtr) -+ return ERROR(maxSymbolValue_tooSmall); -+ while (charnum < n0) -+ normalizedCounter[charnum++] = 0; -+ if ((ip <= iend - 7) || (ip + (bitCount >> 3) <= iend - 4)) { -+ ip += bitCount >> 3; -+ bitCount &= 7; -+ bitStream = ZSTD_readLE32(ip) >> bitCount; -+ } else { -+ bitStream >>= 2; -+ } -+ } -+ { -+ int const max = (2 * threshold - 1) - remaining; -+ int count; -+ -+ if ((bitStream & (threshold - 1)) < (U32)max) { -+ count = bitStream & (threshold - 1); -+ bitCount += nbBits - 1; -+ } else { -+ count = bitStream & (2 * threshold - 1); -+ if (count >= threshold) -+ count -= max; -+ bitCount += nbBits; -+ } -+ -+ count--; /* extra accuracy */ -+ remaining -= count < 0 ? -count : count; /* -1 means +1 */ -+ normalizedCounter[charnum++] = (short)count; -+ previous0 = !count; -+ while (remaining < threshold) { -+ nbBits--; -+ threshold >>= 1; -+ } -+ -+ if ((ip <= iend - 7) || (ip + (bitCount >> 3) <= iend - 4)) { -+ ip += bitCount >> 3; -+ bitCount &= 7; -+ } else { -+ bitCount -= (int)(8 * (iend - 4 - ip)); -+ ip = iend - 4; -+ } -+ bitStream = ZSTD_readLE32(ip) >> (bitCount & 31); -+ } -+ } /* while ((remaining>1) & (charnum<=*maxSVPtr)) */ -+ if (remaining != 1) -+ return ERROR(corruption_detected); -+ if (bitCount > 32) -+ return ERROR(corruption_detected); -+ *maxSVPtr = charnum - 1; -+ -+ ip += (bitCount + 7) >> 3; -+ return ip - istart; -+} -+ -+/*! HUF_readStats() : -+ Read compact Huffman tree, saved by HUF_writeCTable(). -+ `huffWeight` is destination buffer. -+ `rankStats` is assumed to be a table of at least HUF_TABLELOG_MAX U32. -+ @return : size read from `src` , or an error Code . -+ Note : Needed by HUF_readCTable() and HUF_readDTableX?() . -+*/ -+size_t INIT HUF_readStats_wksp(BYTE *huffWeight, size_t hwSize, U32 *rankStats, U32 *nbSymbolsPtr, U32 *tableLogPtr, const void *src, size_t srcSize, void *workspace, size_t workspaceSize) -+{ -+ U32 weightTotal; -+ const BYTE *ip = (const BYTE *)src; -+ size_t iSize; -+ size_t oSize; -+ -+ if (!srcSize) -+ return ERROR(srcSize_wrong); -+ iSize = ip[0]; -+ /* memset(huffWeight, 0, hwSize); */ /* is not necessary, even though some analyzer complain ... */ -+ -+ if (iSize >= 128) { /* special header */ -+ oSize = iSize - 127; -+ iSize = ((oSize + 1) / 2); -+ if (iSize + 1 > srcSize) -+ return ERROR(srcSize_wrong); -+ if (oSize >= hwSize) -+ return ERROR(corruption_detected); -+ ip += 1; -+ { -+ U32 n; -+ for (n = 0; n < oSize; n += 2) { -+ huffWeight[n] = ip[n / 2] >> 4; -+ huffWeight[n + 1] = ip[n / 2] & 15; -+ } -+ } -+ } else { /* header compressed with FSE (normal case) */ -+ if (iSize + 1 > srcSize) -+ return ERROR(srcSize_wrong); -+ oSize = FSE_decompress_wksp(huffWeight, hwSize - 1, ip + 1, iSize, 6, workspace, workspaceSize); /* max (hwSize-1) values decoded, as last one is implied */ -+ if (FSE_isError(oSize)) -+ return oSize; -+ } -+ -+ /* collect weight stats */ -+ memset(rankStats, 0, (HUF_TABLELOG_MAX + 1) * sizeof(U32)); -+ weightTotal = 0; -+ { -+ U32 n; -+ for (n = 0; n < oSize; n++) { -+ if (huffWeight[n] >= HUF_TABLELOG_MAX) -+ return ERROR(corruption_detected); -+ rankStats[huffWeight[n]]++; -+ weightTotal += (1 << huffWeight[n]) >> 1; -+ } -+ } -+ if (weightTotal == 0) -+ return ERROR(corruption_detected); -+ -+ /* get last non-null symbol weight (implied, total must be 2^n) */ -+ { -+ U32 const tableLog = BIT_highbit32(weightTotal) + 1; -+ if (tableLog > HUF_TABLELOG_MAX) -+ return ERROR(corruption_detected); -+ *tableLogPtr = tableLog; -+ /* determine last weight */ -+ { -+ U32 const total = 1 << tableLog; -+ U32 const rest = total - weightTotal; -+ U32 const verif = 1 << BIT_highbit32(rest); -+ U32 const lastWeight = BIT_highbit32(rest) + 1; -+ if (verif != rest) -+ return ERROR(corruption_detected); /* last value must be a clean power of 2 */ -+ huffWeight[oSize] = (BYTE)lastWeight; -+ rankStats[lastWeight]++; -+ } -+ } -+ -+ /* check tree construction validity */ -+ if ((rankStats[1] < 2) || (rankStats[1] & 1)) -+ return ERROR(corruption_detected); /* by construction : at least 2 elts of rank 1, must be even */ -+ -+ /* results */ -+ *nbSymbolsPtr = (U32)(oSize + 1); -+ return iSize + 1; -+} -diff --git a/xen/common/zstd/error_private.h b/xen/common/zstd/error_private.h -new file mode 100644 -index 0000000000..d07bf3cb9b ---- /dev/null -+++ b/xen/common/zstd/error_private.h -@@ -0,0 +1,110 @@ -+/** -+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. -+ * All rights reserved. -+ * -+ * This source code is licensed under the BSD-style license found in the -+ * LICENSE file in the root directory of https://github.com/facebook/zstd. -+ * An additional grant of patent rights can be found in the PATENTS file in the -+ * same directory. -+ * -+ * This program is free software; you can redistribute it and/or modify it under -+ * the terms of the GNU General Public License version 2 as published by the -+ * Free Software Foundation. This program is dual-licensed; you may select -+ * either version 2 of the GNU General Public License ("GPL") or BSD license -+ * ("BSD"). -+ */ -+ -+/* Note : this module is expected to remain private, do not expose it */ -+ -+#ifndef ERROR_H_MODULE -+#define ERROR_H_MODULE -+ -+/* **************************************** -+* Dependencies -+******************************************/ -+#include /* size_t */ -+ -+/** -+ * enum ZSTD_ErrorCode - zstd error codes -+ * -+ * Functions that return size_t can be checked for errors using ZSTD_isError() -+ * and the ZSTD_ErrorCode can be extracted using ZSTD_getErrorCode(). -+ */ -+typedef enum { -+ ZSTD_error_no_error, -+ ZSTD_error_GENERIC, -+ ZSTD_error_prefix_unknown, -+ ZSTD_error_version_unsupported, -+ ZSTD_error_parameter_unknown, -+ ZSTD_error_frameParameter_unsupported, -+ ZSTD_error_frameParameter_unsupportedBy32bits, -+ ZSTD_error_frameParameter_windowTooLarge, -+ ZSTD_error_compressionParameter_unsupported, -+ ZSTD_error_init_missing, -+ ZSTD_error_memory_allocation, -+ ZSTD_error_stage_wrong, -+ ZSTD_error_dstSize_tooSmall, -+ ZSTD_error_srcSize_wrong, -+ ZSTD_error_corruption_detected, -+ ZSTD_error_checksum_wrong, -+ ZSTD_error_tableLog_tooLarge, -+ ZSTD_error_maxSymbolValue_tooLarge, -+ ZSTD_error_maxSymbolValue_tooSmall, -+ ZSTD_error_dictionary_corrupted, -+ ZSTD_error_dictionary_wrong, -+ ZSTD_error_dictionaryCreation_failed, -+ ZSTD_error_maxCode -+} ZSTD_ErrorCode; -+ -+/* **************************************** -+* Compiler-specific -+******************************************/ -+#define ERR_STATIC static __attribute__((unused)) -+ -+/*-**************************************** -+* Customization (error_public.h) -+******************************************/ -+typedef ZSTD_ErrorCode ERR_enum; -+#define PREFIX(name) ZSTD_error_##name -+ -+/*-**************************************** -+* Error codes handling -+******************************************/ -+#define ERROR(name) ((size_t)-PREFIX(name)) -+ -+ERR_STATIC unsigned INIT ERR_isError(size_t code) { return (code > ERROR(maxCode)); } -+ -+ERR_STATIC ERR_enum INIT ERR_getErrorCode(size_t code) -+{ -+ if (!ERR_isError(code)) -+ return (ERR_enum)0; -+ return (ERR_enum)(0 - code); -+} -+ -+/** -+ * ZSTD_isError() - tells if a size_t function result is an error code -+ * @code: The function result to check for error. -+ * -+ * Return: Non-zero iff the code is an error. -+ */ -+static __attribute__((unused)) unsigned int INIT ZSTD_isError(size_t code) -+{ -+ return code > (size_t)-ZSTD_error_maxCode; -+} -+ -+/** -+ * ZSTD_getErrorCode() - translates an error function result to a ZSTD_ErrorCode -+ * @functionResult: The result of a function for which ZSTD_isError() is true. -+ * -+ * Return: The ZSTD_ErrorCode corresponding to the functionResult or 0 -+ * if the functionResult isn't an error. -+ */ -+static __attribute__((unused)) ZSTD_ErrorCode INIT ZSTD_getErrorCode( -+ size_t functionResult) -+{ -+ if (!ZSTD_isError(functionResult)) -+ return (ZSTD_ErrorCode)0; -+ return (ZSTD_ErrorCode)(0 - functionResult); -+} -+ -+#endif /* ERROR_H_MODULE */ -diff --git a/xen/common/zstd/fse.h b/xen/common/zstd/fse.h -new file mode 100644 -index 0000000000..b86717c34d ---- /dev/null -+++ b/xen/common/zstd/fse.h -@@ -0,0 +1,575 @@ -+/* -+ * FSE : Finite State Entropy codec -+ * Public Prototypes declaration -+ * Copyright (C) 2013-2016, Yann Collet. -+ * -+ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are -+ * met: -+ * -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above -+ * copyright notice, this list of conditions and the following disclaimer -+ * in the documentation and/or other materials provided with the -+ * distribution. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ * -+ * This program is free software; you can redistribute it and/or modify it under -+ * the terms of the GNU General Public License version 2 as published by the -+ * Free Software Foundation. This program is dual-licensed; you may select -+ * either version 2 of the GNU General Public License ("GPL") or BSD license -+ * ("BSD"). -+ * -+ * You can contact the author at : -+ * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy -+ */ -+#ifndef FSE_H -+#define FSE_H -+ -+/*-***************************************** -+* Dependencies -+******************************************/ -+#include /* size_t, ptrdiff_t */ -+ -+/*-***************************************** -+* FSE_PUBLIC_API : control library symbols visibility -+******************************************/ -+#define FSE_PUBLIC_API -+ -+/*------ Version ------*/ -+#define FSE_VERSION_MAJOR 0 -+#define FSE_VERSION_MINOR 9 -+#define FSE_VERSION_RELEASE 0 -+ -+#define FSE_LIB_VERSION FSE_VERSION_MAJOR.FSE_VERSION_MINOR.FSE_VERSION_RELEASE -+#define FSE_QUOTE(str) #str -+#define FSE_EXPAND_AND_QUOTE(str) FSE_QUOTE(str) -+#define FSE_VERSION_STRING FSE_EXPAND_AND_QUOTE(FSE_LIB_VERSION) -+ -+#define FSE_VERSION_NUMBER (FSE_VERSION_MAJOR * 100 * 100 + FSE_VERSION_MINOR * 100 + FSE_VERSION_RELEASE) -+FSE_PUBLIC_API unsigned FSE_versionNumber(void); /**< library version number; to be used when checking dll version */ -+ -+/*-***************************************** -+* Tool functions -+******************************************/ -+FSE_PUBLIC_API size_t FSE_compressBound(size_t size); /* maximum compressed size */ -+ -+/* Error Management */ -+FSE_PUBLIC_API unsigned FSE_isError(size_t code); /* tells if a return value is an error code */ -+ -+/*-***************************************** -+* FSE detailed API -+******************************************/ -+/*! -+FSE_compress() does the following: -+1. count symbol occurrence from source[] into table count[] -+2. normalize counters so that sum(count[]) == Power_of_2 (2^tableLog) -+3. save normalized counters to memory buffer using writeNCount() -+4. build encoding table 'CTable' from normalized counters -+5. encode the data stream using encoding table 'CTable' -+ -+FSE_decompress() does the following: -+1. read normalized counters with readNCount() -+2. build decoding table 'DTable' from normalized counters -+3. decode the data stream using decoding table 'DTable' -+ -+The following API allows targeting specific sub-functions for advanced tasks. -+For example, it's possible to compress several blocks using the same 'CTable', -+or to save and provide normalized distribution using external method. -+*/ -+ -+/* *** COMPRESSION *** */ -+/*! FSE_optimalTableLog(): -+ dynamically downsize 'tableLog' when conditions are met. -+ It saves CPU time, by using smaller tables, while preserving or even improving compression ratio. -+ @return : recommended tableLog (necessarily <= 'maxTableLog') */ -+FSE_PUBLIC_API unsigned FSE_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue); -+ -+/*! FSE_normalizeCount(): -+ normalize counts so that sum(count[]) == Power_of_2 (2^tableLog) -+ 'normalizedCounter' is a table of short, of minimum size (maxSymbolValue+1). -+ @return : tableLog, -+ or an errorCode, which can be tested using FSE_isError() */ -+FSE_PUBLIC_API size_t FSE_normalizeCount(short *normalizedCounter, unsigned tableLog, const unsigned *count, size_t srcSize, unsigned maxSymbolValue); -+ -+/*! FSE_NCountWriteBound(): -+ Provides the maximum possible size of an FSE normalized table, given 'maxSymbolValue' and 'tableLog'. -+ Typically useful for allocation purpose. */ -+FSE_PUBLIC_API size_t FSE_NCountWriteBound(unsigned maxSymbolValue, unsigned tableLog); -+ -+/*! FSE_writeNCount(): -+ Compactly save 'normalizedCounter' into 'buffer'. -+ @return : size of the compressed table, -+ or an errorCode, which can be tested using FSE_isError(). */ -+FSE_PUBLIC_API size_t FSE_writeNCount(void *buffer, size_t bufferSize, const short *normalizedCounter, unsigned maxSymbolValue, unsigned tableLog); -+ -+/*! Constructor and Destructor of FSE_CTable. -+ Note that FSE_CTable size depends on 'tableLog' and 'maxSymbolValue' */ -+typedef unsigned FSE_CTable; /* don't allocate that. It's only meant to be more restrictive than void* */ -+ -+/*! FSE_compress_usingCTable(): -+ Compress `src` using `ct` into `dst` which must be already allocated. -+ @return : size of compressed data (<= `dstCapacity`), -+ or 0 if compressed data could not fit into `dst`, -+ or an errorCode, which can be tested using FSE_isError() */ -+FSE_PUBLIC_API size_t FSE_compress_usingCTable(void *dst, size_t dstCapacity, const void *src, size_t srcSize, const FSE_CTable *ct); -+ -+/*! -+Tutorial : -+---------- -+The first step is to count all symbols. FSE_count() does this job very fast. -+Result will be saved into 'count', a table of unsigned int, which must be already allocated, and have 'maxSymbolValuePtr[0]+1' cells. -+'src' is a table of bytes of size 'srcSize'. All values within 'src' MUST be <= maxSymbolValuePtr[0] -+maxSymbolValuePtr[0] will be updated, with its real value (necessarily <= original value) -+FSE_count() will return the number of occurrence of the most frequent symbol. -+This can be used to know if there is a single symbol within 'src', and to quickly evaluate its compressibility. -+If there is an error, the function will return an ErrorCode (which can be tested using FSE_isError()). -+ -+The next step is to normalize the frequencies. -+FSE_normalizeCount() will ensure that sum of frequencies is == 2 ^'tableLog'. -+It also guarantees a minimum of 1 to any Symbol with frequency >= 1. -+You can use 'tableLog'==0 to mean "use default tableLog value". -+If you are unsure of which tableLog value to use, you can ask FSE_optimalTableLog(), -+which will provide the optimal valid tableLog given sourceSize, maxSymbolValue, and a user-defined maximum (0 means "default"). -+ -+The result of FSE_normalizeCount() will be saved into a table, -+called 'normalizedCounter', which is a table of signed short. -+'normalizedCounter' must be already allocated, and have at least 'maxSymbolValue+1' cells. -+The return value is tableLog if everything proceeded as expected. -+It is 0 if there is a single symbol within distribution. -+If there is an error (ex: invalid tableLog value), the function will return an ErrorCode (which can be tested using FSE_isError()). -+ -+'normalizedCounter' can be saved in a compact manner to a memory area using FSE_writeNCount(). -+'buffer' must be already allocated. -+For guaranteed success, buffer size must be at least FSE_headerBound(). -+The result of the function is the number of bytes written into 'buffer'. -+If there is an error, the function will return an ErrorCode (which can be tested using FSE_isError(); ex : buffer size too small). -+ -+'normalizedCounter' can then be used to create the compression table 'CTable'. -+The space required by 'CTable' must be already allocated, using FSE_createCTable(). -+You can then use FSE_buildCTable() to fill 'CTable'. -+If there is an error, both functions will return an ErrorCode (which can be tested using FSE_isError()). -+ -+'CTable' can then be used to compress 'src', with FSE_compress_usingCTable(). -+Similar to FSE_count(), the convention is that 'src' is assumed to be a table of char of size 'srcSize' -+The function returns the size of compressed data (without header), necessarily <= `dstCapacity`. -+If it returns '0', compressed data could not fit into 'dst'. -+If there is an error, the function will return an ErrorCode (which can be tested using FSE_isError()). -+*/ -+ -+/* *** DECOMPRESSION *** */ -+ -+/*! FSE_readNCount(): -+ Read compactly saved 'normalizedCounter' from 'rBuffer'. -+ @return : size read from 'rBuffer', -+ or an errorCode, which can be tested using FSE_isError(). -+ maxSymbolValuePtr[0] and tableLogPtr[0] will also be updated with their respective values */ -+FSE_PUBLIC_API size_t FSE_readNCount(short *normalizedCounter, unsigned *maxSymbolValuePtr, unsigned *tableLogPtr, const void *rBuffer, size_t rBuffSize); -+ -+/*! Constructor and Destructor of FSE_DTable. -+ Note that its size depends on 'tableLog' */ -+typedef unsigned FSE_DTable; /* don't allocate that. It's just a way to be more restrictive than void* */ -+ -+/*! FSE_buildDTable(): -+ Builds 'dt', which must be already allocated, using FSE_createDTable(). -+ return : 0, or an errorCode, which can be tested using FSE_isError() */ -+FSE_PUBLIC_API size_t FSE_buildDTable_wksp(FSE_DTable *dt, const short *normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void *workspace, size_t workspaceSize); -+ -+/*! FSE_decompress_usingDTable(): -+ Decompress compressed source `cSrc` of size `cSrcSize` using `dt` -+ into `dst` which must be already allocated. -+ @return : size of regenerated data (necessarily <= `dstCapacity`), -+ or an errorCode, which can be tested using FSE_isError() */ -+FSE_PUBLIC_API size_t FSE_decompress_usingDTable(void *dst, size_t dstCapacity, const void *cSrc, size_t cSrcSize, const FSE_DTable *dt); -+ -+/*! -+Tutorial : -+---------- -+(Note : these functions only decompress FSE-compressed blocks. -+ If block is uncompressed, use memcpy() instead -+ If block is a single repeated byte, use memset() instead ) -+ -+The first step is to obtain the normalized frequencies of symbols. -+This can be performed by FSE_readNCount() if it was saved using FSE_writeNCount(). -+'normalizedCounter' must be already allocated, and have at least 'maxSymbolValuePtr[0]+1' cells of signed short. -+In practice, that means it's necessary to know 'maxSymbolValue' beforehand, -+or size the table to handle worst case situations (typically 256). -+FSE_readNCount() will provide 'tableLog' and 'maxSymbolValue'. -+The result of FSE_readNCount() is the number of bytes read from 'rBuffer'. -+Note that 'rBufferSize' must be at least 4 bytes, even if useful information is less than that. -+If there is an error, the function will return an error code, which can be tested using FSE_isError(). -+ -+The next step is to build the decompression tables 'FSE_DTable' from 'normalizedCounter'. -+This is performed by the function FSE_buildDTable(). -+The space required by 'FSE_DTable' must be already allocated using FSE_createDTable(). -+If there is an error, the function will return an error code, which can be tested using FSE_isError(). -+ -+`FSE_DTable` can then be used to decompress `cSrc`, with FSE_decompress_usingDTable(). -+`cSrcSize` must be strictly correct, otherwise decompression will fail. -+FSE_decompress_usingDTable() result will tell how many bytes were regenerated (<=`dstCapacity`). -+If there is an error, the function will return an error code, which can be tested using FSE_isError(). (ex: dst buffer too small) -+*/ -+ -+/* *** Dependency *** */ -+#include "bitstream.h" -+ -+/* ***************************************** -+* Static allocation -+*******************************************/ -+/* FSE buffer bounds */ -+#define FSE_NCOUNTBOUND 512 -+#define FSE_BLOCKBOUND(size) (size + (size >> 7)) -+#define FSE_COMPRESSBOUND(size) (FSE_NCOUNTBOUND + FSE_BLOCKBOUND(size)) /* Macro version, useful for static allocation */ -+ -+/* It is possible to statically allocate FSE CTable/DTable as a table of FSE_CTable/FSE_DTable using below macros */ -+#define FSE_CTABLE_SIZE_U32(maxTableLog, maxSymbolValue) (1 + (1 << (maxTableLog - 1)) + ((maxSymbolValue + 1) * 2)) -+#define FSE_DTABLE_SIZE_U32(maxTableLog) (1 + (1 << maxTableLog)) -+ -+/* ***************************************** -+* FSE advanced API -+*******************************************/ -+/* FSE_count_wksp() : -+ * Same as FSE_count(), but using an externally provided scratch buffer. -+ * `workSpace` size must be table of >= `1024` unsigned -+ */ -+size_t FSE_count_wksp(unsigned *count, unsigned *maxSymbolValuePtr, const void *source, size_t sourceSize, unsigned *workSpace); -+ -+/* FSE_countFast_wksp() : -+ * Same as FSE_countFast(), but using an externally provided scratch buffer. -+ * `workSpace` must be a table of minimum `1024` unsigned -+ */ -+size_t FSE_countFast_wksp(unsigned *count, unsigned *maxSymbolValuePtr, const void *src, size_t srcSize, unsigned *workSpace); -+ -+/*! FSE_count_simple -+ * Same as FSE_countFast(), but does not use any additional memory (not even on stack). -+ * This function is unsafe, and will segfault if any value within `src` is `> *maxSymbolValuePtr` (presuming it's also the size of `count`). -+*/ -+size_t FSE_count_simple(unsigned *count, unsigned *maxSymbolValuePtr, const void *src, size_t srcSize); -+ -+unsigned FSE_optimalTableLog_internal(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, unsigned minus); -+/**< same as FSE_optimalTableLog(), which used `minus==2` */ -+ -+size_t FSE_buildCTable_raw(FSE_CTable *ct, unsigned nbBits); -+/**< build a fake FSE_CTable, designed for a flat distribution, where each symbol uses nbBits */ -+ -+size_t FSE_buildCTable_rle(FSE_CTable *ct, unsigned char symbolValue); -+/**< build a fake FSE_CTable, designed to compress always the same symbolValue */ -+ -+/* FSE_buildCTable_wksp() : -+ * Same as FSE_buildCTable(), but using an externally allocated scratch buffer (`workSpace`). -+ * `wkspSize` must be >= `(1<= BIT_DStream_completed -+ -+When it's done, verify decompression is fully completed, by checking both DStream and the relevant states. -+Checking if DStream has reached its end is performed by : -+ BIT_endOfDStream(&DStream); -+Check also the states. There might be some symbols left there, if some high probability ones (>50%) are possible. -+ FSE_endOfDState(&DState); -+*/ -+ -+/* ***************************************** -+* FSE unsafe API -+*******************************************/ -+static unsigned char FSE_decodeSymbolFast(FSE_DState_t *DStatePtr, BIT_DStream_t *bitD); -+/* faster, but works only if nbBits is always >= 1 (otherwise, result will be corrupted) */ -+ -+/* ***************************************** -+* Implementation of inlined functions -+*******************************************/ -+typedef struct { -+ int deltaFindState; -+ U32 deltaNbBits; -+} FSE_symbolCompressionTransform; /* total 8 bytes */ -+ -+ZSTD_STATIC void FSE_initCState(FSE_CState_t *statePtr, const FSE_CTable *ct) -+{ -+ const void *ptr = ct; -+ const U16 *u16ptr = (const U16 *)ptr; -+ const U32 tableLog = ZSTD_read16(ptr); -+ statePtr->value = (ptrdiff_t)1 << tableLog; -+ statePtr->stateTable = u16ptr + 2; -+ statePtr->symbolTT = ((const U32 *)ct + 1 + (tableLog ? (1 << (tableLog - 1)) : 1)); -+ statePtr->stateLog = tableLog; -+} -+ -+/*! FSE_initCState2() : -+* Same as FSE_initCState(), but the first symbol to include (which will be the last to be read) -+* uses the smallest state value possible, saving the cost of this symbol */ -+ZSTD_STATIC void FSE_initCState2(FSE_CState_t *statePtr, const FSE_CTable *ct, U32 symbol) -+{ -+ FSE_initCState(statePtr, ct); -+ { -+ const FSE_symbolCompressionTransform symbolTT = ((const FSE_symbolCompressionTransform *)(statePtr->symbolTT))[symbol]; -+ const U16 *stateTable = (const U16 *)(statePtr->stateTable); -+ U32 nbBitsOut = (U32)((symbolTT.deltaNbBits + (1 << 15)) >> 16); -+ statePtr->value = (nbBitsOut << 16) - symbolTT.deltaNbBits; -+ statePtr->value = stateTable[(statePtr->value >> nbBitsOut) + symbolTT.deltaFindState]; -+ } -+} -+ -+ZSTD_STATIC void FSE_encodeSymbol(BIT_CStream_t *bitC, FSE_CState_t *statePtr, U32 symbol) -+{ -+ const FSE_symbolCompressionTransform symbolTT = ((const FSE_symbolCompressionTransform *)(statePtr->symbolTT))[symbol]; -+ const U16 *const stateTable = (const U16 *)(statePtr->stateTable); -+ U32 nbBitsOut = (U32)((statePtr->value + symbolTT.deltaNbBits) >> 16); -+ BIT_addBits(bitC, statePtr->value, nbBitsOut); -+ statePtr->value = stateTable[(statePtr->value >> nbBitsOut) + symbolTT.deltaFindState]; -+} -+ -+ZSTD_STATIC void FSE_flushCState(BIT_CStream_t *bitC, const FSE_CState_t *statePtr) -+{ -+ BIT_addBits(bitC, statePtr->value, statePtr->stateLog); -+ BIT_flushBits(bitC); -+} -+ -+/* ====== Decompression ====== */ -+ -+typedef struct { -+ U16 tableLog; -+ U16 fastMode; -+} FSE_DTableHeader; /* sizeof U32 */ -+ -+typedef struct { -+ unsigned short newState; -+ unsigned char symbol; -+ unsigned char nbBits; -+} FSE_decode_t; /* size == U32 */ -+ -+ZSTD_STATIC void FSE_initDState(FSE_DState_t *DStatePtr, BIT_DStream_t *bitD, const FSE_DTable *dt) -+{ -+ const void *ptr = dt; -+ const FSE_DTableHeader *const DTableH = (const FSE_DTableHeader *)ptr; -+ DStatePtr->state = BIT_readBits(bitD, DTableH->tableLog); -+ BIT_reloadDStream(bitD); -+ DStatePtr->table = dt + 1; -+} -+ -+ZSTD_STATIC BYTE FSE_peekSymbol(const FSE_DState_t *DStatePtr) -+{ -+ FSE_decode_t const DInfo = ((const FSE_decode_t *)(DStatePtr->table))[DStatePtr->state]; -+ return DInfo.symbol; -+} -+ -+ZSTD_STATIC void FSE_updateState(FSE_DState_t *DStatePtr, BIT_DStream_t *bitD) -+{ -+ FSE_decode_t const DInfo = ((const FSE_decode_t *)(DStatePtr->table))[DStatePtr->state]; -+ U32 const nbBits = DInfo.nbBits; -+ size_t const lowBits = BIT_readBits(bitD, nbBits); -+ DStatePtr->state = DInfo.newState + lowBits; -+} -+ -+ZSTD_STATIC BYTE FSE_decodeSymbol(FSE_DState_t *DStatePtr, BIT_DStream_t *bitD) -+{ -+ FSE_decode_t const DInfo = ((const FSE_decode_t *)(DStatePtr->table))[DStatePtr->state]; -+ U32 const nbBits = DInfo.nbBits; -+ BYTE const symbol = DInfo.symbol; -+ size_t const lowBits = BIT_readBits(bitD, nbBits); -+ -+ DStatePtr->state = DInfo.newState + lowBits; -+ return symbol; -+} -+ -+/*! FSE_decodeSymbolFast() : -+ unsafe, only works if no symbol has a probability > 50% */ -+ZSTD_STATIC BYTE FSE_decodeSymbolFast(FSE_DState_t *DStatePtr, BIT_DStream_t *bitD) -+{ -+ FSE_decode_t const DInfo = ((const FSE_decode_t *)(DStatePtr->table))[DStatePtr->state]; -+ U32 const nbBits = DInfo.nbBits; -+ BYTE const symbol = DInfo.symbol; -+ size_t const lowBits = BIT_readBitsFast(bitD, nbBits); -+ -+ DStatePtr->state = DInfo.newState + lowBits; -+ return symbol; -+} -+ -+ZSTD_STATIC unsigned FSE_endOfDState(const FSE_DState_t *DStatePtr) { return DStatePtr->state == 0; } -+ -+/* ************************************************************** -+* Tuning parameters -+****************************************************************/ -+/*!MEMORY_USAGE : -+* Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) -+* Increasing memory usage improves compression ratio -+* Reduced memory usage can improve speed, due to cache effect -+* Recommended max value is 14, for 16KB, which nicely fits into Intel x86 L1 cache */ -+#ifndef FSE_MAX_MEMORY_USAGE -+#define FSE_MAX_MEMORY_USAGE 14 -+#endif -+#ifndef FSE_DEFAULT_MEMORY_USAGE -+#define FSE_DEFAULT_MEMORY_USAGE 13 -+#endif -+ -+/*!FSE_MAX_SYMBOL_VALUE : -+* Maximum symbol value authorized. -+* Required for proper stack allocation */ -+#ifndef FSE_MAX_SYMBOL_VALUE -+#define FSE_MAX_SYMBOL_VALUE 255 -+#endif -+ -+/* ************************************************************** -+* template functions type & suffix -+****************************************************************/ -+#define FSE_FUNCTION_TYPE BYTE -+#define FSE_FUNCTION_EXTENSION -+#define FSE_DECODE_TYPE FSE_decode_t -+ -+/* *************************************************************** -+* Constants -+*****************************************************************/ -+#define FSE_MAX_TABLELOG (FSE_MAX_MEMORY_USAGE - 2) -+#define FSE_MAX_TABLESIZE (1U << FSE_MAX_TABLELOG) -+#define FSE_MAXTABLESIZE_MASK (FSE_MAX_TABLESIZE - 1) -+#define FSE_DEFAULT_TABLELOG (FSE_DEFAULT_MEMORY_USAGE - 2) -+#define FSE_MIN_TABLELOG 5 -+ -+#define FSE_TABLELOG_ABSOLUTE_MAX 15 -+#if FSE_MAX_TABLELOG > FSE_TABLELOG_ABSOLUTE_MAX -+#error "FSE_MAX_TABLELOG > FSE_TABLELOG_ABSOLUTE_MAX is not supported" -+#endif -+ -+#define FSE_TABLESTEP(tableSize) ((tableSize >> 1) + (tableSize >> 3) + 3) -+ -+#endif /* FSE_H */ -diff --git a/xen/common/zstd/fse_decompress.c b/xen/common/zstd/fse_decompress.c -new file mode 100644 -index 0000000000..cc51206df6 ---- /dev/null -+++ b/xen/common/zstd/fse_decompress.c -@@ -0,0 +1,324 @@ -+/* -+ * FSE : Finite State Entropy decoder -+ * Copyright (C) 2013-2015, Yann Collet. -+ * -+ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are -+ * met: -+ * -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above -+ * copyright notice, this list of conditions and the following disclaimer -+ * in the documentation and/or other materials provided with the -+ * distribution. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ * -+ * This program is free software; you can redistribute it and/or modify it under -+ * the terms of the GNU General Public License version 2 as published by the -+ * Free Software Foundation. This program is dual-licensed; you may select -+ * either version 2 of the GNU General Public License ("GPL") or BSD license -+ * ("BSD"). -+ * -+ * You can contact the author at : -+ * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy -+ */ -+ -+/* ************************************************************** -+* Compiler specifics -+****************************************************************/ -+#define FORCE_INLINE static always_inline -+ -+/* ************************************************************** -+* Includes -+****************************************************************/ -+#include "bitstream.h" -+#include "fse.h" -+#include "zstd_internal.h" -+#include -+#include /* memcpy, memset */ -+ -+/* ************************************************************** -+* Error Management -+****************************************************************/ -+#define FSE_isError ERR_isError -+#define FSE_STATIC_ASSERT(c) \ -+ { \ -+ enum { FSE_static_assert = 1 / (int)(!!(c)) }; \ -+ } /* use only *after* variable declarations */ -+ -+/* ************************************************************** -+* Templates -+****************************************************************/ -+/* -+ designed to be included -+ for type-specific functions (template emulation in C) -+ Objective is to write these functions only once, for improved maintenance -+*/ -+ -+/* safety checks */ -+#ifndef FSE_FUNCTION_EXTENSION -+#error "FSE_FUNCTION_EXTENSION must be defined" -+#endif -+#ifndef FSE_FUNCTION_TYPE -+#error "FSE_FUNCTION_TYPE must be defined" -+#endif -+ -+/* Function names */ -+#define FSE_CAT(X, Y) X##Y -+#define FSE_FUNCTION_NAME(X, Y) FSE_CAT(X, Y) -+#define FSE_TYPE_NAME(X, Y) FSE_CAT(X, Y) -+ -+/* Function templates */ -+ -+size_t INIT FSE_buildDTable_wksp(FSE_DTable *dt, const short *normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void *workspace, size_t workspaceSize) -+{ -+ void *const tdPtr = dt + 1; /* because *dt is unsigned, 32-bits aligned on 32-bits */ -+ FSE_DECODE_TYPE *const tableDecode = (FSE_DECODE_TYPE *)(tdPtr); -+ U16 *symbolNext = (U16 *)workspace; -+ -+ U32 const maxSV1 = maxSymbolValue + 1; -+ U32 const tableSize = 1 << tableLog; -+ U32 highThreshold = tableSize - 1; -+ -+ /* Sanity Checks */ -+ if (workspaceSize < sizeof(U16) * (FSE_MAX_SYMBOL_VALUE + 1)) -+ return ERROR(tableLog_tooLarge); -+ if (maxSymbolValue > FSE_MAX_SYMBOL_VALUE) -+ return ERROR(maxSymbolValue_tooLarge); -+ if (tableLog > FSE_MAX_TABLELOG) -+ return ERROR(tableLog_tooLarge); -+ -+ /* Init, lay down lowprob symbols */ -+ { -+ FSE_DTableHeader DTableH; -+ DTableH.tableLog = (U16)tableLog; -+ DTableH.fastMode = 1; -+ { -+ S16 const largeLimit = (S16)(1 << (tableLog - 1)); -+ U32 s; -+ for (s = 0; s < maxSV1; s++) { -+ if (normalizedCounter[s] == -1) { -+ tableDecode[highThreshold--].symbol = (FSE_FUNCTION_TYPE)s; -+ symbolNext[s] = 1; -+ } else { -+ if (normalizedCounter[s] >= largeLimit) -+ DTableH.fastMode = 0; -+ symbolNext[s] = normalizedCounter[s]; -+ } -+ } -+ } -+ memcpy(dt, &DTableH, sizeof(DTableH)); -+ } -+ -+ /* Spread symbols */ -+ { -+ U32 const tableMask = tableSize - 1; -+ U32 const step = FSE_TABLESTEP(tableSize); -+ U32 s, position = 0; -+ for (s = 0; s < maxSV1; s++) { -+ int i; -+ for (i = 0; i < normalizedCounter[s]; i++) { -+ tableDecode[position].symbol = (FSE_FUNCTION_TYPE)s; -+ position = (position + step) & tableMask; -+ while (position > highThreshold) -+ position = (position + step) & tableMask; /* lowprob area */ -+ } -+ } -+ if (position != 0) -+ return ERROR(GENERIC); /* position must reach all cells once, otherwise normalizedCounter is incorrect */ -+ } -+ -+ /* Build Decoding table */ -+ { -+ U32 u; -+ for (u = 0; u < tableSize; u++) { -+ FSE_FUNCTION_TYPE const symbol = (FSE_FUNCTION_TYPE)(tableDecode[u].symbol); -+ U16 nextState = symbolNext[symbol]++; -+ tableDecode[u].nbBits = (BYTE)(tableLog - BIT_highbit32((U32)nextState)); -+ tableDecode[u].newState = (U16)((nextState << tableDecode[u].nbBits) - tableSize); -+ } -+ } -+ -+ return 0; -+} -+ -+/*-******************************************************* -+* Decompression (Byte symbols) -+*********************************************************/ -+size_t INIT FSE_buildDTable_rle(FSE_DTable *dt, BYTE symbolValue) -+{ -+ void *ptr = dt; -+ FSE_DTableHeader *const DTableH = (FSE_DTableHeader *)ptr; -+ void *dPtr = dt + 1; -+ FSE_decode_t *const cell = (FSE_decode_t *)dPtr; -+ -+ DTableH->tableLog = 0; -+ DTableH->fastMode = 0; -+ -+ cell->newState = 0; -+ cell->symbol = symbolValue; -+ cell->nbBits = 0; -+ -+ return 0; -+} -+ -+size_t INIT FSE_buildDTable_raw(FSE_DTable *dt, unsigned nbBits) -+{ -+ void *ptr = dt; -+ FSE_DTableHeader *const DTableH = (FSE_DTableHeader *)ptr; -+ void *dPtr = dt + 1; -+ FSE_decode_t *const dinfo = (FSE_decode_t *)dPtr; -+ const unsigned tableSize = 1 << nbBits; -+ const unsigned tableMask = tableSize - 1; -+ const unsigned maxSV1 = tableMask + 1; -+ unsigned s; -+ -+ /* Sanity checks */ -+ if (nbBits < 1) -+ return ERROR(GENERIC); /* min size */ -+ -+ /* Build Decoding Table */ -+ DTableH->tableLog = (U16)nbBits; -+ DTableH->fastMode = 1; -+ for (s = 0; s < maxSV1; s++) { -+ dinfo[s].newState = 0; -+ dinfo[s].symbol = (BYTE)s; -+ dinfo[s].nbBits = (BYTE)nbBits; -+ } -+ -+ return 0; -+} -+ -+FORCE_INLINE size_t FSE_decompress_usingDTable_generic(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const FSE_DTable *dt, -+ const unsigned fast) -+{ -+ BYTE *const ostart = (BYTE *)dst; -+ BYTE *op = ostart; -+ BYTE *const omax = op + maxDstSize; -+ BYTE *const olimit = omax - 3; -+ -+ BIT_DStream_t bitD; -+ FSE_DState_t state1; -+ FSE_DState_t state2; -+ -+ /* Init */ -+ CHECK_F(BIT_initDStream(&bitD, cSrc, cSrcSize)); -+ -+ FSE_initDState(&state1, &bitD, dt); -+ FSE_initDState(&state2, &bitD, dt); -+ -+#define FSE_GETSYMBOL(statePtr) fast ? FSE_decodeSymbolFast(statePtr, &bitD) : FSE_decodeSymbol(statePtr, &bitD) -+ -+ /* 4 symbols per loop */ -+ for (; (BIT_reloadDStream(&bitD) == BIT_DStream_unfinished) & (op < olimit); op += 4) { -+ op[0] = FSE_GETSYMBOL(&state1); -+ -+ if (FSE_MAX_TABLELOG * 2 + 7 > sizeof(bitD.bitContainer) * 8) /* This test must be static */ -+ BIT_reloadDStream(&bitD); -+ -+ op[1] = FSE_GETSYMBOL(&state2); -+ -+ if (FSE_MAX_TABLELOG * 4 + 7 > sizeof(bitD.bitContainer) * 8) /* This test must be static */ -+ { -+ if (BIT_reloadDStream(&bitD) > BIT_DStream_unfinished) { -+ op += 2; -+ break; -+ } -+ } -+ -+ op[2] = FSE_GETSYMBOL(&state1); -+ -+ if (FSE_MAX_TABLELOG * 2 + 7 > sizeof(bitD.bitContainer) * 8) /* This test must be static */ -+ BIT_reloadDStream(&bitD); -+ -+ op[3] = FSE_GETSYMBOL(&state2); -+ } -+ -+ /* tail */ -+ /* note : BIT_reloadDStream(&bitD) >= FSE_DStream_partiallyFilled; Ends at exactly BIT_DStream_completed */ -+ while (1) { -+ if (op > (omax - 2)) -+ return ERROR(dstSize_tooSmall); -+ *op++ = FSE_GETSYMBOL(&state1); -+ if (BIT_reloadDStream(&bitD) == BIT_DStream_overflow) { -+ *op++ = FSE_GETSYMBOL(&state2); -+ break; -+ } -+ -+ if (op > (omax - 2)) -+ return ERROR(dstSize_tooSmall); -+ *op++ = FSE_GETSYMBOL(&state2); -+ if (BIT_reloadDStream(&bitD) == BIT_DStream_overflow) { -+ *op++ = FSE_GETSYMBOL(&state1); -+ break; -+ } -+ } -+ -+ return op - ostart; -+} -+ -+size_t INIT FSE_decompress_usingDTable(void *dst, size_t originalSize, const void *cSrc, size_t cSrcSize, const FSE_DTable *dt) -+{ -+ const void *ptr = dt; -+ const FSE_DTableHeader *DTableH = (const FSE_DTableHeader *)ptr; -+ const U32 fastMode = DTableH->fastMode; -+ -+ /* select fast mode (static) */ -+ if (fastMode) -+ return FSE_decompress_usingDTable_generic(dst, originalSize, cSrc, cSrcSize, dt, 1); -+ return FSE_decompress_usingDTable_generic(dst, originalSize, cSrc, cSrcSize, dt, 0); -+} -+ -+size_t INIT FSE_decompress_wksp(void *dst, size_t dstCapacity, const void *cSrc, size_t cSrcSize, unsigned maxLog, void *workspace, size_t workspaceSize) -+{ -+ const BYTE *const istart = (const BYTE *)cSrc; -+ const BYTE *ip = istart; -+ unsigned tableLog; -+ unsigned maxSymbolValue = FSE_MAX_SYMBOL_VALUE; -+ size_t NCountLength; -+ -+ FSE_DTable *dt; -+ short *counting; -+ size_t spaceUsed32 = 0; -+ -+ FSE_STATIC_ASSERT(sizeof(FSE_DTable) == sizeof(U32)); -+ -+ dt = (FSE_DTable *)((U32 *)workspace + spaceUsed32); -+ spaceUsed32 += FSE_DTABLE_SIZE_U32(maxLog); -+ counting = (short *)((U32 *)workspace + spaceUsed32); -+ spaceUsed32 += ALIGN(sizeof(short) * (FSE_MAX_SYMBOL_VALUE + 1), sizeof(U32)) >> 2; -+ -+ if ((spaceUsed32 << 2) > workspaceSize) -+ return ERROR(tableLog_tooLarge); -+ workspace = (U32 *)workspace + spaceUsed32; -+ workspaceSize -= (spaceUsed32 << 2); -+ -+ /* normal FSE decoding mode */ -+ NCountLength = FSE_readNCount(counting, &maxSymbolValue, &tableLog, istart, cSrcSize); -+ if (FSE_isError(NCountLength)) -+ return NCountLength; -+ // if (NCountLength >= cSrcSize) return ERROR(srcSize_wrong); /* too small input size; supposed to be already checked in NCountLength, only remaining -+ // case : NCountLength==cSrcSize */ -+ if (tableLog > maxLog) -+ return ERROR(tableLog_tooLarge); -+ ip += NCountLength; -+ cSrcSize -= NCountLength; -+ -+ CHECK_F(FSE_buildDTable_wksp(dt, counting, maxSymbolValue, tableLog, workspace, workspaceSize)); -+ -+ return FSE_decompress_usingDTable(dst, dstCapacity, ip, cSrcSize, dt); /* always return, even if it is an error code */ -+} -diff --git a/xen/common/zstd/huf.h b/xen/common/zstd/huf.h -new file mode 100644 -index 0000000000..a9d522c7bb ---- /dev/null -+++ b/xen/common/zstd/huf.h -@@ -0,0 +1,212 @@ -+/* -+ * Huffman coder, part of New Generation Entropy library -+ * header file -+ * Copyright (C) 2013-2016, Yann Collet. -+ * -+ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are -+ * met: -+ * -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above -+ * copyright notice, this list of conditions and the following disclaimer -+ * in the documentation and/or other materials provided with the -+ * distribution. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ * -+ * This program is free software; you can redistribute it and/or modify it under -+ * the terms of the GNU General Public License version 2 as published by the -+ * Free Software Foundation. This program is dual-licensed; you may select -+ * either version 2 of the GNU General Public License ("GPL") or BSD license -+ * ("BSD"). -+ * -+ * You can contact the author at : -+ * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy -+ */ -+#ifndef HUF_H_298734234 -+#define HUF_H_298734234 -+ -+/* *** Dependencies *** */ -+#include /* size_t */ -+ -+/* *** Tool functions *** */ -+#define HUF_BLOCKSIZE_MAX (128 * 1024) /**< maximum input size for a single block compressed with HUF_compress */ -+size_t HUF_compressBound(size_t size); /**< maximum compressed size (worst case) */ -+ -+/* Error Management */ -+unsigned HUF_isError(size_t code); /**< tells if a return value is an error code */ -+ -+/* *** Advanced function *** */ -+ -+/** HUF_compress4X_wksp() : -+* Same as HUF_compress2(), but uses externally allocated `workSpace`, which must be a table of >= 1024 unsigned */ -+size_t HUF_compress4X_wksp(void *dst, size_t dstSize, const void *src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void *workSpace, -+ size_t wkspSize); /**< `workSpace` must be a table of at least HUF_COMPRESS_WORKSPACE_SIZE_U32 unsigned */ -+ -+/* *** Dependencies *** */ -+#include "mem.h" /* U32 */ -+ -+/* *** Constants *** */ -+#define HUF_TABLELOG_MAX 12 /* max configured tableLog (for static allocation); can be modified up to HUF_ABSOLUTEMAX_TABLELOG */ -+#define HUF_TABLELOG_DEFAULT 11 /* tableLog by default, when not specified */ -+#define HUF_SYMBOLVALUE_MAX 255 -+ -+#define HUF_TABLELOG_ABSOLUTEMAX 15 /* absolute limit of HUF_MAX_TABLELOG. Beyond that value, code does not work */ -+#if (HUF_TABLELOG_MAX > HUF_TABLELOG_ABSOLUTEMAX) -+#error "HUF_TABLELOG_MAX is too large !" -+#endif -+ -+/* **************************************** -+* Static allocation -+******************************************/ -+/* HUF buffer bounds */ -+#define HUF_CTABLEBOUND 129 -+#define HUF_BLOCKBOUND(size) (size + (size >> 8) + 8) /* only true if incompressible pre-filtered with fast heuristic */ -+#define HUF_COMPRESSBOUND(size) (HUF_CTABLEBOUND + HUF_BLOCKBOUND(size)) /* Macro version, useful for static allocation */ -+ -+/* static allocation of HUF's Compression Table */ -+#define HUF_CREATE_STATIC_CTABLE(name, maxSymbolValue) \ -+ U32 name##hb[maxSymbolValue + 1]; \ -+ void *name##hv = &(name##hb); \ -+ HUF_CElt *name = (HUF_CElt *)(name##hv) /* no final ; */ -+ -+/* static allocation of HUF's DTable */ -+typedef U32 HUF_DTable; -+#define HUF_DTABLE_SIZE(maxTableLog) (1 + (1 << (maxTableLog))) -+#define HUF_CREATE_STATIC_DTABLEX2(DTable, maxTableLog) HUF_DTable DTable[HUF_DTABLE_SIZE((maxTableLog)-1)] = {((U32)((maxTableLog)-1) * 0x01000001)} -+#define HUF_CREATE_STATIC_DTABLEX4(DTable, maxTableLog) HUF_DTable DTable[HUF_DTABLE_SIZE(maxTableLog)] = {((U32)(maxTableLog)*0x01000001)} -+ -+/* The workspace must have alignment at least 4 and be at least this large */ -+#define HUF_COMPRESS_WORKSPACE_SIZE (6 << 10) -+#define HUF_COMPRESS_WORKSPACE_SIZE_U32 (HUF_COMPRESS_WORKSPACE_SIZE / sizeof(U32)) -+ -+/* The workspace must have alignment at least 4 and be at least this large */ -+#define HUF_DECOMPRESS_WORKSPACE_SIZE (3 << 10) -+#define HUF_DECOMPRESS_WORKSPACE_SIZE_U32 (HUF_DECOMPRESS_WORKSPACE_SIZE / sizeof(U32)) -+ -+/* **************************************** -+* Advanced decompression functions -+******************************************/ -+size_t HUF_decompress4X_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize); /**< decodes RLE and uncompressed */ -+size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, -+ size_t workspaceSize); /**< considers RLE and uncompressed as errors */ -+size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, -+ size_t workspaceSize); /**< single-symbol decoder */ -+size_t HUF_decompress4X4_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, -+ size_t workspaceSize); /**< double-symbols decoder */ -+ -+/* **************************************** -+* HUF detailed API -+******************************************/ -+/*! -+HUF_compress() does the following: -+1. count symbol occurrence from source[] into table count[] using FSE_count() -+2. (optional) refine tableLog using HUF_optimalTableLog() -+3. build Huffman table from count using HUF_buildCTable() -+4. save Huffman table to memory buffer using HUF_writeCTable_wksp() -+5. encode the data stream using HUF_compress4X_usingCTable() -+ -+The following API allows targeting specific sub-functions for advanced tasks. -+For example, it's possible to compress several blocks using the same 'CTable', -+or to save and regenerate 'CTable' using external methods. -+*/ -+/* FSE_count() : find it within "fse.h" */ -+unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue); -+typedef struct HUF_CElt_s HUF_CElt; /* incomplete type */ -+size_t HUF_writeCTable_wksp(void *dst, size_t maxDstSize, const HUF_CElt *CTable, unsigned maxSymbolValue, unsigned huffLog, void *workspace, size_t workspaceSize); -+size_t HUF_compress4X_usingCTable(void *dst, size_t dstSize, const void *src, size_t srcSize, const HUF_CElt *CTable); -+ -+typedef enum { -+ HUF_repeat_none, /**< Cannot use the previous table */ -+ HUF_repeat_check, /**< Can use the previous table but it must be checked. Note : The previous table must have been constructed by HUF_compress{1, -+ 4}X_repeat */ -+ HUF_repeat_valid /**< Can use the previous table and it is asumed to be valid */ -+} HUF_repeat; -+/** HUF_compress4X_repeat() : -+* Same as HUF_compress4X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none. -+* If it uses hufTable it does not modify hufTable or repeat. -+* If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used. -+* If preferRepeat then the old table will always be used if valid. */ -+size_t HUF_compress4X_repeat(void *dst, size_t dstSize, const void *src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void *workSpace, -+ size_t wkspSize, HUF_CElt *hufTable, HUF_repeat *repeat, -+ int preferRepeat); /**< `workSpace` must be a table of at least HUF_COMPRESS_WORKSPACE_SIZE_U32 unsigned */ -+ -+/** HUF_buildCTable_wksp() : -+ * Same as HUF_buildCTable(), but using externally allocated scratch buffer. -+ * `workSpace` must be aligned on 4-bytes boundaries, and be at least as large as a table of 1024 unsigned. -+ */ -+size_t HUF_buildCTable_wksp(HUF_CElt *tree, const U32 *count, U32 maxSymbolValue, U32 maxNbBits, void *workSpace, size_t wkspSize); -+ -+/*! HUF_readStats() : -+ Read compact Huffman tree, saved by HUF_writeCTable(). -+ `huffWeight` is destination buffer. -+ @return : size read from `src` , or an error Code . -+ Note : Needed by HUF_readCTable() and HUF_readDTableXn() . */ -+size_t HUF_readStats_wksp(BYTE *huffWeight, size_t hwSize, U32 *rankStats, U32 *nbSymbolsPtr, U32 *tableLogPtr, const void *src, size_t srcSize, -+ void *workspace, size_t workspaceSize); -+ -+/** HUF_readCTable() : -+* Loading a CTable saved with HUF_writeCTable() */ -+size_t HUF_readCTable_wksp(HUF_CElt *CTable, unsigned maxSymbolValue, const void *src, size_t srcSize, void *workspace, size_t workspaceSize); -+ -+/* -+HUF_decompress() does the following: -+1. select the decompression algorithm (X2, X4) based on pre-computed heuristics -+2. build Huffman table from save, using HUF_readDTableXn() -+3. decode 1 or 4 segments in parallel using HUF_decompressSXn_usingDTable -+*/ -+ -+/** HUF_selectDecoder() : -+* Tells which decoder is likely to decode faster, -+* based on a set of pre-determined metrics. -+* @return : 0==HUF_decompress4X2, 1==HUF_decompress4X4 . -+* Assumption : 0 < cSrcSize < dstSize <= 128 KB */ -+U32 HUF_selectDecoder(size_t dstSize, size_t cSrcSize); -+ -+size_t HUF_readDTableX2_wksp(HUF_DTable *DTable, const void *src, size_t srcSize, void *workspace, size_t workspaceSize); -+size_t HUF_readDTableX4_wksp(HUF_DTable *DTable, const void *src, size_t srcSize, void *workspace, size_t workspaceSize); -+ -+size_t HUF_decompress4X_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable); -+size_t HUF_decompress4X2_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable); -+size_t HUF_decompress4X4_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable); -+ -+/* single stream variants */ -+ -+size_t HUF_compress1X_wksp(void *dst, size_t dstSize, const void *src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void *workSpace, -+ size_t wkspSize); /**< `workSpace` must be a table of at least HUF_COMPRESS_WORKSPACE_SIZE_U32 unsigned */ -+size_t HUF_compress1X_usingCTable(void *dst, size_t dstSize, const void *src, size_t srcSize, const HUF_CElt *CTable); -+/** HUF_compress1X_repeat() : -+* Same as HUF_compress1X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none. -+* If it uses hufTable it does not modify hufTable or repeat. -+* If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used. -+* If preferRepeat then the old table will always be used if valid. */ -+size_t HUF_compress1X_repeat(void *dst, size_t dstSize, const void *src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void *workSpace, -+ size_t wkspSize, HUF_CElt *hufTable, HUF_repeat *repeat, -+ int preferRepeat); /**< `workSpace` must be a table of at least HUF_COMPRESS_WORKSPACE_SIZE_U32 unsigned */ -+ -+size_t HUF_decompress1X_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize); -+size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, -+ size_t workspaceSize); /**< single-symbol decoder */ -+size_t HUF_decompress1X4_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, -+ size_t workspaceSize); /**< double-symbols decoder */ -+ -+size_t HUF_decompress1X_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, -+ const HUF_DTable *DTable); /**< automatic selection of sing or double symbol decoder, based on DTable */ -+size_t HUF_decompress1X2_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable); -+size_t HUF_decompress1X4_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable); -+ -+#endif /* HUF_H_298734234 */ -diff --git a/xen/common/zstd/huf_decompress.c b/xen/common/zstd/huf_decompress.c -new file mode 100644 -index 0000000000..341619e642 ---- /dev/null -+++ b/xen/common/zstd/huf_decompress.c -@@ -0,0 +1,960 @@ -+/* -+ * Huffman decoder, part of New Generation Entropy library -+ * Copyright (C) 2013-2016, Yann Collet. -+ * -+ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are -+ * met: -+ * -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above -+ * copyright notice, this list of conditions and the following disclaimer -+ * in the documentation and/or other materials provided with the -+ * distribution. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ * -+ * This program is free software; you can redistribute it and/or modify it under -+ * the terms of the GNU General Public License version 2 as published by the -+ * Free Software Foundation. This program is dual-licensed; you may select -+ * either version 2 of the GNU General Public License ("GPL") or BSD license -+ * ("BSD"). -+ * -+ * You can contact the author at : -+ * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy -+ */ -+ -+/* ************************************************************** -+* Compiler specifics -+****************************************************************/ -+#define FORCE_INLINE static always_inline -+ -+/* ************************************************************** -+* Dependencies -+****************************************************************/ -+#include "bitstream.h" /* BIT_* */ -+#include "fse.h" /* header compression */ -+#include "huf.h" -+#include -+#include /* memcpy, memset */ -+ -+/* ************************************************************** -+* Error Management -+****************************************************************/ -+#define HUF_STATIC_ASSERT(c) \ -+ { \ -+ enum { HUF_static_assert = 1 / (int)(!!(c)) }; \ -+ } /* use only *after* variable declarations */ -+ -+/*-***************************/ -+/* generic DTableDesc */ -+/*-***************************/ -+ -+typedef struct { -+ BYTE maxTableLog; -+ BYTE tableType; -+ BYTE tableLog; -+ BYTE reserved; -+} DTableDesc; -+ -+static DTableDesc INIT HUF_getDTableDesc(const HUF_DTable *table) -+{ -+ DTableDesc dtd; -+ memcpy(&dtd, table, sizeof(dtd)); -+ return dtd; -+} -+ -+/*-***************************/ -+/* single-symbol decoding */ -+/*-***************************/ -+ -+typedef struct { -+ BYTE byte; -+ BYTE nbBits; -+} HUF_DEltX2; /* single-symbol decoding */ -+ -+size_t INIT HUF_readDTableX2_wksp(HUF_DTable *DTable, const void *src, size_t srcSize, void *workspace, size_t workspaceSize) -+{ -+ U32 tableLog = 0; -+ U32 nbSymbols = 0; -+ size_t iSize; -+ void *const dtPtr = DTable + 1; -+ HUF_DEltX2 *const dt = (HUF_DEltX2 *)dtPtr; -+ -+ U32 *rankVal; -+ BYTE *huffWeight; -+ size_t spaceUsed32 = 0; -+ -+ rankVal = (U32 *)workspace + spaceUsed32; -+ spaceUsed32 += HUF_TABLELOG_ABSOLUTEMAX + 1; -+ huffWeight = (BYTE *)((U32 *)workspace + spaceUsed32); -+ spaceUsed32 += ALIGN(HUF_SYMBOLVALUE_MAX + 1, sizeof(U32)) >> 2; -+ -+ if ((spaceUsed32 << 2) > workspaceSize) -+ return ERROR(tableLog_tooLarge); -+ workspace = (U32 *)workspace + spaceUsed32; -+ workspaceSize -= (spaceUsed32 << 2); -+ -+ HUF_STATIC_ASSERT(sizeof(DTableDesc) == sizeof(HUF_DTable)); -+ /* memset(huffWeight, 0, sizeof(huffWeight)); */ /* is not necessary, even though some analyzer complain ... */ -+ -+ iSize = HUF_readStats_wksp(huffWeight, HUF_SYMBOLVALUE_MAX + 1, rankVal, &nbSymbols, &tableLog, src, srcSize, workspace, workspaceSize); -+ if (HUF_isError(iSize)) -+ return iSize; -+ -+ /* Table header */ -+ { -+ DTableDesc dtd = HUF_getDTableDesc(DTable); -+ if (tableLog > (U32)(dtd.maxTableLog + 1)) -+ return ERROR(tableLog_tooLarge); /* DTable too small, Huffman tree cannot fit in */ -+ dtd.tableType = 0; -+ dtd.tableLog = (BYTE)tableLog; -+ memcpy(DTable, &dtd, sizeof(dtd)); -+ } -+ -+ /* Calculate starting value for each rank */ -+ { -+ U32 n, nextRankStart = 0; -+ for (n = 1; n < tableLog + 1; n++) { -+ U32 const curr = nextRankStart; -+ nextRankStart += (rankVal[n] << (n - 1)); -+ rankVal[n] = curr; -+ } -+ } -+ -+ /* fill DTable */ -+ { -+ U32 n; -+ for (n = 0; n < nbSymbols; n++) { -+ U32 const w = huffWeight[n]; -+ U32 const length = (1 << w) >> 1; -+ U32 u; -+ HUF_DEltX2 D; -+ D.byte = (BYTE)n; -+ D.nbBits = (BYTE)(tableLog + 1 - w); -+ for (u = rankVal[w]; u < rankVal[w] + length; u++) -+ dt[u] = D; -+ rankVal[w] += length; -+ } -+ } -+ -+ return iSize; -+} -+ -+static BYTE INIT HUF_decodeSymbolX2(BIT_DStream_t *Dstream, const HUF_DEltX2 *dt, const U32 dtLog) -+{ -+ size_t const val = BIT_lookBitsFast(Dstream, dtLog); /* note : dtLog >= 1 */ -+ BYTE const c = dt[val].byte; -+ BIT_skipBits(Dstream, dt[val].nbBits); -+ return c; -+} -+ -+#define HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) *ptr++ = HUF_decodeSymbolX2(DStreamPtr, dt, dtLog) -+ -+#define HUF_DECODE_SYMBOLX2_1(ptr, DStreamPtr) \ -+ if (ZSTD_64bits() || (HUF_TABLELOG_MAX <= 12)) \ -+ HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) -+ -+#define HUF_DECODE_SYMBOLX2_2(ptr, DStreamPtr) \ -+ if (ZSTD_64bits()) \ -+ HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) -+ -+FORCE_INLINE size_t HUF_decodeStreamX2(BYTE *p, BIT_DStream_t *const bitDPtr, BYTE *const pEnd, const HUF_DEltX2 *const dt, const U32 dtLog) -+{ -+ BYTE *const pStart = p; -+ -+ /* up to 4 symbols at a time */ -+ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) && (p <= pEnd - 4)) { -+ HUF_DECODE_SYMBOLX2_2(p, bitDPtr); -+ HUF_DECODE_SYMBOLX2_1(p, bitDPtr); -+ HUF_DECODE_SYMBOLX2_2(p, bitDPtr); -+ HUF_DECODE_SYMBOLX2_0(p, bitDPtr); -+ } -+ -+ /* closer to the end */ -+ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) && (p < pEnd)) -+ HUF_DECODE_SYMBOLX2_0(p, bitDPtr); -+ -+ /* no more data to retrieve from bitstream, hence no need to reload */ -+ while (p < pEnd) -+ HUF_DECODE_SYMBOLX2_0(p, bitDPtr); -+ -+ return pEnd - pStart; -+} -+ -+static size_t INIT HUF_decompress1X2_usingDTable_internal(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) -+{ -+ BYTE *op = (BYTE *)dst; -+ BYTE *const oend = op + dstSize; -+ const void *dtPtr = DTable + 1; -+ const HUF_DEltX2 *const dt = (const HUF_DEltX2 *)dtPtr; -+ BIT_DStream_t bitD; -+ DTableDesc const dtd = HUF_getDTableDesc(DTable); -+ U32 const dtLog = dtd.tableLog; -+ -+ { -+ size_t const errorCode = BIT_initDStream(&bitD, cSrc, cSrcSize); -+ if (HUF_isError(errorCode)) -+ return errorCode; -+ } -+ -+ HUF_decodeStreamX2(op, &bitD, oend, dt, dtLog); -+ -+ /* check */ -+ if (!BIT_endOfDStream(&bitD)) -+ return ERROR(corruption_detected); -+ -+ return dstSize; -+} -+ -+size_t INIT HUF_decompress1X2_usingDTable(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) -+{ -+ DTableDesc dtd = HUF_getDTableDesc(DTable); -+ if (dtd.tableType != 0) -+ return ERROR(GENERIC); -+ return HUF_decompress1X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable); -+} -+ -+size_t INIT HUF_decompress1X2_DCtx_wksp(HUF_DTable *DCtx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize) -+{ -+ const BYTE *ip = (const BYTE *)cSrc; -+ -+ size_t const hSize = HUF_readDTableX2_wksp(DCtx, cSrc, cSrcSize, workspace, workspaceSize); -+ if (HUF_isError(hSize)) -+ return hSize; -+ if (hSize >= cSrcSize) -+ return ERROR(srcSize_wrong); -+ ip += hSize; -+ cSrcSize -= hSize; -+ -+ return HUF_decompress1X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx); -+} -+ -+static size_t INIT HUF_decompress4X2_usingDTable_internal(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) -+{ -+ /* Check */ -+ if (cSrcSize < 10) -+ return ERROR(corruption_detected); /* strict minimum : jump table + 1 byte per stream */ -+ -+ { -+ const BYTE *const istart = (const BYTE *)cSrc; -+ BYTE *const ostart = (BYTE *)dst; -+ BYTE *const oend = ostart + dstSize; -+ const void *const dtPtr = DTable + 1; -+ const HUF_DEltX2 *const dt = (const HUF_DEltX2 *)dtPtr; -+ -+ /* Init */ -+ BIT_DStream_t bitD1; -+ BIT_DStream_t bitD2; -+ BIT_DStream_t bitD3; -+ BIT_DStream_t bitD4; -+ size_t const length1 = ZSTD_readLE16(istart); -+ size_t const length2 = ZSTD_readLE16(istart + 2); -+ size_t const length3 = ZSTD_readLE16(istart + 4); -+ size_t const length4 = cSrcSize - (length1 + length2 + length3 + 6); -+ const BYTE *const istart1 = istart + 6; /* jumpTable */ -+ const BYTE *const istart2 = istart1 + length1; -+ const BYTE *const istart3 = istart2 + length2; -+ const BYTE *const istart4 = istart3 + length3; -+ const size_t segmentSize = (dstSize + 3) / 4; -+ BYTE *const opStart2 = ostart + segmentSize; -+ BYTE *const opStart3 = opStart2 + segmentSize; -+ BYTE *const opStart4 = opStart3 + segmentSize; -+ BYTE *op1 = ostart; -+ BYTE *op2 = opStart2; -+ BYTE *op3 = opStart3; -+ BYTE *op4 = opStart4; -+ U32 endSignal; -+ DTableDesc const dtd = HUF_getDTableDesc(DTable); -+ U32 const dtLog = dtd.tableLog; -+ -+ if (length4 > cSrcSize) -+ return ERROR(corruption_detected); /* overflow */ -+ { -+ size_t const errorCode = BIT_initDStream(&bitD1, istart1, length1); -+ if (HUF_isError(errorCode)) -+ return errorCode; -+ } -+ { -+ size_t const errorCode = BIT_initDStream(&bitD2, istart2, length2); -+ if (HUF_isError(errorCode)) -+ return errorCode; -+ } -+ { -+ size_t const errorCode = BIT_initDStream(&bitD3, istart3, length3); -+ if (HUF_isError(errorCode)) -+ return errorCode; -+ } -+ { -+ size_t const errorCode = BIT_initDStream(&bitD4, istart4, length4); -+ if (HUF_isError(errorCode)) -+ return errorCode; -+ } -+ -+ /* 16-32 symbols per loop (4-8 symbols per stream) */ -+ endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); -+ for (; (endSignal == BIT_DStream_unfinished) && (op4 < (oend - 7));) { -+ HUF_DECODE_SYMBOLX2_2(op1, &bitD1); -+ HUF_DECODE_SYMBOLX2_2(op2, &bitD2); -+ HUF_DECODE_SYMBOLX2_2(op3, &bitD3); -+ HUF_DECODE_SYMBOLX2_2(op4, &bitD4); -+ HUF_DECODE_SYMBOLX2_1(op1, &bitD1); -+ HUF_DECODE_SYMBOLX2_1(op2, &bitD2); -+ HUF_DECODE_SYMBOLX2_1(op3, &bitD3); -+ HUF_DECODE_SYMBOLX2_1(op4, &bitD4); -+ HUF_DECODE_SYMBOLX2_2(op1, &bitD1); -+ HUF_DECODE_SYMBOLX2_2(op2, &bitD2); -+ HUF_DECODE_SYMBOLX2_2(op3, &bitD3); -+ HUF_DECODE_SYMBOLX2_2(op4, &bitD4); -+ HUF_DECODE_SYMBOLX2_0(op1, &bitD1); -+ HUF_DECODE_SYMBOLX2_0(op2, &bitD2); -+ HUF_DECODE_SYMBOLX2_0(op3, &bitD3); -+ HUF_DECODE_SYMBOLX2_0(op4, &bitD4); -+ endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); -+ } -+ -+ /* check corruption */ -+ if (op1 > opStart2) -+ return ERROR(corruption_detected); -+ if (op2 > opStart3) -+ return ERROR(corruption_detected); -+ if (op3 > opStart4) -+ return ERROR(corruption_detected); -+ /* note : op4 supposed already verified within main loop */ -+ -+ /* finish bitStreams one by one */ -+ HUF_decodeStreamX2(op1, &bitD1, opStart2, dt, dtLog); -+ HUF_decodeStreamX2(op2, &bitD2, opStart3, dt, dtLog); -+ HUF_decodeStreamX2(op3, &bitD3, opStart4, dt, dtLog); -+ HUF_decodeStreamX2(op4, &bitD4, oend, dt, dtLog); -+ -+ /* check */ -+ endSignal = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4); -+ if (!endSignal) -+ return ERROR(corruption_detected); -+ -+ /* decoded size */ -+ return dstSize; -+ } -+} -+ -+size_t INIT HUF_decompress4X2_usingDTable(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) -+{ -+ DTableDesc dtd = HUF_getDTableDesc(DTable); -+ if (dtd.tableType != 0) -+ return ERROR(GENERIC); -+ return HUF_decompress4X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable); -+} -+ -+size_t INIT HUF_decompress4X2_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize) -+{ -+ const BYTE *ip = (const BYTE *)cSrc; -+ -+ size_t const hSize = HUF_readDTableX2_wksp(dctx, cSrc, cSrcSize, workspace, workspaceSize); -+ if (HUF_isError(hSize)) -+ return hSize; -+ if (hSize >= cSrcSize) -+ return ERROR(srcSize_wrong); -+ ip += hSize; -+ cSrcSize -= hSize; -+ -+ return HUF_decompress4X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx); -+} -+ -+/* *************************/ -+/* double-symbols decoding */ -+/* *************************/ -+typedef struct { -+ U16 sequence; -+ BYTE nbBits; -+ BYTE length; -+} HUF_DEltX4; /* double-symbols decoding */ -+ -+typedef struct { -+ BYTE symbol; -+ BYTE weight; -+} sortedSymbol_t; -+ -+/* HUF_fillDTableX4Level2() : -+ * `rankValOrigin` must be a table of at least (HUF_TABLELOG_MAX + 1) U32 */ -+static void INIT HUF_fillDTableX4Level2(HUF_DEltX4 *DTable, U32 sizeLog, const U32 consumed, const U32 *rankValOrigin, const int minWeight, -+ const sortedSymbol_t *sortedSymbols, const U32 sortedListSize, U32 nbBitsBaseline, U16 baseSeq) -+{ -+ HUF_DEltX4 DElt; -+ U32 rankVal[HUF_TABLELOG_MAX + 1]; -+ -+ /* get pre-calculated rankVal */ -+ memcpy(rankVal, rankValOrigin, sizeof(rankVal)); -+ -+ /* fill skipped values */ -+ if (minWeight > 1) { -+ U32 i, skipSize = rankVal[minWeight]; -+ ZSTD_writeLE16(&(DElt.sequence), baseSeq); -+ DElt.nbBits = (BYTE)(consumed); -+ DElt.length = 1; -+ for (i = 0; i < skipSize; i++) -+ DTable[i] = DElt; -+ } -+ -+ /* fill DTable */ -+ { -+ U32 s; -+ for (s = 0; s < sortedListSize; s++) { /* note : sortedSymbols already skipped */ -+ const U32 symbol = sortedSymbols[s].symbol; -+ const U32 weight = sortedSymbols[s].weight; -+ const U32 nbBits = nbBitsBaseline - weight; -+ const U32 length = 1 << (sizeLog - nbBits); -+ const U32 start = rankVal[weight]; -+ U32 i = start; -+ const U32 end = start + length; -+ -+ ZSTD_writeLE16(&(DElt.sequence), (U16)(baseSeq + (symbol << 8))); -+ DElt.nbBits = (BYTE)(nbBits + consumed); -+ DElt.length = 2; -+ do { -+ DTable[i++] = DElt; -+ } while (i < end); /* since length >= 1 */ -+ -+ rankVal[weight] += length; -+ } -+ } -+} -+ -+typedef U32 rankVal_t[HUF_TABLELOG_MAX][HUF_TABLELOG_MAX + 1]; -+typedef U32 rankValCol_t[HUF_TABLELOG_MAX + 1]; -+ -+static void INIT HUF_fillDTableX4(HUF_DEltX4 *DTable, const U32 targetLog, const sortedSymbol_t *sortedList, -+ const U32 sortedListSize, const U32 *rankStart, -+ rankVal_t rankValOrigin, const U32 maxWeight, const U32 nbBitsBaseline) -+{ -+ U32 rankVal[HUF_TABLELOG_MAX + 1]; -+ const int scaleLog = nbBitsBaseline - targetLog; /* note : targetLog >= srcLog, hence scaleLog <= 1 */ -+ const U32 minBits = nbBitsBaseline - maxWeight; -+ U32 s; -+ -+ memcpy(rankVal, rankValOrigin, sizeof(rankVal)); -+ -+ /* fill DTable */ -+ for (s = 0; s < sortedListSize; s++) { -+ const U16 symbol = sortedList[s].symbol; -+ const U32 weight = sortedList[s].weight; -+ const U32 nbBits = nbBitsBaseline - weight; -+ const U32 start = rankVal[weight]; -+ const U32 length = 1 << (targetLog - nbBits); -+ -+ if (targetLog - nbBits >= minBits) { /* enough room for a second symbol */ -+ U32 sortedRank; -+ int minWeight = nbBits + scaleLog; -+ if (minWeight < 1) -+ minWeight = 1; -+ sortedRank = rankStart[minWeight]; -+ HUF_fillDTableX4Level2(DTable + start, targetLog - nbBits, nbBits, rankValOrigin[nbBits], minWeight, sortedList + sortedRank, -+ sortedListSize - sortedRank, nbBitsBaseline, symbol); -+ } else { -+ HUF_DEltX4 DElt; -+ ZSTD_writeLE16(&(DElt.sequence), symbol); -+ DElt.nbBits = (BYTE)(nbBits); -+ DElt.length = 1; -+ { -+ U32 const end = start + length; -+ U32 u; -+ for (u = start; u < end; u++) -+ DTable[u] = DElt; -+ } -+ } -+ rankVal[weight] += length; -+ } -+} -+ -+size_t INIT HUF_readDTableX4_wksp(HUF_DTable *DTable, const void *src, size_t srcSize, void *workspace, size_t workspaceSize) -+{ -+ U32 tableLog, maxW, sizeOfSort, nbSymbols; -+ DTableDesc dtd = HUF_getDTableDesc(DTable); -+ U32 const maxTableLog = dtd.maxTableLog; -+ size_t iSize; -+ void *dtPtr = DTable + 1; /* force compiler to avoid strict-aliasing */ -+ HUF_DEltX4 *const dt = (HUF_DEltX4 *)dtPtr; -+ U32 *rankStart; -+ -+ rankValCol_t *rankVal; -+ U32 *rankStats; -+ U32 *rankStart0; -+ sortedSymbol_t *sortedSymbol; -+ BYTE *weightList; -+ size_t spaceUsed32 = 0; -+ -+ HUF_STATIC_ASSERT((sizeof(rankValCol_t) & 3) == 0); -+ -+ rankVal = (rankValCol_t *)((U32 *)workspace + spaceUsed32); -+ spaceUsed32 += (sizeof(rankValCol_t) * HUF_TABLELOG_MAX) >> 2; -+ rankStats = (U32 *)workspace + spaceUsed32; -+ spaceUsed32 += HUF_TABLELOG_MAX + 1; -+ rankStart0 = (U32 *)workspace + spaceUsed32; -+ spaceUsed32 += HUF_TABLELOG_MAX + 2; -+ sortedSymbol = (sortedSymbol_t *)((U32 *)workspace + spaceUsed32); -+ spaceUsed32 += ALIGN(sizeof(sortedSymbol_t) * (HUF_SYMBOLVALUE_MAX + 1), sizeof(U32)) >> 2; -+ weightList = (BYTE *)((U32 *)workspace + spaceUsed32); -+ spaceUsed32 += ALIGN(HUF_SYMBOLVALUE_MAX + 1, sizeof(U32)) >> 2; -+ -+ if ((spaceUsed32 << 2) > workspaceSize) -+ return ERROR(tableLog_tooLarge); -+ workspace = (U32 *)workspace + spaceUsed32; -+ workspaceSize -= (spaceUsed32 << 2); -+ -+ rankStart = rankStart0 + 1; -+ memset(rankStats, 0, sizeof(U32) * (2 * HUF_TABLELOG_MAX + 2 + 1)); -+ -+ HUF_STATIC_ASSERT(sizeof(HUF_DEltX4) == sizeof(HUF_DTable)); /* if compiler fails here, assertion is wrong */ -+ if (maxTableLog > HUF_TABLELOG_MAX) -+ return ERROR(tableLog_tooLarge); -+ /* memset(weightList, 0, sizeof(weightList)); */ /* is not necessary, even though some analyzer complain ... */ -+ -+ iSize = HUF_readStats_wksp(weightList, HUF_SYMBOLVALUE_MAX + 1, rankStats, &nbSymbols, &tableLog, src, srcSize, workspace, workspaceSize); -+ if (HUF_isError(iSize)) -+ return iSize; -+ -+ /* check result */ -+ if (tableLog > maxTableLog) -+ return ERROR(tableLog_tooLarge); /* DTable can't fit code depth */ -+ -+ /* find maxWeight */ -+ for (maxW = tableLog; rankStats[maxW] == 0; maxW--) { -+ } /* necessarily finds a solution before 0 */ -+ -+ /* Get start index of each weight */ -+ { -+ U32 w, nextRankStart = 0; -+ for (w = 1; w < maxW + 1; w++) { -+ U32 curr = nextRankStart; -+ nextRankStart += rankStats[w]; -+ rankStart[w] = curr; -+ } -+ rankStart[0] = nextRankStart; /* put all 0w symbols at the end of sorted list*/ -+ sizeOfSort = nextRankStart; -+ } -+ -+ /* sort symbols by weight */ -+ { -+ U32 s; -+ for (s = 0; s < nbSymbols; s++) { -+ U32 const w = weightList[s]; -+ U32 const r = rankStart[w]++; -+ sortedSymbol[r].symbol = (BYTE)s; -+ sortedSymbol[r].weight = (BYTE)w; -+ } -+ rankStart[0] = 0; /* forget 0w symbols; this is beginning of weight(1) */ -+ } -+ -+ /* Build rankVal */ -+ { -+ U32 *const rankVal0 = rankVal[0]; -+ { -+ int const rescale = (maxTableLog - tableLog) - 1; /* tableLog <= maxTableLog */ -+ U32 nextRankVal = 0; -+ U32 w; -+ for (w = 1; w < maxW + 1; w++) { -+ U32 curr = nextRankVal; -+ nextRankVal += rankStats[w] << (w + rescale); -+ rankVal0[w] = curr; -+ } -+ } -+ { -+ U32 const minBits = tableLog + 1 - maxW; -+ U32 consumed; -+ for (consumed = minBits; consumed < maxTableLog - minBits + 1; consumed++) { -+ U32 *const rankValPtr = rankVal[consumed]; -+ U32 w; -+ for (w = 1; w < maxW + 1; w++) { -+ rankValPtr[w] = rankVal0[w] >> consumed; -+ } -+ } -+ } -+ } -+ -+ HUF_fillDTableX4(dt, maxTableLog, sortedSymbol, sizeOfSort, rankStart0, rankVal, maxW, tableLog + 1); -+ -+ dtd.tableLog = (BYTE)maxTableLog; -+ dtd.tableType = 1; -+ memcpy(DTable, &dtd, sizeof(dtd)); -+ return iSize; -+} -+ -+static U32 INIT HUF_decodeSymbolX4(void *op, BIT_DStream_t *DStream, const HUF_DEltX4 *dt, const U32 dtLog) -+{ -+ size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */ -+ memcpy(op, dt + val, 2); -+ BIT_skipBits(DStream, dt[val].nbBits); -+ return dt[val].length; -+} -+ -+static U32 INIT HUF_decodeLastSymbolX4(void *op, BIT_DStream_t *DStream, const HUF_DEltX4 *dt, const U32 dtLog) -+{ -+ size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */ -+ memcpy(op, dt + val, 1); -+ if (dt[val].length == 1) -+ BIT_skipBits(DStream, dt[val].nbBits); -+ else { -+ if (DStream->bitsConsumed < (sizeof(DStream->bitContainer) * 8)) { -+ BIT_skipBits(DStream, dt[val].nbBits); -+ if (DStream->bitsConsumed > (sizeof(DStream->bitContainer) * 8)) -+ /* ugly hack; works only because it's the last symbol. Note : can't easily extract nbBits from just this symbol */ -+ DStream->bitsConsumed = (sizeof(DStream->bitContainer) * 8); -+ } -+ } -+ return 1; -+} -+ -+#define HUF_DECODE_SYMBOLX4_0(ptr, DStreamPtr) ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog) -+ -+#define HUF_DECODE_SYMBOLX4_1(ptr, DStreamPtr) \ -+ if (ZSTD_64bits() || (HUF_TABLELOG_MAX <= 12)) \ -+ ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog) -+ -+#define HUF_DECODE_SYMBOLX4_2(ptr, DStreamPtr) \ -+ if (ZSTD_64bits()) \ -+ ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog) -+ -+FORCE_INLINE size_t HUF_decodeStreamX4(BYTE *p, BIT_DStream_t *bitDPtr, BYTE *const pEnd, const HUF_DEltX4 *const dt, const U32 dtLog) -+{ -+ BYTE *const pStart = p; -+ -+ /* up to 8 symbols at a time */ -+ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd - (sizeof(bitDPtr->bitContainer) - 1))) { -+ HUF_DECODE_SYMBOLX4_2(p, bitDPtr); -+ HUF_DECODE_SYMBOLX4_1(p, bitDPtr); -+ HUF_DECODE_SYMBOLX4_2(p, bitDPtr); -+ HUF_DECODE_SYMBOLX4_0(p, bitDPtr); -+ } -+ -+ /* closer to end : up to 2 symbols at a time */ -+ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p <= pEnd - 2)) -+ HUF_DECODE_SYMBOLX4_0(p, bitDPtr); -+ -+ while (p <= pEnd - 2) -+ HUF_DECODE_SYMBOLX4_0(p, bitDPtr); /* no need to reload : reached the end of DStream */ -+ -+ if (p < pEnd) -+ p += HUF_decodeLastSymbolX4(p, bitDPtr, dt, dtLog); -+ -+ return p - pStart; -+} -+ -+static size_t INIT HUF_decompress1X4_usingDTable_internal(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) -+{ -+ BIT_DStream_t bitD; -+ -+ /* Init */ -+ { -+ size_t const errorCode = BIT_initDStream(&bitD, cSrc, cSrcSize); -+ if (HUF_isError(errorCode)) -+ return errorCode; -+ } -+ -+ /* decode */ -+ { -+ BYTE *const ostart = (BYTE *)dst; -+ BYTE *const oend = ostart + dstSize; -+ const void *const dtPtr = DTable + 1; /* force compiler to not use strict-aliasing */ -+ const HUF_DEltX4 *const dt = (const HUF_DEltX4 *)dtPtr; -+ DTableDesc const dtd = HUF_getDTableDesc(DTable); -+ HUF_decodeStreamX4(ostart, &bitD, oend, dt, dtd.tableLog); -+ } -+ -+ /* check */ -+ if (!BIT_endOfDStream(&bitD)) -+ return ERROR(corruption_detected); -+ -+ /* decoded size */ -+ return dstSize; -+} -+ -+size_t INIT HUF_decompress1X4_usingDTable(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) -+{ -+ DTableDesc dtd = HUF_getDTableDesc(DTable); -+ if (dtd.tableType != 1) -+ return ERROR(GENERIC); -+ return HUF_decompress1X4_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable); -+} -+ -+size_t INIT HUF_decompress1X4_DCtx_wksp(HUF_DTable *DCtx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize) -+{ -+ const BYTE *ip = (const BYTE *)cSrc; -+ -+ size_t const hSize = HUF_readDTableX4_wksp(DCtx, cSrc, cSrcSize, workspace, workspaceSize); -+ if (HUF_isError(hSize)) -+ return hSize; -+ if (hSize >= cSrcSize) -+ return ERROR(srcSize_wrong); -+ ip += hSize; -+ cSrcSize -= hSize; -+ -+ return HUF_decompress1X4_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx); -+} -+ -+static size_t INIT HUF_decompress4X4_usingDTable_internal(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) -+{ -+ if (cSrcSize < 10) -+ return ERROR(corruption_detected); /* strict minimum : jump table + 1 byte per stream */ -+ -+ { -+ const BYTE *const istart = (const BYTE *)cSrc; -+ BYTE *const ostart = (BYTE *)dst; -+ BYTE *const oend = ostart + dstSize; -+ const void *const dtPtr = DTable + 1; -+ const HUF_DEltX4 *const dt = (const HUF_DEltX4 *)dtPtr; -+ -+ /* Init */ -+ BIT_DStream_t bitD1; -+ BIT_DStream_t bitD2; -+ BIT_DStream_t bitD3; -+ BIT_DStream_t bitD4; -+ size_t const length1 = ZSTD_readLE16(istart); -+ size_t const length2 = ZSTD_readLE16(istart + 2); -+ size_t const length3 = ZSTD_readLE16(istart + 4); -+ size_t const length4 = cSrcSize - (length1 + length2 + length3 + 6); -+ const BYTE *const istart1 = istart + 6; /* jumpTable */ -+ const BYTE *const istart2 = istart1 + length1; -+ const BYTE *const istart3 = istart2 + length2; -+ const BYTE *const istart4 = istart3 + length3; -+ size_t const segmentSize = (dstSize + 3) / 4; -+ BYTE *const opStart2 = ostart + segmentSize; -+ BYTE *const opStart3 = opStart2 + segmentSize; -+ BYTE *const opStart4 = opStart3 + segmentSize; -+ BYTE *op1 = ostart; -+ BYTE *op2 = opStart2; -+ BYTE *op3 = opStart3; -+ BYTE *op4 = opStart4; -+ U32 endSignal; -+ DTableDesc const dtd = HUF_getDTableDesc(DTable); -+ U32 const dtLog = dtd.tableLog; -+ -+ if (length4 > cSrcSize) -+ return ERROR(corruption_detected); /* overflow */ -+ { -+ size_t const errorCode = BIT_initDStream(&bitD1, istart1, length1); -+ if (HUF_isError(errorCode)) -+ return errorCode; -+ } -+ { -+ size_t const errorCode = BIT_initDStream(&bitD2, istart2, length2); -+ if (HUF_isError(errorCode)) -+ return errorCode; -+ } -+ { -+ size_t const errorCode = BIT_initDStream(&bitD3, istart3, length3); -+ if (HUF_isError(errorCode)) -+ return errorCode; -+ } -+ { -+ size_t const errorCode = BIT_initDStream(&bitD4, istart4, length4); -+ if (HUF_isError(errorCode)) -+ return errorCode; -+ } -+ -+ /* 16-32 symbols per loop (4-8 symbols per stream) */ -+ endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); -+ for (; (endSignal == BIT_DStream_unfinished) & (op4 < (oend - (sizeof(bitD4.bitContainer) - 1)));) { -+ HUF_DECODE_SYMBOLX4_2(op1, &bitD1); -+ HUF_DECODE_SYMBOLX4_2(op2, &bitD2); -+ HUF_DECODE_SYMBOLX4_2(op3, &bitD3); -+ HUF_DECODE_SYMBOLX4_2(op4, &bitD4); -+ HUF_DECODE_SYMBOLX4_1(op1, &bitD1); -+ HUF_DECODE_SYMBOLX4_1(op2, &bitD2); -+ HUF_DECODE_SYMBOLX4_1(op3, &bitD3); -+ HUF_DECODE_SYMBOLX4_1(op4, &bitD4); -+ HUF_DECODE_SYMBOLX4_2(op1, &bitD1); -+ HUF_DECODE_SYMBOLX4_2(op2, &bitD2); -+ HUF_DECODE_SYMBOLX4_2(op3, &bitD3); -+ HUF_DECODE_SYMBOLX4_2(op4, &bitD4); -+ HUF_DECODE_SYMBOLX4_0(op1, &bitD1); -+ HUF_DECODE_SYMBOLX4_0(op2, &bitD2); -+ HUF_DECODE_SYMBOLX4_0(op3, &bitD3); -+ HUF_DECODE_SYMBOLX4_0(op4, &bitD4); -+ -+ endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); -+ } -+ -+ /* check corruption */ -+ if (op1 > opStart2) -+ return ERROR(corruption_detected); -+ if (op2 > opStart3) -+ return ERROR(corruption_detected); -+ if (op3 > opStart4) -+ return ERROR(corruption_detected); -+ /* note : op4 already verified within main loop */ -+ -+ /* finish bitStreams one by one */ -+ HUF_decodeStreamX4(op1, &bitD1, opStart2, dt, dtLog); -+ HUF_decodeStreamX4(op2, &bitD2, opStart3, dt, dtLog); -+ HUF_decodeStreamX4(op3, &bitD3, opStart4, dt, dtLog); -+ HUF_decodeStreamX4(op4, &bitD4, oend, dt, dtLog); -+ -+ /* check */ -+ { -+ U32 const endCheck = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4); -+ if (!endCheck) -+ return ERROR(corruption_detected); -+ } -+ -+ /* decoded size */ -+ return dstSize; -+ } -+} -+ -+size_t INIT HUF_decompress4X4_usingDTable(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) -+{ -+ DTableDesc dtd = HUF_getDTableDesc(DTable); -+ if (dtd.tableType != 1) -+ return ERROR(GENERIC); -+ return HUF_decompress4X4_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable); -+} -+ -+size_t INIT HUF_decompress4X4_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize) -+{ -+ const BYTE *ip = (const BYTE *)cSrc; -+ -+ size_t hSize = HUF_readDTableX4_wksp(dctx, cSrc, cSrcSize, workspace, workspaceSize); -+ if (HUF_isError(hSize)) -+ return hSize; -+ if (hSize >= cSrcSize) -+ return ERROR(srcSize_wrong); -+ ip += hSize; -+ cSrcSize -= hSize; -+ -+ return HUF_decompress4X4_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx); -+} -+ -+/* ********************************/ -+/* Generic decompression selector */ -+/* ********************************/ -+ -+size_t INIT HUF_decompress1X_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) -+{ -+ DTableDesc const dtd = HUF_getDTableDesc(DTable); -+ return dtd.tableType ? HUF_decompress1X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable) -+ : HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable); -+} -+ -+size_t INIT HUF_decompress4X_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) -+{ -+ DTableDesc const dtd = HUF_getDTableDesc(DTable); -+ return dtd.tableType ? HUF_decompress4X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable) -+ : HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable); -+} -+ -+typedef struct { -+ U32 tableTime; -+ U32 decode256Time; -+} algo_time_t; -+static const algo_time_t algoTime[16 /* Quantization */][3 /* single, double, quad */] = { -+ /* single, double, quad */ -+ {{0, 0}, {1, 1}, {2, 2}}, /* Q==0 : impossible */ -+ {{0, 0}, {1, 1}, {2, 2}}, /* Q==1 : impossible */ -+ {{38, 130}, {1313, 74}, {2151, 38}}, /* Q == 2 : 12-18% */ -+ {{448, 128}, {1353, 74}, {2238, 41}}, /* Q == 3 : 18-25% */ -+ {{556, 128}, {1353, 74}, {2238, 47}}, /* Q == 4 : 25-32% */ -+ {{714, 128}, {1418, 74}, {2436, 53}}, /* Q == 5 : 32-38% */ -+ {{883, 128}, {1437, 74}, {2464, 61}}, /* Q == 6 : 38-44% */ -+ {{897, 128}, {1515, 75}, {2622, 68}}, /* Q == 7 : 44-50% */ -+ {{926, 128}, {1613, 75}, {2730, 75}}, /* Q == 8 : 50-56% */ -+ {{947, 128}, {1729, 77}, {3359, 77}}, /* Q == 9 : 56-62% */ -+ {{1107, 128}, {2083, 81}, {4006, 84}}, /* Q ==10 : 62-69% */ -+ {{1177, 128}, {2379, 87}, {4785, 88}}, /* Q ==11 : 69-75% */ -+ {{1242, 128}, {2415, 93}, {5155, 84}}, /* Q ==12 : 75-81% */ -+ {{1349, 128}, {2644, 106}, {5260, 106}}, /* Q ==13 : 81-87% */ -+ {{1455, 128}, {2422, 124}, {4174, 124}}, /* Q ==14 : 87-93% */ -+ {{722, 128}, {1891, 145}, {1936, 146}}, /* Q ==15 : 93-99% */ -+}; -+ -+/** HUF_selectDecoder() : -+* Tells which decoder is likely to decode faster, -+* based on a set of pre-determined metrics. -+* @return : 0==HUF_decompress4X2, 1==HUF_decompress4X4 . -+* Assumption : 0 < cSrcSize < dstSize <= 128 KB */ -+U32 INIT HUF_selectDecoder(size_t dstSize, size_t cSrcSize) -+{ -+ /* decoder timing evaluation */ -+ U32 const Q = (U32)(cSrcSize * 16 / dstSize); /* Q < 16 since dstSize > cSrcSize */ -+ U32 const D256 = (U32)(dstSize >> 8); -+ U32 const DTime0 = algoTime[Q][0].tableTime + (algoTime[Q][0].decode256Time * D256); -+ U32 DTime1 = algoTime[Q][1].tableTime + (algoTime[Q][1].decode256Time * D256); -+ DTime1 += DTime1 >> 3; /* advantage to algorithm using less memory, for cache eviction */ -+ -+ return DTime1 < DTime0; -+} -+ -+typedef size_t (*decompressionAlgo)(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize); -+ -+size_t INIT HUF_decompress4X_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize) -+{ -+ /* validation checks */ -+ if (dstSize == 0) -+ return ERROR(dstSize_tooSmall); -+ if (cSrcSize > dstSize) -+ return ERROR(corruption_detected); /* invalid */ -+ if (cSrcSize == dstSize) { -+ memcpy(dst, cSrc, dstSize); -+ return dstSize; -+ } /* not compressed */ -+ if (cSrcSize == 1) { -+ memset(dst, *(const BYTE *)cSrc, dstSize); -+ return dstSize; -+ } /* RLE */ -+ -+ { -+ U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); -+ return algoNb ? HUF_decompress4X4_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workspace, workspaceSize) -+ : HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workspace, workspaceSize); -+ } -+} -+ -+size_t INIT HUF_decompress4X_hufOnly_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize) -+{ -+ /* validation checks */ -+ if (dstSize == 0) -+ return ERROR(dstSize_tooSmall); -+ if ((cSrcSize >= dstSize) || (cSrcSize <= 1)) -+ return ERROR(corruption_detected); /* invalid */ -+ -+ { -+ U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); -+ return algoNb ? HUF_decompress4X4_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workspace, workspaceSize) -+ : HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workspace, workspaceSize); -+ } -+} -+ -+size_t INIT HUF_decompress1X_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize) -+{ -+ /* validation checks */ -+ if (dstSize == 0) -+ return ERROR(dstSize_tooSmall); -+ if (cSrcSize > dstSize) -+ return ERROR(corruption_detected); /* invalid */ -+ if (cSrcSize == dstSize) { -+ memcpy(dst, cSrc, dstSize); -+ return dstSize; -+ } /* not compressed */ -+ if (cSrcSize == 1) { -+ memset(dst, *(const BYTE *)cSrc, dstSize); -+ return dstSize; -+ } /* RLE */ -+ -+ { -+ U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); -+ return algoNb ? HUF_decompress1X4_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workspace, workspaceSize) -+ : HUF_decompress1X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workspace, workspaceSize); -+ } -+} -diff --git a/xen/common/zstd/mem.h b/xen/common/zstd/mem.h -new file mode 100644 -index 0000000000..2883200696 ---- /dev/null -+++ b/xen/common/zstd/mem.h -@@ -0,0 +1,151 @@ -+/** -+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. -+ * All rights reserved. -+ * -+ * This source code is licensed under the BSD-style license found in the -+ * LICENSE file in the root directory of https://github.com/facebook/zstd. -+ * An additional grant of patent rights can be found in the PATENTS file in the -+ * same directory. -+ * -+ * This program is free software; you can redistribute it and/or modify it under -+ * the terms of the GNU General Public License version 2 as published by the -+ * Free Software Foundation. This program is dual-licensed; you may select -+ * either version 2 of the GNU General Public License ("GPL") or BSD license -+ * ("BSD"). -+ */ -+ -+#ifndef MEM_H_MODULE -+#define MEM_H_MODULE -+ -+/*-**************************************** -+* Dependencies -+******************************************/ -+#include /* memcpy */ -+#include /* size_t, ptrdiff_t */ -+#include -+ -+/*-**************************************** -+* Compiler specifics -+******************************************/ -+#define ZSTD_STATIC static inline -+ -+/*-************************************************************** -+* Basic Types -+*****************************************************************/ -+typedef uint8_t BYTE; -+typedef uint16_t U16; -+typedef int16_t S16; -+typedef uint32_t U32; -+typedef int32_t S32; -+typedef uint64_t U64; -+typedef int64_t S64; -+typedef ptrdiff_t iPtrDiff; -+typedef uintptr_t uPtrDiff; -+ -+/*-************************************************************** -+* Memory I/O -+*****************************************************************/ -+ZSTD_STATIC unsigned ZSTD_32bits(void) { return sizeof(size_t) == 4; } -+ZSTD_STATIC unsigned ZSTD_64bits(void) { return sizeof(size_t) == 8; } -+ -+#if defined(__LITTLE_ENDIAN) -+#define ZSTD_LITTLE_ENDIAN 1 -+#else -+#define ZSTD_LITTLE_ENDIAN 0 -+#endif -+ -+ZSTD_STATIC unsigned ZSTD_isLittleEndian(void) { return ZSTD_LITTLE_ENDIAN; } -+ -+ZSTD_STATIC U16 ZSTD_read16(const void *memPtr) { return get_unaligned((const U16 *)memPtr); } -+ -+ZSTD_STATIC U32 ZSTD_read32(const void *memPtr) { return get_unaligned((const U32 *)memPtr); } -+ -+ZSTD_STATIC U64 ZSTD_read64(const void *memPtr) { return get_unaligned((const U64 *)memPtr); } -+ -+ZSTD_STATIC size_t ZSTD_readST(const void *memPtr) { return get_unaligned((const size_t *)memPtr); } -+ -+ZSTD_STATIC void ZSTD_write16(void *memPtr, U16 value) { put_unaligned(value, (U16 *)memPtr); } -+ -+ZSTD_STATIC void ZSTD_write32(void *memPtr, U32 value) { put_unaligned(value, (U32 *)memPtr); } -+ -+ZSTD_STATIC void ZSTD_write64(void *memPtr, U64 value) { put_unaligned(value, (U64 *)memPtr); } -+ -+/*=== Little endian r/w ===*/ -+ -+ZSTD_STATIC U16 ZSTD_readLE16(const void *memPtr) { return get_unaligned_le16(memPtr); } -+ -+ZSTD_STATIC void ZSTD_writeLE16(void *memPtr, U16 val) { put_unaligned_le16(val, memPtr); } -+ -+ZSTD_STATIC U32 ZSTD_readLE24(const void *memPtr) { return ZSTD_readLE16(memPtr) + (((const BYTE *)memPtr)[2] << 16); } -+ -+ZSTD_STATIC void ZSTD_writeLE24(void *memPtr, U32 val) -+{ -+ ZSTD_writeLE16(memPtr, (U16)val); -+ ((BYTE *)memPtr)[2] = (BYTE)(val >> 16); -+} -+ -+ZSTD_STATIC U32 ZSTD_readLE32(const void *memPtr) { return get_unaligned_le32(memPtr); } -+ -+ZSTD_STATIC void ZSTD_writeLE32(void *memPtr, U32 val32) { put_unaligned_le32(val32, memPtr); } -+ -+ZSTD_STATIC U64 ZSTD_readLE64(const void *memPtr) { return get_unaligned_le64(memPtr); } -+ -+ZSTD_STATIC void ZSTD_writeLE64(void *memPtr, U64 val64) { put_unaligned_le64(val64, memPtr); } -+ -+ZSTD_STATIC size_t ZSTD_readLEST(const void *memPtr) -+{ -+ if (ZSTD_32bits()) -+ return (size_t)ZSTD_readLE32(memPtr); -+ else -+ return (size_t)ZSTD_readLE64(memPtr); -+} -+ -+ZSTD_STATIC void ZSTD_writeLEST(void *memPtr, size_t val) -+{ -+ if (ZSTD_32bits()) -+ ZSTD_writeLE32(memPtr, (U32)val); -+ else -+ ZSTD_writeLE64(memPtr, (U64)val); -+} -+ -+/*=== Big endian r/w ===*/ -+ -+ZSTD_STATIC U32 ZSTD_readBE32(const void *memPtr) { return get_unaligned_be32(memPtr); } -+ -+ZSTD_STATIC void ZSTD_writeBE32(void *memPtr, U32 val32) { put_unaligned_be32(val32, memPtr); } -+ -+ZSTD_STATIC U64 ZSTD_readBE64(const void *memPtr) { return get_unaligned_be64(memPtr); } -+ -+ZSTD_STATIC void ZSTD_writeBE64(void *memPtr, U64 val64) { put_unaligned_be64(val64, memPtr); } -+ -+ZSTD_STATIC size_t ZSTD_readBEST(const void *memPtr) -+{ -+ if (ZSTD_32bits()) -+ return (size_t)ZSTD_readBE32(memPtr); -+ else -+ return (size_t)ZSTD_readBE64(memPtr); -+} -+ -+ZSTD_STATIC void ZSTD_writeBEST(void *memPtr, size_t val) -+{ -+ if (ZSTD_32bits()) -+ ZSTD_writeBE32(memPtr, (U32)val); -+ else -+ ZSTD_writeBE64(memPtr, (U64)val); -+} -+ -+/* function safe only for comparisons */ -+ZSTD_STATIC U32 ZSTD_readMINMATCH(const void *memPtr, U32 length) -+{ -+ switch (length) { -+ default: -+ case 4: return ZSTD_read32(memPtr); -+ case 3: -+ if (ZSTD_isLittleEndian()) -+ return ZSTD_read32(memPtr) << 8; -+ else -+ return ZSTD_read32(memPtr) >> 8; -+ } -+} -+ -+#endif /* MEM_H_MODULE */ -diff --git a/xen/common/zstd/zstd_common.c b/xen/common/zstd/zstd_common.c -new file mode 100644 -index 0000000000..a35c4a5f14 ---- /dev/null -+++ b/xen/common/zstd/zstd_common.c -@@ -0,0 +1,74 @@ -+/** -+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. -+ * All rights reserved. -+ * -+ * This source code is licensed under the BSD-style license found in the -+ * LICENSE file in the root directory of https://github.com/facebook/zstd. -+ * An additional grant of patent rights can be found in the PATENTS file in the -+ * same directory. -+ * -+ * This program is free software; you can redistribute it and/or modify it under -+ * the terms of the GNU General Public License version 2 as published by the -+ * Free Software Foundation. This program is dual-licensed; you may select -+ * either version 2 of the GNU General Public License ("GPL") or BSD license -+ * ("BSD"). -+ */ -+ -+/*-************************************* -+* Dependencies -+***************************************/ -+#include "error_private.h" -+#include "zstd_internal.h" /* declaration of ZSTD_isError, ZSTD_getErrorName, ZSTD_getErrorCode, ZSTD_getErrorString, ZSTD_versionNumber */ -+ -+/*=************************************************************** -+* Custom allocator -+****************************************************************/ -+ -+#define stack_push(stack, size) \ -+ ({ \ -+ void *const ptr = ZSTD_PTR_ALIGN((stack)->ptr); \ -+ (stack)->ptr = (char *)ptr + (size); \ -+ (stack)->ptr <= (stack)->end ? ptr : NULL; \ -+ }) -+ -+ZSTD_customMem INIT ZSTD_initStack(void *workspace, size_t workspaceSize) -+{ -+ ZSTD_customMem stackMem = {ZSTD_stackAlloc, ZSTD_stackFree, workspace}; -+ ZSTD_stack *stack = (ZSTD_stack *)workspace; -+ /* Verify preconditions */ -+ if (!workspace || workspaceSize < sizeof(ZSTD_stack) || workspace != ZSTD_PTR_ALIGN(workspace)) { -+ ZSTD_customMem error = {NULL, NULL, NULL}; -+ return error; -+ } -+ /* Initialize the stack */ -+ stack->ptr = workspace; -+ stack->end = (char *)workspace + workspaceSize; -+ stack_push(stack, sizeof(ZSTD_stack)); -+ return stackMem; -+} -+ -+void *INIT ZSTD_stackAllocAll(void *opaque, size_t *size) -+{ -+ ZSTD_stack *stack = (ZSTD_stack *)opaque; -+ *size = (BYTE const *)stack->end - (BYTE *)ZSTD_PTR_ALIGN(stack->ptr); -+ return stack_push(stack, *size); -+} -+ -+void *INIT ZSTD_stackAlloc(void *opaque, size_t size) -+{ -+ ZSTD_stack *stack = (ZSTD_stack *)opaque; -+ return stack_push(stack, size); -+} -+void INIT ZSTD_stackFree(void *opaque, void *address) -+{ -+ (void)opaque; -+ (void)address; -+} -+ -+void *INIT ZSTD_malloc(size_t size, ZSTD_customMem customMem) { return customMem.customAlloc(customMem.opaque, size); } -+ -+void INIT ZSTD_free(void *ptr, ZSTD_customMem customMem) -+{ -+ if (ptr != NULL) -+ customMem.customFree(customMem.opaque, ptr); -+} -diff --git a/xen/common/zstd/zstd_internal.h b/xen/common/zstd/zstd_internal.h -new file mode 100644 -index 0000000000..7f8e5529eb ---- /dev/null -+++ b/xen/common/zstd/zstd_internal.h -@@ -0,0 +1,372 @@ -+/** -+ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. -+ * All rights reserved. -+ * -+ * This source code is licensed under the BSD-style license found in the -+ * LICENSE file in the root directory of https://github.com/facebook/zstd. -+ * An additional grant of patent rights can be found in the PATENTS file in the -+ * same directory. -+ * -+ * This program is free software; you can redistribute it and/or modify it under -+ * the terms of the GNU General Public License version 2 as published by the -+ * Free Software Foundation. This program is dual-licensed; you may select -+ * either version 2 of the GNU General Public License ("GPL") or BSD license -+ * ("BSD"). -+ */ -+ -+#ifndef ZSTD_CCOMMON_H_MODULE -+#define ZSTD_CCOMMON_H_MODULE -+ -+/*-******************************************************* -+* Compiler specifics -+*********************************************************/ -+#define FORCE_INLINE static always_inline -+#define FORCE_NOINLINE static noinline INIT -+ -+/*-************************************* -+* Dependencies -+***************************************/ -+#include "error_private.h" -+#include "mem.h" -+#include -+#include -+ -+#define ALIGN(x, a) ((x + (a) - 1) & ~((a) - 1)) -+#define PTR_ALIGN(p, a) ((typeof(p))ALIGN((unsigned long)(p), (a))) -+ -+typedef enum { -+ ZSTDnit_frameHeader, -+ ZSTDnit_blockHeader, -+ ZSTDnit_block, -+ ZSTDnit_lastBlock, -+ ZSTDnit_checksum, -+ ZSTDnit_skippableFrame -+} ZSTD_nextInputType_e; -+ -+/** -+ * struct ZSTD_frameParams - zstd frame parameters stored in the frame header -+ * @frameContentSize: The frame content size, or 0 if not present. -+ * @windowSize: The window size, or 0 if the frame is a skippable frame. -+ * @dictID: The dictionary id, or 0 if not present. -+ * @checksumFlag: Whether a checksum was used. -+ */ -+typedef struct { -+ unsigned long long frameContentSize; -+ unsigned int windowSize; -+ unsigned int dictID; -+ unsigned int checksumFlag; -+} ZSTD_frameParams; -+ -+/** -+ * struct ZSTD_inBuffer - input buffer for streaming -+ * @src: Start of the input buffer. -+ * @size: Size of the input buffer. -+ * @pos: Position where reading stopped. Will be updated. -+ * Necessarily 0 <= pos <= size. -+ */ -+typedef struct ZSTD_inBuffer_s { -+ const void *src; -+ size_t size; -+ size_t pos; -+} ZSTD_inBuffer; -+ -+/** -+ * struct ZSTD_outBuffer - output buffer for streaming -+ * @dst: Start of the output buffer. -+ * @size: Size of the output buffer. -+ * @pos: Position where writing stopped. Will be updated. -+ * Necessarily 0 <= pos <= size. -+ */ -+typedef struct ZSTD_outBuffer_s { -+ void *dst; -+ size_t size; -+ size_t pos; -+} ZSTD_outBuffer; -+ -+typedef struct ZSTD_CCtx_s ZSTD_CCtx; -+typedef struct ZSTD_DCtx_s ZSTD_DCtx; -+ -+typedef struct ZSTD_CDict_s ZSTD_CDict; -+typedef struct ZSTD_DDict_s ZSTD_DDict; -+ -+typedef struct ZSTD_CStream_s ZSTD_CStream; -+typedef struct ZSTD_DStream_s ZSTD_DStream; -+ -+/*-************************************* -+* shared macros -+***************************************/ -+#define MIN(a, b) ((a) < (b) ? (a) : (b)) -+#define MAX(a, b) ((a) > (b) ? (a) : (b)) -+#define CHECK_F(f) \ -+ { \ -+ size_t const errcod = f; \ -+ if (ERR_isError(errcod)) \ -+ return errcod; \ -+ } /* check and Forward error code */ -+#define CHECK_E(f, e) \ -+ { \ -+ size_t const errcod = f; \ -+ if (ERR_isError(errcod)) \ -+ return ERROR(e); \ -+ } /* check and send Error code */ -+#define ZSTD_STATIC_ASSERT(c) \ -+ { \ -+ enum { ZSTD_static_assert = 1 / (int)(!!(c)) }; \ -+ } -+ -+/*-************************************* -+* Common constants -+***************************************/ -+#define ZSTD_MAGICNUMBER 0xFD2FB528 /* >= v0.8.0 */ -+#define ZSTD_MAGIC_SKIPPABLE_START 0x184D2A50U -+ -+#define ZSTD_OPT_NUM (1 << 12) -+#define ZSTD_DICT_MAGIC 0xEC30A437 /* v0.7+ */ -+ -+#define ZSTD_CONTENTSIZE_UNKNOWN (0ULL - 1) -+#define ZSTD_CONTENTSIZE_ERROR (0ULL - 2) -+ -+#define ZSTD_WINDOWLOG_MAX_32 27 -+#define ZSTD_WINDOWLOG_MAX_64 27 -+#define ZSTD_WINDOWLOG_MAX \ -+ ((unsigned int)(sizeof(size_t) == 4 \ -+ ? ZSTD_WINDOWLOG_MAX_32 \ -+ : ZSTD_WINDOWLOG_MAX_64)) -+#define ZSTD_WINDOWLOG_MIN 10 -+#define ZSTD_HASHLOG_MAX ZSTD_WINDOWLOG_MAX -+#define ZSTD_HASHLOG_MIN 6 -+#define ZSTD_CHAINLOG_MAX (ZSTD_WINDOWLOG_MAX+1) -+#define ZSTD_CHAINLOG_MIN ZSTD_HASHLOG_MIN -+#define ZSTD_HASHLOG3_MAX 17 -+#define ZSTD_SEARCHLOG_MAX (ZSTD_WINDOWLOG_MAX-1) -+#define ZSTD_SEARCHLOG_MIN 1 -+/* only for ZSTD_fast, other strategies are limited to 6 */ -+#define ZSTD_SEARCHLENGTH_MAX 7 -+/* only for ZSTD_btopt, other strategies are limited to 4 */ -+#define ZSTD_SEARCHLENGTH_MIN 3 -+#define ZSTD_TARGETLENGTH_MIN 4 -+#define ZSTD_TARGETLENGTH_MAX 999 -+ -+#define ZSTD_REP_NUM 3 /* number of repcodes */ -+#define ZSTD_REP_CHECK (ZSTD_REP_NUM) /* number of repcodes to check by the optimal parser */ -+#define ZSTD_REP_MOVE (ZSTD_REP_NUM - 1) -+#define ZSTD_REP_MOVE_OPT (ZSTD_REP_NUM) -+static const U32 repStartValue[ZSTD_REP_NUM] = {1, 4, 8}; -+ -+/* for static allocation */ -+#define ZSTD_FRAMEHEADERSIZE_MAX 18 -+#define ZSTD_FRAMEHEADERSIZE_MIN 6 -+static const size_t ZSTD_frameHeaderSize_prefix = 5; -+static const size_t ZSTD_frameHeaderSize_min = ZSTD_FRAMEHEADERSIZE_MIN; -+static const size_t ZSTD_frameHeaderSize_max = ZSTD_FRAMEHEADERSIZE_MAX; -+/* magic number + skippable frame length */ -+static const size_t ZSTD_skippableHeaderSize = 8; -+ -+#define ZSTD_BLOCKSIZE_ABSOLUTEMAX (128 * 1024) -+ -+#if 0 /* These don't seem to be usable - not sure what their purpose is. */ -+#define KB *(1 << 10) -+#define MB *(1 << 20) -+#define GB *(1U << 30) -+#endif -+ -+#define BIT7 128 -+#define BIT6 64 -+#define BIT5 32 -+#define BIT4 16 -+#define BIT1 2 -+#define BIT0 1 -+ -+#define ZSTD_WINDOWLOG_ABSOLUTEMIN 10 -+static const size_t ZSTD_fcs_fieldSize[4] = {0, 2, 4, 8}; -+static const size_t ZSTD_did_fieldSize[4] = {0, 1, 2, 4}; -+ -+#define ZSTD_BLOCKHEADERSIZE 3 /* C standard doesn't allow `static const` variable to be init using another `static const` variable */ -+static const size_t ZSTD_blockHeaderSize = ZSTD_BLOCKHEADERSIZE; -+typedef enum { bt_raw, bt_rle, bt_compressed, bt_reserved } blockType_e; -+ -+#define MIN_SEQUENCES_SIZE 1 /* nbSeq==0 */ -+#define MIN_CBLOCK_SIZE (1 /*litCSize*/ + 1 /* RLE or RAW */ + MIN_SEQUENCES_SIZE /* nbSeq==0 */) /* for a non-null block */ -+ -+#define HufLog 12 -+typedef enum { set_basic, set_rle, set_compressed, set_repeat } symbolEncodingType_e; -+ -+#define LONGNBSEQ 0x7F00 -+ -+#define MINMATCH 3 -+#define EQUAL_READ32 4 -+ -+#define Litbits 8 -+#define MaxLit ((1 << Litbits) - 1) -+#define MaxML 52 -+#define MaxLL 35 -+#define MaxOff 28 -+#define MaxSeq MAX(MaxLL, MaxML) /* Assumption : MaxOff < MaxLL,MaxML */ -+#define MLFSELog 9 -+#define LLFSELog 9 -+#define OffFSELog 8 -+ -+static const U32 LL_bits[MaxLL + 1] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; -+static const S16 LL_defaultNorm[MaxLL + 1] = {4, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 1, 1, 1, 1, 1, -1, -1, -1, -1}; -+#define LL_DEFAULTNORMLOG 6 /* for static allocation */ -+static const U32 LL_defaultNormLog = LL_DEFAULTNORMLOG; -+ -+static const U32 ML_bits[MaxML + 1] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; -+static const S16 ML_defaultNorm[MaxML + 1] = {1, 4, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1}; -+#define ML_DEFAULTNORMLOG 6 /* for static allocation */ -+static const U32 ML_defaultNormLog = ML_DEFAULTNORMLOG; -+ -+static const S16 OF_defaultNorm[MaxOff + 1] = {1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1}; -+#define OF_DEFAULTNORMLOG 5 /* for static allocation */ -+static const U32 OF_defaultNormLog = OF_DEFAULTNORMLOG; -+ -+/*-******************************************* -+* Shared functions to include for inlining -+*********************************************/ -+ZSTD_STATIC void ZSTD_copy8(void *dst, const void *src) { -+ /* -+ * zstd relies heavily on gcc being able to analyze and inline this -+ * memcpy() call, since it is called in a tight loop. Preboot mode -+ * is compiled in freestanding mode, which stops gcc from analyzing -+ * memcpy(). Use __builtin_memcpy() to tell gcc to analyze this as a -+ * regular memcpy(). -+ */ -+ __builtin_memcpy(dst, src, 8); -+} -+/*! ZSTD_wildcopy() : -+* custom version of memcpy(), can copy up to 7 bytes too many (8 bytes if length==0) */ -+#define WILDCOPY_OVERLENGTH 8 -+ZSTD_STATIC void ZSTD_wildcopy(void *dst, const void *src, ptrdiff_t length) -+{ -+ const BYTE* ip = (const BYTE*)src; -+ BYTE* op = (BYTE*)dst; -+ BYTE* const oend = op + length; -+#if defined(GCC_VERSION) && GCC_VERSION >= 70000 && GCC_VERSION < 70200 -+ /* -+ * Work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81388. -+ * Avoid the bad case where the loop only runs once by handling the -+ * special case separately. This doesn't trigger the bug because it -+ * doesn't involve pointer/integer overflow. -+ */ -+ if (length <= 8) -+ return ZSTD_copy8(dst, src); -+#endif -+ do { -+ ZSTD_copy8(op, ip); -+ op += 8; -+ ip += 8; -+ } while (op < oend); -+} -+ -+/*-******************************************* -+* Private interfaces -+*********************************************/ -+typedef struct ZSTD_stats_s ZSTD_stats_t; -+ -+typedef struct { -+ U32 off; -+ U32 len; -+} ZSTD_match_t; -+ -+typedef struct { -+ U32 price; -+ U32 off; -+ U32 mlen; -+ U32 litlen; -+ U32 rep[ZSTD_REP_NUM]; -+} ZSTD_optimal_t; -+ -+typedef struct seqDef_s { -+ U32 offset; -+ U16 litLength; -+ U16 matchLength; -+} seqDef; -+ -+typedef struct { -+ seqDef *sequencesStart; -+ seqDef *sequences; -+ BYTE *litStart; -+ BYTE *lit; -+ BYTE *llCode; -+ BYTE *mlCode; -+ BYTE *ofCode; -+ U32 longLengthID; /* 0 == no longLength; 1 == Lit.longLength; 2 == Match.longLength; */ -+ U32 longLengthPos; -+ /* opt */ -+ ZSTD_optimal_t *priceTable; -+ ZSTD_match_t *matchTable; -+ U32 *matchLengthFreq; -+ U32 *litLengthFreq; -+ U32 *litFreq; -+ U32 *offCodeFreq; -+ U32 matchLengthSum; -+ U32 matchSum; -+ U32 litLengthSum; -+ U32 litSum; -+ U32 offCodeSum; -+ U32 log2matchLengthSum; -+ U32 log2matchSum; -+ U32 log2litLengthSum; -+ U32 log2litSum; -+ U32 log2offCodeSum; -+ U32 factor; -+ U32 staticPrices; -+ U32 cachedPrice; -+ U32 cachedLitLength; -+ const BYTE *cachedLiterals; -+} seqStore_t; -+ -+const seqStore_t *ZSTD_getSeqStore(const ZSTD_CCtx *ctx); -+void ZSTD_seqToCodes(const seqStore_t *seqStorePtr); -+int ZSTD_isSkipFrame(ZSTD_DCtx *dctx); -+ -+/*= Custom memory allocation functions */ -+typedef void *(*ZSTD_allocFunction)(void *opaque, size_t size); -+typedef void (*ZSTD_freeFunction)(void *opaque, void *address); -+typedef struct { -+ ZSTD_allocFunction customAlloc; -+ ZSTD_freeFunction customFree; -+ void *opaque; -+} ZSTD_customMem; -+ -+void *ZSTD_malloc(size_t size, ZSTD_customMem customMem); -+void ZSTD_free(void *ptr, ZSTD_customMem customMem); -+ -+/*====== stack allocation ======*/ -+ -+typedef struct { -+ void *ptr; -+ const void *end; -+} ZSTD_stack; -+ -+#define ZSTD_ALIGN(x) ALIGN(x, sizeof(size_t)) -+#define ZSTD_PTR_ALIGN(p) PTR_ALIGN(p, sizeof(size_t)) -+ -+ZSTD_customMem ZSTD_initStack(void *workspace, size_t workspaceSize); -+ -+void *ZSTD_stackAllocAll(void *opaque, size_t *size); -+void *ZSTD_stackAlloc(void *opaque, size_t size); -+void ZSTD_stackFree(void *opaque, void *address); -+ -+/*====== common function ======*/ -+ -+ZSTD_STATIC U32 ZSTD_highbit32(U32 val) { return 31 - __builtin_clz(val); } -+ -+/* hidden functions */ -+ -+/* ZSTD_invalidateRepCodes() : -+ * ensures next compression will not use repcodes from previous block. -+ * Note : only works with regular variant; -+ * do not use with extDict variant ! */ -+void ZSTD_invalidateRepCodes(ZSTD_CCtx *cctx); -+ -+size_t ZSTD_freeCCtx(ZSTD_CCtx *cctx); -+size_t ZSTD_freeDCtx(ZSTD_DCtx *dctx); -+size_t ZSTD_freeCDict(ZSTD_CDict *cdict); -+size_t ZSTD_freeDDict(ZSTD_DDict *cdict); -+size_t ZSTD_freeCStream(ZSTD_CStream *zcs); -+size_t ZSTD_freeDStream(ZSTD_DStream *zds); -+ -+#endif /* ZSTD_CCOMMON_H_MODULE */ -diff --git a/xen/include/asm-arm/types.h b/xen/include/asm-arm/types.h -index 89aae25ffe..083acbd151 100644 ---- a/xen/include/asm-arm/types.h -+++ b/xen/include/asm-arm/types.h -@@ -61,6 +61,12 @@ typedef unsigned long size_t; - #endif - typedef signed long ssize_t; - -+#if defined(__PTRDIFF_TYPE__) -+typedef __PTRDIFF_TYPE__ ptrdiff_t; -+#else -+typedef signed long ptrdiff_t; -+#endif -+ - #endif /* __ASSEMBLY__ */ - - #endif /* __ARM_TYPES_H__ */ -diff --git a/xen/include/asm-x86/types.h b/xen/include/asm-x86/types.h -index fdf4f7dcc0..7817132048 100644 ---- a/xen/include/asm-x86/types.h -+++ b/xen/include/asm-x86/types.h -@@ -39,6 +39,12 @@ typedef unsigned long size_t; - #endif - typedef signed long ssize_t; - -+#if defined(__PTRDIFF_TYPE__) -+typedef __PTRDIFF_TYPE__ ptrdiff_t; -+#else -+typedef signed long ptrdiff_t; -+#endif -+ - #endif /* __ASSEMBLY__ */ - - #endif /* __X86_TYPES_H__ */ -diff --git a/xen/include/xen/decompress.h b/xen/include/xen/decompress.h -index b2955faa4b..f5bc17f2b6 100644 ---- a/xen/include/xen/decompress.h -+++ b/xen/include/xen/decompress.h -@@ -31,7 +31,7 @@ typedef int decompress_fn(unsigned char *inbuf, unsigned int len, - * dependent). - */ - --decompress_fn bunzip2, unxz, unlzma, unlzo, unlz4; -+decompress_fn bunzip2, unxz, unlzma, unlzo, unlz4, unzstd; - - int decompress(void *inbuf, unsigned int len, void *outbuf); - --- -2.20.1 - diff --git a/xen.git-d8099d94dfaa3573bd86ebfc457cbc8f70a3ecda.patch b/xen.git-d8099d94dfaa3573bd86ebfc457cbc8f70a3ecda.patch deleted file mode 100644 index 8437382..0000000 --- a/xen.git-d8099d94dfaa3573bd86ebfc457cbc8f70a3ecda.patch +++ /dev/null @@ -1,105 +0,0 @@ -From d8099d94dfaa3573bd86ebfc457cbc8f70a3ecda Mon Sep 17 00:00:00 2001 -From: Jan Beulich -Date: Tue, 26 Jan 2021 14:14:39 +0100 -Subject: [PATCH] libxenguest: add get_unaligned_le32() - -Abstract xc_dom_check_gzip()'s reading of the uncompressed size into a -helper re-usable, in particular, by other decompressor code. - -Sadly in the mini-os case this conflicts with other functions of the -same name (and purpose), which can't be easily replaced individually. -Yet it was requested that no full set of helpers be introduced at this -point in the release cycle. Hence the awkward XG_NEED_UNALIGNED. - -Requested-by: Ian Jackson -Signed-off-by: Jan Beulich -Reviewed-by: Ian Jackson -Release-Acked-by: Ian Jackson ---- - tools/libs/guest/xg_dom_core.c | 5 ++--- - tools/libs/guest/xg_dom_decompress_lz4.c | 1 + - tools/libs/guest/xg_private.h | 9 +++++++++ - xen/common/lz4/defs.h | 5 ----- - 4 files changed, 12 insertions(+), 8 deletions(-) - -diff --git a/tools/libs/guest/xg_dom_core.c b/tools/libs/guest/xg_dom_core.c -index f846d8e1ed..98ef8e8fc9 100644 ---- a/tools/libxc/xc_dom_core.c -+++ b/tools/libxc/xc_dom_core.c -@@ -31,6 +31,7 @@ - #include - #include - -+#define XG_NEED_UNALIGNED - #include "xg_private.h" - #include "xc_dom.h" - #include "_paths.h" -@@ -325,7 +326,6 @@ int xc_dom_kernel_check_size(struct xc_dom_image *dom, size_t sz) - - size_t xc_dom_check_gzip(xc_interface *xch, void *blob, size_t ziplen) - { -- unsigned char *gzlen; - size_t unziplen; - - if ( ziplen < 6 ) -@@ -337,8 +337,7 @@ size_t xc_dom_check_gzip(xc_interface *xch, void *blob, size_t ziplen) - /* not gzipped */ - return 0; - -- gzlen = blob + ziplen - 4; -- unziplen = (size_t)gzlen[3] << 24 | gzlen[2] << 16 | gzlen[1] << 8 | gzlen[0]; -+ unziplen = get_unaligned_le32(blob + ziplen - 4); - if ( unziplen > XC_DOM_DECOMPRESS_MAX ) - { - xc_dom_printf -diff --git a/tools/libs/guest/xg_dom_decompress_lz4.c b/tools/libs/guest/xg_dom_decompress_lz4.c -index 97ba620d86..34a1a13d84 100644 ---- a/tools/libxc/xc_dom_decompress_lz4.c -+++ b/tools/libxc/xc_dom_decompress_lz4.c -@@ -3,6 +3,7 @@ - #include - #include - -+#define XG_NEED_UNALIGNED - #include "xg_private.h" - #include "xc_dom_decompress.h" - -diff --git a/tools/libs/guest/xg_private.h b/tools/libs/guest/xg_private.h -index c3ed8c1257..8f9b257a2f 100644 ---- a/tools/libxc/xg_private.h -+++ b/tools/libxc/xg_private.h -@@ -62,6 +62,15 @@ char *xc_inflate_buffer(xc_interface *xch, - unsigned long in_size, - unsigned long *out_size); - -+#if !defined(__MINIOS__) || defined(XG_NEED_UNALIGNED) -+ -+static inline unsigned int get_unaligned_le32(const uint8_t *buf) -+{ -+ return ((unsigned int)buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; -+} -+ -+#endif /* !__MINIOS__ || XG_NEED_UNALIGNED */ -+ - unsigned long csum_page (void * page); - - #define _PAGE_PRESENT 0x001 -diff --git a/xen/common/lz4/defs.h b/xen/common/lz4/defs.h -index 4fbea2ac3d..10609f5a53 100644 ---- a/xen/common/lz4/defs.h -+++ b/xen/common/lz4/defs.h -@@ -18,11 +18,6 @@ static inline u16 get_unaligned_le16(const void *p) - return le16_to_cpup(p); - } - --static inline u32 get_unaligned_le32(const void *p) --{ -- return le32_to_cpup(p); --} -- - #endif - - /* --- -2.20.1 - diff --git a/xen.ocaml.4.12.fixes.patch b/xen.ocaml.4.12.fixes.patch index a203514..640d6cc 100644 --- a/xen.ocaml.4.12.fixes.patch +++ b/xen.ocaml.4.12.fixes.patch @@ -1,33 +1,42 @@ ---- xen-4.14.1/tools/ocaml/libs/xentoollog/xentoollog_stubs.c.orig 2020-12-17 16:47:25.000000000 +0000 -+++ xen-4.14.1/tools/ocaml/libs/xentoollog/xentoollog_stubs.c 2021-03-01 22:53:23.106205483 +0000 -@@ -53,8 +53,6 @@ - #include "_xtl_levels.inc" - - /* Option type support as per http://www.linux-nantes.org/~fmonnier/ocaml/ocaml-wrapping-c.php */ --#define Val_none Val_int(0) --#define Some_val(v) Field(v,0) - - static value Val_some(value v) - { ---- xen-4.14.1/tools/ocaml/libs/xl/xenlight_stubs.c.orig 2020-12-17 16:47:25.000000000 +0000 -+++ xen-4.14.1/tools/ocaml/libs/xl/xenlight_stubs.c 2021-03-01 23:01:24.229859714 +0000 -@@ -227,8 +227,6 @@ - } - - /* Option type support as per http://www.linux-nantes.org/~fmonnier/ocaml/ocaml-wrapping-c.php */ --#define Val_none Val_int(0) --#define Some_val(v) Field(v,0) - - static value Val_some(value v) - { ---- xen-4.14.1/tools/ocaml/libs/xc/xenctrl_stubs.c.orig 2020-12-17 16:47:25.000000000 +0000 -+++ xen-4.14.1/tools/ocaml/libs/xc/xenctrl_stubs.c 2021-03-01 23:24:45.477290290 +0000 -@@ -43,8 +43,6 @@ +--- xen-4.15.0/tools/ocaml/libs/xc/xenctrl_stubs.c.orig 2021-04-06 18:14:18.000000000 +0100 ++++ xen-4.15.0/tools/ocaml/libs/xc/xenctrl_stubs.c 2021-04-11 21:38:22.684624663 +0100 +@@ -43,7 +43,9 @@ #define _H(__h) ((xc_interface *)(__h)) #define _D(__d) ((uint32_t)Int_val(__d)) --#define Val_none (Val_int(0)) -- ++#ifndef Val_none + #define Val_none (Val_int(0)) ++#endif + #define string_of_option_array(array, index) \ ((Field(array, index) == Val_none) ? NULL : String_val(Field(Field(array, index), 0))) +--- xen-4.15.0/tools/ocaml/libs/xl/xenlight_stubs.c.orig 2021-04-06 18:14:18.000000000 +0100 ++++ xen-4.15.0/tools/ocaml/libs/xl/xenlight_stubs.c 2021-04-11 21:40:41.651115504 +0100 +@@ -227,8 +227,12 @@ + } + /* Option type support as per http://www.linux-nantes.org/~fmonnier/ocaml/ocaml-wrapping-c.php */ ++#ifndef Val_none + #define Val_none Val_int(0) ++#endif ++#ifndef Some_val + #define Some_val(v) Field(v,0) ++#endif + + static value Val_some(value v) + { +--- xen-4.15.0/tools/ocaml/libs/xentoollog/xentoollog_stubs.c.orig 2021-04-06 18:14:18.000000000 +0100 ++++ xen-4.15.0/tools/ocaml/libs/xentoollog/xentoollog_stubs.c 2021-04-11 21:44:26.659529408 +0100 +@@ -53,8 +53,12 @@ + #include "_xtl_levels.inc" + + /* Option type support as per http://www.linux-nantes.org/~fmonnier/ocaml/ocaml-wrapping-c.php */ ++#ifndef Val_none + #define Val_none Val_int(0) ++#endif ++#ifndef Some_val + #define Some_val(v) Field(v,0) ++#endif + + static value Val_some(value v) + { diff --git a/xen.spec b/xen.spec index 89d63c9..2ef5254 100644 --- a/xen.spec +++ b/xen.spec @@ -53,12 +53,12 @@ %endif # Hypervisor ABI -%define hv_abi 4.14 +%define hv_abi 4.15 Summary: Xen is a virtual machine monitor Name: xen -Version: 4.14.1 -Release: 8%{?dist} +Version: 4.15.0 +Release: 1%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -112,18 +112,8 @@ Patch40: xen.drop.brctl.patch Patch41: xen.gcc9.fixes.patch Patch42: xen.gcc10.fixes.patch Patch43: xen.gcc11.fixes.patch -Patch44: xsa360-4.14.patch -Patch45: xen.git-7c9f81687ad611515474b1c17afc2f79f19faef5.patch -Patch46: xen.git-35d2960ae65f28106fdc5c2130f5f08fadca0e4c.patch -Patch47: xen.git-d6627cf1b63ce57a6a7e2c1800dbc50eed742c32.patch -Patch48: xen.git-d8099d94dfaa3573bd86ebfc457cbc8f70a3ecda.patch -Patch49: xen.git-8169f82049efb5b2044b33aa482ba3a136b7804d.patch -Patch50: xsa363.patch -Patch51: xsa364.patch -Patch52: xsa368-4.14.patch +Patch44: xen.ocaml.4.12.fixes.patch -Patch100: xen.ocaml.4.12.fixes.patch -Patch101: xen.efi.build.fix.patch %if %build_qemutrad BuildRequires: libidn-devel zlib-devel SDL-devel curl-devel @@ -332,17 +322,6 @@ manage Xen virtual machines. %patch42 -p1 %patch43 -p1 %patch44 -p1 -%patch45 -p1 -%patch46 -p1 -%patch47 -p1 -%patch48 -p1 -%patch49 -p1 -%patch50 -p1 -%patch51 -p1 -%patch52 -p1 - -%patch100 -p1 -%patch101 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -619,12 +598,13 @@ do_it() { if [ ! -d $TARGET ]; then mkdir $TARGET fi - if [ -f $DIR/relocator.mod -a ! -f $TARGET/relocator.mod ]; then - cp -p $DIR/relocator.mod $TARGET/relocator.mod - fi - if [ -f $DIR/multiboot2.mod -a ! -f $TARGET/multiboot2.mod ]; then - cp -p $DIR/multiboot2.mod $TARGET/multiboot2.mod - fi + for m in relocator.mod multiboot2.mod; do + if [ -f $DIR/$m ]; then + if [ ! -f $TARGET/$m ] || ! cmp -s $DIR/$m $TARGET/$m; then + cp -p $DIR/$m $TARGET/$m + fi + fi + done fi } if [ $1 == 1 -a -f /sbin/grub2-mkconfig ]; then @@ -699,11 +679,11 @@ fi %{_libdir}/libxencall.so.1.2 %{_libdir}/libxenctrl.so.4.* %{_libdir}/libxendevicemodel.so.1 -%{_libdir}/libxendevicemodel.so.1.3 +%{_libdir}/libxendevicemodel.so.1.4 %{_libdir}/libxenevtchn.so.1 -%{_libdir}/libxenevtchn.so.1.1 +%{_libdir}/libxenevtchn.so.1.2 %{_libdir}/libxenforeignmemory.so.1 -%{_libdir}/libxenforeignmemory.so.1.3 +%{_libdir}/libxenforeignmemory.so.1.4 %{_libdir}/libxenfsimage.so.4.* %{_libdir}/libxengnttab.so.1 %{_libdir}/libxengnttab.so.1.2 @@ -730,7 +710,7 @@ fi %dir %attr(0700,root,root) %{_sysconfdir}/%{name}/scripts/ %config %attr(0700,root,root) %{_sysconfdir}/%{name}/scripts/* -%{_sysconfdir}/bash_completion.d/xl.sh +%{_sysconfdir}/bash_completion.d/xl %{_unitdir}/proc-xen.mount %{_unitdir}/var-lib-xenstored.mount @@ -782,6 +762,7 @@ fi %{_mandir}/man7/xl-numa-placement.7.gz %{_mandir}/man1/xenhypfs.1.gz %{_mandir}/man7/xen-vbd-interface.7.gz +%{_mandir}/man5/xl-pci-configuration.5.gz %endif %{python3_sitearch}/xenfsimage*.so @@ -803,6 +784,7 @@ fi %{_libexecdir}/xen/boot/ioemu-stubdom.gz %{_libexecdir}/xen/boot/xenstore-stubdom.gz %{_libexecdir}/xen/boot/pv-grub*.gz +%{_libexecdir}/xen/boot/xenstorepvh-stubdom.gz %endif %endif %if "%{_libdir}" != "/usr/lib" @@ -873,6 +855,8 @@ fi %endif %{_bindir}/vchan-socket-proxy %{_sbindir}/xenhypfs +%{_sbindir}/xen-access +%{_sbindir}/xen-memshare # Xen logfiles %dir %attr(0700,root,root) %{_localstatedir}/log/xen @@ -935,6 +919,18 @@ fi %endif %changelog +* Wed May 05 2021 Michael Young - 4.15.0-1 +- update to xen-4.15.0 + adjust xen.canonicalize.patch + remove or adjust patches now included or superceded upstream + renumber patch + update libxendevicemodel libxenevtchn libxenforeignmemory versions + /etc/bash_completion.d/xl.sh is now xl + package xen-access xen-memshare xenstorepvh-stubdom.gz + xl-pci-configuration.5.gz +- adjust xen.ocaml.4.12.fixes.patch to work with earlier ocaml +- re-copy grub modules if they have changed + * Fri Mar 19 2021 Michael Young - 4.14.1-8 - HVM soft-reset crashes toolstack [XSA-368, CVE-2021-28687] (#1940610) - adjust efi test to stop build failing diff --git a/xsa360-4.14.patch b/xsa360-4.14.patch deleted file mode 100644 index 1bc185b..0000000 --- a/xsa360-4.14.patch +++ /dev/null @@ -1,97 +0,0 @@ -From: Roger Pau Monne -Subject: x86/dpci: do not remove pirqs from domain tree on unbind - -A fix for a previous issue removed the pirqs from the domain tree when -they are unbound in order to prevent shared pirqs from triggering a -BUG_ON in __pirq_guest_unbind if they are unbound multiple times. That -caused free_domain_pirqs to no longer unmap the pirqs because they -are gone from the domain pirq tree, thus leaving stale unbound pirqs -after domain destruction if the domain had mapped dpci pirqs after -shutdown. - -Take a different approach to fix the original issue, instead of -removing the pirq from d->pirq_tree clear the flags of the dpci pirq -struct to signal that the pirq is now unbound. This prevents calling -pirq_guest_unbind multiple times for the same pirq without having to -remove it from the domain pirq tree. - -This is XSA-360. - -Fixes: 5b58dad089 ('x86/pass-through: avoid double IRQ unbind during domain cleanup') -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich - ---- a/xen/arch/x86/irq.c -+++ b/xen/arch/x86/irq.c -@@ -1331,7 +1331,7 @@ void (pirq_cleanup_check)(struct pirq *p - } - - if ( radix_tree_delete(&d->pirq_tree, pirq->pirq) != pirq ) -- BUG_ON(!d->is_dying); -+ BUG(); - } - - /* Flush all ready EOIs from the top of this CPU's pending-EOI stack. */ ---- a/xen/drivers/passthrough/pci.c -+++ b/xen/drivers/passthrough/pci.c -@@ -862,6 +862,10 @@ static int pci_clean_dpci_irq(struct dom - { - struct dev_intx_gsi_link *digl, *tmp; - -+ if ( !pirq_dpci->flags ) -+ /* Already processed. */ -+ return 0; -+ - pirq_guest_unbind(d, dpci_pirq(pirq_dpci)); - - if ( pt_irq_need_timer(pirq_dpci->flags) ) -@@ -872,15 +876,10 @@ static int pci_clean_dpci_irq(struct dom - list_del(&digl->list); - xfree(digl); - } -+ /* Note the pirq is now unbound. */ -+ pirq_dpci->flags = 0; - -- radix_tree_delete(&d->pirq_tree, dpci_pirq(pirq_dpci)->pirq); -- -- if ( !pt_pirq_softirq_active(pirq_dpci) ) -- return 0; -- -- domain_get_irq_dpci(d)->pending_pirq_dpci = pirq_dpci; -- -- return -ERESTART; -+ return pt_pirq_softirq_active(pirq_dpci) ? -ERESTART : 0; - } - - static int pci_clean_dpci_irqs(struct domain *d) -@@ -897,18 +896,8 @@ static int pci_clean_dpci_irqs(struct do - hvm_irq_dpci = domain_get_irq_dpci(d); - if ( hvm_irq_dpci != NULL ) - { -- int ret = 0; -- -- if ( hvm_irq_dpci->pending_pirq_dpci ) -- { -- if ( pt_pirq_softirq_active(hvm_irq_dpci->pending_pirq_dpci) ) -- ret = -ERESTART; -- else -- hvm_irq_dpci->pending_pirq_dpci = NULL; -- } -+ int ret = pt_pirq_iterate(d, pci_clean_dpci_irq, NULL); - -- if ( !ret ) -- ret = pt_pirq_iterate(d, pci_clean_dpci_irq, NULL); - if ( ret ) - { - spin_unlock(&d->event_lock); ---- a/xen/include/asm-x86/hvm/irq.h -+++ b/xen/include/asm-x86/hvm/irq.h -@@ -160,8 +160,6 @@ struct hvm_irq_dpci { - DECLARE_BITMAP(isairq_map, NR_ISAIRQS); - /* Record of mapped Links */ - uint8_t link_cnt[NR_LINK]; -- /* Clean up: Entry with a softirq invocation pending / in progress. */ -- struct hvm_pirq_dpci *pending_pirq_dpci; - }; - - /* Machine IRQ to guest device/intx mapping. */ diff --git a/xsa363.patch b/xsa363.patch deleted file mode 100644 index c8a3de3..0000000 --- a/xsa363.patch +++ /dev/null @@ -1,22 +0,0 @@ -From: Jan Beulich -Subject: SUPPORT.md: PV display frontend is unsupported in "backend allocation" mode - -This wasn't meant to be supported, but wasn't stated this way. - -This is XSA-363. - -Reported-by: Jan Belich -Signed-off-by: Jan Beulich - ---- a/SUPPORT.md -+++ b/SUPPORT.md -@@ -414,7 +414,8 @@ Guest-side driver capable of speaking th - - Guest-side driver capable of speaking the Xen PV display protocol - -- Status, Linux: Supported -+ Status, Linux: Supported (outside of "backend allocation" mode) -+ Status, Linux: Experimental (in "backend allocation" mode) - - ### PV Console (frontend) - diff --git a/xsa364.patch b/xsa364.patch deleted file mode 100644 index 2d4b057..0000000 --- a/xsa364.patch +++ /dev/null @@ -1,69 +0,0 @@ -From dadb5b4b21c904ce59024c686eb1c55be8f46c52 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Thu, 21 Jan 2021 10:16:08 +0000 -Subject: [PATCH] xen/page_alloc: Only flush the page to RAM once we know they - are scrubbed - -At the moment, each page are flushed to RAM just after the allocator -found some free pages. However, this is happening before check if the -page was scrubbed. - -As a consequence, on Arm, a guest may be able to access the old content -of the scrubbed pages if it has cache disabled (default at boot) and -the content didn't reach the Point of Coherency. - -The flush is now moved after we know the content of the page will not -change. This also has the benefit to reduce the amount of work happening -with the heap_lock held. - -This is XSA-364. - -Fixes: 307c3be3ccb2 ("mm: Don't scrub pages while holding heap lock in alloc_heap_pages()") -Signed-off-by: Julien Grall -Reviewed-by: Jan Beulich ---- - xen/common/page_alloc.c | 14 +++++++++----- - 1 file changed, 9 insertions(+), 5 deletions(-) - -diff --git a/xen/common/page_alloc.c b/xen/common/page_alloc.c -index 02ac1fa613e7..1744e6faa5c4 100644 ---- a/xen/common/page_alloc.c -+++ b/xen/common/page_alloc.c -@@ -924,6 +924,7 @@ static struct page_info *alloc_heap_pages( - bool need_tlbflush = false; - uint32_t tlbflush_timestamp = 0; - unsigned int dirty_cnt = 0; -+ mfn_t mfn; - - /* Make sure there are enough bits in memflags for nodeID. */ - BUILD_BUG_ON((_MEMF_bits - _MEMF_node) < (8 * sizeof(nodeid_t))); -@@ -1022,11 +1023,6 @@ static struct page_info *alloc_heap_pages( - pg[i].u.inuse.type_info = 0; - page_set_owner(&pg[i], NULL); - -- /* Ensure cache and RAM are consistent for platforms where the -- * guest can control its own visibility of/through the cache. -- */ -- flush_page_to_ram(mfn_x(page_to_mfn(&pg[i])), -- !(memflags & MEMF_no_icache_flush)); - } - - spin_unlock(&heap_lock); -@@ -1062,6 +1058,14 @@ static struct page_info *alloc_heap_pages( - if ( need_tlbflush ) - filtered_flush_tlb_mask(tlbflush_timestamp); - -+ /* -+ * Ensure cache and RAM are consistent for platforms where the guest -+ * can control its own visibility of/through the cache. -+ */ -+ mfn = page_to_mfn(pg); -+ for ( i = 0; i < (1U << order); i++ ) -+ flush_page_to_ram(mfn_x(mfn) + i, !(memflags & MEMF_no_icache_flush)); -+ - return pg; - } - --- -2.17.1 - diff --git a/xsa368-4.14.patch b/xsa368-4.14.patch deleted file mode 100644 index 815c756..0000000 --- a/xsa368-4.14.patch +++ /dev/null @@ -1,112 +0,0 @@ -From b1d5e033df1858edd6fa328abd126522947440aa Mon Sep 17 00:00:00 2001 -From: Anthony PERARD -Date: Wed, 24 Feb 2021 18:39:20 +0000 -Subject: [PATCH] libxl: Fix domain soft reset state handling - -In do_domain_soft_reset(), a `libxl__domain_suspend_state' is used -without been properly initialised and disposed of. This lead do a -abort() in libxl due to the `dsps.qmp' state been used before been -initialised: - libxl__ev_qmp_send: Assertion `ev->state == qmp_state_disconnected || ev->state == qmp_state_connected' failed. - -Once initialised, `dsps' also needs to be disposed of as the `qmp' -state might still be in the `Connected' state in the callback for -libxl__domain_suspend_device_model(). So this patch adds -libxl__domain_suspend_dispose() which can be called from the two -places where we need to dispose of `dsps'. - -Reported-by: Olaf Hering -Signed-off-by: Anthony PERARD -Reviewed-by: Ian Jackson -Tested-by: Olaf Hering ---- - tools/libxl/libxl_create.c | 11 ++++++++--- - tools/libxl/libxl_dom_suspend.c | 15 +++++++++++---- - tools/libxl/libxl_internal.h | 2 ++ - 3 files changed, 21 insertions(+), 7 deletions(-) - -diff --git a/tools/libxl/libxl_create.c b/tools/libxl/libxl_create.c -index 2814818e34..83b0eb00bf 100644 ---- a/tools/libxl/libxl_create.c -+++ b/tools/libxl/libxl_create.c -@@ -2174,9 +2174,7 @@ static int do_domain_soft_reset(libxl_ctx *ctx, - state->console_tty = libxl__strdup(gc, console_tty); - - dss->ao = ao; -- dss->domid = dss->dsps.domid = domid; -- dss->dsps.dm_savefile = GCSPRINTF(LIBXL_DEVICE_MODEL_SAVE_FILE".%d", -- domid); -+ dss->domid = domid; - - rc = libxl__save_emulator_xenstore_data(dss, &srs->toolstack_buf, - &srs->toolstack_len); -@@ -2186,6 +2184,11 @@ static int do_domain_soft_reset(libxl_ctx *ctx, - } - - dss->dsps.ao = ao; -+ dss->dsps.domid = domid; -+ dss->dsps.live = false; -+ rc = libxl__domain_suspend_init(egc, &dss->dsps, d_config->b_info.type); -+ if (rc) -+ goto out; - dss->dsps.callback_device_model_done = soft_reset_dm_suspended; - libxl__domain_suspend_device_model(egc, &dss->dsps); /* must be last */ - -@@ -2204,6 +2207,8 @@ static void soft_reset_dm_suspended(libxl__egc *egc, - CONTAINER_OF(dsps, *srs, dss.dsps); - libxl__app_domain_create_state *cdcs = &srs->cdcs; - -+ libxl__domain_suspend_dispose(gc, dsps); -+ - /* - * Ask all backends to disconnect by removing the domain from - * xenstore. On the creation path the domain will be introduced to -diff --git a/tools/libxl/libxl_dom_suspend.c b/tools/libxl/libxl_dom_suspend.c -index 25d1571895..2a280f69a1 100644 ---- a/tools/libxl/libxl_dom_suspend.c -+++ b/tools/libxl/libxl_dom_suspend.c -@@ -67,6 +67,16 @@ out: - return rc; - } - -+void libxl__domain_suspend_dispose(libxl__gc *gc, -+ libxl__domain_suspend_state *dsps) -+{ -+ libxl__xswait_stop(gc, &dsps->pvcontrol); -+ libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn); -+ libxl__ev_xswatch_deregister(gc, &dsps->guest_watch); -+ libxl__ev_time_deregister(gc, &dsps->guest_timeout); -+ libxl__ev_qmp_dispose(gc, &dsps->qmp); -+} -+ - /*----- callbacks, called by xc_domain_save -----*/ - - void libxl__domain_suspend_device_model(libxl__egc *egc, -@@ -388,10 +398,7 @@ static void domain_suspend_common_done(libxl__egc *egc, - { - EGC_GC; - assert(!libxl__xswait_inuse(&dsps->pvcontrol)); -- libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn); -- libxl__ev_xswatch_deregister(gc, &dsps->guest_watch); -- libxl__ev_time_deregister(gc, &dsps->guest_timeout); -- libxl__ev_qmp_dispose(gc, &dsps->qmp); -+ libxl__domain_suspend_dispose(gc, dsps); - dsps->callback_common_done(egc, dsps, rc); - } - -diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h -index 94a23179d3..3bc3bbcf84 100644 ---- a/tools/libxl/libxl_internal.h -+++ b/tools/libxl/libxl_internal.h -@@ -3615,6 +3615,8 @@ struct libxl__domain_suspend_state { - int libxl__domain_suspend_init(libxl__egc *egc, - libxl__domain_suspend_state *dsps, - libxl_domain_type type); -+void libxl__domain_suspend_dispose(libxl__gc *gc, -+ libxl__domain_suspend_state *dsps); - - /* calls dsps->callback_device_model_done when done - * may synchronously calls this callback */ --- -2.30.1 - From e48c1754f3308e0b5c900e075f537a61f40eb9de Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 5 May 2021 23:27:58 +0100 Subject: [PATCH 006/137] xen-memshare doesn't exist on armv7hl and aarch64 --- xen.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 2ef5254..ec9f0e0 100644 --- a/xen.spec +++ b/xen.spec @@ -852,11 +852,11 @@ fi %{_sbindir}/xen-diag %ifnarch armv7hl aarch64 %{_sbindir}/xen-ucode +%{_sbindir}/xen-memshare %endif %{_bindir}/vchan-socket-proxy %{_sbindir}/xenhypfs %{_sbindir}/xen-access -%{_sbindir}/xen-memshare # Xen logfiles %dir %attr(0700,root,root) %{_localstatedir}/log/xen From e525cde8811935f919d867f30d69b57c93f4c701 Mon Sep 17 00:00:00 2001 From: Python Maint Date: Fri, 4 Jun 2021 21:20:24 +0200 Subject: [PATCH 007/137] Rebuilt for Python 3.10 --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index ec9f0e0..15596bc 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.15.0 -Release: 1%{?dist} +Release: 2%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -919,6 +919,9 @@ fi %endif %changelog +* Fri Jun 04 2021 Python Maint - 4.15.0-2 +- Rebuilt for Python 3.10 + * Wed May 05 2021 Michael Young - 4.15.0-1 - update to xen-4.15.0 adjust xen.canonicalize.patch From 84f0fa5dbfe4bcc9ab5af87a170444b2817d1017 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 8 Jun 2021 22:20:24 +0100 Subject: [PATCH 008/137] 4 security updates xen/arm: Boot modules are not scrubbed [XSA-372, CVE-2021-28693] inappropriate x86 IOMMU timeout detection / handling [XSA-373, CVE-2021-28692] Speculative Code Store Bypass [XSA-375, CVE-2021-0089] x86: TSX Async Abort protections not restored after S3 [XSA-377, CVE-2021-28690] --- xen.spec | 28 ++- ...en-arm-Create-dom0less-domUs-earlier.patch | 85 +++++++++ ...ules-should-always-be-scrubbed-if-bo.patch | 59 +++++++ xsa373-4.15-1.patch | 120 +++++++++++++ xsa373-4.15-2.patch | 102 +++++++++++ xsa373-4.15-3.patch | 163 ++++++++++++++++++ xsa373-4.15-4.patch | 79 +++++++++ xsa373-4.15-5.patch | 141 +++++++++++++++ xsa375.patch | 50 ++++++ xsa377.patch | 27 +++ 10 files changed, 853 insertions(+), 1 deletion(-) create mode 100644 xsa372-4.15-0001-xen-arm-Create-dom0less-domUs-earlier.patch create mode 100644 xsa372-4.15-0002-xen-arm-Boot-modules-should-always-be-scrubbed-if-bo.patch create mode 100644 xsa373-4.15-1.patch create mode 100644 xsa373-4.15-2.patch create mode 100644 xsa373-4.15-3.patch create mode 100644 xsa373-4.15-4.patch create mode 100644 xsa373-4.15-5.patch create mode 100644 xsa375.patch create mode 100644 xsa377.patch diff --git a/xen.spec b/xen.spec index 15596bc..7ecc7e0 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.15.0 -Release: 2%{?dist} +Release: 3%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -113,6 +113,15 @@ Patch41: xen.gcc9.fixes.patch Patch42: xen.gcc10.fixes.patch Patch43: xen.gcc11.fixes.patch Patch44: xen.ocaml.4.12.fixes.patch +Patch45: xsa372-4.15-0001-xen-arm-Create-dom0less-domUs-earlier.patch +Patch46: xsa372-4.15-0002-xen-arm-Boot-modules-should-always-be-scrubbed-if-bo.patch +Patch47: xsa373-4.15-1.patch +Patch48: xsa373-4.15-2.patch +Patch49: xsa373-4.15-3.patch +Patch50: xsa373-4.15-4.patch +Patch51: xsa373-4.15-5.patch +Patch52: xsa375.patch +Patch53: xsa377.patch %if %build_qemutrad @@ -322,6 +331,15 @@ manage Xen virtual machines. %patch42 -p1 %patch43 -p1 %patch44 -p1 +%patch45 -p1 +%patch46 -p1 +%patch47 -p1 +%patch48 -p1 +%patch49 -p1 +%patch50 -p1 +%patch51 -p1 +%patch52 -p1 +%patch53 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -919,6 +937,14 @@ fi %endif %changelog +* Tue Jun 08 2021 Michael Young - 4.15.0-3 +- xen/arm: Boot modules are not scrubbed [XSA-372, CVE-2021-28693] +- inappropriate x86 IOMMU timeout detection / handling + [XSA-373, CVE-2021-28692] +- Speculative Code Store Bypass [XSA-375, CVE-2021-0089] +- x86: TSX Async Abort protections not restored after S3 + [XSA-377, CVE-2021-28690] + * Fri Jun 04 2021 Python Maint - 4.15.0-2 - Rebuilt for Python 3.10 diff --git a/xsa372-4.15-0001-xen-arm-Create-dom0less-domUs-earlier.patch b/xsa372-4.15-0001-xen-arm-Create-dom0less-domUs-earlier.patch new file mode 100644 index 0000000..a21dba4 --- /dev/null +++ b/xsa372-4.15-0001-xen-arm-Create-dom0less-domUs-earlier.patch @@ -0,0 +1,85 @@ +From b1e5a89f19d9919c3eae17ab9c6a663b0801ad9c Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 17 May 2021 17:47:13 +0100 +Subject: [PATCH 1/2] xen/arm: Create dom0less domUs earlier + +In a follow-up patch we will need to unallocate the boot modules +before heap_init_late() is called. + +The modules will contain the domUs kernel and initramfs. Therefore Xen +will need to create extra domUs (used by dom0less) before heap_init_late(). + +This has two consequences on dom0less: + 1) Domains will not be unpaused as soon as they are created but + once all have been created. However, Xen doesn't guarantee an order + to unpause, so this is not something one could rely on. + + 2) The memory allocated for a domU will not be scrubbed anymore when an + admin select bootscrub=on. This is not something we advertised, but if + this is a concern we can introduce either force scrub for all domUs or + a per-domain flag in the DT. The behavior for bootscrub=off and + bootscrub=idle (default) has not changed. + +This is part of XSA-372 / CVE-2021-28693. + +Signed-off-by: Julien Grall +Reviewed-by: Jan Beulich +Reviewed-by: Stefano Stabellini +Tested-by: Stefano Stabellini +--- + xen/arch/arm/domain_build.c | 2 -- + xen/arch/arm/setup.c | 11 ++++++----- + 2 files changed, 6 insertions(+), 7 deletions(-) + +diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c +index 374bf655ee34..4203ddcca0e3 100644 +--- a/xen/arch/arm/domain_build.c ++++ b/xen/arch/arm/domain_build.c +@@ -2515,8 +2515,6 @@ void __init create_domUs(void) + + if ( construct_domU(d, node) != 0 ) + panic("Could not set up domain %s\n", dt_node_name(node)); +- +- domain_unpause_by_systemcontroller(d); + } + } + +diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c +index 2532ec973913..441e0e16e9f0 100644 +--- a/xen/arch/arm/setup.c ++++ b/xen/arch/arm/setup.c +@@ -804,7 +804,7 @@ void __init start_xen(unsigned long boot_phys_offset, + int cpus, i; + const char *cmdline; + struct bootmodule *xen_bootmodule; +- struct domain *dom0; ++ struct domain *dom0, *d; + struct xen_domctl_createdomain dom0_cfg = { + .flags = XEN_DOMCTL_CDF_hvm | XEN_DOMCTL_CDF_hap, + .max_evtchn_port = -1, +@@ -987,6 +987,9 @@ void __init start_xen(unsigned long boot_phys_offset, + if ( construct_dom0(dom0) != 0) + panic("Could not set up DOM0 guest OS\n"); + ++ if ( acpi_disabled ) ++ create_domUs(); ++ + heap_init_late(); + + init_trace_bufs(); +@@ -1000,10 +1003,8 @@ void __init start_xen(unsigned long boot_phys_offset, + + system_state = SYS_STATE_active; + +- if ( acpi_disabled ) +- create_domUs(); +- +- domain_unpause_by_systemcontroller(dom0); ++ for_each_domain( d ) ++ domain_unpause_by_systemcontroller(d); + + /* Switch on to the dynamically allocated stack for the idle vcpu + * since the static one we're running on is about to be freed. */ +-- +2.17.1 + diff --git a/xsa372-4.15-0002-xen-arm-Boot-modules-should-always-be-scrubbed-if-bo.patch b/xsa372-4.15-0002-xen-arm-Boot-modules-should-always-be-scrubbed-if-bo.patch new file mode 100644 index 0000000..9c322b1 --- /dev/null +++ b/xsa372-4.15-0002-xen-arm-Boot-modules-should-always-be-scrubbed-if-bo.patch @@ -0,0 +1,59 @@ +From 09bb28bdef3fb5e7d08bdd641601ca0c0d4d82b4 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Sat, 17 Apr 2021 17:38:28 +0100 +Subject: [PATCH 2/2] xen/arm: Boot modules should always be scrubbed if + bootscrub={on, idle} + +The function to initialize the pages (see init_heap_pages()) will request +scrub when the admin request idle bootscrub (default) and state == +SYS_STATE_active. When bootscrub=on, Xen will scrub any free pages in +heap_init_late(). + +Currently, the boot modules (e.g. kernels, initramfs) will be discarded/ +freed after heap_init_late() is called and system_state switched to +SYS_STATE_active. This means the pages associated with the boot modules +will not get scrubbed before getting re-purposed. + +If the memory is assigned to an untrusted domU, it may be able to +retrieve secrets from the modules. + +This is part of XSA-372 / CVE-2021-28693. + +Fixes: 1774e9b1df27 ("xen/arm: introduce create_domUs") +Signed-off-by: Julien Grall +Reviewed-by: Jan Beulich +Reviewed-by: Stefano Stabellini +Tested-by: Stefano Stabellini +--- + xen/arch/arm/setup.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c +index 441e0e16e9f0..8afb78f2c985 100644 +--- a/xen/arch/arm/setup.c ++++ b/xen/arch/arm/setup.c +@@ -72,8 +72,6 @@ domid_t __read_mostly max_init_domid; + + static __used void init_done(void) + { +- discard_initial_modules(); +- + /* Must be done past setting system_state. */ + unregister_init_virtual_region(); + +@@ -990,6 +988,12 @@ void __init start_xen(unsigned long boot_phys_offset, + if ( acpi_disabled ) + create_domUs(); + ++ /* ++ * This needs to be called **before** heap_init_late() so modules ++ * will be scrubbed (unless suppressed). ++ */ ++ discard_initial_modules(); ++ + heap_init_late(); + + init_trace_bufs(); +-- +2.17.1 + diff --git a/xsa373-4.15-1.patch b/xsa373-4.15-1.patch new file mode 100644 index 0000000..ee5229a --- /dev/null +++ b/xsa373-4.15-1.patch @@ -0,0 +1,120 @@ +From: Jan Beulich +Subject: VT-d: size qinval queue dynamically + +With the present synchronous model, we need two slots for every +operation (the operation itself and a wait descriptor). There can be +one such pair of requests pending per CPU. To ensure that under all +normal circumstances a slot is always available when one is requested, +size the queue ring according to the number of present CPUs. + +This is part of XSA-373 / CVE-2021-28692. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/vtd/iommu.h ++++ b/xen/drivers/passthrough/vtd/iommu.h +@@ -450,17 +450,9 @@ struct qinval_entry { + }q; + }; + +-/* Order of queue invalidation pages(max is 8) */ +-#define QINVAL_PAGE_ORDER 2 +- +-#define QINVAL_ARCH_PAGE_ORDER (QINVAL_PAGE_ORDER + PAGE_SHIFT_4K - PAGE_SHIFT) +-#define QINVAL_ARCH_PAGE_NR ( QINVAL_ARCH_PAGE_ORDER < 0 ? \ +- 1 : \ +- 1 << QINVAL_ARCH_PAGE_ORDER ) +- + /* Each entry is 16 bytes, so 2^8 entries per page */ + #define QINVAL_ENTRY_ORDER ( PAGE_SHIFT - 4 ) +-#define QINVAL_ENTRY_NR (1 << (QINVAL_PAGE_ORDER + 8)) ++#define QINVAL_MAX_ENTRY_NR (1u << (7 + QINVAL_ENTRY_ORDER)) + + /* Status data flag */ + #define QINVAL_STAT_INIT 0 +--- a/xen/drivers/passthrough/vtd/qinval.c ++++ b/xen/drivers/passthrough/vtd/qinval.c +@@ -31,6 +31,9 @@ + + #define VTD_QI_TIMEOUT 1 + ++static unsigned int __read_mostly qi_pg_order; ++static unsigned int __read_mostly qi_entry_nr; ++ + static int __must_check invalidate_sync(struct vtd_iommu *iommu); + + static void print_qi_regs(struct vtd_iommu *iommu) +@@ -55,7 +58,7 @@ static unsigned int qinval_next_index(st + tail >>= QINVAL_INDEX_SHIFT; + + /* (tail+1 == head) indicates a full queue, wait for HW */ +- while ( ( tail + 1 ) % QINVAL_ENTRY_NR == ++ while ( ((tail + 1) & (qi_entry_nr - 1)) == + ( dmar_readq(iommu->reg, DMAR_IQH_REG) >> QINVAL_INDEX_SHIFT ) ) + cpu_relax(); + +@@ -68,7 +71,7 @@ static void qinval_update_qtail(struct v + + /* Need hold register lock when update tail */ + ASSERT( spin_is_locked(&iommu->register_lock) ); +- val = (index + 1) % QINVAL_ENTRY_NR; ++ val = (index + 1) & (qi_entry_nr - 1); + dmar_writeq(iommu->reg, DMAR_IQT_REG, (val << QINVAL_INDEX_SHIFT)); + } + +@@ -403,8 +406,28 @@ int enable_qinval(struct vtd_iommu *iomm + + if ( iommu->qinval_maddr == 0 ) + { +- iommu->qinval_maddr = alloc_pgtable_maddr(QINVAL_ARCH_PAGE_NR, +- iommu->node); ++ if ( !qi_entry_nr ) ++ { ++ /* ++ * With the present synchronous model, we need two slots for every ++ * operation (the operation itself and a wait descriptor). There ++ * can be one such pair of requests pending per CPU. One extra ++ * entry is needed as the ring is considered full when there's ++ * only one entry left. ++ */ ++ BUILD_BUG_ON(CONFIG_NR_CPUS * 2 >= QINVAL_MAX_ENTRY_NR); ++ qi_pg_order = get_order_from_bytes((num_present_cpus() * 2 + 1) << ++ (PAGE_SHIFT - ++ QINVAL_ENTRY_ORDER)); ++ qi_entry_nr = 1u << (qi_pg_order + QINVAL_ENTRY_ORDER); ++ ++ dprintk(XENLOG_INFO VTDPREFIX, ++ "QI: using %u-entry ring(s)\n", qi_entry_nr); ++ } ++ ++ iommu->qinval_maddr = ++ alloc_pgtable_maddr(qi_entry_nr >> QINVAL_ENTRY_ORDER, ++ iommu->node); + if ( iommu->qinval_maddr == 0 ) + { + dprintk(XENLOG_WARNING VTDPREFIX, +@@ -418,15 +441,16 @@ int enable_qinval(struct vtd_iommu *iomm + + spin_lock_irqsave(&iommu->register_lock, flags); + +- /* Setup Invalidation Queue Address(IQA) register with the +- * address of the page we just allocated. QS field at +- * bits[2:0] to indicate size of queue is one 4KB page. +- * That's 256 entries. Queued Head (IQH) and Queue Tail (IQT) +- * registers are automatically reset to 0 with write +- * to IQA register. ++ /* ++ * Setup Invalidation Queue Address (IQA) register with the address of the ++ * pages we just allocated. The QS field at bits[2:0] indicates the size ++ * (page order) of the queue. ++ * ++ * Queued Head (IQH) and Queue Tail (IQT) registers are automatically ++ * reset to 0 with write to IQA register. + */ + dmar_writeq(iommu->reg, DMAR_IQA_REG, +- iommu->qinval_maddr | QINVAL_PAGE_ORDER); ++ iommu->qinval_maddr | qi_pg_order); + + dmar_writeq(iommu->reg, DMAR_IQT_REG, 0); + diff --git a/xsa373-4.15-2.patch b/xsa373-4.15-2.patch new file mode 100644 index 0000000..d61a3b4 --- /dev/null +++ b/xsa373-4.15-2.patch @@ -0,0 +1,102 @@ +From: Jan Beulich +Subject: AMD/IOMMU: size command buffer dynamically + +With the present synchronous model, we need two slots for every +operation (the operation itself and a wait command). There can be one +such pair of commands pending per CPU. To ensure that under all normal +circumstances a slot is always available when one is requested, size the +command ring according to the number of present CPUs. + +This is part of XSA-373 / CVE-2021-28692. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/amd/iommu-defs.h ++++ b/xen/drivers/passthrough/amd/iommu-defs.h +@@ -20,9 +20,6 @@ + #ifndef AMD_IOMMU_DEFS_H + #define AMD_IOMMU_DEFS_H + +-/* IOMMU Command Buffer entries: in power of 2 increments, minimum of 256 */ +-#define IOMMU_CMD_BUFFER_DEFAULT_ENTRIES 512 +- + /* IOMMU Event Log entries: in power of 2 increments, minimum of 256 */ + #define IOMMU_EVENT_LOG_DEFAULT_ENTRIES 512 + +@@ -164,8 +161,8 @@ struct amd_iommu_dte { + #define IOMMU_CMD_BUFFER_LENGTH_MASK 0x0F000000 + #define IOMMU_CMD_BUFFER_LENGTH_SHIFT 24 + +-#define IOMMU_CMD_BUFFER_ENTRY_SIZE 16 +-#define IOMMU_CMD_BUFFER_POWER_OF2_ENTRIES_PER_PAGE 8 ++#define IOMMU_CMD_BUFFER_ENTRY_ORDER 4 ++#define IOMMU_CMD_BUFFER_MAX_ENTRIES (1u << 15) + + #define IOMMU_CMD_OPCODE_MASK 0xF0000000 + #define IOMMU_CMD_OPCODE_SHIFT 28 +--- a/xen/drivers/passthrough/amd/iommu_cmd.c ++++ b/xen/drivers/passthrough/amd/iommu_cmd.c +@@ -24,7 +24,7 @@ static int queue_iommu_command(struct am + { + uint32_t tail, head; + +- tail = iommu->cmd_buffer.tail + IOMMU_CMD_BUFFER_ENTRY_SIZE; ++ tail = iommu->cmd_buffer.tail + sizeof(cmd_entry_t); + if ( tail == iommu->cmd_buffer.size ) + tail = 0; + +@@ -33,7 +33,7 @@ static int queue_iommu_command(struct am + if ( head != tail ) + { + memcpy(iommu->cmd_buffer.buffer + iommu->cmd_buffer.tail, +- cmd, IOMMU_CMD_BUFFER_ENTRY_SIZE); ++ cmd, sizeof(cmd_entry_t)); + + iommu->cmd_buffer.tail = tail; + return 1; +--- a/xen/drivers/passthrough/amd/iommu_init.c ++++ b/xen/drivers/passthrough/amd/iommu_init.c +@@ -118,7 +118,7 @@ static void register_iommu_cmd_buffer_in + writel(entry, iommu->mmio_base + IOMMU_CMD_BUFFER_BASE_LOW_OFFSET); + + power_of2_entries = get_order_from_bytes(iommu->cmd_buffer.size) + +- IOMMU_CMD_BUFFER_POWER_OF2_ENTRIES_PER_PAGE; ++ PAGE_SHIFT - IOMMU_CMD_BUFFER_ENTRY_ORDER; + + entry = 0; + iommu_set_addr_hi_to_reg(&entry, addr_hi); +@@ -1018,9 +1018,31 @@ static void *__init allocate_ring_buffer + static void * __init allocate_cmd_buffer(struct amd_iommu *iommu) + { + /* allocate 'command buffer' in power of 2 increments of 4K */ ++ static unsigned int __read_mostly nr_ents; ++ ++ if ( !nr_ents ) ++ { ++ unsigned int order; ++ ++ /* ++ * With the present synchronous model, we need two slots for every ++ * operation (the operation itself and a wait command). There can be ++ * one such pair of requests pending per CPU. One extra entry is ++ * needed as the ring is considered full when there's only one entry ++ * left. ++ */ ++ BUILD_BUG_ON(CONFIG_NR_CPUS * 2 >= IOMMU_CMD_BUFFER_MAX_ENTRIES); ++ order = get_order_from_bytes((num_present_cpus() * 2 + 1) << ++ IOMMU_CMD_BUFFER_ENTRY_ORDER); ++ nr_ents = 1u << (order + PAGE_SHIFT - IOMMU_CMD_BUFFER_ENTRY_ORDER); ++ ++ AMD_IOMMU_DEBUG("using %u-entry cmd ring(s)\n", nr_ents); ++ } ++ ++ BUILD_BUG_ON(sizeof(cmd_entry_t) != (1u << IOMMU_CMD_BUFFER_ENTRY_ORDER)); ++ + return allocate_ring_buffer(&iommu->cmd_buffer, sizeof(cmd_entry_t), +- IOMMU_CMD_BUFFER_DEFAULT_ENTRIES, +- "Command Buffer", false); ++ nr_ents, "Command Buffer", false); + } + + static void * __init allocate_event_log(struct amd_iommu *iommu) diff --git a/xsa373-4.15-3.patch b/xsa373-4.15-3.patch new file mode 100644 index 0000000..c7ddf5d --- /dev/null +++ b/xsa373-4.15-3.patch @@ -0,0 +1,163 @@ +From: Jan Beulich +Subject: VT-d: eliminate flush related timeouts + +Leaving an in-progress operation pending when it appears to take too +long is problematic: If e.g. a QI command completed later, the write to +the "poll slot" may instead be understood to signal a subsequently +started command's completion. Also our accounting of the timeout period +was actually wrong: We included the time it took for the command to +actually make it to the front of the queue, which could be heavily +affected by guests other than the one for which the flush is being +performed. + +Do away with all timeout detection on all flush related code paths. +Log excessively long processing times (with a progressive threshold) to +have some indication of problems in this area. + +Additionally log (once) if qinval_next_index() didn't immediately find +an available slot. Together with the earlier change sizing the queue(s) +dynamically, we should now have a guarantee that with our fully +synchronous model any demand for slots can actually be satisfied. + +This is part of XSA-373 / CVE-2021-28692. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/vtd/dmar.h ++++ b/xen/drivers/passthrough/vtd/dmar.h +@@ -127,6 +127,34 @@ do { + } \ + } while (0) + ++#define IOMMU_FLUSH_WAIT(what, iommu, offset, op, cond, sts) \ ++do { \ ++ static unsigned int __read_mostly threshold = 1; \ ++ s_time_t start = NOW(); \ ++ s_time_t timeout = start + DMAR_OPERATION_TIMEOUT * threshold; \ ++ \ ++ for ( ; ; ) \ ++ { \ ++ sts = op(iommu->reg, offset); \ ++ if ( cond ) \ ++ break; \ ++ if ( timeout && NOW() > timeout ) \ ++ { \ ++ threshold |= threshold << 1; \ ++ printk(XENLOG_WARNING VTDPREFIX \ ++ " IOMMU#%u: %s flush taking too long\n", \ ++ iommu->index, what); \ ++ timeout = 0; \ ++ } \ ++ cpu_relax(); \ ++ } \ ++ \ ++ if ( !timeout ) \ ++ printk(XENLOG_WARNING VTDPREFIX \ ++ " IOMMU#%u: %s flush took %lums\n", \ ++ iommu->index, what, (NOW() - start) / 10000000); \ ++} while ( false ) ++ + int vtd_hw_check(void); + void disable_pmr(struct vtd_iommu *iommu); + int is_igd_drhd(struct acpi_drhd_unit *drhd); +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -373,8 +373,8 @@ static void iommu_flush_write_buffer(str + dmar_writel(iommu->reg, DMAR_GCMD_REG, val | DMA_GCMD_WBF); + + /* Make sure hardware complete it */ +- IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, dmar_readl, +- !(val & DMA_GSTS_WBFS), val); ++ IOMMU_FLUSH_WAIT("write buffer", iommu, DMAR_GSTS_REG, dmar_readl, ++ !(val & DMA_GSTS_WBFS), val); + + spin_unlock_irqrestore(&iommu->register_lock, flags); + } +@@ -423,8 +423,8 @@ int vtd_flush_context_reg(struct vtd_iom + dmar_writeq(iommu->reg, DMAR_CCMD_REG, val); + + /* Make sure hardware complete it */ +- IOMMU_WAIT_OP(iommu, DMAR_CCMD_REG, dmar_readq, +- !(val & DMA_CCMD_ICC), val); ++ IOMMU_FLUSH_WAIT("context", iommu, DMAR_CCMD_REG, dmar_readq, ++ !(val & DMA_CCMD_ICC), val); + + spin_unlock_irqrestore(&iommu->register_lock, flags); + /* flush context entry will implicitly flush write buffer */ +@@ -501,8 +501,8 @@ int vtd_flush_iotlb_reg(struct vtd_iommu + dmar_writeq(iommu->reg, tlb_offset + 8, val); + + /* Make sure hardware complete it */ +- IOMMU_WAIT_OP(iommu, (tlb_offset + 8), dmar_readq, +- !(val & DMA_TLB_IVT), val); ++ IOMMU_FLUSH_WAIT("iotlb", iommu, (tlb_offset + 8), dmar_readq, ++ !(val & DMA_TLB_IVT), val); + spin_unlock_irqrestore(&iommu->register_lock, flags); + + /* check IOTLB invalidation granularity */ +--- a/xen/drivers/passthrough/vtd/qinval.c ++++ b/xen/drivers/passthrough/vtd/qinval.c +@@ -29,8 +29,6 @@ + #include "extern.h" + #include "../ats.h" + +-#define VTD_QI_TIMEOUT 1 +- + static unsigned int __read_mostly qi_pg_order; + static unsigned int __read_mostly qi_entry_nr; + +@@ -60,7 +58,11 @@ static unsigned int qinval_next_index(st + /* (tail+1 == head) indicates a full queue, wait for HW */ + while ( ((tail + 1) & (qi_entry_nr - 1)) == + ( dmar_readq(iommu->reg, DMAR_IQH_REG) >> QINVAL_INDEX_SHIFT ) ) ++ { ++ printk_once(XENLOG_ERR VTDPREFIX " IOMMU#%u: no QI slot available\n", ++ iommu->index); + cpu_relax(); ++ } + + return tail; + } +@@ -180,23 +182,32 @@ static int __must_check queue_invalidate + /* Now we don't support interrupt method */ + if ( sw ) + { +- s_time_t timeout; +- +- /* In case all wait descriptor writes to same addr with same data */ +- timeout = NOW() + MILLISECS(flush_dev_iotlb ? +- iommu_dev_iotlb_timeout : VTD_QI_TIMEOUT); ++ static unsigned int __read_mostly threshold = 1; ++ s_time_t start = NOW(); ++ s_time_t timeout = start + (flush_dev_iotlb ++ ? iommu_dev_iotlb_timeout ++ : 100) * MILLISECS(threshold); + + while ( ACCESS_ONCE(*this_poll_slot) != QINVAL_STAT_DONE ) + { +- if ( NOW() > timeout ) ++ if ( timeout && NOW() > timeout ) + { +- print_qi_regs(iommu); ++ threshold |= threshold << 1; + printk(XENLOG_WARNING VTDPREFIX +- " Queue invalidate wait descriptor timed out\n"); +- return -ETIMEDOUT; ++ " IOMMU#%u: QI%s wait descriptor taking too long\n", ++ iommu->index, flush_dev_iotlb ? " dev" : ""); ++ print_qi_regs(iommu); ++ timeout = 0; + } + cpu_relax(); + } ++ ++ if ( !timeout ) ++ printk(XENLOG_WARNING VTDPREFIX ++ " IOMMU#%u: QI%s wait descriptor took %lums\n", ++ iommu->index, flush_dev_iotlb ? " dev" : "", ++ (NOW() - start) / 10000000); ++ + return 0; + } + diff --git a/xsa373-4.15-4.patch b/xsa373-4.15-4.patch new file mode 100644 index 0000000..17592cb --- /dev/null +++ b/xsa373-4.15-4.patch @@ -0,0 +1,79 @@ +From: Jan Beulich +Subject: AMD/IOMMU: wait for command slot to be available + +No caller cared about send_iommu_command() indicating unavailability of +a slot. Hence if a sufficient number prior commands timed out, we did +blindly assume that the requested command was submitted to the IOMMU +when really it wasn't. This could mean both a hanging system (waiting +for a command to complete that was never seen by the IOMMU) or blindly +propagating success back to callers, making them believe they're fine +to e.g. free previously unmapped pages. + +Fold the three involved functions into one, add spin waiting for an +available slot along the lines of VT-d's qinval_next_index(), and as a +consequence drop all error indicator return types/values. + +This is part of XSA-373 / CVE-2021-28692. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/amd/iommu_cmd.c ++++ b/xen/drivers/passthrough/amd/iommu_cmd.c +@@ -20,43 +20,30 @@ + #include "iommu.h" + #include "../ats.h" + +-static int queue_iommu_command(struct amd_iommu *iommu, u32 cmd[]) ++static void send_iommu_command(struct amd_iommu *iommu, ++ const uint32_t cmd[4]) + { +- uint32_t tail, head; ++ uint32_t tail; + + tail = iommu->cmd_buffer.tail + sizeof(cmd_entry_t); + if ( tail == iommu->cmd_buffer.size ) + tail = 0; + +- head = readl(iommu->mmio_base + +- IOMMU_CMD_BUFFER_HEAD_OFFSET) & IOMMU_RING_BUFFER_PTR_MASK; +- if ( head != tail ) ++ while ( tail == (readl(iommu->mmio_base + ++ IOMMU_CMD_BUFFER_HEAD_OFFSET) & ++ IOMMU_RING_BUFFER_PTR_MASK) ) + { +- memcpy(iommu->cmd_buffer.buffer + iommu->cmd_buffer.tail, +- cmd, sizeof(cmd_entry_t)); +- +- iommu->cmd_buffer.tail = tail; +- return 1; ++ printk_once(XENLOG_ERR "AMD IOMMU %pp: no cmd slot available\n", ++ &PCI_SBDF2(iommu->seg, iommu->bdf)); ++ cpu_relax(); + } + +- return 0; +-} +- +-static void commit_iommu_command_buffer(struct amd_iommu *iommu) +-{ +- writel(iommu->cmd_buffer.tail, +- iommu->mmio_base + IOMMU_CMD_BUFFER_TAIL_OFFSET); +-} ++ memcpy(iommu->cmd_buffer.buffer + iommu->cmd_buffer.tail, ++ cmd, sizeof(cmd_entry_t)); + +-static int send_iommu_command(struct amd_iommu *iommu, u32 cmd[]) +-{ +- if ( queue_iommu_command(iommu, cmd) ) +- { +- commit_iommu_command_buffer(iommu); +- return 1; +- } ++ iommu->cmd_buffer.tail = tail; + +- return 0; ++ writel(tail, iommu->mmio_base + IOMMU_CMD_BUFFER_TAIL_OFFSET); + } + + static void flush_command_buffer(struct amd_iommu *iommu) diff --git a/xsa373-4.15-5.patch b/xsa373-4.15-5.patch new file mode 100644 index 0000000..0c6b1ea --- /dev/null +++ b/xsa373-4.15-5.patch @@ -0,0 +1,141 @@ +From: Jan Beulich +Subject: AMD/IOMMU: drop command completion timeout + +First and foremost - such timeouts were not signaled to callers, making +them believe they're fine to e.g. free previously unmapped pages. + +Mirror VT-d's behavior: A fixed number of loop iterations is not a +suitable way to detect timeouts in an environment (CPU and bus speeds) +independent manner anyway. Furthermore, leaving an in-progress operation +pending when it appears to take too long is problematic: If a command +completed later, the signaling of its completion may instead be +understood to signal a subsequently started command's completion. + +Log excessively long processing times (with a progressive threshold) to +have some indication of problems in this area. Allow callers to specify +a non-default timeout bias for this logging, using the same values as +VT-d does, which in particular means a (by default) much larger value +for device IO TLB invalidation. + +This is part of XSA-373 / CVE-2021-28692. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/amd/iommu_cmd.c ++++ b/xen/drivers/passthrough/amd/iommu_cmd.c +@@ -46,10 +46,12 @@ static void send_iommu_command(struct am + writel(tail, iommu->mmio_base + IOMMU_CMD_BUFFER_TAIL_OFFSET); + } + +-static void flush_command_buffer(struct amd_iommu *iommu) ++static void flush_command_buffer(struct amd_iommu *iommu, ++ unsigned int timeout_base) + { +- unsigned int cmd[4], status, loop_count; +- bool comp_wait; ++ uint32_t cmd[4]; ++ s_time_t start, timeout; ++ static unsigned int __read_mostly threshold = 1; + + /* RW1C 'ComWaitInt' in status register */ + writel(IOMMU_STATUS_COMP_WAIT_INT, +@@ -65,22 +67,29 @@ static void flush_command_buffer(struct + IOMMU_COMP_WAIT_I_FLAG_SHIFT, &cmd[0]); + send_iommu_command(iommu, cmd); + +- /* Make loop_count long enough for polling completion wait bit */ +- loop_count = 1000; +- do { +- status = readl(iommu->mmio_base + IOMMU_STATUS_MMIO_OFFSET); +- comp_wait = status & IOMMU_STATUS_COMP_WAIT_INT; +- --loop_count; +- } while ( !comp_wait && loop_count ); +- +- if ( comp_wait ) ++ start = NOW(); ++ timeout = start + (timeout_base ?: 100) * MILLISECS(threshold); ++ while ( !(readl(iommu->mmio_base + IOMMU_STATUS_MMIO_OFFSET) & ++ IOMMU_STATUS_COMP_WAIT_INT) ) + { +- /* RW1C 'ComWaitInt' in status register */ +- writel(IOMMU_STATUS_COMP_WAIT_INT, +- iommu->mmio_base + IOMMU_STATUS_MMIO_OFFSET); +- return; ++ if ( timeout && NOW() > timeout ) ++ { ++ threshold |= threshold << 1; ++ printk(XENLOG_WARNING ++ "AMD IOMMU %pp: %scompletion wait taking too long\n", ++ &PCI_SBDF2(iommu->seg, iommu->bdf), ++ timeout_base ? "iotlb " : ""); ++ timeout = 0; ++ } ++ cpu_relax(); + } +- AMD_IOMMU_DEBUG("Warning: ComWaitInt bit did not assert!\n"); ++ ++ if ( !timeout ) ++ printk(XENLOG_WARNING ++ "AMD IOMMU %pp: %scompletion wait took %lums\n", ++ &PCI_SBDF2(iommu->seg, iommu->bdf), ++ timeout_base ? "iotlb " : "", ++ (NOW() - start) / 10000000); + } + + /* Build low level iommu command messages */ +@@ -291,7 +300,7 @@ void amd_iommu_flush_iotlb(u8 devfn, con + /* send INVALIDATE_IOTLB_PAGES command */ + spin_lock_irqsave(&iommu->lock, flags); + invalidate_iotlb_pages(iommu, maxpend, 0, queueid, daddr, req_id, order); +- flush_command_buffer(iommu); ++ flush_command_buffer(iommu, iommu_dev_iotlb_timeout); + spin_unlock_irqrestore(&iommu->lock, flags); + } + +@@ -328,7 +337,7 @@ static void _amd_iommu_flush_pages(struc + { + spin_lock_irqsave(&iommu->lock, flags); + invalidate_iommu_pages(iommu, daddr, dom_id, order); +- flush_command_buffer(iommu); ++ flush_command_buffer(iommu, 0); + spin_unlock_irqrestore(&iommu->lock, flags); + } + +@@ -352,7 +361,7 @@ void amd_iommu_flush_device(struct amd_i + ASSERT( spin_is_locked(&iommu->lock) ); + + invalidate_dev_table_entry(iommu, bdf); +- flush_command_buffer(iommu); ++ flush_command_buffer(iommu, 0); + } + + void amd_iommu_flush_intremap(struct amd_iommu *iommu, uint16_t bdf) +@@ -360,7 +369,7 @@ void amd_iommu_flush_intremap(struct amd + ASSERT( spin_is_locked(&iommu->lock) ); + + invalidate_interrupt_table(iommu, bdf); +- flush_command_buffer(iommu); ++ flush_command_buffer(iommu, 0); + } + + void amd_iommu_flush_all_caches(struct amd_iommu *iommu) +@@ -368,7 +377,7 @@ void amd_iommu_flush_all_caches(struct a + ASSERT( spin_is_locked(&iommu->lock) ); + + invalidate_iommu_all(iommu); +- flush_command_buffer(iommu); ++ flush_command_buffer(iommu, 0); + } + + void amd_iommu_send_guest_cmd(struct amd_iommu *iommu, u32 cmd[]) +@@ -378,7 +387,8 @@ void amd_iommu_send_guest_cmd(struct amd + spin_lock_irqsave(&iommu->lock, flags); + + send_iommu_command(iommu, cmd); +- flush_command_buffer(iommu); ++ /* TBD: Timeout selection may require peeking into cmd[]. */ ++ flush_command_buffer(iommu, 0); + + spin_unlock_irqrestore(&iommu->lock, flags); + } diff --git a/xsa375.patch b/xsa375.patch new file mode 100644 index 0000000..aa2e5ad --- /dev/null +++ b/xsa375.patch @@ -0,0 +1,50 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Protect against Speculative Code Store Bypass + +Modern x86 processors have far-better-than-architecturally-guaranteed self +modifying code detection. Typically, when a write hits an instruction in +flight, a Machine Clear occurs to flush stale content in the frontend and +backend. + +For self modifying code, before a write which hits an instruction in flight +retires, the frontend can speculatively decode and execute the old instruction +stream. Speculation of this form can suffer from type confusion in registers, +and potentially leak data. + +Furthermore, updates are typically byte-wise, rather than atomic. Depending +on timing, speculation can race ahead multiple times between individual +writes, and execute the transiently-malformed instruction stream. + +Xen has stubs which are used in certain cases for emulation purposes. Inhibit +speculation between updating the stub and executing it. + +This is XSA-375 / CVE-2021-0089. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c +index 8889509d2a..11467a1e3a 100644 +--- a/xen/arch/x86/pv/emul-priv-op.c ++++ b/xen/arch/x86/pv/emul-priv-op.c +@@ -138,6 +138,8 @@ static io_emul_stub_t *io_emul_stub_setup(struct priv_op_ctxt *ctxt, u8 opcode, + /* Runtime confirmation that we haven't clobbered an adjacent stub. */ + BUG_ON(STUB_BUF_SIZE / 2 < (p - ctxt->io_emul_stub)); + ++ block_speculation(); /* SCSB */ ++ + /* Handy function-typed pointer to the stub. */ + return (void *)stub_va; + +diff --git a/xen/arch/x86/x86_emulate/x86_emulate.c b/xen/arch/x86/x86_emulate/x86_emulate.c +index c25d88d0d8..f42ff2a837 100644 +--- a/xen/arch/x86/x86_emulate/x86_emulate.c ++++ b/xen/arch/x86/x86_emulate/x86_emulate.c +@@ -1257,6 +1257,7 @@ static inline int mkec(uint8_t e, int32_t ec, ...) + # define invoke_stub(pre, post, constraints...) do { \ + stub_exn.info = (union stub_exception_token) { .raw = ~0 }; \ + stub_exn.line = __LINE__; /* Utility outweighs livepatching cost */ \ ++ block_speculation(); /* SCSB */ \ + asm volatile ( pre "\n\tINDIRECT_CALL %[stub]\n\t" post "\n" \ + ".Lret%=:\n\t" \ + ".pushsection .fixup,\"ax\"\n" \ diff --git a/xsa377.patch b/xsa377.patch new file mode 100644 index 0000000..1a1887b --- /dev/null +++ b/xsa377.patch @@ -0,0 +1,27 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Mitigate TAA after S3 resume + +The user chosen setting for MSR_TSX_CTRL needs restoring after S3. + +All APs get the correct setting via start_secondary(), but the BSP was missed +out. + +This is XSA-377 / CVE-2021-28690. + +Fixes: 8c4330818f6 ("x86/spec-ctrl: Mitigate the TSX Asynchronous Abort sidechannel") +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/acpi/power.c b/xen/arch/x86/acpi/power.c +index 91a8c4d0bd..31a56f02d0 100644 +--- a/xen/arch/x86/acpi/power.c ++++ b/xen/arch/x86/acpi/power.c +@@ -288,6 +288,8 @@ static int enter_state(u32 state) + + microcode_update_one(); + ++ tsx_init(); /* Needs microcode. May change HLE/RTM feature bits. */ ++ + if ( !recheck_cpu_features(0) ) + panic("Missing previously available feature(s)\n"); + From e0018c4e82f3d411b0572d3947733ab6adde4353 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 8 Jun 2021 22:56:27 +0100 Subject: [PATCH 009/137] 4 security updates xen/arm: Boot modules are not scrubbed [XSA-372, CVE-2021-28693] inappropriate x86 IOMMU timeout detection / handling [XSA-373, CVE-2021-28692] Speculative Code Store Bypass [XSA-375, CVE-2021-0089] x86: TSX Async Abort protections not restored after S3 [XSA-377, CVE-2021-28690] --- xen.spec | 28 ++- ...en-arm-Create-dom0less-domUs-earlier.patch | 83 +++++++++ ...ules-should-always-be-scrubbed-if-bo.patch | 58 +++++++ xsa373-4.14-1.patch | 120 +++++++++++++ xsa373-4.14-2.patch | 102 +++++++++++ xsa373-4.14-3.patch | 163 ++++++++++++++++++ xsa373-4.14-4.patch | 81 +++++++++ xsa373-4.14-5.patch | 143 +++++++++++++++ xsa375.patch | 50 ++++++ xsa377.patch | 27 +++ 10 files changed, 854 insertions(+), 1 deletion(-) create mode 100644 xsa372-4.14-0001-xen-arm-Create-dom0less-domUs-earlier.patch create mode 100644 xsa372-4.14-0002-xen-arm-Boot-modules-should-always-be-scrubbed-if-bo.patch create mode 100644 xsa373-4.14-1.patch create mode 100644 xsa373-4.14-2.patch create mode 100644 xsa373-4.14-3.patch create mode 100644 xsa373-4.14-4.patch create mode 100644 xsa373-4.14-5.patch create mode 100644 xsa375.patch create mode 100644 xsa377.patch diff --git a/xen.spec b/xen.spec index c4773ac..0ded91e 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.14.2 -Release: 1%{?dist} +Release: 2%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -118,6 +118,15 @@ Patch47: xen.git-d6627cf1b63ce57a6a7e2c1800dbc50eed742c32.patch Patch48: xen.git-d8099d94dfaa3573bd86ebfc457cbc8f70a3ecda.patch Patch49: xen.git-8169f82049efb5b2044b33aa482ba3a136b7804d.patch Patch50: xsa363.patch +Patch51: xsa372-4.14-0001-xen-arm-Create-dom0less-domUs-earlier.patch +Patch52: xsa372-4.14-0002-xen-arm-Boot-modules-should-always-be-scrubbed-if-bo.patch +Patch53: xsa373-4.14-1.patch +Patch54: xsa373-4.14-2.patch +Patch55: xsa373-4.14-3.patch +Patch56: xsa373-4.14-4.patch +Patch57: xsa373-4.14-5.patch +Patch58: xsa375.patch +Patch59: xsa377.patch %if %build_qemutrad @@ -332,6 +341,15 @@ manage Xen virtual machines. %patch48 -p1 %patch49 -p1 %patch50 -p1 +%patch51 -p1 +%patch52 -p1 +%patch53 -p1 +%patch54 -p1 +%patch55 -p1 +%patch56 -p1 +%patch57 -p1 +%patch58 -p1 +%patch59 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -925,6 +943,14 @@ fi %endif %changelog +* Tue Jun 08 2021 Michael Young - 4.14.2-2 +- xen/arm: Boot modules are not scrubbed [XSA-372, CVE-2021-28693] +- inappropriate x86 IOMMU timeout detection / handling + [XSA-373, CVE-2021-28692] +- Speculative Code Store Bypass [XSA-375, CVE-2021-0089] +- x86: TSX Async Abort protections not restored after S3 + [XSA-377, CVE-2021-28690] + * Tue May 04 2021 Michael Young - 4.14.2-1 - update to 4.14.2 remove or adjust patch content now included or superceded upstream diff --git a/xsa372-4.14-0001-xen-arm-Create-dom0less-domUs-earlier.patch b/xsa372-4.14-0001-xen-arm-Create-dom0less-domUs-earlier.patch new file mode 100644 index 0000000..a5289a8 --- /dev/null +++ b/xsa372-4.14-0001-xen-arm-Create-dom0less-domUs-earlier.patch @@ -0,0 +1,83 @@ +From f98c20aaaf909be04ada5cb6cb88c14b9bc75e15 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 17 May 2021 17:47:13 +0100 +Subject: [PATCH 1/2] xen/arm: Create dom0less domUs earlier + +In a follow-up patch we will need to unallocate the boot modules +before heap_init_late() is called. + +The modules will contain the domUs kernel and initramfs. Therefore Xen +will need to create extra domUs (used by dom0less) before heap_init_late(). + +This has two consequences on dom0less: + 1) Domains will not be unpaused as soon as they are created but + once all have been created. However, Xen doesn't guarantee an order + to unpause, so this is not something one could rely on. + + 2) The memory allocated for a domU will not be scrubbed anymore when an + admin select bootscrub=on. This is not something we advertised, but if + this is a concern we can introduce either force scrub for all domUs or + a per-domain flag in the DT. The behavior for bootscrub=off and + bootscrub=idle (default) has not changed. + +This is part of XSA-372 / CVE-2021-28693. + +Signed-off-by: Julien Grall +Reviewed-by: Jan Beulich +Reviewed-by: Stefano Stabellini +Tested-by: Stefano Stabellini +--- + xen/arch/arm/domain_build.c | 2 -- + xen/arch/arm/setup.c | 9 +++++---- + 2 files changed, 5 insertions(+), 6 deletions(-) + +diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c +index e824ba34b012..b07461f5d376 100644 +--- a/xen/arch/arm/domain_build.c ++++ b/xen/arch/arm/domain_build.c +@@ -2515,8 +2515,6 @@ void __init create_domUs(void) + + if ( construct_domU(d, node) != 0 ) + panic("Could not set up domain %s\n", dt_node_name(node)); +- +- domain_unpause_by_systemcontroller(d); + } + } + +diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c +index 7968cee47d05..1f26080b30bf 100644 +--- a/xen/arch/arm/setup.c ++++ b/xen/arch/arm/setup.c +@@ -779,7 +779,7 @@ void __init start_xen(unsigned long boot_phys_offset, + int cpus, i; + const char *cmdline; + struct bootmodule *xen_bootmodule; +- struct domain *dom0; ++ struct domain *dom0, *d; + struct xen_domctl_createdomain dom0_cfg = { + .flags = XEN_DOMCTL_CDF_hvm | XEN_DOMCTL_CDF_hap, + .max_evtchn_port = -1, +@@ -962,6 +962,8 @@ void __init start_xen(unsigned long boot_phys_offset, + if ( construct_dom0(dom0) != 0) + panic("Could not set up DOM0 guest OS\n"); + ++ create_domUs(); ++ + heap_init_late(); + + init_trace_bufs(); +@@ -975,9 +977,8 @@ void __init start_xen(unsigned long boot_phys_offset, + + system_state = SYS_STATE_active; + +- create_domUs(); +- +- domain_unpause_by_systemcontroller(dom0); ++ for_each_domain( d ) ++ domain_unpause_by_systemcontroller(d); + + /* Switch on to the dynamically allocated stack for the idle vcpu + * since the static one we're running on is about to be freed. */ +-- +2.17.1 + diff --git a/xsa372-4.14-0002-xen-arm-Boot-modules-should-always-be-scrubbed-if-bo.patch b/xsa372-4.14-0002-xen-arm-Boot-modules-should-always-be-scrubbed-if-bo.patch new file mode 100644 index 0000000..3ed62f3 --- /dev/null +++ b/xsa372-4.14-0002-xen-arm-Boot-modules-should-always-be-scrubbed-if-bo.patch @@ -0,0 +1,58 @@ +From e7e475c1a3dc6b149252413589eebaa4ae138824 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Sat, 17 Apr 2021 17:38:28 +0100 +Subject: [PATCH 2/2] xen/arm: Boot modules should always be scrubbed if + bootscrub={on, idle} + +The function to initialize the pages (see init_heap_pages()) will request +scrub when the admin request idle bootscrub (default) and state == +SYS_STATE_active. When bootscrub=on, Xen will scrub any free pages in +heap_init_late(). + +Currently, the boot modules (e.g. kernels, initramfs) will be discarded/ +freed after heap_init_late() is called and system_state switched to +SYS_STATE_active. This means the pages associated with the boot modules +will not get scrubbed before getting re-purposed. + +If the memory is assigned to an untrusted domU, it may be able to +retrieve secrets from the modules. + +This is part of XSA-372 / CVE-2021-28693. + +Fixes: 1774e9b1df27 ("xen/arm: introduce create_domUs") +Signed-off-by: Julien Grall +Reviewed-by: Jan Beulich +Reviewed-by: Stefano Stabellini +Tested-by: Stefano Stabellini +--- + xen/arch/arm/setup.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c +index 1f26080b30bf..34b1c1a11ef6 100644 +--- a/xen/arch/arm/setup.c ++++ b/xen/arch/arm/setup.c +@@ -75,7 +75,6 @@ static __used void init_done(void) + /* Must be done past setting system_state. */ + unregister_init_virtual_region(); + +- discard_initial_modules(); + free_init_memory(); + startup_cpu_idle_loop(); + } +@@ -964,6 +963,12 @@ void __init start_xen(unsigned long boot_phys_offset, + + create_domUs(); + ++ /* ++ * This needs to be called **before** heap_init_late() so modules ++ * will be scrubbed (unless suppressed). ++ */ ++ discard_initial_modules(); ++ + heap_init_late(); + + init_trace_bufs(); +-- +2.17.1 + diff --git a/xsa373-4.14-1.patch b/xsa373-4.14-1.patch new file mode 100644 index 0000000..ee5229a --- /dev/null +++ b/xsa373-4.14-1.patch @@ -0,0 +1,120 @@ +From: Jan Beulich +Subject: VT-d: size qinval queue dynamically + +With the present synchronous model, we need two slots for every +operation (the operation itself and a wait descriptor). There can be +one such pair of requests pending per CPU. To ensure that under all +normal circumstances a slot is always available when one is requested, +size the queue ring according to the number of present CPUs. + +This is part of XSA-373 / CVE-2021-28692. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/vtd/iommu.h ++++ b/xen/drivers/passthrough/vtd/iommu.h +@@ -450,17 +450,9 @@ struct qinval_entry { + }q; + }; + +-/* Order of queue invalidation pages(max is 8) */ +-#define QINVAL_PAGE_ORDER 2 +- +-#define QINVAL_ARCH_PAGE_ORDER (QINVAL_PAGE_ORDER + PAGE_SHIFT_4K - PAGE_SHIFT) +-#define QINVAL_ARCH_PAGE_NR ( QINVAL_ARCH_PAGE_ORDER < 0 ? \ +- 1 : \ +- 1 << QINVAL_ARCH_PAGE_ORDER ) +- + /* Each entry is 16 bytes, so 2^8 entries per page */ + #define QINVAL_ENTRY_ORDER ( PAGE_SHIFT - 4 ) +-#define QINVAL_ENTRY_NR (1 << (QINVAL_PAGE_ORDER + 8)) ++#define QINVAL_MAX_ENTRY_NR (1u << (7 + QINVAL_ENTRY_ORDER)) + + /* Status data flag */ + #define QINVAL_STAT_INIT 0 +--- a/xen/drivers/passthrough/vtd/qinval.c ++++ b/xen/drivers/passthrough/vtd/qinval.c +@@ -31,6 +31,9 @@ + + #define VTD_QI_TIMEOUT 1 + ++static unsigned int __read_mostly qi_pg_order; ++static unsigned int __read_mostly qi_entry_nr; ++ + static int __must_check invalidate_sync(struct vtd_iommu *iommu); + + static void print_qi_regs(struct vtd_iommu *iommu) +@@ -55,7 +58,7 @@ static unsigned int qinval_next_index(st + tail >>= QINVAL_INDEX_SHIFT; + + /* (tail+1 == head) indicates a full queue, wait for HW */ +- while ( ( tail + 1 ) % QINVAL_ENTRY_NR == ++ while ( ((tail + 1) & (qi_entry_nr - 1)) == + ( dmar_readq(iommu->reg, DMAR_IQH_REG) >> QINVAL_INDEX_SHIFT ) ) + cpu_relax(); + +@@ -68,7 +71,7 @@ static void qinval_update_qtail(struct v + + /* Need hold register lock when update tail */ + ASSERT( spin_is_locked(&iommu->register_lock) ); +- val = (index + 1) % QINVAL_ENTRY_NR; ++ val = (index + 1) & (qi_entry_nr - 1); + dmar_writeq(iommu->reg, DMAR_IQT_REG, (val << QINVAL_INDEX_SHIFT)); + } + +@@ -403,8 +406,28 @@ int enable_qinval(struct vtd_iommu *iomm + + if ( iommu->qinval_maddr == 0 ) + { +- iommu->qinval_maddr = alloc_pgtable_maddr(QINVAL_ARCH_PAGE_NR, +- iommu->node); ++ if ( !qi_entry_nr ) ++ { ++ /* ++ * With the present synchronous model, we need two slots for every ++ * operation (the operation itself and a wait descriptor). There ++ * can be one such pair of requests pending per CPU. One extra ++ * entry is needed as the ring is considered full when there's ++ * only one entry left. ++ */ ++ BUILD_BUG_ON(CONFIG_NR_CPUS * 2 >= QINVAL_MAX_ENTRY_NR); ++ qi_pg_order = get_order_from_bytes((num_present_cpus() * 2 + 1) << ++ (PAGE_SHIFT - ++ QINVAL_ENTRY_ORDER)); ++ qi_entry_nr = 1u << (qi_pg_order + QINVAL_ENTRY_ORDER); ++ ++ dprintk(XENLOG_INFO VTDPREFIX, ++ "QI: using %u-entry ring(s)\n", qi_entry_nr); ++ } ++ ++ iommu->qinval_maddr = ++ alloc_pgtable_maddr(qi_entry_nr >> QINVAL_ENTRY_ORDER, ++ iommu->node); + if ( iommu->qinval_maddr == 0 ) + { + dprintk(XENLOG_WARNING VTDPREFIX, +@@ -418,15 +441,16 @@ int enable_qinval(struct vtd_iommu *iomm + + spin_lock_irqsave(&iommu->register_lock, flags); + +- /* Setup Invalidation Queue Address(IQA) register with the +- * address of the page we just allocated. QS field at +- * bits[2:0] to indicate size of queue is one 4KB page. +- * That's 256 entries. Queued Head (IQH) and Queue Tail (IQT) +- * registers are automatically reset to 0 with write +- * to IQA register. ++ /* ++ * Setup Invalidation Queue Address (IQA) register with the address of the ++ * pages we just allocated. The QS field at bits[2:0] indicates the size ++ * (page order) of the queue. ++ * ++ * Queued Head (IQH) and Queue Tail (IQT) registers are automatically ++ * reset to 0 with write to IQA register. + */ + dmar_writeq(iommu->reg, DMAR_IQA_REG, +- iommu->qinval_maddr | QINVAL_PAGE_ORDER); ++ iommu->qinval_maddr | qi_pg_order); + + dmar_writeq(iommu->reg, DMAR_IQT_REG, 0); + diff --git a/xsa373-4.14-2.patch b/xsa373-4.14-2.patch new file mode 100644 index 0000000..773cbfd --- /dev/null +++ b/xsa373-4.14-2.patch @@ -0,0 +1,102 @@ +From: Jan Beulich +Subject: AMD/IOMMU: size command buffer dynamically + +With the present synchronous model, we need two slots for every +operation (the operation itself and a wait command). There can be one +such pair of commands pending per CPU. To ensure that under all normal +circumstances a slot is always available when one is requested, size the +command ring according to the number of present CPUs. + +This is part of XSA-373 / CVE-2021-28692. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/amd/iommu-defs.h ++++ b/xen/drivers/passthrough/amd/iommu-defs.h +@@ -20,9 +20,6 @@ + #ifndef AMD_IOMMU_DEFS_H + #define AMD_IOMMU_DEFS_H + +-/* IOMMU Command Buffer entries: in power of 2 increments, minimum of 256 */ +-#define IOMMU_CMD_BUFFER_DEFAULT_ENTRIES 512 +- + /* IOMMU Event Log entries: in power of 2 increments, minimum of 256 */ + #define IOMMU_EVENT_LOG_DEFAULT_ENTRIES 512 + +@@ -164,8 +161,8 @@ struct amd_iommu_dte { + #define IOMMU_CMD_BUFFER_LENGTH_MASK 0x0F000000 + #define IOMMU_CMD_BUFFER_LENGTH_SHIFT 24 + +-#define IOMMU_CMD_BUFFER_ENTRY_SIZE 16 +-#define IOMMU_CMD_BUFFER_POWER_OF2_ENTRIES_PER_PAGE 8 ++#define IOMMU_CMD_BUFFER_ENTRY_ORDER 4 ++#define IOMMU_CMD_BUFFER_MAX_ENTRIES (1u << 15) + + #define IOMMU_CMD_OPCODE_MASK 0xF0000000 + #define IOMMU_CMD_OPCODE_SHIFT 28 +--- a/xen/drivers/passthrough/amd/iommu_cmd.c ++++ b/xen/drivers/passthrough/amd/iommu_cmd.c +@@ -24,7 +24,7 @@ static int queue_iommu_command(struct am + { + uint32_t tail, head; + +- tail = iommu->cmd_buffer.tail + IOMMU_CMD_BUFFER_ENTRY_SIZE; ++ tail = iommu->cmd_buffer.tail + sizeof(cmd_entry_t); + if ( tail == iommu->cmd_buffer.size ) + tail = 0; + +@@ -33,7 +33,7 @@ static int queue_iommu_command(struct am + if ( head != tail ) + { + memcpy(iommu->cmd_buffer.buffer + iommu->cmd_buffer.tail, +- cmd, IOMMU_CMD_BUFFER_ENTRY_SIZE); ++ cmd, sizeof(cmd_entry_t)); + + iommu->cmd_buffer.tail = tail; + return 1; +--- a/xen/drivers/passthrough/amd/iommu_init.c ++++ b/xen/drivers/passthrough/amd/iommu_init.c +@@ -118,7 +118,7 @@ static void register_iommu_cmd_buffer_in + writel(entry, iommu->mmio_base + IOMMU_CMD_BUFFER_BASE_LOW_OFFSET); + + power_of2_entries = get_order_from_bytes(iommu->cmd_buffer.size) + +- IOMMU_CMD_BUFFER_POWER_OF2_ENTRIES_PER_PAGE; ++ PAGE_SHIFT - IOMMU_CMD_BUFFER_ENTRY_ORDER; + + entry = 0; + iommu_set_addr_hi_to_reg(&entry, addr_hi); +@@ -1022,9 +1022,31 @@ static void *__init allocate_ring_buffer + static void * __init allocate_cmd_buffer(struct amd_iommu *iommu) + { + /* allocate 'command buffer' in power of 2 increments of 4K */ ++ static unsigned int __read_mostly nr_ents; ++ ++ if ( !nr_ents ) ++ { ++ unsigned int order; ++ ++ /* ++ * With the present synchronous model, we need two slots for every ++ * operation (the operation itself and a wait command). There can be ++ * one such pair of requests pending per CPU. One extra entry is ++ * needed as the ring is considered full when there's only one entry ++ * left. ++ */ ++ BUILD_BUG_ON(CONFIG_NR_CPUS * 2 >= IOMMU_CMD_BUFFER_MAX_ENTRIES); ++ order = get_order_from_bytes((num_present_cpus() * 2 + 1) << ++ IOMMU_CMD_BUFFER_ENTRY_ORDER); ++ nr_ents = 1u << (order + PAGE_SHIFT - IOMMU_CMD_BUFFER_ENTRY_ORDER); ++ ++ AMD_IOMMU_DEBUG("using %u-entry cmd ring(s)\n", nr_ents); ++ } ++ ++ BUILD_BUG_ON(sizeof(cmd_entry_t) != (1u << IOMMU_CMD_BUFFER_ENTRY_ORDER)); ++ + return allocate_ring_buffer(&iommu->cmd_buffer, sizeof(cmd_entry_t), +- IOMMU_CMD_BUFFER_DEFAULT_ENTRIES, +- "Command Buffer", false); ++ nr_ents, "Command Buffer", false); + } + + static void * __init allocate_event_log(struct amd_iommu *iommu) diff --git a/xsa373-4.14-3.patch b/xsa373-4.14-3.patch new file mode 100644 index 0000000..fe34546 --- /dev/null +++ b/xsa373-4.14-3.patch @@ -0,0 +1,163 @@ +From: Jan Beulich +Subject: VT-d: eliminate flush related timeouts + +Leaving an in-progress operation pending when it appears to take too +long is problematic: If e.g. a QI command completed later, the write to +the "poll slot" may instead be understood to signal a subsequently +started command's completion. Also our accounting of the timeout period +was actually wrong: We included the time it took for the command to +actually make it to the front of the queue, which could be heavily +affected by guests other than the one for which the flush is being +performed. + +Do away with all timeout detection on all flush related code paths. +Log excessively long processing times (with a progressive threshold) to +have some indication of problems in this area. + +Additionally log (once) if qinval_next_index() didn't immediately find +an available slot. Together with the earlier change sizing the queue(s) +dynamically, we should now have a guarantee that with our fully +synchronous model any demand for slots can actually be satisfied. + +This is part of XSA-373 / CVE-2021-28692. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/vtd/dmar.h ++++ b/xen/drivers/passthrough/vtd/dmar.h +@@ -127,6 +127,34 @@ do { + } \ + } while (0) + ++#define IOMMU_FLUSH_WAIT(what, iommu, offset, op, cond, sts) \ ++do { \ ++ static unsigned int __read_mostly threshold = 1; \ ++ s_time_t start = NOW(); \ ++ s_time_t timeout = start + DMAR_OPERATION_TIMEOUT * threshold; \ ++ \ ++ for ( ; ; ) \ ++ { \ ++ sts = op(iommu->reg, offset); \ ++ if ( cond ) \ ++ break; \ ++ if ( timeout && NOW() > timeout ) \ ++ { \ ++ threshold |= threshold << 1; \ ++ printk(XENLOG_WARNING VTDPREFIX \ ++ " IOMMU#%u: %s flush taking too long\n", \ ++ iommu->index, what); \ ++ timeout = 0; \ ++ } \ ++ cpu_relax(); \ ++ } \ ++ \ ++ if ( !timeout ) \ ++ printk(XENLOG_WARNING VTDPREFIX \ ++ " IOMMU#%u: %s flush took %lums\n", \ ++ iommu->index, what, (NOW() - start) / 10000000); \ ++} while ( false ) ++ + int vtd_hw_check(void); + void disable_pmr(struct vtd_iommu *iommu); + int is_igd_drhd(struct acpi_drhd_unit *drhd); +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -326,8 +326,8 @@ static void iommu_flush_write_buffer(str + dmar_writel(iommu->reg, DMAR_GCMD_REG, val | DMA_GCMD_WBF); + + /* Make sure hardware complete it */ +- IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, dmar_readl, +- !(val & DMA_GSTS_WBFS), val); ++ IOMMU_FLUSH_WAIT("write buffer", iommu, DMAR_GSTS_REG, dmar_readl, ++ !(val & DMA_GSTS_WBFS), val); + + spin_unlock_irqrestore(&iommu->register_lock, flags); + } +@@ -376,8 +376,8 @@ int vtd_flush_context_reg(struct vtd_iom + dmar_writeq(iommu->reg, DMAR_CCMD_REG, val); + + /* Make sure hardware complete it */ +- IOMMU_WAIT_OP(iommu, DMAR_CCMD_REG, dmar_readq, +- !(val & DMA_CCMD_ICC), val); ++ IOMMU_FLUSH_WAIT("context", iommu, DMAR_CCMD_REG, dmar_readq, ++ !(val & DMA_CCMD_ICC), val); + + spin_unlock_irqrestore(&iommu->register_lock, flags); + /* flush context entry will implicitly flush write buffer */ +@@ -454,8 +454,8 @@ int vtd_flush_iotlb_reg(struct vtd_iommu + dmar_writeq(iommu->reg, tlb_offset + 8, val); + + /* Make sure hardware complete it */ +- IOMMU_WAIT_OP(iommu, (tlb_offset + 8), dmar_readq, +- !(val & DMA_TLB_IVT), val); ++ IOMMU_FLUSH_WAIT("iotlb", iommu, (tlb_offset + 8), dmar_readq, ++ !(val & DMA_TLB_IVT), val); + spin_unlock_irqrestore(&iommu->register_lock, flags); + + /* check IOTLB invalidation granularity */ +--- a/xen/drivers/passthrough/vtd/qinval.c ++++ b/xen/drivers/passthrough/vtd/qinval.c +@@ -29,8 +29,6 @@ + #include "extern.h" + #include "../ats.h" + +-#define VTD_QI_TIMEOUT 1 +- + static unsigned int __read_mostly qi_pg_order; + static unsigned int __read_mostly qi_entry_nr; + +@@ -60,7 +58,11 @@ static unsigned int qinval_next_index(st + /* (tail+1 == head) indicates a full queue, wait for HW */ + while ( ((tail + 1) & (qi_entry_nr - 1)) == + ( dmar_readq(iommu->reg, DMAR_IQH_REG) >> QINVAL_INDEX_SHIFT ) ) ++ { ++ printk_once(XENLOG_ERR VTDPREFIX " IOMMU#%u: no QI slot available\n", ++ iommu->index); + cpu_relax(); ++ } + + return tail; + } +@@ -180,23 +182,32 @@ static int __must_check queue_invalidate + /* Now we don't support interrupt method */ + if ( sw ) + { +- s_time_t timeout; +- +- /* In case all wait descriptor writes to same addr with same data */ +- timeout = NOW() + MILLISECS(flush_dev_iotlb ? +- iommu_dev_iotlb_timeout : VTD_QI_TIMEOUT); ++ static unsigned int __read_mostly threshold = 1; ++ s_time_t start = NOW(); ++ s_time_t timeout = start + (flush_dev_iotlb ++ ? iommu_dev_iotlb_timeout ++ : 100) * MILLISECS(threshold); + + while ( ACCESS_ONCE(*this_poll_slot) != QINVAL_STAT_DONE ) + { +- if ( NOW() > timeout ) ++ if ( timeout && NOW() > timeout ) + { +- print_qi_regs(iommu); ++ threshold |= threshold << 1; + printk(XENLOG_WARNING VTDPREFIX +- " Queue invalidate wait descriptor timed out\n"); +- return -ETIMEDOUT; ++ " IOMMU#%u: QI%s wait descriptor taking too long\n", ++ iommu->index, flush_dev_iotlb ? " dev" : ""); ++ print_qi_regs(iommu); ++ timeout = 0; + } + cpu_relax(); + } ++ ++ if ( !timeout ) ++ printk(XENLOG_WARNING VTDPREFIX ++ " IOMMU#%u: QI%s wait descriptor took %lums\n", ++ iommu->index, flush_dev_iotlb ? " dev" : "", ++ (NOW() - start) / 10000000); ++ + return 0; + } + diff --git a/xsa373-4.14-4.patch b/xsa373-4.14-4.patch new file mode 100644 index 0000000..a1f186b --- /dev/null +++ b/xsa373-4.14-4.patch @@ -0,0 +1,81 @@ +From: Jan Beulich +Subject: AMD/IOMMU: wait for command slot to be available + +No caller cared about send_iommu_command() indicating unavailability of +a slot. Hence if a sufficient number prior commands timed out, we did +blindly assume that the requested command was submitted to the IOMMU +when really it wasn't. This could mean both a hanging system (waiting +for a command to complete that was never seen by the IOMMU) or blindly +propagating success back to callers, making them believe they're fine +to e.g. free previously unmapped pages. + +Fold the three involved functions into one, add spin waiting for an +available slot along the lines of VT-d's qinval_next_index(), and as a +consequence drop all error indicator return types/values. + +This is part of XSA-373 / CVE-2021-28692. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/amd/iommu_cmd.c ++++ b/xen/drivers/passthrough/amd/iommu_cmd.c +@@ -20,43 +20,32 @@ + #include "iommu.h" + #include "../ats.h" + +-static int queue_iommu_command(struct amd_iommu *iommu, u32 cmd[]) ++static void send_iommu_command(struct amd_iommu *iommu, ++ const uint32_t cmd[4]) + { +- uint32_t tail, head; ++ uint32_t tail; + + tail = iommu->cmd_buffer.tail + sizeof(cmd_entry_t); + if ( tail == iommu->cmd_buffer.size ) + tail = 0; + +- head = readl(iommu->mmio_base + +- IOMMU_CMD_BUFFER_HEAD_OFFSET) & IOMMU_RING_BUFFER_PTR_MASK; +- if ( head != tail ) ++ while ( tail == (readl(iommu->mmio_base + ++ IOMMU_CMD_BUFFER_HEAD_OFFSET) & ++ IOMMU_RING_BUFFER_PTR_MASK) ) + { +- memcpy(iommu->cmd_buffer.buffer + iommu->cmd_buffer.tail, +- cmd, sizeof(cmd_entry_t)); +- +- iommu->cmd_buffer.tail = tail; +- return 1; ++ printk_once(XENLOG_ERR ++ "AMD IOMMU %04x:%02x:%02x.%u: no cmd slot available\n", ++ iommu->seg, PCI_BUS(iommu->bdf), ++ PCI_SLOT(iommu->bdf), PCI_FUNC(iommu->bdf)); ++ cpu_relax(); + } + +- return 0; +-} +- +-static void commit_iommu_command_buffer(struct amd_iommu *iommu) +-{ +- writel(iommu->cmd_buffer.tail, +- iommu->mmio_base + IOMMU_CMD_BUFFER_TAIL_OFFSET); +-} ++ memcpy(iommu->cmd_buffer.buffer + iommu->cmd_buffer.tail, ++ cmd, sizeof(cmd_entry_t)); + +-static int send_iommu_command(struct amd_iommu *iommu, u32 cmd[]) +-{ +- if ( queue_iommu_command(iommu, cmd) ) +- { +- commit_iommu_command_buffer(iommu); +- return 1; +- } ++ iommu->cmd_buffer.tail = tail; + +- return 0; ++ writel(tail, iommu->mmio_base + IOMMU_CMD_BUFFER_TAIL_OFFSET); + } + + static void flush_command_buffer(struct amd_iommu *iommu) diff --git a/xsa373-4.14-5.patch b/xsa373-4.14-5.patch new file mode 100644 index 0000000..01556a8 --- /dev/null +++ b/xsa373-4.14-5.patch @@ -0,0 +1,143 @@ +From: Jan Beulich +Subject: AMD/IOMMU: drop command completion timeout + +First and foremost - such timeouts were not signaled to callers, making +them believe they're fine to e.g. free previously unmapped pages. + +Mirror VT-d's behavior: A fixed number of loop iterations is not a +suitable way to detect timeouts in an environment (CPU and bus speeds) +independent manner anyway. Furthermore, leaving an in-progress operation +pending when it appears to take too long is problematic: If a command +completed later, the signaling of its completion may instead be +understood to signal a subsequently started command's completion. + +Log excessively long processing times (with a progressive threshold) to +have some indication of problems in this area. Allow callers to specify +a non-default timeout bias for this logging, using the same values as +VT-d does, which in particular means a (by default) much larger value +for device IO TLB invalidation. + +This is part of XSA-373 / CVE-2021-28692. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/amd/iommu_cmd.c ++++ b/xen/drivers/passthrough/amd/iommu_cmd.c +@@ -48,10 +48,12 @@ static void send_iommu_command(struct am + writel(tail, iommu->mmio_base + IOMMU_CMD_BUFFER_TAIL_OFFSET); + } + +-static void flush_command_buffer(struct amd_iommu *iommu) ++static void flush_command_buffer(struct amd_iommu *iommu, ++ unsigned int timeout_base) + { +- unsigned int cmd[4], status, loop_count; +- bool comp_wait; ++ uint32_t cmd[4]; ++ s_time_t start, timeout; ++ static unsigned int __read_mostly threshold = 1; + + /* RW1C 'ComWaitInt' in status register */ + writel(IOMMU_STATUS_COMP_WAIT_INT, +@@ -67,22 +69,31 @@ static void flush_command_buffer(struct + IOMMU_COMP_WAIT_I_FLAG_SHIFT, &cmd[0]); + send_iommu_command(iommu, cmd); + +- /* Make loop_count long enough for polling completion wait bit */ +- loop_count = 1000; +- do { +- status = readl(iommu->mmio_base + IOMMU_STATUS_MMIO_OFFSET); +- comp_wait = status & IOMMU_STATUS_COMP_WAIT_INT; +- --loop_count; +- } while ( !comp_wait && loop_count ); +- +- if ( comp_wait ) ++ start = NOW(); ++ timeout = start + (timeout_base ?: 100) * MILLISECS(threshold); ++ while ( !(readl(iommu->mmio_base + IOMMU_STATUS_MMIO_OFFSET) & ++ IOMMU_STATUS_COMP_WAIT_INT) ) + { +- /* RW1C 'ComWaitInt' in status register */ +- writel(IOMMU_STATUS_COMP_WAIT_INT, +- iommu->mmio_base + IOMMU_STATUS_MMIO_OFFSET); +- return; ++ if ( timeout && NOW() > timeout ) ++ { ++ threshold |= threshold << 1; ++ printk(XENLOG_WARNING ++ "AMD IOMMU %04x:%02x:%02x.%u: %scompletion wait taking too long\n", ++ iommu->seg, PCI_BUS(iommu->bdf), ++ PCI_SLOT(iommu->bdf), PCI_FUNC(iommu->bdf), ++ timeout_base ? "iotlb " : ""); ++ timeout = 0; ++ } ++ cpu_relax(); + } +- AMD_IOMMU_DEBUG("Warning: ComWaitInt bit did not assert!\n"); ++ ++ if ( !timeout ) ++ printk(XENLOG_WARNING ++ "AMD IOMMU %04x:%02x:%02x.%u: %scompletion wait took %lums\n", ++ iommu->seg, PCI_BUS(iommu->bdf), ++ PCI_SLOT(iommu->bdf), PCI_FUNC(iommu->bdf), ++ timeout_base ? "iotlb " : "", ++ (NOW() - start) / 10000000); + } + + /* Build low level iommu command messages */ +@@ -294,7 +305,7 @@ void amd_iommu_flush_iotlb(u8 devfn, con + /* send INVALIDATE_IOTLB_PAGES command */ + spin_lock_irqsave(&iommu->lock, flags); + invalidate_iotlb_pages(iommu, maxpend, 0, queueid, daddr, req_id, order); +- flush_command_buffer(iommu); ++ flush_command_buffer(iommu, iommu_dev_iotlb_timeout); + spin_unlock_irqrestore(&iommu->lock, flags); + } + +@@ -331,7 +342,7 @@ static void _amd_iommu_flush_pages(struc + { + spin_lock_irqsave(&iommu->lock, flags); + invalidate_iommu_pages(iommu, daddr, dom_id, order); +- flush_command_buffer(iommu); ++ flush_command_buffer(iommu, 0); + spin_unlock_irqrestore(&iommu->lock, flags); + } + +@@ -355,7 +366,7 @@ void amd_iommu_flush_device(struct amd_i + ASSERT( spin_is_locked(&iommu->lock) ); + + invalidate_dev_table_entry(iommu, bdf); +- flush_command_buffer(iommu); ++ flush_command_buffer(iommu, 0); + } + + void amd_iommu_flush_intremap(struct amd_iommu *iommu, uint16_t bdf) +@@ -363,7 +374,7 @@ void amd_iommu_flush_intremap(struct amd + ASSERT( spin_is_locked(&iommu->lock) ); + + invalidate_interrupt_table(iommu, bdf); +- flush_command_buffer(iommu); ++ flush_command_buffer(iommu, 0); + } + + void amd_iommu_flush_all_caches(struct amd_iommu *iommu) +@@ -371,7 +382,7 @@ void amd_iommu_flush_all_caches(struct a + ASSERT( spin_is_locked(&iommu->lock) ); + + invalidate_iommu_all(iommu); +- flush_command_buffer(iommu); ++ flush_command_buffer(iommu, 0); + } + + void amd_iommu_send_guest_cmd(struct amd_iommu *iommu, u32 cmd[]) +@@ -381,7 +392,8 @@ void amd_iommu_send_guest_cmd(struct amd + spin_lock_irqsave(&iommu->lock, flags); + + send_iommu_command(iommu, cmd); +- flush_command_buffer(iommu); ++ /* TBD: Timeout selection may require peeking into cmd[]. */ ++ flush_command_buffer(iommu, 0); + + spin_unlock_irqrestore(&iommu->lock, flags); + } diff --git a/xsa375.patch b/xsa375.patch new file mode 100644 index 0000000..aa2e5ad --- /dev/null +++ b/xsa375.patch @@ -0,0 +1,50 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Protect against Speculative Code Store Bypass + +Modern x86 processors have far-better-than-architecturally-guaranteed self +modifying code detection. Typically, when a write hits an instruction in +flight, a Machine Clear occurs to flush stale content in the frontend and +backend. + +For self modifying code, before a write which hits an instruction in flight +retires, the frontend can speculatively decode and execute the old instruction +stream. Speculation of this form can suffer from type confusion in registers, +and potentially leak data. + +Furthermore, updates are typically byte-wise, rather than atomic. Depending +on timing, speculation can race ahead multiple times between individual +writes, and execute the transiently-malformed instruction stream. + +Xen has stubs which are used in certain cases for emulation purposes. Inhibit +speculation between updating the stub and executing it. + +This is XSA-375 / CVE-2021-0089. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c +index 8889509d2a..11467a1e3a 100644 +--- a/xen/arch/x86/pv/emul-priv-op.c ++++ b/xen/arch/x86/pv/emul-priv-op.c +@@ -138,6 +138,8 @@ static io_emul_stub_t *io_emul_stub_setup(struct priv_op_ctxt *ctxt, u8 opcode, + /* Runtime confirmation that we haven't clobbered an adjacent stub. */ + BUG_ON(STUB_BUF_SIZE / 2 < (p - ctxt->io_emul_stub)); + ++ block_speculation(); /* SCSB */ ++ + /* Handy function-typed pointer to the stub. */ + return (void *)stub_va; + +diff --git a/xen/arch/x86/x86_emulate/x86_emulate.c b/xen/arch/x86/x86_emulate/x86_emulate.c +index c25d88d0d8..f42ff2a837 100644 +--- a/xen/arch/x86/x86_emulate/x86_emulate.c ++++ b/xen/arch/x86/x86_emulate/x86_emulate.c +@@ -1257,6 +1257,7 @@ static inline int mkec(uint8_t e, int32_t ec, ...) + # define invoke_stub(pre, post, constraints...) do { \ + stub_exn.info = (union stub_exception_token) { .raw = ~0 }; \ + stub_exn.line = __LINE__; /* Utility outweighs livepatching cost */ \ ++ block_speculation(); /* SCSB */ \ + asm volatile ( pre "\n\tINDIRECT_CALL %[stub]\n\t" post "\n" \ + ".Lret%=:\n\t" \ + ".pushsection .fixup,\"ax\"\n" \ diff --git a/xsa377.patch b/xsa377.patch new file mode 100644 index 0000000..1a1887b --- /dev/null +++ b/xsa377.patch @@ -0,0 +1,27 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Mitigate TAA after S3 resume + +The user chosen setting for MSR_TSX_CTRL needs restoring after S3. + +All APs get the correct setting via start_secondary(), but the BSP was missed +out. + +This is XSA-377 / CVE-2021-28690. + +Fixes: 8c4330818f6 ("x86/spec-ctrl: Mitigate the TSX Asynchronous Abort sidechannel") +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/acpi/power.c b/xen/arch/x86/acpi/power.c +index 91a8c4d0bd..31a56f02d0 100644 +--- a/xen/arch/x86/acpi/power.c ++++ b/xen/arch/x86/acpi/power.c +@@ -288,6 +288,8 @@ static int enter_state(u32 state) + + microcode_update_one(); + ++ tsx_init(); /* Needs microcode. May change HLE/RTM feature bits. */ ++ + if ( !recheck_cpu_features(0) ) + panic("Missing previously available feature(s)\n"); + From 928ca23fa071290cac289709d52db0f6abfebcf2 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 10 Jun 2021 21:09:02 +0100 Subject: [PATCH 010/137] add bug references and an extra CVE --- xen.spec | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/xen.spec b/xen.spec index 0ded91e..e3052d0 100644 --- a/xen.spec +++ b/xen.spec @@ -945,11 +945,13 @@ fi %changelog * Tue Jun 08 2021 Michael Young - 4.14.2-2 - xen/arm: Boot modules are not scrubbed [XSA-372, CVE-2021-28693] + (#1970542) - inappropriate x86 IOMMU timeout detection / handling - [XSA-373, CVE-2021-28692] -- Speculative Code Store Bypass [XSA-375, CVE-2021-0089] + [XSA-373, CVE-2021-28692] (#1970540) +- Speculative Code Store Bypass [XSA-375, CVE-2021-0089, CVE-2021-26313] + (#1970531) - x86: TSX Async Abort protections not restored after S3 - [XSA-377, CVE-2021-28690] + [XSA-377, CVE-2021-28690] (#1970546) * Tue May 04 2021 Michael Young - 4.14.2-1 - update to 4.14.2 From 2f753f7e9bae2370d7d1f4861d7c31a67f6fcf44 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 10 Jun 2021 21:21:02 +0100 Subject: [PATCH 011/137] add bug references and an extra CVE --- xen.spec | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/xen.spec b/xen.spec index 7ecc7e0..94e1adf 100644 --- a/xen.spec +++ b/xen.spec @@ -939,11 +939,13 @@ fi %changelog * Tue Jun 08 2021 Michael Young - 4.15.0-3 - xen/arm: Boot modules are not scrubbed [XSA-372, CVE-2021-28693] + (#1970542) - inappropriate x86 IOMMU timeout detection / handling - [XSA-373, CVE-2021-28692] -- Speculative Code Store Bypass [XSA-375, CVE-2021-0089] + [XSA-373, CVE-2021-28692] (#1970540) +- Speculative Code Store Bypass [XSA-375, CVE-2021-0089, CVE-2021-26313] + (#1970531) - x86: TSX Async Abort protections not restored after S3 - [XSA-377, CVE-2021-28690] + [XSA-377, CVE-2021-28690] (#1970546) * Fri Jun 04 2021 Python Maint - 4.15.0-2 - Rebuilt for Python 3.10 From cf76d0c21e9adcf117ff7813f52b4458051a2f83 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Fri, 23 Jul 2021 21:26:33 +0000 Subject: [PATCH 012/137] - Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 94e1adf..1a6a4de 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.15.0 -Release: 3%{?dist} +Release: 4%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -937,6 +937,9 @@ fi %endif %changelog +* Fri Jul 23 2021 Fedora Release Engineering - 4.15.0-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild + * Tue Jun 08 2021 Michael Young - 4.15.0-3 - xen/arm: Boot modules are not scrubbed [XSA-372, CVE-2021-28693] (#1970542) From be079110100f84c7706465a6bd764b1a6092d526 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 12 Aug 2021 23:55:23 +0100 Subject: [PATCH 013/137] work around build issue with GNU ld 2.37 --- ...654ebce7ccb272a3f4f3482c03aaad850d31.patch | 36 +++++++++++++++++++ xen.spec | 5 +++ 2 files changed, 41 insertions(+) create mode 100644 xen.git-58ad654ebce7ccb272a3f4f3482c03aaad850d31.patch diff --git a/xen.git-58ad654ebce7ccb272a3f4f3482c03aaad850d31.patch b/xen.git-58ad654ebce7ccb272a3f4f3482c03aaad850d31.patch new file mode 100644 index 0000000..7229049 --- /dev/null +++ b/xen.git-58ad654ebce7ccb272a3f4f3482c03aaad850d31.patch @@ -0,0 +1,36 @@ +From 58ad654ebce7ccb272a3f4f3482c03aaad850d31 Mon Sep 17 00:00:00 2001 +From: Jan Beulich +Date: Thu, 22 Jul 2021 11:20:38 +0200 +Subject: [PATCH] x86: work around build issue with GNU ld 2.37 + +I suspect it is commit 40726f16a8d7 ("ld script expression parsing") +which broke the hypervisor build, by no longer accepting section names +with a dash in them inside ADDR() (and perhaps other script directives +expecting just a section name, not an expression): .note.gnu.build-id +is such a section. + +Quoting all section names passed to ADDR() via DECL_SECTION() works +around the regression. + +Signed-off-by: Jan Beulich +Acked-by: Andrew Cooper +--- + xen/arch/x86/xen.lds.S | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S +index 9c6c1c8005..955d5cf4a0 100644 +--- a/xen/arch/x86/xen.lds.S ++++ b/xen/arch/x86/xen.lds.S +@@ -18,7 +18,7 @@ ENTRY(efi_start) + #else /* !EFI */ + + #define FORMAT "elf64-x86-64" +-#define DECL_SECTION(x) x : AT(ADDR(x) - __XEN_VIRT_START) ++#define DECL_SECTION(x) x : AT(ADDR(#x) - __XEN_VIRT_START) + + ENTRY(start_pa) + +-- +2.30.2 + diff --git a/xen.spec b/xen.spec index 1a6a4de..dfac027 100644 --- a/xen.spec +++ b/xen.spec @@ -122,6 +122,7 @@ Patch50: xsa373-4.15-4.patch Patch51: xsa373-4.15-5.patch Patch52: xsa375.patch Patch53: xsa377.patch +Patch54: xen.git-58ad654ebce7ccb272a3f4f3482c03aaad850d31.patch %if %build_qemutrad @@ -340,6 +341,7 @@ manage Xen virtual machines. %patch51 -p1 %patch52 -p1 %patch53 -p1 +%patch54 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -937,6 +939,9 @@ fi %endif %changelog +* Thu Aug 12 2021 Michael Young - 4.15.0-5 + - work around build issue with GNU ld 2.37 (#1990344) + * Fri Jul 23 2021 Fedora Release Engineering - 4.15.0-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild From 0fd71b054e3d7a802ed232548e0b34a34b4181f3 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Fri, 13 Aug 2021 00:22:54 +0100 Subject: [PATCH 014/137] fix build release --- xen.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index dfac027..8786b4e 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.15.0 -Release: 4%{?dist} +Release: 5%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz From 2a7e6767df4e433dd16f108497b7765777a2a0e6 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 25 Aug 2021 21:28:07 +0100 Subject: [PATCH 015/137] 5 security fixes, 1 grub fix IOMMU page mapping issues on x86 [XSA-378, CVE-2021-28694, CVE-2021-28695, CVE-2021-28696] (#1997531) (#1997568) (#1997537) grant table v2 status pages may remain accessible after de-allocation [XSA-379, CVE-2021-28697] (#1997520) long running loops in grant table handling [XSA-380, CVE-2021-28698] (#1997526) inadequate grant-v2 status frames array bounds check [XSA-382, CVE-2021-28699] (#1997523) xen/arm: No memory limit for dom0less domUs [XSA-383, CVE-2021-28700] (#1997527) grub x86_64-efi modules now go into /boot/grub2 --- ...714729cf84df50d6831c4da076e21587408c.patch | 117 +++++ xen.spec | 47 +- xsa378-4.15-1.patch | 142 ++++++ xsa378-4.15-2.patch | 218 ++++++++++ xsa378-4.15-3.patch | 102 +++++ xsa378-4.15-4.patch | 399 +++++++++++++++++ xsa378-4.15-5.patch | 208 +++++++++ xsa378-4.15-6.patch | 411 ++++++++++++++++++ xsa378-4.15-7.patch | 88 ++++ xsa378-4.15-8.patch | 155 +++++++ xsa379-4.15.patch | 82 ++++ xsa380-1.patch | 178 ++++++++ xsa380-2.patch | 410 +++++++++++++++++ xsa380-4.14-1.patch | 148 +++++++ xsa380-4.14-2.patch | 383 ++++++++++++++++ xsa382.patch | 34 ++ xsa383.patch | 55 +++ 17 files changed, 3176 insertions(+), 1 deletion(-) create mode 100644 xen.git-3581714729cf84df50d6831c4da076e21587408c.patch create mode 100644 xsa378-4.15-1.patch create mode 100644 xsa378-4.15-2.patch create mode 100644 xsa378-4.15-3.patch create mode 100644 xsa378-4.15-4.patch create mode 100644 xsa378-4.15-5.patch create mode 100644 xsa378-4.15-6.patch create mode 100644 xsa378-4.15-7.patch create mode 100644 xsa378-4.15-8.patch create mode 100644 xsa379-4.15.patch create mode 100644 xsa380-1.patch create mode 100644 xsa380-2.patch create mode 100644 xsa380-4.14-1.patch create mode 100644 xsa380-4.14-2.patch create mode 100644 xsa382.patch create mode 100644 xsa383.patch diff --git a/xen.git-3581714729cf84df50d6831c4da076e21587408c.patch b/xen.git-3581714729cf84df50d6831c4da076e21587408c.patch new file mode 100644 index 0000000..6eb4f71 --- /dev/null +++ b/xen.git-3581714729cf84df50d6831c4da076e21587408c.patch @@ -0,0 +1,117 @@ +From 3581714729cf84df50d6831c4da076e21587408c Mon Sep 17 00:00:00 2001 +From: Jan Beulich +Date: Thu, 15 Jul 2021 09:21:04 +0200 +Subject: [PATCH] VT-d: adjust domid map updating when unmapping context + +When an earlier error occurred, cleaning up the domid mapping data is +wrong, as references likely still exist. The only exception to this is +when the actual unmapping worked, but some flush failed (supposedly +impossible after XSA-373). The guest will get crashed in such a case +though, so add fallback cleanup to domain destruction to cover this +case. This in turn makes it desirable to silence the dprintk() in +domain_iommu_domid(). + +Note that no error will be returned anymore when the lookup fails - in +the common case lookup failure would already have caused +domain_context_unmap_one() to fail, yet even from a more general +perspective it doesn't look right to fail domain_context_unmap() in such +a case when this was the last device, but not when any earlier unmap was +otherwise successful. + +Signed-off-by: Jan Beulich +Reviewed-by: Kevin Tian +master commit: 32655880057ce2829f962d46916ea6cec60f98d3 +master date: 2021-06-24 16:29:13 +0200 +--- + xen/drivers/passthrough/vtd/iommu.c | 39 ++++++++++++++++++----------- + 1 file changed, 24 insertions(+), 15 deletions(-) + +diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c +index c0070f1c99..2b3609dae2 100644 +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -80,9 +80,11 @@ static int domain_iommu_domid(struct domain *d, + i = find_next_bit(iommu->domid_bitmap, nr_dom, i+1); + } + +- dprintk(XENLOG_ERR VTDPREFIX, +- "Cannot get valid iommu domid: domid=%d iommu->index=%d\n", +- d->domain_id, iommu->index); ++ if ( !d->is_dying ) ++ dprintk(XENLOG_ERR VTDPREFIX, ++ "Cannot get valid iommu %u domid: %pd\n", ++ iommu->index, d); ++ + return -1; + } + +@@ -147,6 +149,17 @@ static int context_get_domain_id(struct context_entry *context, + return domid; + } + ++static void cleanup_domid_map(struct domain *domain, struct vtd_iommu *iommu) ++{ ++ int iommu_domid = domain_iommu_domid(domain, iommu); ++ ++ if ( iommu_domid >= 0 ) ++ { ++ clear_bit(iommu_domid, iommu->domid_bitmap); ++ iommu->domid_map[iommu_domid] = 0; ++ } ++} ++ + static void sync_cache(const void *addr, unsigned int size) + { + static unsigned long clflush_size = 0; +@@ -1732,6 +1745,9 @@ static int domain_context_unmap(struct domain *domain, u8 devfn, + goto out; + } + ++ if ( ret ) ++ goto out; ++ + /* + * if no other devices under the same iommu owned by this domain, + * clear iommu in iommu_bitmap and clear domain_id in domid_bitmp +@@ -1751,19 +1767,8 @@ static int domain_context_unmap(struct domain *domain, u8 devfn, + + if ( found == 0 ) + { +- int iommu_domid; +- + clear_bit(iommu->index, &dom_iommu(domain)->arch.vtd.iommu_bitmap); +- +- iommu_domid = domain_iommu_domid(domain, iommu); +- if ( iommu_domid == -1 ) +- { +- ret = -EINVAL; +- goto out; +- } +- +- clear_bit(iommu_domid, iommu->domid_bitmap); +- iommu->domid_map[iommu_domid] = 0; ++ cleanup_domid_map(domain, iommu); + } + + out: +@@ -1783,6 +1788,7 @@ static void iommu_domain_teardown(struct domain *d) + { + struct domain_iommu *hd = dom_iommu(d); + struct mapped_rmrr *mrmrr, *tmp; ++ const struct acpi_drhd_unit *drhd; + + if ( list_empty(&acpi_drhd_units) ) + return; +@@ -1794,6 +1800,9 @@ static void iommu_domain_teardown(struct domain *d) + } + + ASSERT(!hd->arch.vtd.pgd_maddr); ++ ++ for_each_drhd_unit ( drhd ) ++ cleanup_domid_map(d, drhd->iommu); + } + + static int __must_check intel_iommu_map_page(struct domain *d, dfn_t dfn, +-- +2.30.2 + diff --git a/xen.spec b/xen.spec index 8786b4e..93276be 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.15.0 -Release: 5%{?dist} +Release: 6%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -123,6 +123,20 @@ Patch51: xsa373-4.15-5.patch Patch52: xsa375.patch Patch53: xsa377.patch Patch54: xen.git-58ad654ebce7ccb272a3f4f3482c03aaad850d31.patch +Patch55: xen.git-3581714729cf84df50d6831c4da076e21587408c.patch +Patch56: xsa378-4.15-1.patch +Patch57: xsa378-4.15-2.patch +Patch58: xsa378-4.15-3.patch +Patch59: xsa378-4.15-4.patch +Patch60: xsa378-4.15-5.patch +Patch61: xsa378-4.15-6.patch +Patch62: xsa378-4.15-7.patch +Patch63: xsa378-4.15-8.patch +Patch64: xsa379-4.15.patch +Patch65: xsa380-1.patch +Patch66: xsa380-2.patch +Patch67: xsa382.patch +Patch68: xsa383.patch %if %build_qemutrad @@ -342,6 +356,20 @@ manage Xen virtual machines. %patch52 -p1 %patch53 -p1 %patch54 -p1 +%patch55 -p1 +%patch56 -p1 +%patch57 -p1 +%patch58 -p1 +%patch59 -p1 +%patch60 -p1 +%patch61 -p1 +%patch62 -p1 +%patch63 -p1 +%patch64 -p1 +%patch65 -p1 +%patch66 -p1 +%patch67 -p1 +%patch68 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -640,6 +668,9 @@ if [ -f /sbin/grub2-mkconfig ]; then DIR=/usr/lib/grub/i386-pc TARGET=/boot/grub2/i386-pc do_it $DIR $TARGET + DIR=/usr/lib/grub/x86_64-efi + TARGET=/boot/grub2/x86_64-efi + do_it $DIR $TARGET fi if [ -f /boot/efi/EFI/fedora/grub.cfg ]; then DIR=/usr/lib/grub/x86_64-efi @@ -939,6 +970,20 @@ fi %endif %changelog +* Wed Aug 25 2021 Michael Young - 4.15.0-6 +- IOMMU page mapping issues on x86 [XSA-378, CVE-2021-28694, + CVE-2021-28695, CVE-2021-28696] (#1997531) (#1997568) + (#1997537) +- grant table v2 status pages may remain accessible after de-allocation + [XSA-379, CVE-2021-28697] (#1997520) +- long running loops in grant table handling [XSA-380, CVE-2021-28698] + (#1997526) +- inadequate grant-v2 status frames array bounds check [XSA-382, + CVE-2021-28699] (#1997523) +- xen/arm: No memory limit for dom0less domUs [XSA-383, CVE-2021-28700] + (#1997527) +- grub x86_64-efi modules now go into /boot/grub2 + * Thu Aug 12 2021 Michael Young - 4.15.0-5 - work around build issue with GNU ld 2.37 (#1990344) diff --git a/xsa378-4.15-1.patch b/xsa378-4.15-1.patch new file mode 100644 index 0000000..4b38f96 --- /dev/null +++ b/xsa378-4.15-1.patch @@ -0,0 +1,142 @@ +From: Jan Beulich +Subject: AMD/IOMMU: correct global exclusion range extending + +Besides unity mapping regions, the AMD IOMMU spec also provides for +exclusion ranges (areas of memory not to be subject to DMA translation) +to be specified by firmware in the ACPI tables. The spec does not put +any constraints on the number of such regions. + +Blindly assuming all addresses between any two such ranges should also +be excluded can't be right. Since hardware has room for just a single +such range (comprised of the Exclusion Base Register and the Exclusion +Range Limit Register), combine only adjacent or overlapping regions (for +now; this may require further adjustment in case table entries aren't +sorted by address) with matching exclusion_allow_all settings. This +requires bubbling up error indicators, such that IOMMU init can be +failed when concatenation wasn't possible. + +Furthermore, since the exclusion range specified in IOMMU registers +implies R/W access, reject requests asking for less permissions (this +will be brought closer to the spec by a subsequent change). + +This is part of XSA-378 / CVE-2021-28695. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/amd/iommu_acpi.c ++++ b/xen/drivers/passthrough/amd/iommu_acpi.c +@@ -116,12 +116,21 @@ static struct amd_iommu * __init find_io + return NULL; + } + +-static void __init reserve_iommu_exclusion_range( +- struct amd_iommu *iommu, uint64_t base, uint64_t limit) ++static int __init reserve_iommu_exclusion_range( ++ struct amd_iommu *iommu, uint64_t base, uint64_t limit, ++ bool all, bool iw, bool ir) + { ++ if ( !ir || !iw ) ++ return -EPERM; ++ + /* need to extend exclusion range? */ + if ( iommu->exclusion_enable ) + { ++ if ( iommu->exclusion_limit + PAGE_SIZE < base || ++ limit + PAGE_SIZE < iommu->exclusion_base || ++ iommu->exclusion_allow_all != all ) ++ return -EBUSY; ++ + if ( iommu->exclusion_base < base ) + base = iommu->exclusion_base; + if ( iommu->exclusion_limit > limit ) +@@ -129,16 +138,11 @@ static void __init reserve_iommu_exclusi + } + + iommu->exclusion_enable = IOMMU_CONTROL_ENABLED; ++ iommu->exclusion_allow_all = all; + iommu->exclusion_base = base; + iommu->exclusion_limit = limit; +-} + +-static void __init reserve_iommu_exclusion_range_all( +- struct amd_iommu *iommu, +- unsigned long base, unsigned long limit) +-{ +- reserve_iommu_exclusion_range(iommu, base, limit); +- iommu->exclusion_allow_all = IOMMU_CONTROL_ENABLED; ++ return 0; + } + + static void __init reserve_unity_map_for_device( +@@ -176,6 +180,7 @@ static int __init register_exclusion_ran + unsigned long range_top, iommu_top, length; + struct amd_iommu *iommu; + unsigned int bdf; ++ int rc = 0; + + /* is part of exclusion range inside of IOMMU virtual address space? */ + /* note: 'limit' parameter is assumed to be page-aligned */ +@@ -197,10 +202,15 @@ static int __init register_exclusion_ran + if ( limit >= iommu_top ) + { + for_each_amd_iommu( iommu ) +- reserve_iommu_exclusion_range_all(iommu, base, limit); ++ { ++ rc = reserve_iommu_exclusion_range(iommu, base, limit, ++ true /* all */, iw, ir); ++ if ( rc ) ++ break; ++ } + } + +- return 0; ++ return rc; + } + + static int __init register_exclusion_range_for_device( +@@ -211,6 +221,7 @@ static int __init register_exclusion_ran + unsigned long range_top, iommu_top, length; + struct amd_iommu *iommu; + u16 req; ++ int rc = 0; + + iommu = find_iommu_for_device(seg, bdf); + if ( !iommu ) +@@ -240,12 +251,13 @@ static int __init register_exclusion_ran + /* register IOMMU exclusion range settings for device */ + if ( limit >= iommu_top ) + { +- reserve_iommu_exclusion_range(iommu, base, limit); ++ rc = reserve_iommu_exclusion_range(iommu, base, limit, ++ false /* all */, iw, ir); + ivrs_mappings[bdf].dte_allow_exclusion = true; + ivrs_mappings[req].dte_allow_exclusion = true; + } + +- return 0; ++ return rc; + } + + static int __init register_exclusion_range_for_iommu_devices( +@@ -255,6 +267,7 @@ static int __init register_exclusion_ran + unsigned long range_top, iommu_top, length; + unsigned int bdf; + u16 req; ++ int rc = 0; + + /* is part of exclusion range inside of IOMMU virtual address space? */ + /* note: 'limit' parameter is assumed to be page-aligned */ +@@ -285,8 +298,10 @@ static int __init register_exclusion_ran + + /* register IOMMU exclusion range settings */ + if ( limit >= iommu_top ) +- reserve_iommu_exclusion_range_all(iommu, base, limit); +- return 0; ++ rc = reserve_iommu_exclusion_range(iommu, base, limit, ++ true /* all */, iw, ir); ++ ++ return rc; + } + + static int __init parse_ivmd_device_select( diff --git a/xsa378-4.15-2.patch b/xsa378-4.15-2.patch new file mode 100644 index 0000000..aa19772 --- /dev/null +++ b/xsa378-4.15-2.patch @@ -0,0 +1,218 @@ +From: Jan Beulich +Subject: AMD/IOMMU: correct device unity map handling + +Blindly assuming all addresses between any two such ranges, specified by +firmware in the ACPI tables, should also be unity-mapped can't be right. +Nor can it be correct to merge ranges with differing permissions. Track +ranges individually; don't merge at all, but check for overlaps instead. +This requires bubbling up error indicators, such that IOMMU init can be +failed when allocation of a new tracking struct wasn't possible, or an +overlap was detected. + +At this occasion also stop ignoring +amd_iommu_reserve_domain_unity_map()'s return value. + +This is part of XSA-378 / CVE-2021-28695. + +Signed-off-by: Jan Beulich +Reviewed-by: George Dunlap +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/amd/iommu.h ++++ b/xen/drivers/passthrough/amd/iommu.h +@@ -107,20 +107,24 @@ struct amd_iommu { + struct list_head ats_devices; + }; + ++struct ivrs_unity_map { ++ bool read:1; ++ bool write:1; ++ paddr_t addr; ++ unsigned long length; ++ struct ivrs_unity_map *next; ++}; ++ + struct ivrs_mappings { + uint16_t dte_requestor_id; + bool valid:1; + bool dte_allow_exclusion:1; +- bool unity_map_enable:1; +- bool write_permission:1; +- bool read_permission:1; + + /* ivhd device data settings */ + uint8_t device_flags; + +- unsigned long addr_range_start; +- unsigned long addr_range_length; + struct amd_iommu *iommu; ++ struct ivrs_unity_map *unity_map; + + /* per device interrupt remapping table */ + void *intremap_table; +--- a/xen/drivers/passthrough/amd/iommu_acpi.c ++++ b/xen/drivers/passthrough/amd/iommu_acpi.c +@@ -145,32 +145,48 @@ static int __init reserve_iommu_exclusio + return 0; + } + +-static void __init reserve_unity_map_for_device( +- u16 seg, u16 bdf, unsigned long base, +- unsigned long length, u8 iw, u8 ir) ++static int __init reserve_unity_map_for_device( ++ uint16_t seg, uint16_t bdf, unsigned long base, ++ unsigned long length, bool iw, bool ir) + { + struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(seg); +- unsigned long old_top, new_top; ++ struct ivrs_unity_map *unity_map = ivrs_mappings[bdf].unity_map; + +- /* need to extend unity-mapped range? */ +- if ( ivrs_mappings[bdf].unity_map_enable ) ++ /* Check for overlaps. */ ++ for ( ; unity_map; unity_map = unity_map->next ) + { +- old_top = ivrs_mappings[bdf].addr_range_start + +- ivrs_mappings[bdf].addr_range_length; +- new_top = base + length; +- if ( old_top > new_top ) +- new_top = old_top; +- if ( ivrs_mappings[bdf].addr_range_start < base ) +- base = ivrs_mappings[bdf].addr_range_start; +- length = new_top - base; +- } +- +- /* extend r/w permissioms and keep aggregate */ +- ivrs_mappings[bdf].write_permission = iw; +- ivrs_mappings[bdf].read_permission = ir; +- ivrs_mappings[bdf].unity_map_enable = true; +- ivrs_mappings[bdf].addr_range_start = base; +- ivrs_mappings[bdf].addr_range_length = length; ++ /* ++ * Exact matches are okay. This can in particular happen when ++ * register_exclusion_range_for_device() calls here twice for the ++ * same (s,b,d,f). ++ */ ++ if ( base == unity_map->addr && length == unity_map->length && ++ ir == unity_map->read && iw == unity_map->write ) ++ return 0; ++ ++ if ( unity_map->addr + unity_map->length > base && ++ base + length > unity_map->addr ) ++ { ++ AMD_IOMMU_DEBUG("IVMD Error: overlap [%lx,%lx) vs [%lx,%lx)\n", ++ base, base + length, unity_map->addr, ++ unity_map->addr + unity_map->length); ++ return -EPERM; ++ } ++ } ++ ++ /* Populate and insert a new unity map. */ ++ unity_map = xmalloc(struct ivrs_unity_map); ++ if ( !unity_map ) ++ return -ENOMEM; ++ ++ unity_map->read = ir; ++ unity_map->write = iw; ++ unity_map->addr = base; ++ unity_map->length = length; ++ unity_map->next = ivrs_mappings[bdf].unity_map; ++ ivrs_mappings[bdf].unity_map = unity_map; ++ ++ return 0; + } + + static int __init register_exclusion_range_for_all_devices( +@@ -193,13 +209,13 @@ static int __init register_exclusion_ran + length = range_top - base; + /* reserve r/w unity-mapped page entries for devices */ + /* note: these entries are part of the exclusion range */ +- for ( bdf = 0; bdf < ivrs_bdf_entries; bdf++ ) +- reserve_unity_map_for_device(seg, bdf, base, length, iw, ir); ++ for ( bdf = 0; !rc && bdf < ivrs_bdf_entries; bdf++ ) ++ rc = reserve_unity_map_for_device(seg, bdf, base, length, iw, ir); + /* push 'base' just outside of virtual address space */ + base = iommu_top; + } + /* register IOMMU exclusion range settings */ +- if ( limit >= iommu_top ) ++ if ( !rc && limit >= iommu_top ) + { + for_each_amd_iommu( iommu ) + { +@@ -241,15 +257,15 @@ static int __init register_exclusion_ran + length = range_top - base; + /* reserve unity-mapped page entries for device */ + /* note: these entries are part of the exclusion range */ +- reserve_unity_map_for_device(seg, bdf, base, length, iw, ir); +- reserve_unity_map_for_device(seg, req, base, length, iw, ir); ++ rc = reserve_unity_map_for_device(seg, bdf, base, length, iw, ir) ?: ++ reserve_unity_map_for_device(seg, req, base, length, iw, ir); + + /* push 'base' just outside of virtual address space */ + base = iommu_top; + } + + /* register IOMMU exclusion range settings for device */ +- if ( limit >= iommu_top ) ++ if ( !rc && limit >= iommu_top ) + { + rc = reserve_iommu_exclusion_range(iommu, base, limit, + false /* all */, iw, ir); +@@ -280,15 +296,15 @@ static int __init register_exclusion_ran + length = range_top - base; + /* reserve r/w unity-mapped page entries for devices */ + /* note: these entries are part of the exclusion range */ +- for ( bdf = 0; bdf < ivrs_bdf_entries; bdf++ ) ++ for ( bdf = 0; !rc && bdf < ivrs_bdf_entries; bdf++ ) + { + if ( iommu == find_iommu_for_device(iommu->seg, bdf) ) + { +- reserve_unity_map_for_device(iommu->seg, bdf, base, length, +- iw, ir); + req = get_ivrs_mappings(iommu->seg)[bdf].dte_requestor_id; +- reserve_unity_map_for_device(iommu->seg, req, base, length, +- iw, ir); ++ rc = reserve_unity_map_for_device(iommu->seg, bdf, base, length, ++ iw, ir) ?: ++ reserve_unity_map_for_device(iommu->seg, req, base, length, ++ iw, ir); + } + } + +@@ -297,7 +313,7 @@ static int __init register_exclusion_ran + } + + /* register IOMMU exclusion range settings */ +- if ( limit >= iommu_top ) ++ if ( !rc && limit >= iommu_top ) + rc = reserve_iommu_exclusion_range(iommu, base, limit, + true /* all */, iw, ir); + +--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c ++++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c +@@ -367,15 +367,17 @@ static int amd_iommu_assign_device(struc + struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(pdev->seg); + int bdf = PCI_BDF2(pdev->bus, devfn); + int req_id = get_dma_requestor_id(pdev->seg, bdf); ++ const struct ivrs_unity_map *unity_map; + +- if ( ivrs_mappings[req_id].unity_map_enable ) ++ for ( unity_map = ivrs_mappings[req_id].unity_map; unity_map; ++ unity_map = unity_map->next ) + { +- amd_iommu_reserve_domain_unity_map( +- d, +- ivrs_mappings[req_id].addr_range_start, +- ivrs_mappings[req_id].addr_range_length, +- ivrs_mappings[req_id].write_permission, +- ivrs_mappings[req_id].read_permission); ++ int rc = amd_iommu_reserve_domain_unity_map( ++ d, unity_map->addr, unity_map->length, ++ unity_map->write, unity_map->read); ++ ++ if ( rc ) ++ return rc; + } + + return reassign_device(pdev->domain, d, devfn, pdev); diff --git a/xsa378-4.15-3.patch b/xsa378-4.15-3.patch new file mode 100644 index 0000000..9e1bd18 --- /dev/null +++ b/xsa378-4.15-3.patch @@ -0,0 +1,102 @@ +From: Jan Beulich +Subject: IOMMU: also pass p2m_access_t to p2m_get_iommu_flags() + +A subsequent change will want to customize the IOMMU permissions based +on this. + +This is part of XSA-378. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/arch/x86/mm/p2m-ept.c ++++ b/xen/arch/x86/mm/p2m-ept.c +@@ -681,7 +681,7 @@ ept_set_entry(struct p2m_domain *p2m, gf + uint8_t ipat = 0; + bool_t need_modify_vtd_table = 1; + bool_t vtd_pte_present = 0; +- unsigned int iommu_flags = p2m_get_iommu_flags(p2mt, mfn); ++ unsigned int iommu_flags = p2m_get_iommu_flags(p2mt, p2ma, mfn); + bool_t needs_sync = 1; + ept_entry_t old_entry = { .epte = 0 }; + ept_entry_t new_entry = { .epte = 0 }; +@@ -809,8 +809,8 @@ ept_set_entry(struct p2m_domain *p2m, gf + + /* Safe to read-then-write because we hold the p2m lock */ + if ( ept_entry->mfn == new_entry.mfn && +- p2m_get_iommu_flags(ept_entry->sa_p2mt, _mfn(ept_entry->mfn)) == +- iommu_flags ) ++ p2m_get_iommu_flags(ept_entry->sa_p2mt, ept_entry->access, ++ _mfn(ept_entry->mfn)) == iommu_flags ) + need_modify_vtd_table = 0; + + ept_p2m_type_to_flags(p2m, &new_entry); +--- a/xen/arch/x86/mm/p2m-pt.c ++++ b/xen/arch/x86/mm/p2m-pt.c +@@ -545,6 +545,16 @@ int p2m_pt_handle_deferred_changes(uint6 + return rc; + } + ++/* Reconstruct a fake p2m_access_t from stored PTE flags. */ ++static p2m_access_t p2m_flags_to_access(unsigned int flags) ++{ ++ if ( flags & _PAGE_PRESENT ) ++ return p2m_access_n; ++ ++ /* No need to look at _PAGE_NX for now. */ ++ return flags & _PAGE_RW ? p2m_access_rw : p2m_access_r; ++} ++ + /* Checks only applicable to entries with order > PAGE_ORDER_4K */ + static void check_entry(mfn_t mfn, p2m_type_t new, p2m_type_t old, + unsigned int order) +@@ -579,7 +589,7 @@ p2m_pt_set_entry(struct p2m_domain *p2m, + l2_pgentry_t l2e_content; + l3_pgentry_t l3e_content; + int rc; +- unsigned int iommu_pte_flags = p2m_get_iommu_flags(p2mt, mfn); ++ unsigned int iommu_pte_flags = p2m_get_iommu_flags(p2mt, p2ma, mfn); + /* + * old_mfn and iommu_old_flags control possible flush/update needs on the + * IOMMU: We need to flush when MFN or flags (i.e. permissions) change. +@@ -642,6 +652,7 @@ p2m_pt_set_entry(struct p2m_domain *p2m, + old_mfn = l1e_get_pfn(*p2m_entry); + iommu_old_flags = + p2m_get_iommu_flags(p2m_flags_to_type(flags), ++ p2m_flags_to_access(flags), + _mfn(old_mfn)); + } + else +@@ -684,9 +695,10 @@ p2m_pt_set_entry(struct p2m_domain *p2m, + 0, L1_PAGETABLE_ENTRIES); + ASSERT(p2m_entry); + old_mfn = l1e_get_pfn(*p2m_entry); ++ flags = l1e_get_flags(*p2m_entry); + iommu_old_flags = +- p2m_get_iommu_flags(p2m_flags_to_type(l1e_get_flags(*p2m_entry)), +- _mfn(old_mfn)); ++ p2m_get_iommu_flags(p2m_flags_to_type(flags), ++ p2m_flags_to_access(flags), _mfn(old_mfn)); + + if ( mfn_valid(mfn) || p2m_allows_invalid_mfn(p2mt) ) + entry_content = p2m_l1e_from_pfn(mfn_x(mfn), +@@ -714,6 +726,7 @@ p2m_pt_set_entry(struct p2m_domain *p2m, + old_mfn = l1e_get_pfn(*p2m_entry); + iommu_old_flags = + p2m_get_iommu_flags(p2m_flags_to_type(flags), ++ p2m_flags_to_access(flags), + _mfn(old_mfn)); + } + else +--- a/xen/include/asm-x86/p2m.h ++++ b/xen/include/asm-x86/p2m.h +@@ -915,7 +915,8 @@ static inline void p2m_altp2m_check(stru + /* + * p2m type to IOMMU flags + */ +-static inline unsigned int p2m_get_iommu_flags(p2m_type_t p2mt, mfn_t mfn) ++static inline unsigned int p2m_get_iommu_flags(p2m_type_t p2mt, ++ p2m_access_t p2ma, mfn_t mfn) + { + unsigned int flags; + diff --git a/xsa378-4.15-4.patch b/xsa378-4.15-4.patch new file mode 100644 index 0000000..a1d86db --- /dev/null +++ b/xsa378-4.15-4.patch @@ -0,0 +1,399 @@ +From: Jan Beulich +Subject: IOMMU: generalize VT-d's tracking of mapped RMRR regions + +In order to re-use it elsewhere, move the logic to vendor independent +code and strip it of RMRR specifics. + +Note that the prior "map" parameter gets folded into the new "p2ma" one +(which AMD IOMMU code will want to make use of), assigning alternative +meaning ("unmap") to p2m_access_x. Prepare set_identity_p2m_entry() and +p2m_get_iommu_flags() for getting passed access types other than +p2m_access_rw (in the latter case just for p2m_mmio_direct requests). + +Note also that, to be on the safe side, an overlap check gets added to +the main loop of iommu_identity_mapping(). + +This is part of XSA-378. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/arch/x86/mm/p2m.c ++++ b/xen/arch/x86/mm/p2m.c +@@ -1365,7 +1365,7 @@ int set_identity_p2m_entry(struct domain + return 0; + return iommu_legacy_map(d, _dfn(gfn_l), _mfn(gfn_l), + 1ul << PAGE_ORDER_4K, +- IOMMUF_readable | IOMMUF_writable); ++ p2m_access_to_iommu_flags(p2ma)); + } + + gfn_lock(p2m, gfn, 0); +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -42,12 +42,6 @@ + #include "vtd.h" + #include "../ats.h" + +-struct mapped_rmrr { +- struct list_head list; +- u64 base, end; +- unsigned int count; +-}; +- + /* Possible unfiltered LAPIC/MSI messages from untrusted sources? */ + bool __read_mostly untrusted_msi; + +@@ -1311,7 +1305,6 @@ static int intel_iommu_domain_init(struc + struct domain_iommu *hd = dom_iommu(d); + + hd->arch.vtd.agaw = width_to_agaw(DEFAULT_DOMAIN_ADDRESS_WIDTH); +- INIT_LIST_HEAD(&hd->arch.vtd.mapped_rmrrs); + + return 0; + } +@@ -1788,17 +1781,12 @@ static void iommu_clear_root_pgtable(str + static void iommu_domain_teardown(struct domain *d) + { + struct domain_iommu *hd = dom_iommu(d); +- struct mapped_rmrr *mrmrr, *tmp; + const struct acpi_drhd_unit *drhd; + + if ( list_empty(&acpi_drhd_units) ) + return; + +- list_for_each_entry_safe ( mrmrr, tmp, &hd->arch.vtd.mapped_rmrrs, list ) +- { +- list_del(&mrmrr->list); +- xfree(mrmrr); +- } ++ iommu_identity_map_teardown(d); + + ASSERT(!hd->arch.vtd.pgd_maddr); + +@@ -1946,74 +1934,6 @@ static int __init vtd_ept_page_compatibl + (ept_has_1gb(ept_cap) && opt_hap_1gb) <= cap_sps_1gb(vtd_cap); + } + +-static int rmrr_identity_mapping(struct domain *d, bool_t map, +- const struct acpi_rmrr_unit *rmrr, +- u32 flag) +-{ +- unsigned long base_pfn = rmrr->base_address >> PAGE_SHIFT_4K; +- unsigned long end_pfn = PAGE_ALIGN_4K(rmrr->end_address) >> PAGE_SHIFT_4K; +- struct mapped_rmrr *mrmrr; +- struct domain_iommu *hd = dom_iommu(d); +- +- ASSERT(pcidevs_locked()); +- ASSERT(rmrr->base_address < rmrr->end_address); +- +- /* +- * No need to acquire hd->arch.mapping_lock: Both insertion and removal +- * get done while holding pcidevs_lock. +- */ +- list_for_each_entry( mrmrr, &hd->arch.vtd.mapped_rmrrs, list ) +- { +- if ( mrmrr->base == rmrr->base_address && +- mrmrr->end == rmrr->end_address ) +- { +- int ret = 0; +- +- if ( map ) +- { +- ++mrmrr->count; +- return 0; +- } +- +- if ( --mrmrr->count ) +- return 0; +- +- while ( base_pfn < end_pfn ) +- { +- if ( clear_identity_p2m_entry(d, base_pfn) ) +- ret = -ENXIO; +- base_pfn++; +- } +- +- list_del(&mrmrr->list); +- xfree(mrmrr); +- return ret; +- } +- } +- +- if ( !map ) +- return -ENOENT; +- +- while ( base_pfn < end_pfn ) +- { +- int err = set_identity_p2m_entry(d, base_pfn, p2m_access_rw, flag); +- +- if ( err ) +- return err; +- base_pfn++; +- } +- +- mrmrr = xmalloc(struct mapped_rmrr); +- if ( !mrmrr ) +- return -ENOMEM; +- mrmrr->base = rmrr->base_address; +- mrmrr->end = rmrr->end_address; +- mrmrr->count = 1; +- list_add_tail(&mrmrr->list, &hd->arch.vtd.mapped_rmrrs); +- +- return 0; +-} +- + static int intel_iommu_add_device(u8 devfn, struct pci_dev *pdev) + { + struct acpi_rmrr_unit *rmrr; +@@ -2045,7 +1965,9 @@ static int intel_iommu_add_device(u8 dev + * Since RMRRs are always reserved in the e820 map for the hardware + * domain, there shouldn't be a conflict. + */ +- ret = rmrr_identity_mapping(pdev->domain, 1, rmrr, 0); ++ ret = iommu_identity_mapping(pdev->domain, p2m_access_rw, ++ rmrr->base_address, rmrr->end_address, ++ 0); + if ( ret ) + dprintk(XENLOG_ERR VTDPREFIX, "d%d: RMRR mapping failed\n", + pdev->domain->domain_id); +@@ -2090,7 +2012,8 @@ static int intel_iommu_remove_device(u8 + * Any flag is nothing to clear these mappings but here + * its always safe and strict to set 0. + */ +- rmrr_identity_mapping(pdev->domain, 0, rmrr, 0); ++ iommu_identity_mapping(pdev->domain, p2m_access_x, rmrr->base_address, ++ rmrr->end_address, 0); + } + + return domain_context_unmap(pdev->domain, devfn, pdev); +@@ -2289,7 +2212,8 @@ static void __hwdom_init setup_hwdom_rmr + * domain, there shouldn't be a conflict. So its always safe and + * strict to set 0. + */ +- ret = rmrr_identity_mapping(d, 1, rmrr, 0); ++ ret = iommu_identity_mapping(d, p2m_access_rw, rmrr->base_address, ++ rmrr->end_address, 0); + if ( ret ) + dprintk(XENLOG_ERR VTDPREFIX, + "IOMMU: mapping reserved region failed\n"); +@@ -2460,7 +2384,9 @@ static int reassign_device_ownership( + * Any RMRR flag is always ignored when remove a device, + * but its always safe and strict to set 0. + */ +- ret = rmrr_identity_mapping(source, 0, rmrr, 0); ++ ret = iommu_identity_mapping(source, p2m_access_x, ++ rmrr->base_address, ++ rmrr->end_address, 0); + if ( ret != -ENOENT ) + return ret; + } +@@ -2556,7 +2482,8 @@ static int intel_iommu_assign_device( + PCI_BUS(bdf) == bus && + PCI_DEVFN2(bdf) == devfn ) + { +- ret = rmrr_identity_mapping(d, 1, rmrr, flag); ++ ret = iommu_identity_mapping(d, p2m_access_rw, rmrr->base_address, ++ rmrr->end_address, flag); + if ( ret ) + { + int rc; +--- a/xen/drivers/passthrough/x86/iommu.c ++++ b/xen/drivers/passthrough/x86/iommu.c +@@ -143,6 +143,7 @@ int arch_iommu_domain_init(struct domain + + INIT_PAGE_LIST_HEAD(&hd->arch.pgtables.list); + spin_lock_init(&hd->arch.pgtables.lock); ++ INIT_LIST_HEAD(&hd->arch.identity_maps); + + return 0; + } +@@ -158,6 +159,99 @@ void arch_iommu_domain_destroy(struct do + page_list_empty(&dom_iommu(d)->arch.pgtables.list)); + } + ++struct identity_map { ++ struct list_head list; ++ paddr_t base, end; ++ p2m_access_t access; ++ unsigned int count; ++}; ++ ++int iommu_identity_mapping(struct domain *d, p2m_access_t p2ma, ++ paddr_t base, paddr_t end, ++ unsigned int flag) ++{ ++ unsigned long base_pfn = base >> PAGE_SHIFT_4K; ++ unsigned long end_pfn = PAGE_ALIGN_4K(end) >> PAGE_SHIFT_4K; ++ struct identity_map *map; ++ struct domain_iommu *hd = dom_iommu(d); ++ ++ ASSERT(pcidevs_locked()); ++ ASSERT(base < end); ++ ++ /* ++ * No need to acquire hd->arch.mapping_lock: Both insertion and removal ++ * get done while holding pcidevs_lock. ++ */ ++ list_for_each_entry( map, &hd->arch.identity_maps, list ) ++ { ++ if ( map->base == base && map->end == end ) ++ { ++ int ret = 0; ++ ++ if ( p2ma != p2m_access_x ) ++ { ++ if ( map->access != p2ma ) ++ return -EADDRINUSE; ++ ++map->count; ++ return 0; ++ } ++ ++ if ( --map->count ) ++ return 0; ++ ++ while ( base_pfn < end_pfn ) ++ { ++ if ( clear_identity_p2m_entry(d, base_pfn) ) ++ ret = -ENXIO; ++ base_pfn++; ++ } ++ ++ list_del(&map->list); ++ xfree(map); ++ ++ return ret; ++ } ++ ++ if ( end >= map->base && map->end >= base ) ++ return -EADDRINUSE; ++ } ++ ++ if ( p2ma == p2m_access_x ) ++ return -ENOENT; ++ ++ while ( base_pfn < end_pfn ) ++ { ++ int err = set_identity_p2m_entry(d, base_pfn, p2ma, flag); ++ ++ if ( err ) ++ return err; ++ base_pfn++; ++ } ++ ++ map = xmalloc(struct identity_map); ++ if ( !map ) ++ return -ENOMEM; ++ map->base = base; ++ map->end = end; ++ map->access = p2ma; ++ map->count = 1; ++ list_add_tail(&map->list, &hd->arch.identity_maps); ++ ++ return 0; ++} ++ ++void iommu_identity_map_teardown(struct domain *d) ++{ ++ struct domain_iommu *hd = dom_iommu(d); ++ struct identity_map *map, *tmp; ++ ++ list_for_each_entry_safe ( map, tmp, &hd->arch.identity_maps, list ) ++ { ++ list_del(&map->list); ++ xfree(map); ++ } ++} ++ + static bool __hwdom_init hwdom_iommu_map(const struct domain *d, + unsigned long pfn, + unsigned long max_pfn) +--- a/xen/include/asm-x86/iommu.h ++++ b/xen/include/asm-x86/iommu.h +@@ -16,6 +16,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -51,13 +52,14 @@ struct arch_iommu + spinlock_t lock; + } pgtables; + ++ struct list_head identity_maps; ++ + union { + /* Intel VT-d */ + struct { + uint64_t pgd_maddr; /* io page directory machine address */ + unsigned int agaw; /* adjusted guest address width, 0 is level 2 30-bit */ + uint64_t iommu_bitmap; /* bitmap of iommu(s) that the domain uses */ +- struct list_head mapped_rmrrs; + } vtd; + /* AMD IOMMU */ + struct { +@@ -123,6 +125,11 @@ static inline void iommu_disable_x2apic( + iommu_ops.disable_x2apic(); + } + ++int iommu_identity_mapping(struct domain *d, p2m_access_t p2ma, ++ paddr_t base, paddr_t end, ++ unsigned int flag); ++void iommu_identity_map_teardown(struct domain *d); ++ + extern bool untrusted_msi; + + int pi_update_irte(const struct pi_desc *pi_desc, const struct pirq *pirq, +--- a/xen/include/asm-x86/p2m.h ++++ b/xen/include/asm-x86/p2m.h +@@ -912,6 +912,34 @@ struct p2m_domain *p2m_get_altp2m(struct + static inline void p2m_altp2m_check(struct vcpu *v, uint16_t idx) {} + #endif + ++/* p2m access to IOMMU flags */ ++static inline unsigned int p2m_access_to_iommu_flags(p2m_access_t p2ma) ++{ ++ switch ( p2ma ) ++ { ++ case p2m_access_rw: ++ case p2m_access_rwx: ++ return IOMMUF_readable | IOMMUF_writable; ++ ++ case p2m_access_r: ++ case p2m_access_rx: ++ case p2m_access_rx2rw: ++ return IOMMUF_readable; ++ ++ case p2m_access_w: ++ case p2m_access_wx: ++ return IOMMUF_writable; ++ ++ case p2m_access_n: ++ case p2m_access_x: ++ case p2m_access_n2rwx: ++ return 0; ++ } ++ ++ ASSERT_UNREACHABLE(); ++ return 0; ++} ++ + /* + * p2m type to IOMMU flags + */ +@@ -933,9 +961,10 @@ static inline unsigned int p2m_get_iommu + flags = IOMMUF_readable; + break; + case p2m_mmio_direct: +- flags = IOMMUF_readable; +- if ( !rangeset_contains_singleton(mmio_ro_ranges, mfn_x(mfn)) ) +- flags |= IOMMUF_writable; ++ flags = p2m_access_to_iommu_flags(p2ma); ++ if ( (flags & IOMMUF_writable) && ++ rangeset_contains_singleton(mmio_ro_ranges, mfn_x(mfn)) ) ++ flags &= ~IOMMUF_writable; + break; + default: + flags = 0; diff --git a/xsa378-4.15-5.patch b/xsa378-4.15-5.patch new file mode 100644 index 0000000..134f425 --- /dev/null +++ b/xsa378-4.15-5.patch @@ -0,0 +1,208 @@ +From: Jan Beulich +Subject: AMD/IOMMU: re-arrange/complete re-assignment handling + +Prior to the assignment step having completed successfully, devices +should not get associated with their new owner. Hand the device to DomIO +(perhaps temporarily), until after the de-assignment step has completed. + +De-assignment of a device (from other than Dom0) as well as failure of +reassign_device() during assignment should result in unity mappings +getting torn down. This in turn requires switching to a refcounted +mapping approach, as was already used by VT-d for its RMRRs, to prevent +unmapping a region used by multiple devices. + +This is CVE-2021-28696 / part of XSA-378. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/amd/iommu.h ++++ b/xen/drivers/passthrough/amd/iommu.h +@@ -232,8 +232,10 @@ int __must_check amd_iommu_unmap_page(st + unsigned int *flush_flags); + int __must_check amd_iommu_alloc_root(struct domain *d); + int amd_iommu_reserve_domain_unity_map(struct domain *domain, +- paddr_t phys_addr, unsigned long size, +- int iw, int ir); ++ const struct ivrs_unity_map *map, ++ unsigned int flag); ++int amd_iommu_reserve_domain_unity_unmap(struct domain *d, ++ const struct ivrs_unity_map *map); + int __must_check amd_iommu_flush_iotlb_pages(struct domain *d, dfn_t dfn, + unsigned long page_count, + unsigned int flush_flags); +--- a/xen/drivers/passthrough/amd/iommu_map.c ++++ b/xen/drivers/passthrough/amd/iommu_map.c +@@ -419,38 +419,49 @@ int amd_iommu_flush_iotlb_all(struct dom + return 0; + } + +-int amd_iommu_reserve_domain_unity_map(struct domain *domain, +- paddr_t phys_addr, +- unsigned long size, int iw, int ir) ++int amd_iommu_reserve_domain_unity_map(struct domain *d, ++ const struct ivrs_unity_map *map, ++ unsigned int flag) + { +- unsigned long npages, i; +- unsigned long gfn; +- unsigned int flags = !!ir; +- unsigned int flush_flags = 0; +- int rt = 0; +- +- if ( iw ) +- flags |= IOMMUF_writable; +- +- npages = region_to_pages(phys_addr, size); +- gfn = phys_addr >> PAGE_SHIFT; +- for ( i = 0; i < npages; i++ ) ++ int rc; ++ ++ if ( d == dom_io ) ++ return 0; ++ ++ for ( rc = 0; !rc && map; map = map->next ) + { +- unsigned long frame = gfn + i; ++ p2m_access_t p2ma = p2m_access_n; ++ ++ if ( map->read ) ++ p2ma |= p2m_access_r; ++ if ( map->write ) ++ p2ma |= p2m_access_w; + +- rt = amd_iommu_map_page(domain, _dfn(frame), _mfn(frame), flags, +- &flush_flags); +- if ( rt != 0 ) +- break; ++ rc = iommu_identity_mapping(d, p2ma, map->addr, ++ map->addr + map->length - 1, flag); + } + +- /* Use while-break to avoid compiler warning */ +- while ( flush_flags && +- amd_iommu_flush_iotlb_pages(domain, _dfn(gfn), +- npages, flush_flags) ) +- break; ++ return rc; ++} ++ ++int amd_iommu_reserve_domain_unity_unmap(struct domain *d, ++ const struct ivrs_unity_map *map) ++{ ++ int rc; ++ ++ if ( d == dom_io ) ++ return 0; ++ ++ for ( rc = 0; map; map = map->next ) ++ { ++ int ret = iommu_identity_mapping(d, p2m_access_x, map->addr, ++ map->addr + map->length - 1, 0); ++ ++ if ( ret && ret != -ENOENT && !rc ) ++ rc = ret; ++ } + +- return rt; ++ return rc; + } + + int __init amd_iommu_quarantine_init(struct domain *d) +--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c ++++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c +@@ -329,6 +329,7 @@ static int reassign_device(struct domain + { + struct amd_iommu *iommu; + int bdf, rc; ++ const struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(pdev->seg); + + bdf = PCI_BDF2(pdev->bus, pdev->devfn); + iommu = find_iommu_for_device(pdev->seg, bdf); +@@ -343,10 +344,24 @@ static int reassign_device(struct domain + + amd_iommu_disable_domain_device(source, iommu, devfn, pdev); + +- if ( devfn == pdev->devfn ) ++ /* ++ * If the device belongs to the hardware domain, and it has a unity mapping, ++ * don't remove it from the hardware domain, because BIOS may reference that ++ * mapping. ++ */ ++ if ( !is_hardware_domain(source) ) + { +- list_move(&pdev->domain_list, &target->pdev_list); +- pdev->domain = target; ++ rc = amd_iommu_reserve_domain_unity_unmap( ++ source, ++ ivrs_mappings[get_dma_requestor_id(pdev->seg, bdf)].unity_map); ++ if ( rc ) ++ return rc; ++ } ++ ++ if ( devfn == pdev->devfn && pdev->domain != dom_io ) ++ { ++ list_move(&pdev->domain_list, &dom_io->pdev_list); ++ pdev->domain = dom_io; + } + + rc = allocate_domain_resources(target); +@@ -357,6 +372,12 @@ static int reassign_device(struct domain + AMD_IOMMU_DEBUG("Re-assign %pp from dom%d to dom%d\n", + &pdev->sbdf, source->domain_id, target->domain_id); + ++ if ( devfn == pdev->devfn && pdev->domain != target ) ++ { ++ list_move(&pdev->domain_list, &target->pdev_list); ++ pdev->domain = target; ++ } ++ + return 0; + } + +@@ -367,20 +388,28 @@ static int amd_iommu_assign_device(struc + struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(pdev->seg); + int bdf = PCI_BDF2(pdev->bus, devfn); + int req_id = get_dma_requestor_id(pdev->seg, bdf); +- const struct ivrs_unity_map *unity_map; ++ int rc = amd_iommu_reserve_domain_unity_map( ++ d, ivrs_mappings[req_id].unity_map, flag); + +- for ( unity_map = ivrs_mappings[req_id].unity_map; unity_map; +- unity_map = unity_map->next ) ++ if ( !rc ) ++ rc = reassign_device(pdev->domain, d, devfn, pdev); ++ ++ if ( rc && !is_hardware_domain(d) ) + { +- int rc = amd_iommu_reserve_domain_unity_map( +- d, unity_map->addr, unity_map->length, +- unity_map->write, unity_map->read); ++ int ret = amd_iommu_reserve_domain_unity_unmap( ++ d, ivrs_mappings[req_id].unity_map); + +- if ( rc ) +- return rc; ++ if ( ret ) ++ { ++ printk(XENLOG_ERR "AMD-Vi: " ++ "unity-unmap for %pd/%04x:%02x:%02x.%u failed (%d)\n", ++ d, pdev->seg, pdev->bus, ++ PCI_SLOT(devfn), PCI_FUNC(devfn), ret); ++ domain_crash(d); ++ } + } + +- return reassign_device(pdev->domain, d, devfn, pdev); ++ return rc; + } + + static void amd_iommu_clear_root_pgtable(struct domain *d) +@@ -394,6 +423,7 @@ static void amd_iommu_clear_root_pgtable + + static void amd_iommu_domain_destroy(struct domain *d) + { ++ iommu_identity_map_teardown(d); + ASSERT(!dom_iommu(d)->arch.amd.root_table); + } + diff --git a/xsa378-4.15-6.patch b/xsa378-4.15-6.patch new file mode 100644 index 0000000..d6ebc2a --- /dev/null +++ b/xsa378-4.15-6.patch @@ -0,0 +1,411 @@ +From: Jan Beulich +Subject: AMD/IOMMU: re-arrange exclusion range and unity map recording + +The spec makes no provisions for OS behavior here to depend on the +amount of RAM found on the system. While the spec may not sufficiently +clearly distinguish both kinds of regions, they are surely meant to be +separate things: Only regions with ACPI_IVMD_EXCLUSION_RANGE set should +be candidates for putting in the exclusion range registers. (As there's +only a single such pair of registers per IOMMU, secondary non-adjacent +regions with the flag set already get converted to unity mapped +regions.) + +First of all, drop the dependency on max_page. With commit b4f042236ae0 +("AMD/IOMMU: Cease using a dynamic height for the IOMMU pagetables") the +use of it here was stale anyway; it was bogus already before, as it +didn't account for max_page getting increased later on. Simply try an +exclusion range registration first, and if it fails (for being +unsuitable or non-mergeable), register a unity mapping range. + +With this various local variables become unnecessary and hence get +dropped at the same time. + +With the max_page boundary dropped for using unity maps, the minimum +page table tree height now needs both recording and enforcing in +amd_iommu_domain_init(). Since we can't predict which devices may get +assigned to a domain, our only option is to uniformly force at least +that height for all domains, now that the height isn't dynamic anymore. + +Further don't make use of the exclusion range unless ACPI data says so. + +Note that exclusion range registration in +register_range_for_all_devices() is on a best effort basis. Hence unity +map entries also registered are redundant when the former succeeded, but +they also do no harm. Improvements in this area can be done later imo. + +Also adjust types where suitable without touching extra lines. + +This is part of XSA-378. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/amd/iommu.h ++++ b/xen/drivers/passthrough/amd/iommu.h +@@ -304,6 +304,8 @@ extern struct hpet_sbdf { + } init; + } hpet_sbdf; + ++extern int amd_iommu_min_paging_mode; ++ + extern void *shared_intremap_table; + extern unsigned long *shared_intremap_inuse; + +--- a/xen/drivers/passthrough/amd/iommu_acpi.c ++++ b/xen/drivers/passthrough/amd/iommu_acpi.c +@@ -117,12 +117,8 @@ static struct amd_iommu * __init find_io + } + + static int __init reserve_iommu_exclusion_range( +- struct amd_iommu *iommu, uint64_t base, uint64_t limit, +- bool all, bool iw, bool ir) ++ struct amd_iommu *iommu, paddr_t base, paddr_t limit, bool all) + { +- if ( !ir || !iw ) +- return -EPERM; +- + /* need to extend exclusion range? */ + if ( iommu->exclusion_enable ) + { +@@ -151,14 +147,18 @@ static int __init reserve_unity_map_for_ + { + struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(seg); + struct ivrs_unity_map *unity_map = ivrs_mappings[bdf].unity_map; ++ int paging_mode = amd_iommu_get_paging_mode(PFN_UP(base + length)); ++ ++ if ( paging_mode < 0 ) ++ return paging_mode; + + /* Check for overlaps. */ + for ( ; unity_map; unity_map = unity_map->next ) + { + /* + * Exact matches are okay. This can in particular happen when +- * register_exclusion_range_for_device() calls here twice for the +- * same (s,b,d,f). ++ * register_range_for_device() calls here twice for the same ++ * (s,b,d,f). + */ + if ( base == unity_map->addr && length == unity_map->length && + ir == unity_map->read && iw == unity_map->write ) +@@ -186,55 +186,52 @@ static int __init reserve_unity_map_for_ + unity_map->next = ivrs_mappings[bdf].unity_map; + ivrs_mappings[bdf].unity_map = unity_map; + ++ if ( paging_mode > amd_iommu_min_paging_mode ) ++ amd_iommu_min_paging_mode = paging_mode; ++ + return 0; + } + +-static int __init register_exclusion_range_for_all_devices( +- unsigned long base, unsigned long limit, u8 iw, u8 ir) ++static int __init register_range_for_all_devices( ++ paddr_t base, paddr_t limit, bool iw, bool ir, bool exclusion) + { + int seg = 0; /* XXX */ +- unsigned long range_top, iommu_top, length; + struct amd_iommu *iommu; +- unsigned int bdf; + int rc = 0; + + /* is part of exclusion range inside of IOMMU virtual address space? */ + /* note: 'limit' parameter is assumed to be page-aligned */ +- range_top = limit + PAGE_SIZE; +- iommu_top = max_page * PAGE_SIZE; +- if ( base < iommu_top ) +- { +- if ( range_top > iommu_top ) +- range_top = iommu_top; +- length = range_top - base; +- /* reserve r/w unity-mapped page entries for devices */ +- /* note: these entries are part of the exclusion range */ +- for ( bdf = 0; !rc && bdf < ivrs_bdf_entries; bdf++ ) +- rc = reserve_unity_map_for_device(seg, bdf, base, length, iw, ir); +- /* push 'base' just outside of virtual address space */ +- base = iommu_top; +- } +- /* register IOMMU exclusion range settings */ +- if ( !rc && limit >= iommu_top ) ++ if ( exclusion ) + { + for_each_amd_iommu( iommu ) + { +- rc = reserve_iommu_exclusion_range(iommu, base, limit, +- true /* all */, iw, ir); +- if ( rc ) +- break; ++ int ret = reserve_iommu_exclusion_range(iommu, base, limit, ++ true /* all */); ++ ++ if ( ret && !rc ) ++ rc = ret; + } + } + ++ if ( !exclusion || rc ) ++ { ++ paddr_t length = limit + PAGE_SIZE - base; ++ unsigned int bdf; ++ ++ /* reserve r/w unity-mapped page entries for devices */ ++ for ( bdf = rc = 0; !rc && bdf < ivrs_bdf_entries; bdf++ ) ++ rc = reserve_unity_map_for_device(seg, bdf, base, length, iw, ir); ++ } ++ + return rc; + } + +-static int __init register_exclusion_range_for_device( +- u16 bdf, unsigned long base, unsigned long limit, u8 iw, u8 ir) ++static int __init register_range_for_device( ++ unsigned int bdf, paddr_t base, paddr_t limit, ++ bool iw, bool ir, bool exclusion) + { + int seg = 0; /* XXX */ + struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(seg); +- unsigned long range_top, iommu_top, length; + struct amd_iommu *iommu; + u16 req; + int rc = 0; +@@ -248,27 +245,19 @@ static int __init register_exclusion_ran + req = ivrs_mappings[bdf].dte_requestor_id; + + /* note: 'limit' parameter is assumed to be page-aligned */ +- range_top = limit + PAGE_SIZE; +- iommu_top = max_page * PAGE_SIZE; +- if ( base < iommu_top ) +- { +- if ( range_top > iommu_top ) +- range_top = iommu_top; +- length = range_top - base; ++ if ( exclusion ) ++ rc = reserve_iommu_exclusion_range(iommu, base, limit, ++ false /* all */); ++ if ( !exclusion || rc ) ++ { ++ paddr_t length = limit + PAGE_SIZE - base; ++ + /* reserve unity-mapped page entries for device */ +- /* note: these entries are part of the exclusion range */ + rc = reserve_unity_map_for_device(seg, bdf, base, length, iw, ir) ?: + reserve_unity_map_for_device(seg, req, base, length, iw, ir); +- +- /* push 'base' just outside of virtual address space */ +- base = iommu_top; + } +- +- /* register IOMMU exclusion range settings for device */ +- if ( !rc && limit >= iommu_top ) ++ else + { +- rc = reserve_iommu_exclusion_range(iommu, base, limit, +- false /* all */, iw, ir); + ivrs_mappings[bdf].dte_allow_exclusion = true; + ivrs_mappings[req].dte_allow_exclusion = true; + } +@@ -276,53 +265,42 @@ static int __init register_exclusion_ran + return rc; + } + +-static int __init register_exclusion_range_for_iommu_devices( +- struct amd_iommu *iommu, +- unsigned long base, unsigned long limit, u8 iw, u8 ir) ++static int __init register_range_for_iommu_devices( ++ struct amd_iommu *iommu, paddr_t base, paddr_t limit, ++ bool iw, bool ir, bool exclusion) + { +- unsigned long range_top, iommu_top, length; ++ /* note: 'limit' parameter is assumed to be page-aligned */ ++ paddr_t length = limit + PAGE_SIZE - base; + unsigned int bdf; + u16 req; +- int rc = 0; ++ int rc; + +- /* is part of exclusion range inside of IOMMU virtual address space? */ +- /* note: 'limit' parameter is assumed to be page-aligned */ +- range_top = limit + PAGE_SIZE; +- iommu_top = max_page * PAGE_SIZE; +- if ( base < iommu_top ) +- { +- if ( range_top > iommu_top ) +- range_top = iommu_top; +- length = range_top - base; +- /* reserve r/w unity-mapped page entries for devices */ +- /* note: these entries are part of the exclusion range */ +- for ( bdf = 0; !rc && bdf < ivrs_bdf_entries; bdf++ ) +- { +- if ( iommu == find_iommu_for_device(iommu->seg, bdf) ) +- { +- req = get_ivrs_mappings(iommu->seg)[bdf].dte_requestor_id; +- rc = reserve_unity_map_for_device(iommu->seg, bdf, base, length, +- iw, ir) ?: +- reserve_unity_map_for_device(iommu->seg, req, base, length, +- iw, ir); +- } +- } +- +- /* push 'base' just outside of virtual address space */ +- base = iommu_top; ++ if ( exclusion ) ++ { ++ rc = reserve_iommu_exclusion_range(iommu, base, limit, true /* all */); ++ if ( !rc ) ++ return 0; + } + +- /* register IOMMU exclusion range settings */ +- if ( !rc && limit >= iommu_top ) +- rc = reserve_iommu_exclusion_range(iommu, base, limit, +- true /* all */, iw, ir); ++ /* reserve unity-mapped page entries for devices */ ++ for ( bdf = rc = 0; !rc && bdf < ivrs_bdf_entries; bdf++ ) ++ { ++ if ( iommu != find_iommu_for_device(iommu->seg, bdf) ) ++ continue; ++ ++ req = get_ivrs_mappings(iommu->seg)[bdf].dte_requestor_id; ++ rc = reserve_unity_map_for_device(iommu->seg, bdf, base, length, ++ iw, ir) ?: ++ reserve_unity_map_for_device(iommu->seg, req, base, length, ++ iw, ir); ++ } + + return rc; + } + + static int __init parse_ivmd_device_select( + const struct acpi_ivrs_memory *ivmd_block, +- unsigned long base, unsigned long limit, u8 iw, u8 ir) ++ paddr_t base, paddr_t limit, bool iw, bool ir, bool exclusion) + { + u16 bdf; + +@@ -333,12 +311,12 @@ static int __init parse_ivmd_device_sele + return -ENODEV; + } + +- return register_exclusion_range_for_device(bdf, base, limit, iw, ir); ++ return register_range_for_device(bdf, base, limit, iw, ir, exclusion); + } + + static int __init parse_ivmd_device_range( + const struct acpi_ivrs_memory *ivmd_block, +- unsigned long base, unsigned long limit, u8 iw, u8 ir) ++ paddr_t base, paddr_t limit, bool iw, bool ir, bool exclusion) + { + unsigned int first_bdf, last_bdf, bdf; + int error; +@@ -360,15 +338,15 @@ static int __init parse_ivmd_device_rang + } + + for ( bdf = first_bdf, error = 0; (bdf <= last_bdf) && !error; bdf++ ) +- error = register_exclusion_range_for_device( +- bdf, base, limit, iw, ir); ++ error = register_range_for_device( ++ bdf, base, limit, iw, ir, exclusion); + + return error; + } + + static int __init parse_ivmd_device_iommu( + const struct acpi_ivrs_memory *ivmd_block, +- unsigned long base, unsigned long limit, u8 iw, u8 ir) ++ paddr_t base, paddr_t limit, bool iw, bool ir, bool exclusion) + { + int seg = 0; /* XXX */ + struct amd_iommu *iommu; +@@ -383,14 +361,14 @@ static int __init parse_ivmd_device_iomm + return -ENODEV; + } + +- return register_exclusion_range_for_iommu_devices( +- iommu, base, limit, iw, ir); ++ return register_range_for_iommu_devices( ++ iommu, base, limit, iw, ir, exclusion); + } + + static int __init parse_ivmd_block(const struct acpi_ivrs_memory *ivmd_block) + { + unsigned long start_addr, mem_length, base, limit; +- u8 iw, ir; ++ bool iw = true, ir = true, exclusion = false; + + if ( ivmd_block->header.length < sizeof(*ivmd_block) ) + { +@@ -407,13 +385,11 @@ static int __init parse_ivmd_block(const + ivmd_block->header.type, start_addr, mem_length); + + if ( ivmd_block->header.flags & ACPI_IVMD_EXCLUSION_RANGE ) +- iw = ir = IOMMU_CONTROL_ENABLED; ++ exclusion = true; + else if ( ivmd_block->header.flags & ACPI_IVMD_UNITY ) + { +- iw = ivmd_block->header.flags & ACPI_IVMD_READ ? +- IOMMU_CONTROL_ENABLED : IOMMU_CONTROL_DISABLED; +- ir = ivmd_block->header.flags & ACPI_IVMD_WRITE ? +- IOMMU_CONTROL_ENABLED : IOMMU_CONTROL_DISABLED; ++ iw = ivmd_block->header.flags & ACPI_IVMD_READ; ++ ir = ivmd_block->header.flags & ACPI_IVMD_WRITE; + } + else + { +@@ -424,20 +400,20 @@ static int __init parse_ivmd_block(const + switch( ivmd_block->header.type ) + { + case ACPI_IVRS_TYPE_MEMORY_ALL: +- return register_exclusion_range_for_all_devices( +- base, limit, iw, ir); ++ return register_range_for_all_devices( ++ base, limit, iw, ir, exclusion); + + case ACPI_IVRS_TYPE_MEMORY_ONE: +- return parse_ivmd_device_select(ivmd_block, +- base, limit, iw, ir); ++ return parse_ivmd_device_select(ivmd_block, base, limit, ++ iw, ir, exclusion); + + case ACPI_IVRS_TYPE_MEMORY_RANGE: +- return parse_ivmd_device_range(ivmd_block, +- base, limit, iw, ir); ++ return parse_ivmd_device_range(ivmd_block, base, limit, ++ iw, ir, exclusion); + + case ACPI_IVRS_TYPE_MEMORY_IOMMU: +- return parse_ivmd_device_iommu(ivmd_block, +- base, limit, iw, ir); ++ return parse_ivmd_device_iommu(ivmd_block, base, limit, ++ iw, ir, exclusion); + + default: + AMD_IOMMU_DEBUG("IVMD Error: Invalid Block Type!\n"); +--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c ++++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c +@@ -234,6 +234,8 @@ static int __must_check allocate_domain_ + return rc; + } + ++int __read_mostly amd_iommu_min_paging_mode = 1; ++ + static int amd_iommu_domain_init(struct domain *d) + { + struct domain_iommu *hd = dom_iommu(d); +@@ -245,11 +247,13 @@ static int amd_iommu_domain_init(struct + * - HVM could in principle use 3 or 4 depending on how much guest + * physical address space we give it, but this isn't known yet so use 4 + * unilaterally. ++ * - Unity maps may require an even higher number. + */ +- hd->arch.amd.paging_mode = amd_iommu_get_paging_mode( +- is_hvm_domain(d) +- ? 1ul << (DEFAULT_DOMAIN_ADDRESS_WIDTH - PAGE_SHIFT) +- : get_upper_mfn_bound() + 1); ++ hd->arch.amd.paging_mode = max(amd_iommu_get_paging_mode( ++ is_hvm_domain(d) ++ ? 1ul << (DEFAULT_DOMAIN_ADDRESS_WIDTH - PAGE_SHIFT) ++ : get_upper_mfn_bound() + 1), ++ amd_iommu_min_paging_mode); + + return 0; + } diff --git a/xsa378-4.15-7.patch b/xsa378-4.15-7.patch new file mode 100644 index 0000000..0f59532 --- /dev/null +++ b/xsa378-4.15-7.patch @@ -0,0 +1,88 @@ +From: Jan Beulich +Subject: x86/p2m: introduce p2m_is_special() + +Seeing the similarity of grant, foreign, and (subsequently) direct-MMIO +handling, introduce a new P2M type group named "special" (as in "needing +special accessors to create/destroy"). + +Also use -EPERM instead of other error codes on the two domain_crash() +paths touched. + +This is part of XSA-378. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/arch/x86/mm/p2m.c ++++ b/xen/arch/x86/mm/p2m.c +@@ -811,7 +811,7 @@ p2m_remove_page(struct p2m_domain *p2m, + for ( i = 0; i < (1UL << page_order); i++ ) + { + p2m->get_entry(p2m, gfn_add(gfn, i), &t, &a, 0, NULL, NULL); +- if ( !p2m_is_grant(t) && !p2m_is_shared(t) && !p2m_is_foreign(t) ) ++ if ( !p2m_is_special(t) && !p2m_is_shared(t) ) + set_gpfn_from_mfn(mfn_x(mfn) + i, INVALID_M2P_ENTRY); + } + } +@@ -941,13 +941,13 @@ guest_physmap_add_entry(struct domain *d + &ot, &a, 0, NULL, NULL); + ASSERT(!p2m_is_shared(ot)); + } +- if ( p2m_is_grant(ot) || p2m_is_foreign(ot) ) ++ if ( p2m_is_special(ot) ) + { +- /* Really shouldn't be unmapping grant/foreign maps this way */ ++ /* Don't permit unmapping grant/foreign this way. */ + domain_crash(d); + p2m_unlock(p2m); + +- return -EINVAL; ++ return -EPERM; + } + else if ( p2m_is_ram(ot) && !p2m_is_paged(ot) ) + { +@@ -1041,8 +1041,7 @@ int p2m_change_type_one(struct domain *d + struct p2m_domain *p2m = p2m_get_hostp2m(d); + int rc; + +- BUG_ON(p2m_is_grant(ot) || p2m_is_grant(nt)); +- BUG_ON(p2m_is_foreign(ot) || p2m_is_foreign(nt)); ++ BUG_ON(p2m_is_special(ot) || p2m_is_special(nt)); + + gfn_lock(p2m, gfn, 0); + +@@ -1289,11 +1288,11 @@ static int set_typed_p2m_entry(struct do + gfn_unlock(p2m, gfn, order); + return cur_order + 1; + } +- if ( p2m_is_grant(ot) || p2m_is_foreign(ot) ) ++ if ( p2m_is_special(ot) ) + { + gfn_unlock(p2m, gfn, order); + domain_crash(d); +- return -ENOENT; ++ return -EPERM; + } + else if ( p2m_is_ram(ot) ) + { +--- a/xen/include/asm-x86/p2m.h ++++ b/xen/include/asm-x86/p2m.h +@@ -149,6 +149,10 @@ typedef unsigned int p2m_query_t; + | p2m_to_mask(p2m_ram_logdirty) ) + #define P2M_SHARED_TYPES (p2m_to_mask(p2m_ram_shared)) + ++/* Types established/cleaned up via special accessors. */ ++#define P2M_SPECIAL_TYPES (P2M_GRANT_TYPES | \ ++ p2m_to_mask(p2m_map_foreign)) ++ + /* Valid types not necessarily associated with a (valid) MFN. */ + #define P2M_INVALID_MFN_TYPES (P2M_POD_TYPES \ + | p2m_to_mask(p2m_mmio_direct) \ +@@ -177,6 +181,7 @@ typedef unsigned int p2m_query_t; + #define p2m_is_paged(_t) (p2m_to_mask(_t) & P2M_PAGED_TYPES) + #define p2m_is_sharable(_t) (p2m_to_mask(_t) & P2M_SHARABLE_TYPES) + #define p2m_is_shared(_t) (p2m_to_mask(_t) & P2M_SHARED_TYPES) ++#define p2m_is_special(_t) (p2m_to_mask(_t) & P2M_SPECIAL_TYPES) + #define p2m_is_broken(_t) (p2m_to_mask(_t) & P2M_BROKEN_TYPES) + #define p2m_is_foreign(_t) (p2m_to_mask(_t) & p2m_to_mask(p2m_map_foreign)) + diff --git a/xsa378-4.15-8.patch b/xsa378-4.15-8.patch new file mode 100644 index 0000000..391a0bc --- /dev/null +++ b/xsa378-4.15-8.patch @@ -0,0 +1,155 @@ +From: Jan Beulich +Subject: x86/p2m: guard (in particular) identity mapping entries + +Such entries, created by set_identity_p2m_entry(), should only be +destroyed by clear_identity_p2m_entry(). However, similarly, entries +created by set_mmio_p2m_entry() should only be torn down by +clear_mmio_p2m_entry(), so the logic gets based upon p2m_mmio_direct as +the entry type (separation between "ordinary" and 1:1 mappings would +require a further indicator to tell apart the two). + +As to the guest_remove_page() change, commit 48dfb297a20a ("x86/PVH: +allow guest_remove_page to remove p2m_mmio_direct pages"), which +introduced the call to clear_mmio_p2m_entry(), claimed this was done for +hwdom only without this actually having been the case. However, this +code shouldn't be there in the first place, as MMIO entries shouldn't be +dropped this way. Avoid triggering the warning again that 48dfb297a20a +silenced by an adjustment to xenmem_add_to_physmap_one() instead. + +Note that guest_physmap_mark_populate_on_demand() gets tightened beyond +the immediate purpose of this change. + +Note also that I didn't inspect code which isn't security supported, +e.g. sharing, paging, or altp2m. + +This is CVE-2021-28694 / part of XSA-378. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/arch/x86/mm/p2m.c ++++ b/xen/arch/x86/mm/p2m.c +@@ -799,7 +799,8 @@ p2m_remove_page(struct p2m_domain *p2m, + &cur_order, NULL); + + if ( p2m_is_valid(t) && +- (!mfn_valid(mfn) || !mfn_eq(mfn_add(mfn, i), mfn_return)) ) ++ (!mfn_valid(mfn) || t == p2m_mmio_direct || ++ !mfn_eq(mfn_add(mfn, i), mfn_return)) ) + return -EILSEQ; + + i += (1UL << cur_order) - +@@ -899,7 +900,7 @@ guest_physmap_add_entry(struct domain *d + if ( p2m_is_foreign(t) ) + return -EINVAL; + +- if ( !mfn_valid(mfn) ) ++ if ( !mfn_valid(mfn) || t == p2m_mmio_direct ) + { + ASSERT_UNREACHABLE(); + return -EINVAL; +@@ -943,7 +944,7 @@ guest_physmap_add_entry(struct domain *d + } + if ( p2m_is_special(ot) ) + { +- /* Don't permit unmapping grant/foreign this way. */ ++ /* Don't permit unmapping grant/foreign/direct-MMIO this way. */ + domain_crash(d); + p2m_unlock(p2m); + +@@ -1399,8 +1400,8 @@ int set_identity_p2m_entry(struct domain + * order+1 for caller to retry with order (guaranteed smaller than + * the order value passed in) + */ +-int clear_mmio_p2m_entry(struct domain *d, unsigned long gfn_l, mfn_t mfn, +- unsigned int order) ++static int clear_mmio_p2m_entry(struct domain *d, unsigned long gfn_l, ++ mfn_t mfn, unsigned int order) + { + int rc = -EINVAL; + gfn_t gfn = _gfn(gfn_l); +@@ -2731,7 +2732,9 @@ int xenmem_add_to_physmap_one( + + /* Remove previously mapped page if it was present. */ + prev_mfn = get_gfn(d, gfn_x(gpfn), &p2mt); +- if ( mfn_valid(prev_mfn) ) ++ if ( p2mt == p2m_mmio_direct ) ++ rc = -EPERM; ++ else if ( mfn_valid(prev_mfn) ) + { + if ( is_special_page(mfn_to_page(prev_mfn)) ) + /* Special pages are simply unhooked from this phys slot. */ +--- a/xen/arch/x86/mm/p2m-pod.c ++++ b/xen/arch/x86/mm/p2m-pod.c +@@ -1299,17 +1299,17 @@ guest_physmap_mark_populate_on_demand(st + + p2m->get_entry(p2m, gfn_add(gfn, i), &ot, &a, 0, &cur_order, NULL); + n = 1UL << min(order, cur_order); +- if ( p2m_is_ram(ot) ) ++ if ( ot == p2m_populate_on_demand ) ++ { ++ /* Count how many PoD entries we'll be replacing if successful */ ++ pod_count += n; ++ } ++ else if ( ot != p2m_invalid && ot != p2m_mmio_dm ) + { + P2M_DEBUG("gfn_to_mfn returned type %d!\n", ot); + rc = -EBUSY; + goto out; + } +- else if ( ot == p2m_populate_on_demand ) +- { +- /* Count how man PoD entries we'll be replacing if successful */ +- pod_count += n; +- } + } + + /* Now, actually do the two-way mapping */ +--- a/xen/common/memory.c ++++ b/xen/common/memory.c +@@ -330,7 +330,7 @@ int guest_remove_page(struct domain *d, + } + if ( p2mt == p2m_mmio_direct ) + { +- rc = clear_mmio_p2m_entry(d, gmfn, mfn, PAGE_ORDER_4K); ++ rc = -EPERM; + goto out_put_gfn; + } + #else +@@ -1875,6 +1875,15 @@ int check_get_page_from_gfn(struct domai + return -EAGAIN; + } + #endif ++#ifdef CONFIG_X86 ++ if ( p2mt == p2m_mmio_direct ) ++ { ++ if ( page ) ++ put_page(page); ++ ++ return -EPERM; ++ } ++#endif + + if ( !page ) + return -EINVAL; +--- a/xen/include/asm-x86/p2m.h ++++ b/xen/include/asm-x86/p2m.h +@@ -151,7 +151,8 @@ typedef unsigned int p2m_query_t; + + /* Types established/cleaned up via special accessors. */ + #define P2M_SPECIAL_TYPES (P2M_GRANT_TYPES | \ +- p2m_to_mask(p2m_map_foreign)) ++ p2m_to_mask(p2m_map_foreign) | \ ++ p2m_to_mask(p2m_mmio_direct)) + + /* Valid types not necessarily associated with a (valid) MFN. */ + #define P2M_INVALID_MFN_TYPES (P2M_POD_TYPES \ +@@ -666,8 +667,6 @@ int p2m_is_logdirty_range(struct p2m_dom + /* Set mmio addresses in the p2m table (for pass-through) */ + int set_mmio_p2m_entry(struct domain *d, gfn_t gfn, mfn_t mfn, + unsigned int order); +-int clear_mmio_p2m_entry(struct domain *d, unsigned long gfn, mfn_t mfn, +- unsigned int order); + + /* Set identity addresses in the p2m table (for pass-through) */ + int set_identity_p2m_entry(struct domain *d, unsigned long gfn, diff --git a/xsa379-4.15.patch b/xsa379-4.15.patch new file mode 100644 index 0000000..004f56e --- /dev/null +++ b/xsa379-4.15.patch @@ -0,0 +1,82 @@ +From: Jan Beulich +Subject: x86/mm: widen locked region in xenmem_add_to_physmap_one() + +For pages which can be made part of the P2M by the guest, but which can +also later be de-allocated (grant table v2 status pages being the +present example), it is imperative that they be mapped at no more than a +single GFN. We therefore need to make sure that of two parallel +XENMAPSPACE_grant_table requests for the same status page one completes +before the second checks at which other GFN the underlying MFN is +presently mapped. + +Push down the respective put_gfn(). This leverages that gfn_lock() +really aliases p2m_lock(), but the function makes this assumption +already anyway: In the XENMAPSPACE_gmfn case lock nesting constraints +for both involved GFNs would otherwise need to be enforced to avoid ABBA +deadlocks. + +This is CVE-2021-28697 / XSA-379. + +Signed-off-by: Jan Beulich +Reviewed-by: Julien Grall +--- +Since there was some re-ordering of the checks in staging/master (the +-EXDEV now sitting earlier there), I deemed it better to drop the +earlier "if ( rc )" and allow an earlier error to be overwritten by +-EXDEV here. + +--- a/xen/arch/x86/mm/p2m.c ++++ b/xen/arch/x86/mm/p2m.c +@@ -2730,8 +2730,20 @@ int xenmem_add_to_physmap_one( + goto put_both; + } + +- /* Remove previously mapped page if it was present. */ ++ /* ++ * Note that we're (ab)using GFN locking (to really be locking of the ++ * entire P2M) here in (at least) two ways: Finer grained locking would ++ * expose lock order violations in the XENMAPSPACE_gmfn case (due to the ++ * earlier get_gfn_unshare() above). Plus at the very least for the grant ++ * table v2 status page case we need to guarantee that the same page can ++ * only appear at a single GFN. While this is a property we want in ++ * general, for pages which can subsequently be freed this imperative: ++ * Upon freeing we wouldn't be able to find other mappings in the P2M ++ * (unless we did a brute force search). ++ */ + prev_mfn = get_gfn(d, gfn_x(gpfn), &p2mt); ++ ++ /* Remove previously mapped page if it was present. */ + if ( p2mt == p2m_mmio_direct ) + rc = -EPERM; + else if ( mfn_valid(prev_mfn) ) +@@ -2743,27 +2755,21 @@ int xenmem_add_to_physmap_one( + /* Normal domain memory is freed, to avoid leaking memory. */ + rc = guest_remove_page(d, gfn_x(gpfn)); + } +- /* In the XENMAPSPACE_gmfn case we still hold a ref on the old page. */ +- put_gfn(d, gfn_x(gpfn)); +- +- if ( rc ) +- goto put_both; + + /* Unmap from old location, if any. */ + old_gpfn = get_gpfn_from_mfn(mfn_x(mfn)); + ASSERT(!SHARED_M2P(old_gpfn)); + if ( space == XENMAPSPACE_gmfn && old_gpfn != gfn ) +- { + rc = -EXDEV; +- goto put_both; +- } +- if ( old_gpfn != INVALID_M2P_ENTRY ) ++ else if ( !rc && old_gpfn != INVALID_M2P_ENTRY ) + rc = guest_physmap_remove_page(d, _gfn(old_gpfn), mfn, PAGE_ORDER_4K); + + /* Map at new location. */ + if ( !rc ) + rc = guest_physmap_add_page(d, gpfn, mfn, PAGE_ORDER_4K); + ++ put_gfn(d, gfn_x(gpfn)); ++ + put_both: + /* + * In the XENMAPSPACE_gmfn case, we took a ref of the gfn at the top. diff --git a/xsa380-1.patch b/xsa380-1.patch new file mode 100644 index 0000000..9212d73 --- /dev/null +++ b/xsa380-1.patch @@ -0,0 +1,178 @@ +From: Jan Beulich +Subject: gnttab: add preemption check to gnttab_release_mappings() + +A guest may die with many grant mappings still in place, or simply with +a large maptrack table. Iterating through this may take more time than +is reasonable without intermediate preemption (to run softirqs and +perhaps the scheduler). + +Move the invocation of the function to the section where other +restartable functions get invoked, and have the function itself check +for preemption every once in a while. Have it iterate the table +backwards, such that decreasing the maptrack limit is all it takes to +convey restart information. + +In domain_teardown() introduce PROG_none such that inserting at the +front will be easier going forward. + +This is part of CVE-2021-28698 / XSA-380. + +Reported-by: Andrew Cooper +Signed-off-by: Jan Beulich +Reviewed-by: Julien Grall +--- +While I consider removal of the freeing of t->maptrack[i] from +grant_table_destroy() an integral part of this change, also freeing +t->maptrack right in gnttab_release_mappings() would seem like an +unrelated change to me, so I'm not moving that one for now. If others +think it would better be moved, I'd be happy to do so. + +While in principle it would be nice to also eliminate the other loops +from grant_table_destroy() (which can all take long as well provided a +large enough max_grant_frames), ->maptrack[] really is special in that +it only gets accessed when processing requests by the domain itself. The +other arrays may all continue to be accessed as remote domains drop uses +of grants by the dying domain. +--- +v3: Add comment. +v2: Move gnttab_release_mappings() invocation into domain_teardown(). + Don't crash when cleaning up domain without maptrack table. Extend + comment next to maptrack_limit. + +--- a/xen/common/domain.c ++++ b/xen/common/domain.c +@@ -412,11 +412,18 @@ static int domain_teardown(struct domain + v = d->teardown.vcpu + + enum { +- PROG_vcpu_teardown = 1, ++ PROG_none, ++ PROG_gnttab_mappings, ++ PROG_vcpu_teardown, + PROG_done, + }; + +- case 0: ++ case PROG_none: ++ rc = gnttab_release_mappings(d); ++ if ( rc ) ++ return rc; ++ ++ PROGRESS(gnttab_mappings): + for_each_vcpu ( d, v ) + { + PROGRESS_VCPU(teardown); +@@ -908,7 +915,6 @@ int domain_kill(struct domain *d) + return domain_kill(d); + d->is_dying = DOMDYING_dying; + argo_destroy(d); +- gnttab_release_mappings(d); + vnuma_destroy(d->vnuma); + domain_set_outstanding_pages(d, 0); + /* fallthrough */ +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -64,7 +64,13 @@ struct grant_table { + unsigned int nr_grant_frames; + /* Number of grant status frames shared with guest (for version 2) */ + unsigned int nr_status_frames; +- /* Number of available maptrack entries. */ ++ /* ++ * Number of available maptrack entries. For cleanup purposes it is ++ * important to realize that this field and @maptrack further down will ++ * only ever be accessed by the local domain. Thus it is okay to clean ++ * up early, and to shrink the limit for the purpose of tracking cleanup ++ * progress. ++ */ + unsigned int maptrack_limit; + /* Shared grant table (see include/public/grant_table.h). */ + union { +@@ -3679,9 +3685,7 @@ do_grant_table_op( + #include "compat/grant_table.c" + #endif + +-void +-gnttab_release_mappings( +- struct domain *d) ++int gnttab_release_mappings(struct domain *d) + { + struct grant_table *gt = d->grant_table, *rgt; + struct grant_mapping *map; +@@ -3695,8 +3699,32 @@ gnttab_release_mappings( + + BUG_ON(!d->is_dying); + +- for ( handle = 0; handle < gt->maptrack_limit; handle++ ) ++ if ( !gt || !gt->maptrack ) ++ return 0; ++ ++ for ( handle = gt->maptrack_limit; handle; ) + { ++ /* ++ * Deal with full pages such that their freeing (in the body of the ++ * if()) remains simple. ++ */ ++ if ( handle < gt->maptrack_limit && !(handle % MAPTRACK_PER_PAGE) ) ++ { ++ /* ++ * Changing maptrack_limit alters nr_maptrack_frames()'es return ++ * value. Free the then excess trailing page right here, rather ++ * than leaving it to grant_table_destroy() (and in turn requiring ++ * to leave gt->maptrack_limit unaltered). ++ */ ++ gt->maptrack_limit = handle; ++ FREE_XENHEAP_PAGE(gt->maptrack[nr_maptrack_frames(gt)]); ++ ++ if ( hypercall_preempt_check() ) ++ return -ERESTART; ++ } ++ ++ --handle; ++ + map = &maptrack_entry(gt, handle); + if ( !(map->flags & (GNTMAP_device_map|GNTMAP_host_map)) ) + continue; +@@ -3780,6 +3808,11 @@ gnttab_release_mappings( + + map->flags = 0; + } ++ ++ gt->maptrack_limit = 0; ++ FREE_XENHEAP_PAGE(gt->maptrack[0]); ++ ++ return 0; + } + + void grant_table_warn_active_grants(struct domain *d) +@@ -3843,8 +3876,7 @@ grant_table_destroy( + free_xenheap_page(t->shared_raw[i]); + xfree(t->shared_raw); + +- for ( i = 0; i < nr_maptrack_frames(t); i++ ) +- free_xenheap_page(t->maptrack[i]); ++ ASSERT(!t->maptrack_limit); + vfree(t->maptrack); + + for ( i = 0; i < nr_active_grant_frames(t); i++ ) +--- a/xen/include/xen/grant_table.h ++++ b/xen/include/xen/grant_table.h +@@ -47,9 +47,7 @@ void grant_table_init_vcpu(struct vcpu * + void grant_table_warn_active_grants(struct domain *d); + + /* Domain death release of granted mappings of other domains' memory. */ +-void +-gnttab_release_mappings( +- struct domain *d); ++int gnttab_release_mappings(struct domain *d); + + int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref, + gfn_t *gfn, uint16_t *status); +@@ -80,7 +78,7 @@ static inline void grant_table_init_vcpu + + static inline void grant_table_warn_active_grants(struct domain *d) {} + +-static inline void gnttab_release_mappings(struct domain *d) {} ++static inline int gnttab_release_mappings(struct domain *d) { return 0; } + + static inline int mem_sharing_gref_to_gfn(struct grant_table *gt, + grant_ref_t ref, diff --git a/xsa380-2.patch b/xsa380-2.patch new file mode 100644 index 0000000..e5a134d --- /dev/null +++ b/xsa380-2.patch @@ -0,0 +1,410 @@ +From: Jan Beulich +Subject: gnttab: replace mapkind() + +mapkind() doesn't scale very well with larger maptrack entry counts, +using a brute force linear search through all entries, with the only +option of an early loop exit if a matching writable entry was found. +Introduce a radix tree alongside the main maptrack table, thus +allowing much faster MFN-based lookup. To avoid the need to actually +allocate space for the individual nodes, encode the two counters in the +node pointers themselves, thus limiting the number of permitted +simultaneous r/o and r/w mappings of the same MFN to 2³¹-1 (64-bit) / +2¹⁵-1 (32-bit) each. + +To avoid enforcing an unnecessarily low bound on the number of +simultaneous mappings of a single MFN, introduce +radix_tree_{ulong_to_ptr,ptr_to_ulong} paralleling +radix_tree_{int_to_ptr,ptr_to_int}. + +As a consequence locking changes are also applicable: With there no +longer being any inspection of the remote domain's active entries, +there's also no need anymore to hold the remote domain's grant table +lock. And since we're no longer iterating over the local domain's map +track table, the lock in map_grant_ref() can also be dropped before the +new maptrack entry actually gets populated. + +As a nice side effect this also reduces the number of IOMMU operations +in unmap_common(): Previously we would have "established" a readable +mapping whenever we didn't find a writable entry anymore (yet, of +course, at least one readable one). But we only need to do this if we +actually dropped the last writable entry, not if there were none already +before. + +This is part of CVE-2021-28698 / XSA-380. + +Signed-off-by: Jan Beulich +Reviewed-by: Julien Grall +--- +I hope that limiting the map count to 32k on Arm32 is good enough. I +also hope it is out of question that 2G of mappings are enough on 64-bit +architectures. + +I'm using the grant table lock for synchronization to limit differences +in behavior to prior code. I think in principle the maptrack lock could +be used equally well. + +Shouldn't IOMMU insertions be limited anyway to GNTMAP_device_map +requests? This would further save on the number of radix tree nodes in +need of maintaining. + +I'm hesitant to introduce GNTST_* in a security patch, but being able to +tell allocation failure or counter overflow from other errors might be +worthwhile. + +I don't think adding anything to gnttab_usage_print() is useful: +radix_tree_gang_lookup() requires nodes to record their own indexes into +the tree, which we don't do to save space. Yet without indexes printing +node contents isn't very useful. Plus there's also no printing of the +main maptrack table contents. +--- +v3: Check for radix_tree_lookup_slot() returning NULL. Convert -EEXIST + to -EBUSY. Add comments. Re-base over comment addition in patch 1. +v2: New. + +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -37,6 +37,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -82,8 +83,13 @@ struct grant_table { + grant_status_t **status; + /* Active grant table. */ + struct active_grant_entry **active; +- /* Mapping tracking table per vcpu. */ ++ /* Handle-indexed tracking table of mappings. */ + struct grant_mapping **maptrack; ++ /* ++ * MFN-indexed tracking tree of mappings, if needed. Note that this is ++ * protected by @lock, not @maptrack_lock. ++ */ ++ struct radix_tree_root maptrack_tree; + + /* Domain to which this struct grant_table belongs. */ + const struct domain *domain; +@@ -516,34 +522,6 @@ static int get_paged_frame(unsigned long + return GNTST_okay; + } + +-static inline void +-double_gt_lock(struct grant_table *lgt, struct grant_table *rgt) +-{ +- /* +- * See mapkind() for why the write lock is also required for the +- * remote domain. +- */ +- if ( lgt < rgt ) +- { +- grant_write_lock(lgt); +- grant_write_lock(rgt); +- } +- else +- { +- if ( lgt != rgt ) +- grant_write_lock(rgt); +- grant_write_lock(lgt); +- } +-} +- +-static inline void +-double_gt_unlock(struct grant_table *lgt, struct grant_table *rgt) +-{ +- grant_write_unlock(lgt); +- if ( lgt != rgt ) +- grant_write_unlock(rgt); +-} +- + #define INVALID_MAPTRACK_HANDLE UINT_MAX + + static inline grant_handle_t +@@ -970,41 +948,17 @@ static struct active_grant_entry *grant_ + return ERR_PTR(-EINVAL); + } + +-#define MAPKIND_READ 1 +-#define MAPKIND_WRITE 2 +-static unsigned int mapkind( +- struct grant_table *lgt, const struct domain *rd, mfn_t mfn) +-{ +- struct grant_mapping *map; +- grant_handle_t handle, limit = lgt->maptrack_limit; +- unsigned int kind = 0; +- +- /* +- * Must have the local domain's grant table write lock when +- * iterating over its maptrack entries. +- */ +- ASSERT(percpu_rw_is_write_locked(&lgt->lock)); +- /* +- * Must have the remote domain's grant table write lock while +- * counting its active entries. +- */ +- ASSERT(percpu_rw_is_write_locked(&rd->grant_table->lock)); +- +- smp_rmb(); +- +- for ( handle = 0; !(kind & MAPKIND_WRITE) && handle < limit; handle++ ) +- { +- map = &maptrack_entry(lgt, handle); +- if ( !(map->flags & (GNTMAP_device_map|GNTMAP_host_map)) || +- map->domid != rd->domain_id ) +- continue; +- if ( mfn_eq(_active_entry(rd->grant_table, map->ref).mfn, mfn) ) +- kind |= map->flags & GNTMAP_readonly ? +- MAPKIND_READ : MAPKIND_WRITE; +- } +- +- return kind; +-} ++union maptrack_node { ++ struct { ++ /* Radix tree slot pointers use two of the bits. */ ++#ifdef __BIG_ENDIAN_BITFIELD ++ unsigned long : 2; ++#endif ++ unsigned long rd : BITS_PER_LONG / 2 - 1; ++ unsigned long wr : BITS_PER_LONG / 2 - 1; ++ } cnt; ++ unsigned long raw; ++}; + + static void + map_grant_ref( +@@ -1023,7 +977,6 @@ map_grant_ref( + struct grant_mapping *mt; + grant_entry_header_t *shah; + uint16_t *status; +- bool_t need_iommu; + + ld = current->domain; + +@@ -1244,31 +1197,75 @@ map_grant_ref( + * as mem-sharing and IOMMU use are incompatible). The dom_io case would + * need checking separately if we compared against owner here. + */ +- need_iommu = ld != rd && gnttab_need_iommu_mapping(ld); +- if ( need_iommu ) ++ if ( ld != rd && gnttab_need_iommu_mapping(ld) ) + { ++ union maptrack_node node = { ++ .cnt.rd = !!(op->flags & GNTMAP_readonly), ++ .cnt.wr = !(op->flags & GNTMAP_readonly), ++ }; ++ int err; ++ void **slot = NULL; + unsigned int kind; + +- double_gt_lock(lgt, rgt); ++ grant_write_lock(lgt); ++ ++ err = radix_tree_insert(&lgt->maptrack_tree, mfn_x(mfn), ++ radix_tree_ulong_to_ptr(node.raw)); ++ if ( err == -EEXIST ) ++ { ++ slot = radix_tree_lookup_slot(&lgt->maptrack_tree, mfn_x(mfn)); ++ if ( likely(slot) ) ++ { ++ node.raw = radix_tree_ptr_to_ulong(*slot); ++ err = -EBUSY; ++ ++ /* Update node only when refcount doesn't overflow. */ ++ if ( op->flags & GNTMAP_readonly ? ++node.cnt.rd ++ : ++node.cnt.wr ) ++ { ++ radix_tree_replace_slot(slot, ++ radix_tree_ulong_to_ptr(node.raw)); ++ err = 0; ++ } ++ } ++ else ++ ASSERT_UNREACHABLE(); ++ } + + /* + * We're not translated, so we know that dfns and mfns are + * the same things, so the IOMMU entry is always 1-to-1. + */ +- kind = mapkind(lgt, rd, mfn); +- if ( !(op->flags & GNTMAP_readonly) && +- !(kind & MAPKIND_WRITE) ) ++ if ( !(op->flags & GNTMAP_readonly) && node.cnt.wr == 1 ) + kind = IOMMUF_readable | IOMMUF_writable; +- else if ( !kind ) ++ else if ( (op->flags & GNTMAP_readonly) && ++ node.cnt.rd == 1 && !node.cnt.wr ) + kind = IOMMUF_readable; + else + kind = 0; +- if ( kind && iommu_legacy_map(ld, _dfn(mfn_x(mfn)), mfn, 1, kind) ) ++ if ( err || ++ (kind && iommu_legacy_map(ld, _dfn(mfn_x(mfn)), mfn, 1, kind)) ) + { +- double_gt_unlock(lgt, rgt); ++ if ( !err ) ++ { ++ if ( slot ) ++ { ++ op->flags & GNTMAP_readonly ? node.cnt.rd-- ++ : node.cnt.wr--; ++ radix_tree_replace_slot(slot, ++ radix_tree_ulong_to_ptr(node.raw)); ++ } ++ else ++ radix_tree_delete(&lgt->maptrack_tree, mfn_x(mfn)); ++ } ++ + rc = GNTST_general_error; +- goto undo_out; + } ++ ++ grant_write_unlock(lgt); ++ ++ if ( rc != GNTST_okay ) ++ goto undo_out; + } + + TRACE_1D(TRC_MEM_PAGE_GRANT_MAP, op->dom); +@@ -1276,10 +1273,6 @@ map_grant_ref( + /* + * All maptrack entry users check mt->flags first before using the + * other fields so just ensure the flags field is stored last. +- * +- * However, if gnttab_need_iommu_mapping() then this would race +- * with a concurrent mapkind() call (on an unmap, for example) +- * and a lock is required. + */ + mt = &maptrack_entry(lgt, handle); + mt->domid = op->dom; +@@ -1287,9 +1280,6 @@ map_grant_ref( + smp_wmb(); + write_atomic(&mt->flags, op->flags); + +- if ( need_iommu ) +- double_gt_unlock(lgt, rgt); +- + op->dev_bus_addr = mfn_to_maddr(mfn); + op->handle = handle; + op->status = GNTST_okay; +@@ -1497,19 +1487,34 @@ unmap_common( + /* See the respective comment in map_grant_ref(). */ + if ( rc == GNTST_okay && ld != rd && gnttab_need_iommu_mapping(ld) ) + { +- unsigned int kind; ++ void **slot; ++ union maptrack_node node; + int err = 0; + +- double_gt_lock(lgt, rgt); ++ grant_write_lock(lgt); ++ slot = radix_tree_lookup_slot(&lgt->maptrack_tree, mfn_x(op->mfn)); ++ node.raw = likely(slot) ? radix_tree_ptr_to_ulong(*slot) : 0; ++ ++ /* Refcount must not underflow. */ ++ if ( !(flags & GNTMAP_readonly ? node.cnt.rd-- ++ : node.cnt.wr--) ) ++ BUG(); + +- kind = mapkind(lgt, rd, op->mfn); +- if ( !kind ) ++ if ( !node.raw ) + err = iommu_legacy_unmap(ld, _dfn(mfn_x(op->mfn)), 1); +- else if ( !(kind & MAPKIND_WRITE) ) ++ else if ( !(flags & GNTMAP_readonly) && !node.cnt.wr ) + err = iommu_legacy_map(ld, _dfn(mfn_x(op->mfn)), op->mfn, 1, + IOMMUF_readable); + +- double_gt_unlock(lgt, rgt); ++ if ( err ) ++ ; ++ else if ( !node.raw ) ++ radix_tree_delete(&lgt->maptrack_tree, mfn_x(op->mfn)); ++ else ++ radix_tree_replace_slot(slot, ++ radix_tree_ulong_to_ptr(node.raw)); ++ ++ grant_write_unlock(lgt); + + if ( err ) + rc = GNTST_general_error; +@@ -1956,6 +1961,8 @@ int grant_table_init(struct domain *d, i + gt->maptrack = vzalloc(gt->max_maptrack_frames * sizeof(*gt->maptrack)); + if ( gt->maptrack == NULL ) + goto out; ++ ++ radix_tree_init(>->maptrack_tree); + } + + /* Shared grant table. */ +@@ -3704,6 +3711,8 @@ int gnttab_release_mappings(struct domai + + for ( handle = gt->maptrack_limit; handle; ) + { ++ mfn_t mfn; ++ + /* + * Deal with full pages such that their freeing (in the body of the + * if()) remains simple. +@@ -3801,17 +3810,31 @@ int gnttab_release_mappings(struct domai + + reduce_status_for_pin(rd, act, status, map->flags & GNTMAP_readonly); + ++ mfn = act->mfn; ++ + active_entry_release(act); + grant_read_unlock(rgt); + + rcu_unlock_domain(rd); + + map->flags = 0; ++ ++ /* ++ * This is excessive in that a single such call would suffice per ++ * mapped MFN (or none at all, if no entry was ever inserted). But it ++ * should be the common case for an MFN to be mapped just once, and ++ * this way we don't need to further maintain the counters. We also ++ * don't want to leave cleaning up of the tree as a whole to the end ++ * of the function, as this could take quite some time. ++ */ ++ radix_tree_delete(>->maptrack_tree, mfn_x(mfn)); + } + + gt->maptrack_limit = 0; + FREE_XENHEAP_PAGE(gt->maptrack[0]); + ++ radix_tree_destroy(>->maptrack_tree, NULL); ++ + return 0; + } + +--- a/xen/include/xen/radix-tree.h ++++ b/xen/include/xen/radix-tree.h +@@ -190,6 +190,25 @@ static inline int radix_tree_ptr_to_int( + return (int)((long)ptr >> 2); + } + ++/** ++ * radix_tree_{ulong_to_ptr,ptr_to_ulong}: ++ * ++ * Same for unsigned long values. Beware though that only BITS_PER_LONG-2 ++ * bits are actually usable for the value. ++ */ ++static inline void *radix_tree_ulong_to_ptr(unsigned long val) ++{ ++ unsigned long ptr = (val << 2) | 0x2; ++ ASSERT((ptr >> 2) == val); ++ return (void *)ptr; ++} ++ ++static inline unsigned long radix_tree_ptr_to_ulong(void *ptr) ++{ ++ ASSERT(((unsigned long)ptr & 0x3) == 0x2); ++ return (unsigned long)ptr >> 2; ++} ++ + int radix_tree_insert(struct radix_tree_root *, unsigned long, void *); + void *radix_tree_lookup(struct radix_tree_root *, unsigned long); + void **radix_tree_lookup_slot(struct radix_tree_root *, unsigned long); diff --git a/xsa380-4.14-1.patch b/xsa380-4.14-1.patch new file mode 100644 index 0000000..7221248 --- /dev/null +++ b/xsa380-4.14-1.patch @@ -0,0 +1,148 @@ +From: Jan Beulich +Subject: gnttab: add preemption check to gnttab_release_mappings() + +A guest may die with many grant mappings still in place, or simply with +a large maptrack table. Iterating through this may take more time than +is reasonable without intermediate preemption (to run softirqs and +perhaps the scheduler). + +Move the invocation of the function to the section where other +restartable functions get invoked, and have the function itself check +for preemption every once in a while. Have it iterate the table +backwards, such that decreasing the maptrack limit is all it takes to +convey restart information. + +In domain_teardown() introduce PROG_none such that inserting at the +front will be easier going forward. + +This is part of CVE-2021-28698 / XSA-380. + +Reported-by: Andrew Cooper +Signed-off-by: Jan Beulich +Reviewed-by: Julien Grall + +--- a/xen/common/domain.c ++++ b/xen/common/domain.c +@@ -721,11 +721,13 @@ int domain_kill(struct domain *d) + return domain_kill(d); + d->is_dying = DOMDYING_dying; + argo_destroy(d); +- gnttab_release_mappings(d); + vnuma_destroy(d->vnuma); + domain_set_outstanding_pages(d, 0); + /* fallthrough */ + case DOMDYING_dying: ++ rc = gnttab_release_mappings(d); ++ if ( rc ) ++ break; + rc = evtchn_destroy(d); + if ( rc ) + break; +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -64,7 +64,13 @@ struct grant_table { + unsigned int nr_grant_frames; + /* Number of grant status frames shared with guest (for version 2) */ + unsigned int nr_status_frames; +- /* Number of available maptrack entries. */ ++ /* ++ * Number of available maptrack entries. For cleanup purposes it is ++ * important to realize that this field and @maptrack further down will ++ * only ever be accessed by the local domain. Thus it is okay to clean ++ * up early, and to shrink the limit for the purpose of tracking cleanup ++ * progress. ++ */ + unsigned int maptrack_limit; + /* Shared grant table (see include/public/grant_table.h). */ + union { +@@ -3708,9 +3714,7 @@ do_grant_table_op( + #include "compat/grant_table.c" + #endif + +-void +-gnttab_release_mappings( +- struct domain *d) ++int gnttab_release_mappings(struct domain *d) + { + struct grant_table *gt = d->grant_table, *rgt; + struct grant_mapping *map; +@@ -3724,10 +3728,34 @@ gnttab_release_mappings( + + BUG_ON(!d->is_dying); + +- for ( handle = 0; handle < gt->maptrack_limit; handle++ ) ++ if ( !gt || !gt->maptrack ) ++ return 0; ++ ++ for ( handle = gt->maptrack_limit; handle; ) + { + unsigned int clear_flags = 0; + ++ /* ++ * Deal with full pages such that their freeing (in the body of the ++ * if()) remains simple. ++ */ ++ if ( handle < gt->maptrack_limit && !(handle % MAPTRACK_PER_PAGE) ) ++ { ++ /* ++ * Changing maptrack_limit alters nr_maptrack_frames()'es return ++ * value. Free the then excess trailing page right here, rather ++ * than leaving it to grant_table_destroy() (and in turn requiring ++ * to leave gt->maptrack_limit unaltered). ++ */ ++ gt->maptrack_limit = handle; ++ FREE_XENHEAP_PAGE(gt->maptrack[nr_maptrack_frames(gt)]); ++ ++ if ( hypercall_preempt_check() ) ++ return -ERESTART; ++ } ++ ++ --handle; ++ + map = &maptrack_entry(gt, handle); + if ( !(map->flags & (GNTMAP_device_map|GNTMAP_host_map)) ) + continue; +@@ -3818,6 +3846,11 @@ gnttab_release_mappings( + + map->flags = 0; + } ++ ++ gt->maptrack_limit = 0; ++ FREE_XENHEAP_PAGE(gt->maptrack[0]); ++ ++ return 0; + } + + void grant_table_warn_active_grants(struct domain *d) +@@ -3881,8 +3914,7 @@ grant_table_destroy( + free_xenheap_page(t->shared_raw[i]); + xfree(t->shared_raw); + +- for ( i = 0; i < nr_maptrack_frames(t); i++ ) +- free_xenheap_page(t->maptrack[i]); ++ ASSERT(!t->maptrack_limit); + vfree(t->maptrack); + + for ( i = 0; i < nr_active_grant_frames(t); i++ ) +--- a/xen/include/xen/grant_table.h ++++ b/xen/include/xen/grant_table.h +@@ -47,9 +47,7 @@ void grant_table_init_vcpu(struct vcpu * + void grant_table_warn_active_grants(struct domain *d); + + /* Domain death release of granted mappings of other domains' memory. */ +-void +-gnttab_release_mappings( +- struct domain *d); ++int gnttab_release_mappings(struct domain *d); + + int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref, + gfn_t *gfn, uint16_t *status); +@@ -78,7 +76,7 @@ static inline void grant_table_init_vcpu + + static inline void grant_table_warn_active_grants(struct domain *d) {} + +-static inline void gnttab_release_mappings(struct domain *d) {} ++static inline int gnttab_release_mappings(struct domain *d) { return 0; } + + static inline int mem_sharing_gref_to_gfn(struct grant_table *gt, + grant_ref_t ref, diff --git a/xsa380-4.14-2.patch b/xsa380-4.14-2.patch new file mode 100644 index 0000000..d8b38b9 --- /dev/null +++ b/xsa380-4.14-2.patch @@ -0,0 +1,383 @@ +From: Jan Beulich +Subject: gnttab: replace mapkind() + +mapkind() doesn't scale very well with larger maptrack entry counts, +using a brute force linear search through all entries, with the only +option of an early loop exit if a matching writable entry was found. +Introduce a radix tree alongside the main maptrack table, thus +allowing much faster MFN-based lookup. To avoid the need to actually +allocate space for the individual nodes, encode the two counters in the +node pointers themselves, thus limiting the number of permitted +simultaneous r/o and r/w mappings of the same MFN to 2³¹-1 (64-bit) / +2¹⁵-1 (32-bit) each. + +To avoid enforcing an unnecessarily low bound on the number of +simultaneous mappings of a single MFN, introduce +radix_tree_{ulong_to_ptr,ptr_to_ulong} paralleling +radix_tree_{int_to_ptr,ptr_to_int}. + +As a consequence locking changes are also applicable: With there no +longer being any inspection of the remote domain's active entries, +there's also no need anymore to hold the remote domain's grant table +lock. And since we're no longer iterating over the local domain's map +track table, the lock in map_grant_ref() can also be dropped before the +new maptrack entry actually gets populated. + +As a nice side effect this also reduces the number of IOMMU operations +in unmap_common(): Previously we would have "established" a readable +mapping whenever we didn't find a writable entry anymore (yet, of +course, at least one readable one). But we only need to do this if we +actually dropped the last writable entry, not if there were none already +before. + +This is part of CVE-2021-28698 / XSA-380. + +Signed-off-by: Jan Beulich +Reviewed-by: Julien Grall + +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -37,6 +37,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -82,8 +83,13 @@ struct grant_table { + grant_status_t **status; + /* Active grant table. */ + struct active_grant_entry **active; +- /* Mapping tracking table per vcpu. */ ++ /* Handle-indexed tracking table of mappings. */ + struct grant_mapping **maptrack; ++ /* ++ * MFN-indexed tracking tree of mappings, if needed. Note that this is ++ * protected by @lock, not @maptrack_lock. ++ */ ++ struct radix_tree_root maptrack_tree; + + /* Domain to which this struct grant_table belongs. */ + const struct domain *domain; +@@ -501,34 +507,6 @@ static int get_paged_frame(unsigned long + return GNTST_okay; + } + +-static inline void +-double_gt_lock(struct grant_table *lgt, struct grant_table *rgt) +-{ +- /* +- * See mapkind() for why the write lock is also required for the +- * remote domain. +- */ +- if ( lgt < rgt ) +- { +- grant_write_lock(lgt); +- grant_write_lock(rgt); +- } +- else +- { +- if ( lgt != rgt ) +- grant_write_lock(rgt); +- grant_write_lock(lgt); +- } +-} +- +-static inline void +-double_gt_unlock(struct grant_table *lgt, struct grant_table *rgt) +-{ +- grant_write_unlock(lgt); +- if ( lgt != rgt ) +- grant_write_unlock(rgt); +-} +- + #define INVALID_MAPTRACK_HANDLE UINT_MAX + + static inline grant_handle_t +@@ -948,41 +926,17 @@ static struct active_grant_entry *grant_ + return ERR_PTR(-EINVAL); + } + +-#define MAPKIND_READ 1 +-#define MAPKIND_WRITE 2 +-static unsigned int mapkind( +- struct grant_table *lgt, const struct domain *rd, mfn_t mfn) +-{ +- struct grant_mapping *map; +- grant_handle_t handle, limit = lgt->maptrack_limit; +- unsigned int kind = 0; +- +- /* +- * Must have the local domain's grant table write lock when +- * iterating over its maptrack entries. +- */ +- ASSERT(percpu_rw_is_write_locked(&lgt->lock)); +- /* +- * Must have the remote domain's grant table write lock while +- * counting its active entries. +- */ +- ASSERT(percpu_rw_is_write_locked(&rd->grant_table->lock)); +- +- smp_rmb(); +- +- for ( handle = 0; !(kind & MAPKIND_WRITE) && handle < limit; handle++ ) +- { +- map = &maptrack_entry(lgt, handle); +- if ( !(map->flags & (GNTMAP_device_map|GNTMAP_host_map)) || +- map->domid != rd->domain_id ) +- continue; +- if ( mfn_eq(_active_entry(rd->grant_table, map->ref).mfn, mfn) ) +- kind |= map->flags & GNTMAP_readonly ? +- MAPKIND_READ : MAPKIND_WRITE; +- } +- +- return kind; +-} ++union maptrack_node { ++ struct { ++ /* Radix tree slot pointers use two of the bits. */ ++#ifdef __BIG_ENDIAN_BITFIELD ++ unsigned long : 2; ++#endif ++ unsigned long rd : BITS_PER_LONG / 2 - 1; ++ unsigned long wr : BITS_PER_LONG / 2 - 1; ++ } cnt; ++ unsigned long raw; ++}; + + static void + map_grant_ref( +@@ -1001,7 +955,6 @@ map_grant_ref( + struct grant_mapping *mt; + grant_entry_header_t *shah; + uint16_t *status; +- bool_t need_iommu; + + ld = current->domain; + +@@ -1220,31 +1173,75 @@ map_grant_ref( + * as mem-sharing and IOMMU use are incompatible). The dom_io case would + * need checking separately if we compared against owner here. + */ +- need_iommu = ld != rd && gnttab_need_iommu_mapping(ld); +- if ( need_iommu ) ++ if ( ld != rd && gnttab_need_iommu_mapping(ld) ) + { ++ union maptrack_node node = { ++ .cnt.rd = !!(op->flags & GNTMAP_readonly), ++ .cnt.wr = !(op->flags & GNTMAP_readonly), ++ }; ++ int err; ++ void **slot = NULL; + unsigned int kind; + +- double_gt_lock(lgt, rgt); ++ grant_write_lock(lgt); ++ ++ err = radix_tree_insert(&lgt->maptrack_tree, mfn_x(mfn), ++ radix_tree_ulong_to_ptr(node.raw)); ++ if ( err == -EEXIST ) ++ { ++ slot = radix_tree_lookup_slot(&lgt->maptrack_tree, mfn_x(mfn)); ++ if ( likely(slot) ) ++ { ++ node.raw = radix_tree_ptr_to_ulong(*slot); ++ err = -EBUSY; ++ ++ /* Update node only when refcount doesn't overflow. */ ++ if ( op->flags & GNTMAP_readonly ? ++node.cnt.rd ++ : ++node.cnt.wr ) ++ { ++ radix_tree_replace_slot(slot, ++ radix_tree_ulong_to_ptr(node.raw)); ++ err = 0; ++ } ++ } ++ else ++ ASSERT_UNREACHABLE(); ++ } + + /* + * We're not translated, so we know that dfns and mfns are + * the same things, so the IOMMU entry is always 1-to-1. + */ +- kind = mapkind(lgt, rd, mfn); +- if ( !(op->flags & GNTMAP_readonly) && +- !(kind & MAPKIND_WRITE) ) ++ if ( !(op->flags & GNTMAP_readonly) && node.cnt.wr == 1 ) + kind = IOMMUF_readable | IOMMUF_writable; +- else if ( !kind ) ++ else if ( (op->flags & GNTMAP_readonly) && ++ node.cnt.rd == 1 && !node.cnt.wr ) + kind = IOMMUF_readable; + else + kind = 0; +- if ( kind && iommu_legacy_map(ld, _dfn(mfn_x(mfn)), mfn, 0, kind) ) ++ if ( err || ++ (kind && iommu_legacy_map(ld, _dfn(mfn_x(mfn)), mfn, 0, kind)) ) + { +- double_gt_unlock(lgt, rgt); ++ if ( !err ) ++ { ++ if ( slot ) ++ { ++ op->flags & GNTMAP_readonly ? node.cnt.rd-- ++ : node.cnt.wr--; ++ radix_tree_replace_slot(slot, ++ radix_tree_ulong_to_ptr(node.raw)); ++ } ++ else ++ radix_tree_delete(&lgt->maptrack_tree, mfn_x(mfn)); ++ } ++ + rc = GNTST_general_error; +- goto undo_out; + } ++ ++ grant_write_unlock(lgt); ++ ++ if ( rc != GNTST_okay ) ++ goto undo_out; + } + + TRACE_1D(TRC_MEM_PAGE_GRANT_MAP, op->dom); +@@ -1252,10 +1249,6 @@ map_grant_ref( + /* + * All maptrack entry users check mt->flags first before using the + * other fields so just ensure the flags field is stored last. +- * +- * However, if gnttab_need_iommu_mapping() then this would race +- * with a concurrent mapkind() call (on an unmap, for example) +- * and a lock is required. + */ + mt = &maptrack_entry(lgt, handle); + mt->domid = op->dom; +@@ -1263,9 +1256,6 @@ map_grant_ref( + smp_wmb(); + write_atomic(&mt->flags, op->flags); + +- if ( need_iommu ) +- double_gt_unlock(lgt, rgt); +- + op->dev_bus_addr = mfn_to_maddr(mfn); + op->handle = handle; + op->status = GNTST_okay; +@@ -1487,19 +1477,34 @@ unmap_common( + /* See the respective comment in map_grant_ref(). */ + if ( rc == GNTST_okay && ld != rd && gnttab_need_iommu_mapping(ld) ) + { +- unsigned int kind; ++ void **slot; ++ union maptrack_node node; + int err = 0; + +- double_gt_lock(lgt, rgt); ++ grant_write_lock(lgt); ++ slot = radix_tree_lookup_slot(&lgt->maptrack_tree, mfn_x(op->mfn)); ++ node.raw = likely(slot) ? radix_tree_ptr_to_ulong(*slot) : 0; ++ ++ /* Refcount must not underflow. */ ++ if ( !(flags & GNTMAP_readonly ? node.cnt.rd-- ++ : node.cnt.wr--) ) ++ BUG(); + +- kind = mapkind(lgt, rd, op->mfn); +- if ( !kind ) ++ if ( !node.raw ) + err = iommu_legacy_unmap(ld, _dfn(mfn_x(op->mfn)), 0); +- else if ( !(kind & MAPKIND_WRITE) ) ++ else if ( !(flags & GNTMAP_readonly) && !node.cnt.wr ) + err = iommu_legacy_map(ld, _dfn(mfn_x(op->mfn)), op->mfn, 0, + IOMMUF_readable); + +- double_gt_unlock(lgt, rgt); ++ if ( err ) ++ ; ++ else if ( !node.raw ) ++ radix_tree_delete(&lgt->maptrack_tree, mfn_x(op->mfn)); ++ else ++ radix_tree_replace_slot(slot, ++ radix_tree_ulong_to_ptr(node.raw)); ++ ++ grant_write_unlock(lgt); + + if ( err ) + rc = GNTST_general_error; +@@ -1951,6 +1956,8 @@ int grant_table_init(struct domain *d, i + gt->maptrack = vzalloc(gt->max_maptrack_frames * sizeof(*gt->maptrack)); + if ( gt->maptrack == NULL ) + goto out; ++ ++ radix_tree_init(>->maptrack_tree); + } + + /* Shared grant table. */ +@@ -3734,6 +3741,7 @@ int gnttab_release_mappings(struct domai + for ( handle = gt->maptrack_limit; handle; ) + { + unsigned int clear_flags = 0; ++ mfn_t mfn; + + /* + * Deal with full pages such that their freeing (in the body of the +@@ -3839,17 +3847,31 @@ int gnttab_release_mappings(struct domai + if ( clear_flags ) + gnttab_clear_flags(rd, clear_flags, status); + ++ mfn = act->mfn; ++ + active_entry_release(act); + grant_read_unlock(rgt); + + rcu_unlock_domain(rd); + + map->flags = 0; ++ ++ /* ++ * This is excessive in that a single such call would suffice per ++ * mapped MFN (or none at all, if no entry was ever inserted). But it ++ * should be the common case for an MFN to be mapped just once, and ++ * this way we don't need to further maintain the counters. We also ++ * don't want to leave cleaning up of the tree as a whole to the end ++ * of the function, as this could take quite some time. ++ */ ++ radix_tree_delete(>->maptrack_tree, mfn_x(mfn)); + } + + gt->maptrack_limit = 0; + FREE_XENHEAP_PAGE(gt->maptrack[0]); + ++ radix_tree_destroy(>->maptrack_tree, NULL); ++ + return 0; + } + +--- a/xen/include/xen/radix-tree.h ++++ b/xen/include/xen/radix-tree.h +@@ -190,6 +190,25 @@ static inline int radix_tree_ptr_to_int( + return (int)((long)ptr >> 2); + } + ++/** ++ * radix_tree_{ulong_to_ptr,ptr_to_ulong}: ++ * ++ * Same for unsigned long values. Beware though that only BITS_PER_LONG-2 ++ * bits are actually usable for the value. ++ */ ++static inline void *radix_tree_ulong_to_ptr(unsigned long val) ++{ ++ unsigned long ptr = (val << 2) | 0x2; ++ ASSERT((ptr >> 2) == val); ++ return (void *)ptr; ++} ++ ++static inline unsigned long radix_tree_ptr_to_ulong(void *ptr) ++{ ++ ASSERT(((unsigned long)ptr & 0x3) == 0x2); ++ return (unsigned long)ptr >> 2; ++} ++ + int radix_tree_insert(struct radix_tree_root *, unsigned long, void *); + void *radix_tree_lookup(struct radix_tree_root *, unsigned long); + void **radix_tree_lookup_slot(struct radix_tree_root *, unsigned long); diff --git a/xsa382.patch b/xsa382.patch new file mode 100644 index 0000000..936c2de --- /dev/null +++ b/xsa382.patch @@ -0,0 +1,34 @@ +From: Jan Beulich +Subject: gnttab: fix array capacity check in gnttab_get_status_frames() + +The number of grant frames is of no interest here; converting the passed +in op.nr_frames this way means we allow for 8 times as many GFNs to be +written as actually fit in the array. We would corrupt xlat areas of +higher vCPU-s (after having faulted many times while trying to write to +the guard pages between any two areas) for 32-bit PV guests. For HVM +guests we'd simply crash as soon as we hit the first guard page, as +accesses to the xlat area are simply memcpy() there. + +This is CVE-2021-28699 / XSA-382. + +Fixes: 18b1be5e324b ("gnttab: make resource limits per domain") +Signed-off-by: Jan Beulich + +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -3243,12 +3243,11 @@ gnttab_get_status_frames(XEN_GUEST_HANDL + goto unlock; + } + +- if ( unlikely(limit_max < grant_to_status_frames(op.nr_frames)) ) ++ if ( unlikely(limit_max < op.nr_frames) ) + { + gdprintk(XENLOG_WARNING, +- "grant_to_status_frames(%u) for d%d is too large (%u,%u)\n", +- op.nr_frames, d->domain_id, +- grant_to_status_frames(op.nr_frames), limit_max); ++ "nr_status_frames for %pd is too large (%u,%u)\n", ++ d, op.nr_frames, limit_max); + op.status = GNTST_general_error; + goto unlock; + } diff --git a/xsa383.patch b/xsa383.patch new file mode 100644 index 0000000..9ab5eb3 --- /dev/null +++ b/xsa383.patch @@ -0,0 +1,55 @@ +From: Julien Grall +Date: Sat, 3 Jul 2021 14:03:36 +0100 +Subject: [PATCH] xen/arm: Restrict the amount of memory that dom0less domU and + dom0 can allocate + +Currently, both dom0less domUs and dom0 can allocate an "unlimited" +amount of memory because d->max_pages is set to ~0U. + +In particular, the former are meant to be unprivileged. Therefore the +memory they could allocate should be bounded. As the domain are not yet +officially aware of Xen (we don't expose advertise it in the DT, yet +the hypercalls are accessible), they should not need to allocate more +than the initial amount. So cap set d->max_pages directly the amount of +memory we are meant to allocate. + +Take the opportunity to also restrict the memory for dom0 as the +domain is direct mapped (e.g. MFN == GFN) and therefore cannot +allocate outside of the pre-allocated region. + +This is CVE-2021-28700 / XSA-383. + +Reported-by: Julien Grall +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +Tested-by: Stefano Stabellini +--- + xen/arch/arm/domain_build.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c +index 6c86d527810f..206038d1c022 100644 +--- a/xen/arch/arm/domain_build.c ++++ b/xen/arch/arm/domain_build.c +@@ -2440,7 +2440,8 @@ static int __init construct_domU(struct domain *d, + + if ( vcpu_create(d, 0) == NULL ) + return -ENOMEM; +- d->max_pages = ~0U; ++ ++ d->max_pages = ((paddr_t)mem * SZ_1K) >> PAGE_SHIFT; + + kinfo.d = d; + +@@ -2546,7 +2547,7 @@ static int __init construct_dom0(struct domain *d) + + iommu_hwdom_init(d); + +- d->max_pages = ~0U; ++ d->max_pages = dom0_mem >> PAGE_SHIFT; + + kinfo.unassigned_mem = dom0_mem; + kinfo.d = d; +-- +2.17.1 + From 659a9b49ea204ab292870368e1c09f37d0ca2259 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Sat, 28 Aug 2021 22:39:19 +0100 Subject: [PATCH 016/137] 5 security fixes, 1 grub fix IOMMU page mapping issues on x86 [XSA-378, CVE-2021-28694, CVE-2021-28695, CVE-2021-28696] (#1997531) (#1997568) (#1997537) grant table v2 status pages may remain accessible after de-allocation [XSA-379, CVE-2021-28697] (#1997520) long running loops in grant table handling [XSA-380, CVE-2021-28698] (#1997526) inadequate grant-v2 status frames array bounds check [XSA-382, CVE-2021-28699] (#1997523) xen/arm: No memory limit for dom0less domUs [XSA-383, CVE-2021-28700] (#1997527) grub x86_64-efi modules now go into /boot/grub2 --- ...0c113e0067b86186db94aabae9c91aa09f35.patch | 117 +++++ xen.spec | 47 +- xsa378-4.14-1.patch | 142 ++++++ xsa378-4.14-2.patch | 218 ++++++++++ xsa378-4.14-3.patch | 102 +++++ xsa378-4.14-4.patch | 385 ++++++++++++++++ xsa378-4.14-5.patch | 208 +++++++++ xsa378-4.14-6.patch | 411 ++++++++++++++++++ xsa378-4.14-7.patch | 88 ++++ xsa378-4.14-8.patch | 157 +++++++ xsa379-4.14.patch | 77 ++++ xsa380-4.14-1.patch | 148 +++++++ xsa380-4.14-2.patch | 383 ++++++++++++++++ xsa382.patch | 34 ++ xsa383.patch | 55 +++ 15 files changed, 2571 insertions(+), 1 deletion(-) create mode 100644 xen.git-e06d0c113e0067b86186db94aabae9c91aa09f35.patch create mode 100644 xsa378-4.14-1.patch create mode 100644 xsa378-4.14-2.patch create mode 100644 xsa378-4.14-3.patch create mode 100644 xsa378-4.14-4.patch create mode 100644 xsa378-4.14-5.patch create mode 100644 xsa378-4.14-6.patch create mode 100644 xsa378-4.14-7.patch create mode 100644 xsa378-4.14-8.patch create mode 100644 xsa379-4.14.patch create mode 100644 xsa380-4.14-1.patch create mode 100644 xsa380-4.14-2.patch create mode 100644 xsa382.patch create mode 100644 xsa383.patch diff --git a/xen.git-e06d0c113e0067b86186db94aabae9c91aa09f35.patch b/xen.git-e06d0c113e0067b86186db94aabae9c91aa09f35.patch new file mode 100644 index 0000000..e36de31 --- /dev/null +++ b/xen.git-e06d0c113e0067b86186db94aabae9c91aa09f35.patch @@ -0,0 +1,117 @@ +From e06d0c113e0067b86186db94aabae9c91aa09f35 Mon Sep 17 00:00:00 2001 +From: Jan Beulich +Date: Thu, 15 Jul 2021 09:32:21 +0200 +Subject: [PATCH] VT-d: adjust domid map updating when unmapping context + +When an earlier error occurred, cleaning up the domid mapping data is +wrong, as references likely still exist. The only exception to this is +when the actual unmapping worked, but some flush failed (supposedly +impossible after XSA-373). The guest will get crashed in such a case +though, so add fallback cleanup to domain destruction to cover this +case. This in turn makes it desirable to silence the dprintk() in +domain_iommu_domid(). + +Note that no error will be returned anymore when the lookup fails - in +the common case lookup failure would already have caused +domain_context_unmap_one() to fail, yet even from a more general +perspective it doesn't look right to fail domain_context_unmap() in such +a case when this was the last device, but not when any earlier unmap was +otherwise successful. + +Signed-off-by: Jan Beulich +Reviewed-by: Kevin Tian +master commit: 32655880057ce2829f962d46916ea6cec60f98d3 +master date: 2021-06-24 16:29:13 +0200 +--- + xen/drivers/passthrough/vtd/iommu.c | 39 ++++++++++++++++++----------- + 1 file changed, 24 insertions(+), 15 deletions(-) + +diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c +index 7d1813a615..e4c0e4368e 100644 +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -79,9 +79,11 @@ static int domain_iommu_domid(struct domain *d, + i = find_next_bit(iommu->domid_bitmap, nr_dom, i+1); + } + +- dprintk(XENLOG_ERR VTDPREFIX, +- "Cannot get valid iommu domid: domid=%d iommu->index=%d\n", +- d->domain_id, iommu->index); ++ if ( !d->is_dying ) ++ dprintk(XENLOG_ERR VTDPREFIX, ++ "Cannot get valid iommu %u domid: %pd\n", ++ iommu->index, d); ++ + return -1; + } + +@@ -146,6 +148,17 @@ static int context_get_domain_id(struct context_entry *context, + return domid; + } + ++static void cleanup_domid_map(struct domain *domain, struct vtd_iommu *iommu) ++{ ++ int iommu_domid = domain_iommu_domid(domain, iommu); ++ ++ if ( iommu_domid >= 0 ) ++ { ++ clear_bit(iommu_domid, iommu->domid_bitmap); ++ iommu->domid_map[iommu_domid] = 0; ++ } ++} ++ + static int iommus_incoherent; + + static void sync_cache(const void *addr, unsigned int size) +@@ -1753,6 +1766,9 @@ static int domain_context_unmap(struct domain *domain, u8 devfn, + goto out; + } + ++ if ( ret ) ++ goto out; ++ + /* + * if no other devices under the same iommu owned by this domain, + * clear iommu in iommu_bitmap and clear domain_id in domid_bitmp +@@ -1772,19 +1788,8 @@ static int domain_context_unmap(struct domain *domain, u8 devfn, + + if ( found == 0 ) + { +- int iommu_domid; +- + clear_bit(iommu->index, &dom_iommu(domain)->arch.iommu_bitmap); +- +- iommu_domid = domain_iommu_domid(domain, iommu); +- if ( iommu_domid == -1 ) +- { +- ret = -EINVAL; +- goto out; +- } +- +- clear_bit(iommu_domid, iommu->domid_bitmap); +- iommu->domid_map[iommu_domid] = 0; ++ cleanup_domid_map(domain, iommu); + } + + out: +@@ -1795,6 +1800,7 @@ static void iommu_domain_teardown(struct domain *d) + { + struct domain_iommu *hd = dom_iommu(d); + struct mapped_rmrr *mrmrr, *tmp; ++ const struct acpi_drhd_unit *drhd; + + if ( list_empty(&acpi_drhd_units) ) + return; +@@ -1814,6 +1820,9 @@ static void iommu_domain_teardown(struct domain *d) + iommu_free_pagetable(hd->arch.pgd_maddr, agaw_to_level(hd->arch.agaw)); + hd->arch.pgd_maddr = 0; + spin_unlock(&hd->arch.mapping_lock); ++ ++ for_each_drhd_unit ( drhd ) ++ cleanup_domid_map(d, drhd->iommu); + } + + static int __must_check intel_iommu_map_page(struct domain *d, dfn_t dfn, +-- +2.30.2 + diff --git a/xen.spec b/xen.spec index e3052d0..27c2825 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.14.2 -Release: 2%{?dist} +Release: 3%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -127,6 +127,20 @@ Patch56: xsa373-4.14-4.patch Patch57: xsa373-4.14-5.patch Patch58: xsa375.patch Patch59: xsa377.patch +Patch60: xen.git-e06d0c113e0067b86186db94aabae9c91aa09f35.patch +Patch61: xsa378-4.14-1.patch +Patch62: xsa378-4.14-2.patch +Patch63: xsa378-4.14-3.patch +Patch64: xsa378-4.14-4.patch +Patch65: xsa378-4.14-5.patch +Patch66: xsa378-4.14-6.patch +Patch67: xsa378-4.14-7.patch +Patch68: xsa378-4.14-8.patch +Patch69: xsa379-4.14.patch +Patch70: xsa380-4.14-1.patch +Patch71: xsa380-4.14-2.patch +Patch72: xsa382.patch +Patch73: xsa383.patch %if %build_qemutrad @@ -350,6 +364,20 @@ manage Xen virtual machines. %patch57 -p1 %patch58 -p1 %patch59 -p1 +%patch60 -p1 +%patch61 -p1 +%patch62 -p1 +%patch63 -p1 +%patch64 -p1 +%patch65 -p1 +%patch66 -p1 +%patch67 -p1 +%patch68 -p1 +%patch69 -p1 +%patch70 -p1 +%patch71 -p1 +%patch72 -p1 +%patch73 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -648,6 +676,9 @@ if [ -f /sbin/grub2-mkconfig ]; then DIR=/usr/lib/grub/i386-pc TARGET=/boot/grub2/i386-pc do_it $DIR $TARGET + DIR=/usr/lib/grub/x86_64-efi + TARGET=/boot/grub2/x86_64-efi + do_it $DIR $TARGET fi if [ -f /boot/efi/EFI/fedora/grub.cfg ]; then DIR=/usr/lib/grub/x86_64-efi @@ -943,6 +974,20 @@ fi %endif %changelog +* Sat Aug 28 2021 Michael Young - 4.14.2-3 +- IOMMU page mapping issues on x86 [XSA-378, CVE-2021-28694, + CVE-2021-28695, CVE-2021-28696] (#1997531) (#1997568) + (#1997537) +- grant table v2 status pages may remain accessible after de-allocation + [XSA-379, CVE-2021-28697] (#1997520) +- long running loops in grant table handling [XSA-380, CVE-2021-28698] + (#1997526) +- inadequate grant-v2 status frames array bounds check [XSA-382, + CVE-2021-28699] (#1997523) +- xen/arm: No memory limit for dom0less domUs [XSA-383, CVE-2021-28700] + (#1997527) +- grub x86_64-efi modules now go into /boot/grub2 + * Tue Jun 08 2021 Michael Young - 4.14.2-2 - xen/arm: Boot modules are not scrubbed [XSA-372, CVE-2021-28693] (#1970542) diff --git a/xsa378-4.14-1.patch b/xsa378-4.14-1.patch new file mode 100644 index 0000000..73956ea --- /dev/null +++ b/xsa378-4.14-1.patch @@ -0,0 +1,142 @@ +From: Jan Beulich +Subject: AMD/IOMMU: correct global exclusion range extending + +Besides unity mapping regions, the AMD IOMMU spec also provides for +exclusion ranges (areas of memory not to be subject to DMA translation) +to be specified by firmware in the ACPI tables. The spec does not put +any constraints on the number of such regions. + +Blindly assuming all addresses between any two such ranges should also +be excluded can't be right. Since hardware has room for just a single +such range (comprised of the Exclusion Base Register and the Exclusion +Range Limit Register), combine only adjacent or overlapping regions (for +now; this may require further adjustment in case table entries aren't +sorted by address) with matching exclusion_allow_all settings. This +requires bubbling up error indicators, such that IOMMU init can be +failed when concatenation wasn't possible. + +Furthermore, since the exclusion range specified in IOMMU registers +implies R/W access, reject requests asking for less permissions (this +will be brought closer to the spec by a subsequent change). + +This is part of XSA-378 / CVE-2021-28695. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/amd/iommu_acpi.c ++++ b/xen/drivers/passthrough/amd/iommu_acpi.c +@@ -117,12 +117,21 @@ static struct amd_iommu * __init find_io + return NULL; + } + +-static void __init reserve_iommu_exclusion_range( +- struct amd_iommu *iommu, uint64_t base, uint64_t limit) ++static int __init reserve_iommu_exclusion_range( ++ struct amd_iommu *iommu, uint64_t base, uint64_t limit, ++ bool all, bool iw, bool ir) + { ++ if ( !ir || !iw ) ++ return -EPERM; ++ + /* need to extend exclusion range? */ + if ( iommu->exclusion_enable ) + { ++ if ( iommu->exclusion_limit + PAGE_SIZE < base || ++ limit + PAGE_SIZE < iommu->exclusion_base || ++ iommu->exclusion_allow_all != all ) ++ return -EBUSY; ++ + if ( iommu->exclusion_base < base ) + base = iommu->exclusion_base; + if ( iommu->exclusion_limit > limit ) +@@ -130,16 +139,11 @@ static void __init reserve_iommu_exclusi + } + + iommu->exclusion_enable = IOMMU_CONTROL_ENABLED; ++ iommu->exclusion_allow_all = all; + iommu->exclusion_base = base; + iommu->exclusion_limit = limit; +-} + +-static void __init reserve_iommu_exclusion_range_all( +- struct amd_iommu *iommu, +- unsigned long base, unsigned long limit) +-{ +- reserve_iommu_exclusion_range(iommu, base, limit); +- iommu->exclusion_allow_all = IOMMU_CONTROL_ENABLED; ++ return 0; + } + + static void __init reserve_unity_map_for_device( +@@ -177,6 +181,7 @@ static int __init register_exclusion_ran + unsigned long range_top, iommu_top, length; + struct amd_iommu *iommu; + unsigned int bdf; ++ int rc = 0; + + /* is part of exclusion range inside of IOMMU virtual address space? */ + /* note: 'limit' parameter is assumed to be page-aligned */ +@@ -198,10 +203,15 @@ static int __init register_exclusion_ran + if ( limit >= iommu_top ) + { + for_each_amd_iommu( iommu ) +- reserve_iommu_exclusion_range_all(iommu, base, limit); ++ { ++ rc = reserve_iommu_exclusion_range(iommu, base, limit, ++ true /* all */, iw, ir); ++ if ( rc ) ++ break; ++ } + } + +- return 0; ++ return rc; + } + + static int __init register_exclusion_range_for_device( +@@ -212,6 +222,7 @@ static int __init register_exclusion_ran + unsigned long range_top, iommu_top, length; + struct amd_iommu *iommu; + u16 req; ++ int rc = 0; + + iommu = find_iommu_for_device(seg, bdf); + if ( !iommu ) +@@ -241,12 +252,13 @@ static int __init register_exclusion_ran + /* register IOMMU exclusion range settings for device */ + if ( limit >= iommu_top ) + { +- reserve_iommu_exclusion_range(iommu, base, limit); ++ rc = reserve_iommu_exclusion_range(iommu, base, limit, ++ false /* all */, iw, ir); + ivrs_mappings[bdf].dte_allow_exclusion = true; + ivrs_mappings[req].dte_allow_exclusion = true; + } + +- return 0; ++ return rc; + } + + static int __init register_exclusion_range_for_iommu_devices( +@@ -256,6 +268,7 @@ static int __init register_exclusion_ran + unsigned long range_top, iommu_top, length; + unsigned int bdf; + u16 req; ++ int rc = 0; + + /* is part of exclusion range inside of IOMMU virtual address space? */ + /* note: 'limit' parameter is assumed to be page-aligned */ +@@ -286,8 +299,10 @@ static int __init register_exclusion_ran + + /* register IOMMU exclusion range settings */ + if ( limit >= iommu_top ) +- reserve_iommu_exclusion_range_all(iommu, base, limit); +- return 0; ++ rc = reserve_iommu_exclusion_range(iommu, base, limit, ++ true /* all */, iw, ir); ++ ++ return rc; + } + + static int __init parse_ivmd_device_select( diff --git a/xsa378-4.14-2.patch b/xsa378-4.14-2.patch new file mode 100644 index 0000000..5d44561 --- /dev/null +++ b/xsa378-4.14-2.patch @@ -0,0 +1,218 @@ +From: Jan Beulich +Subject: AMD/IOMMU: correct device unity map handling + +Blindly assuming all addresses between any two such ranges, specified by +firmware in the ACPI tables, should also be unity-mapped can't be right. +Nor can it be correct to merge ranges with differing permissions. Track +ranges individually; don't merge at all, but check for overlaps instead. +This requires bubbling up error indicators, such that IOMMU init can be +failed when allocation of a new tracking struct wasn't possible, or an +overlap was detected. + +At this occasion also stop ignoring +amd_iommu_reserve_domain_unity_map()'s return value. + +This is part of XSA-378 / CVE-2021-28695. + +Signed-off-by: Jan Beulich +Reviewed-by: George Dunlap +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/amd/iommu.h ++++ b/xen/drivers/passthrough/amd/iommu.h +@@ -107,20 +107,24 @@ struct amd_iommu { + struct list_head ats_devices; + }; + ++struct ivrs_unity_map { ++ bool read:1; ++ bool write:1; ++ paddr_t addr; ++ unsigned long length; ++ struct ivrs_unity_map *next; ++}; ++ + struct ivrs_mappings { + uint16_t dte_requestor_id; + bool valid:1; + bool dte_allow_exclusion:1; +- bool unity_map_enable:1; +- bool write_permission:1; +- bool read_permission:1; + + /* ivhd device data settings */ + uint8_t device_flags; + +- unsigned long addr_range_start; +- unsigned long addr_range_length; + struct amd_iommu *iommu; ++ struct ivrs_unity_map *unity_map; + + /* per device interrupt remapping table */ + void *intremap_table; +--- a/xen/drivers/passthrough/amd/iommu_acpi.c ++++ b/xen/drivers/passthrough/amd/iommu_acpi.c +@@ -146,32 +146,48 @@ static int __init reserve_iommu_exclusio + return 0; + } + +-static void __init reserve_unity_map_for_device( +- u16 seg, u16 bdf, unsigned long base, +- unsigned long length, u8 iw, u8 ir) ++static int __init reserve_unity_map_for_device( ++ uint16_t seg, uint16_t bdf, unsigned long base, ++ unsigned long length, bool iw, bool ir) + { + struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(seg); +- unsigned long old_top, new_top; ++ struct ivrs_unity_map *unity_map = ivrs_mappings[bdf].unity_map; + +- /* need to extend unity-mapped range? */ +- if ( ivrs_mappings[bdf].unity_map_enable ) ++ /* Check for overlaps. */ ++ for ( ; unity_map; unity_map = unity_map->next ) + { +- old_top = ivrs_mappings[bdf].addr_range_start + +- ivrs_mappings[bdf].addr_range_length; +- new_top = base + length; +- if ( old_top > new_top ) +- new_top = old_top; +- if ( ivrs_mappings[bdf].addr_range_start < base ) +- base = ivrs_mappings[bdf].addr_range_start; +- length = new_top - base; +- } +- +- /* extend r/w permissioms and keep aggregate */ +- ivrs_mappings[bdf].write_permission = iw; +- ivrs_mappings[bdf].read_permission = ir; +- ivrs_mappings[bdf].unity_map_enable = true; +- ivrs_mappings[bdf].addr_range_start = base; +- ivrs_mappings[bdf].addr_range_length = length; ++ /* ++ * Exact matches are okay. This can in particular happen when ++ * register_exclusion_range_for_device() calls here twice for the ++ * same (s,b,d,f). ++ */ ++ if ( base == unity_map->addr && length == unity_map->length && ++ ir == unity_map->read && iw == unity_map->write ) ++ return 0; ++ ++ if ( unity_map->addr + unity_map->length > base && ++ base + length > unity_map->addr ) ++ { ++ AMD_IOMMU_DEBUG("IVMD Error: overlap [%lx,%lx) vs [%lx,%lx)\n", ++ base, base + length, unity_map->addr, ++ unity_map->addr + unity_map->length); ++ return -EPERM; ++ } ++ } ++ ++ /* Populate and insert a new unity map. */ ++ unity_map = xmalloc(struct ivrs_unity_map); ++ if ( !unity_map ) ++ return -ENOMEM; ++ ++ unity_map->read = ir; ++ unity_map->write = iw; ++ unity_map->addr = base; ++ unity_map->length = length; ++ unity_map->next = ivrs_mappings[bdf].unity_map; ++ ivrs_mappings[bdf].unity_map = unity_map; ++ ++ return 0; + } + + static int __init register_exclusion_range_for_all_devices( +@@ -194,13 +210,13 @@ static int __init register_exclusion_ran + length = range_top - base; + /* reserve r/w unity-mapped page entries for devices */ + /* note: these entries are part of the exclusion range */ +- for ( bdf = 0; bdf < ivrs_bdf_entries; bdf++ ) +- reserve_unity_map_for_device(seg, bdf, base, length, iw, ir); ++ for ( bdf = 0; !rc && bdf < ivrs_bdf_entries; bdf++ ) ++ rc = reserve_unity_map_for_device(seg, bdf, base, length, iw, ir); + /* push 'base' just outside of virtual address space */ + base = iommu_top; + } + /* register IOMMU exclusion range settings */ +- if ( limit >= iommu_top ) ++ if ( !rc && limit >= iommu_top ) + { + for_each_amd_iommu( iommu ) + { +@@ -242,15 +258,15 @@ static int __init register_exclusion_ran + length = range_top - base; + /* reserve unity-mapped page entries for device */ + /* note: these entries are part of the exclusion range */ +- reserve_unity_map_for_device(seg, bdf, base, length, iw, ir); +- reserve_unity_map_for_device(seg, req, base, length, iw, ir); ++ rc = reserve_unity_map_for_device(seg, bdf, base, length, iw, ir) ?: ++ reserve_unity_map_for_device(seg, req, base, length, iw, ir); + + /* push 'base' just outside of virtual address space */ + base = iommu_top; + } + + /* register IOMMU exclusion range settings for device */ +- if ( limit >= iommu_top ) ++ if ( !rc && limit >= iommu_top ) + { + rc = reserve_iommu_exclusion_range(iommu, base, limit, + false /* all */, iw, ir); +@@ -281,15 +297,15 @@ static int __init register_exclusion_ran + length = range_top - base; + /* reserve r/w unity-mapped page entries for devices */ + /* note: these entries are part of the exclusion range */ +- for ( bdf = 0; bdf < ivrs_bdf_entries; bdf++ ) ++ for ( bdf = 0; !rc && bdf < ivrs_bdf_entries; bdf++ ) + { + if ( iommu == find_iommu_for_device(iommu->seg, bdf) ) + { +- reserve_unity_map_for_device(iommu->seg, bdf, base, length, +- iw, ir); + req = get_ivrs_mappings(iommu->seg)[bdf].dte_requestor_id; +- reserve_unity_map_for_device(iommu->seg, req, base, length, +- iw, ir); ++ rc = reserve_unity_map_for_device(iommu->seg, bdf, base, length, ++ iw, ir) ?: ++ reserve_unity_map_for_device(iommu->seg, req, base, length, ++ iw, ir); + } + } + +@@ -298,7 +314,7 @@ static int __init register_exclusion_ran + } + + /* register IOMMU exclusion range settings */ +- if ( limit >= iommu_top ) ++ if ( !rc && limit >= iommu_top ) + rc = reserve_iommu_exclusion_range(iommu, base, limit, + true /* all */, iw, ir); + +--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c ++++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c +@@ -366,15 +366,17 @@ static int amd_iommu_assign_device(struc + struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(pdev->seg); + int bdf = PCI_BDF2(pdev->bus, devfn); + int req_id = get_dma_requestor_id(pdev->seg, bdf); ++ const struct ivrs_unity_map *unity_map; + +- if ( ivrs_mappings[req_id].unity_map_enable ) ++ for ( unity_map = ivrs_mappings[req_id].unity_map; unity_map; ++ unity_map = unity_map->next ) + { +- amd_iommu_reserve_domain_unity_map( +- d, +- ivrs_mappings[req_id].addr_range_start, +- ivrs_mappings[req_id].addr_range_length, +- ivrs_mappings[req_id].write_permission, +- ivrs_mappings[req_id].read_permission); ++ int rc = amd_iommu_reserve_domain_unity_map( ++ d, unity_map->addr, unity_map->length, ++ unity_map->write, unity_map->read); ++ ++ if ( rc ) ++ return rc; + } + + return reassign_device(pdev->domain, d, devfn, pdev); diff --git a/xsa378-4.14-3.patch b/xsa378-4.14-3.patch new file mode 100644 index 0000000..90474ad --- /dev/null +++ b/xsa378-4.14-3.patch @@ -0,0 +1,102 @@ +From: Jan Beulich +Subject: IOMMU: also pass p2m_access_t to p2m_get_iommu_flags() + +A subsequent change will want to customize the IOMMU permissions based +on this. + +This is part of XSA-378. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/arch/x86/mm/p2m-ept.c ++++ b/xen/arch/x86/mm/p2m-ept.c +@@ -680,7 +680,7 @@ ept_set_entry(struct p2m_domain *p2m, gf + uint8_t ipat = 0; + bool_t need_modify_vtd_table = 1; + bool_t vtd_pte_present = 0; +- unsigned int iommu_flags = p2m_get_iommu_flags(p2mt, mfn); ++ unsigned int iommu_flags = p2m_get_iommu_flags(p2mt, p2ma, mfn); + bool_t needs_sync = 1; + ept_entry_t old_entry = { .epte = 0 }; + ept_entry_t new_entry = { .epte = 0 }; +@@ -808,8 +808,8 @@ ept_set_entry(struct p2m_domain *p2m, gf + + /* Safe to read-then-write because we hold the p2m lock */ + if ( ept_entry->mfn == new_entry.mfn && +- p2m_get_iommu_flags(ept_entry->sa_p2mt, _mfn(ept_entry->mfn)) == +- iommu_flags ) ++ p2m_get_iommu_flags(ept_entry->sa_p2mt, ept_entry->access, ++ _mfn(ept_entry->mfn)) == iommu_flags ) + need_modify_vtd_table = 0; + + ept_p2m_type_to_flags(p2m, &new_entry); +--- a/xen/arch/x86/mm/p2m-pt.c ++++ b/xen/arch/x86/mm/p2m-pt.c +@@ -480,6 +480,16 @@ int p2m_pt_handle_deferred_changes(uint6 + return rc; + } + ++/* Reconstruct a fake p2m_access_t from stored PTE flags. */ ++static p2m_access_t p2m_flags_to_access(unsigned int flags) ++{ ++ if ( flags & _PAGE_PRESENT ) ++ return p2m_access_n; ++ ++ /* No need to look at _PAGE_NX for now. */ ++ return flags & _PAGE_RW ? p2m_access_rw : p2m_access_r; ++} ++ + /* Checks only applicable to entries with order > PAGE_ORDER_4K */ + static void check_entry(mfn_t mfn, p2m_type_t new, p2m_type_t old, + unsigned int order) +@@ -514,7 +524,7 @@ p2m_pt_set_entry(struct p2m_domain *p2m, + l2_pgentry_t l2e_content; + l3_pgentry_t l3e_content; + int rc; +- unsigned int iommu_pte_flags = p2m_get_iommu_flags(p2mt, mfn); ++ unsigned int iommu_pte_flags = p2m_get_iommu_flags(p2mt, p2ma, mfn); + /* + * old_mfn and iommu_old_flags control possible flush/update needs on the + * IOMMU: We need to flush when MFN or flags (i.e. permissions) change. +@@ -577,6 +587,7 @@ p2m_pt_set_entry(struct p2m_domain *p2m, + old_mfn = l1e_get_pfn(*p2m_entry); + iommu_old_flags = + p2m_get_iommu_flags(p2m_flags_to_type(flags), ++ p2m_flags_to_access(flags), + _mfn(old_mfn)); + } + else +@@ -619,9 +630,10 @@ p2m_pt_set_entry(struct p2m_domain *p2m, + 0, L1_PAGETABLE_ENTRIES); + ASSERT(p2m_entry); + old_mfn = l1e_get_pfn(*p2m_entry); ++ flags = l1e_get_flags(*p2m_entry); + iommu_old_flags = +- p2m_get_iommu_flags(p2m_flags_to_type(l1e_get_flags(*p2m_entry)), +- _mfn(old_mfn)); ++ p2m_get_iommu_flags(p2m_flags_to_type(flags), ++ p2m_flags_to_access(flags), _mfn(old_mfn)); + + if ( mfn_valid(mfn) || p2m_allows_invalid_mfn(p2mt) ) + entry_content = p2m_l1e_from_pfn(mfn_x(mfn), +@@ -649,6 +661,7 @@ p2m_pt_set_entry(struct p2m_domain *p2m, + old_mfn = l1e_get_pfn(*p2m_entry); + iommu_old_flags = + p2m_get_iommu_flags(p2m_flags_to_type(flags), ++ p2m_flags_to_access(flags), + _mfn(old_mfn)); + } + else +--- a/xen/include/asm-x86/p2m.h ++++ b/xen/include/asm-x86/p2m.h +@@ -903,7 +903,8 @@ static inline void p2m_altp2m_check(stru + /* + * p2m type to IOMMU flags + */ +-static inline unsigned int p2m_get_iommu_flags(p2m_type_t p2mt, mfn_t mfn) ++static inline unsigned int p2m_get_iommu_flags(p2m_type_t p2mt, ++ p2m_access_t p2ma, mfn_t mfn) + { + unsigned int flags; + diff --git a/xsa378-4.14-4.patch b/xsa378-4.14-4.patch new file mode 100644 index 0000000..62e63fd --- /dev/null +++ b/xsa378-4.14-4.patch @@ -0,0 +1,385 @@ +From: Jan Beulich +Subject: IOMMU: generalize VT-d's tracking of mapped RMRR regions + +In order to re-use it elsewhere, move the logic to vendor independent +code and strip it of RMRR specifics. + +Note that the prior "map" parameter gets folded into the new "p2ma" one +(which AMD IOMMU code will want to make use of), assigning alternative +meaning ("unmap") to p2m_access_x. Prepare set_identity_p2m_entry() and +p2m_get_iommu_flags() for getting passed access types other than +p2m_access_rw (in the latter case just for p2m_mmio_direct requests). + +Note also that, to be on the safe side, an overlap check gets added to +the main loop of iommu_identity_mapping(). + +This is part of XSA-378. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/arch/x86/mm/p2m.c ++++ b/xen/arch/x86/mm/p2m.c +@@ -1353,7 +1353,7 @@ int set_identity_p2m_entry(struct domain + if ( !is_iommu_enabled(d) ) + return 0; + return iommu_legacy_map(d, _dfn(gfn_l), _mfn(gfn_l), PAGE_ORDER_4K, +- IOMMUF_readable | IOMMUF_writable); ++ p2m_access_to_iommu_flags(p2ma)); + } + + gfn_lock(p2m, gfn, 0); +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -42,12 +42,6 @@ + #include "vtd.h" + #include "../ats.h" + +-struct mapped_rmrr { +- struct list_head list; +- u64 base, end; +- unsigned int count; +-}; +- + /* Possible unfiltered LAPIC/MSI messages from untrusted sources? */ + bool __read_mostly untrusted_msi; + +@@ -1800,17 +1794,12 @@ out: + static void iommu_domain_teardown(struct domain *d) + { + struct domain_iommu *hd = dom_iommu(d); +- struct mapped_rmrr *mrmrr, *tmp; + const struct acpi_drhd_unit *drhd; + + if ( list_empty(&acpi_drhd_units) ) + return; + +- list_for_each_entry_safe ( mrmrr, tmp, &hd->arch.mapped_rmrrs, list ) +- { +- list_del(&mrmrr->list); +- xfree(mrmrr); +- } ++ iommu_identity_map_teardown(d); + + ASSERT(is_iommu_enabled(d)); + +@@ -1966,74 +1955,6 @@ static void iommu_set_pgd(struct domain + pagetable_get_paddr(pagetable_from_mfn(pgd_mfn)); + } + +-static int rmrr_identity_mapping(struct domain *d, bool_t map, +- const struct acpi_rmrr_unit *rmrr, +- u32 flag) +-{ +- unsigned long base_pfn = rmrr->base_address >> PAGE_SHIFT_4K; +- unsigned long end_pfn = PAGE_ALIGN_4K(rmrr->end_address) >> PAGE_SHIFT_4K; +- struct mapped_rmrr *mrmrr; +- struct domain_iommu *hd = dom_iommu(d); +- +- ASSERT(pcidevs_locked()); +- ASSERT(rmrr->base_address < rmrr->end_address); +- +- /* +- * No need to acquire hd->arch.mapping_lock: Both insertion and removal +- * get done while holding pcidevs_lock. +- */ +- list_for_each_entry( mrmrr, &hd->arch.mapped_rmrrs, list ) +- { +- if ( mrmrr->base == rmrr->base_address && +- mrmrr->end == rmrr->end_address ) +- { +- int ret = 0; +- +- if ( map ) +- { +- ++mrmrr->count; +- return 0; +- } +- +- if ( --mrmrr->count ) +- return 0; +- +- while ( base_pfn < end_pfn ) +- { +- if ( clear_identity_p2m_entry(d, base_pfn) ) +- ret = -ENXIO; +- base_pfn++; +- } +- +- list_del(&mrmrr->list); +- xfree(mrmrr); +- return ret; +- } +- } +- +- if ( !map ) +- return -ENOENT; +- +- while ( base_pfn < end_pfn ) +- { +- int err = set_identity_p2m_entry(d, base_pfn, p2m_access_rw, flag); +- +- if ( err ) +- return err; +- base_pfn++; +- } +- +- mrmrr = xmalloc(struct mapped_rmrr); +- if ( !mrmrr ) +- return -ENOMEM; +- mrmrr->base = rmrr->base_address; +- mrmrr->end = rmrr->end_address; +- mrmrr->count = 1; +- list_add_tail(&mrmrr->list, &hd->arch.mapped_rmrrs); +- +- return 0; +-} +- + static int intel_iommu_add_device(u8 devfn, struct pci_dev *pdev) + { + struct acpi_rmrr_unit *rmrr; +@@ -2065,7 +1986,9 @@ static int intel_iommu_add_device(u8 dev + * Since RMRRs are always reserved in the e820 map for the hardware + * domain, there shouldn't be a conflict. + */ +- ret = rmrr_identity_mapping(pdev->domain, 1, rmrr, 0); ++ ret = iommu_identity_mapping(pdev->domain, p2m_access_rw, ++ rmrr->base_address, rmrr->end_address, ++ 0); + if ( ret ) + dprintk(XENLOG_ERR VTDPREFIX, "d%d: RMRR mapping failed\n", + pdev->domain->domain_id); +@@ -2110,7 +2033,8 @@ static int intel_iommu_remove_device(u8 + * Any flag is nothing to clear these mappings but here + * its always safe and strict to set 0. + */ +- rmrr_identity_mapping(pdev->domain, 0, rmrr, 0); ++ iommu_identity_mapping(pdev->domain, p2m_access_x, rmrr->base_address, ++ rmrr->end_address, 0); + } + + return domain_context_unmap(pdev->domain, devfn, pdev); +@@ -2309,7 +2233,8 @@ static void __hwdom_init setup_hwdom_rmr + * domain, there shouldn't be a conflict. So its always safe and + * strict to set 0. + */ +- ret = rmrr_identity_mapping(d, 1, rmrr, 0); ++ ret = iommu_identity_mapping(d, p2m_access_rw, rmrr->base_address, ++ rmrr->end_address, 0); + if ( ret ) + dprintk(XENLOG_ERR VTDPREFIX, + "IOMMU: mapping reserved region failed\n"); +@@ -2480,7 +2405,9 @@ static int reassign_device_ownership( + * Any RMRR flag is always ignored when remove a device, + * but its always safe and strict to set 0. + */ +- ret = rmrr_identity_mapping(source, 0, rmrr, 0); ++ ret = iommu_identity_mapping(source, p2m_access_x, ++ rmrr->base_address, ++ rmrr->end_address, 0); + if ( ret != -ENOENT ) + return ret; + } +@@ -2577,7 +2504,8 @@ static int intel_iommu_assign_device( + PCI_BUS(bdf) == bus && + PCI_DEVFN2(bdf) == devfn ) + { +- ret = rmrr_identity_mapping(d, 1, rmrr, flag); ++ ret = iommu_identity_mapping(d, p2m_access_rw, rmrr->base_address, ++ rmrr->end_address, flag); + if ( ret ) + { + int rc; +--- a/xen/drivers/passthrough/x86/iommu.c ++++ b/xen/drivers/passthrough/x86/iommu.c +@@ -139,7 +139,7 @@ int arch_iommu_domain_init(struct domain + struct domain_iommu *hd = dom_iommu(d); + + spin_lock_init(&hd->arch.mapping_lock); +- INIT_LIST_HEAD(&hd->arch.mapped_rmrrs); ++ INIT_LIST_HEAD(&hd->arch.identity_maps); + + return 0; + } +@@ -148,6 +148,99 @@ void arch_iommu_domain_destroy(struct do + { + } + ++struct identity_map { ++ struct list_head list; ++ paddr_t base, end; ++ p2m_access_t access; ++ unsigned int count; ++}; ++ ++int iommu_identity_mapping(struct domain *d, p2m_access_t p2ma, ++ paddr_t base, paddr_t end, ++ unsigned int flag) ++{ ++ unsigned long base_pfn = base >> PAGE_SHIFT_4K; ++ unsigned long end_pfn = PAGE_ALIGN_4K(end) >> PAGE_SHIFT_4K; ++ struct identity_map *map; ++ struct domain_iommu *hd = dom_iommu(d); ++ ++ ASSERT(pcidevs_locked()); ++ ASSERT(base < end); ++ ++ /* ++ * No need to acquire hd->arch.mapping_lock: Both insertion and removal ++ * get done while holding pcidevs_lock. ++ */ ++ list_for_each_entry( map, &hd->arch.identity_maps, list ) ++ { ++ if ( map->base == base && map->end == end ) ++ { ++ int ret = 0; ++ ++ if ( p2ma != p2m_access_x ) ++ { ++ if ( map->access != p2ma ) ++ return -EADDRINUSE; ++ ++map->count; ++ return 0; ++ } ++ ++ if ( --map->count ) ++ return 0; ++ ++ while ( base_pfn < end_pfn ) ++ { ++ if ( clear_identity_p2m_entry(d, base_pfn) ) ++ ret = -ENXIO; ++ base_pfn++; ++ } ++ ++ list_del(&map->list); ++ xfree(map); ++ ++ return ret; ++ } ++ ++ if ( end >= map->base && map->end >= base ) ++ return -EADDRINUSE; ++ } ++ ++ if ( p2ma == p2m_access_x ) ++ return -ENOENT; ++ ++ while ( base_pfn < end_pfn ) ++ { ++ int err = set_identity_p2m_entry(d, base_pfn, p2ma, flag); ++ ++ if ( err ) ++ return err; ++ base_pfn++; ++ } ++ ++ map = xmalloc(struct identity_map); ++ if ( !map ) ++ return -ENOMEM; ++ map->base = base; ++ map->end = end; ++ map->access = p2ma; ++ map->count = 1; ++ list_add_tail(&map->list, &hd->arch.identity_maps); ++ ++ return 0; ++} ++ ++void iommu_identity_map_teardown(struct domain *d) ++{ ++ struct domain_iommu *hd = dom_iommu(d); ++ struct identity_map *map, *tmp; ++ ++ list_for_each_entry_safe ( map, tmp, &hd->arch.identity_maps, list ) ++ { ++ list_del(&map->list); ++ xfree(map); ++ } ++} ++ + static bool __hwdom_init hwdom_iommu_map(const struct domain *d, + unsigned long pfn, + unsigned long max_pfn) +--- a/xen/include/asm-x86/iommu.h ++++ b/xen/include/asm-x86/iommu.h +@@ -16,6 +16,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -49,7 +50,7 @@ struct arch_iommu + spinlock_t mapping_lock; /* io page table lock */ + int agaw; /* adjusted guest address width, 0 is level 2 30-bit */ + u64 iommu_bitmap; /* bitmap of iommu(s) that the domain uses */ +- struct list_head mapped_rmrrs; ++ struct list_head identity_maps; + + /* amd iommu support */ + int paging_mode; +@@ -112,6 +113,11 @@ static inline void iommu_disable_x2apic( + iommu_ops.disable_x2apic(); + } + ++int iommu_identity_mapping(struct domain *d, p2m_access_t p2ma, ++ paddr_t base, paddr_t end, ++ unsigned int flag); ++void iommu_identity_map_teardown(struct domain *d); ++ + extern bool untrusted_msi; + + int pi_update_irte(const struct pi_desc *pi_desc, const struct pirq *pirq, +--- a/xen/include/asm-x86/p2m.h ++++ b/xen/include/asm-x86/p2m.h +@@ -900,6 +900,34 @@ struct p2m_domain *p2m_get_altp2m(struct + static inline void p2m_altp2m_check(struct vcpu *v, uint16_t idx) {} + #endif + ++/* p2m access to IOMMU flags */ ++static inline unsigned int p2m_access_to_iommu_flags(p2m_access_t p2ma) ++{ ++ switch ( p2ma ) ++ { ++ case p2m_access_rw: ++ case p2m_access_rwx: ++ return IOMMUF_readable | IOMMUF_writable; ++ ++ case p2m_access_r: ++ case p2m_access_rx: ++ case p2m_access_rx2rw: ++ return IOMMUF_readable; ++ ++ case p2m_access_w: ++ case p2m_access_wx: ++ return IOMMUF_writable; ++ ++ case p2m_access_n: ++ case p2m_access_x: ++ case p2m_access_n2rwx: ++ return 0; ++ } ++ ++ ASSERT_UNREACHABLE(); ++ return 0; ++} ++ + /* + * p2m type to IOMMU flags + */ +@@ -921,9 +949,10 @@ static inline unsigned int p2m_get_iommu + flags = IOMMUF_readable; + break; + case p2m_mmio_direct: +- flags = IOMMUF_readable; +- if ( !rangeset_contains_singleton(mmio_ro_ranges, mfn_x(mfn)) ) +- flags |= IOMMUF_writable; ++ flags = p2m_access_to_iommu_flags(p2ma); ++ if ( (flags & IOMMUF_writable) && ++ rangeset_contains_singleton(mmio_ro_ranges, mfn_x(mfn)) ) ++ flags &= ~IOMMUF_writable; + break; + default: + flags = 0; diff --git a/xsa378-4.14-5.patch b/xsa378-4.14-5.patch new file mode 100644 index 0000000..020f87c --- /dev/null +++ b/xsa378-4.14-5.patch @@ -0,0 +1,208 @@ +From: Jan Beulich +Subject: AMD/IOMMU: re-arrange/complete re-assignment handling + +Prior to the assignment step having completed successfully, devices +should not get associated with their new owner. Hand the device to DomIO +(perhaps temporarily), until after the de-assignment step has completed. + +De-assignment of a device (from other than Dom0) as well as failure of +reassign_device() during assignment should result in unity mappings +getting torn down. This in turn requires switching to a refcounted +mapping approach, as was already used by VT-d for its RMRRs, to prevent +unmapping a region used by multiple devices. + +This is CVE-2021-28696 / part of XSA-378. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/amd/iommu.h ++++ b/xen/drivers/passthrough/amd/iommu.h +@@ -232,8 +232,10 @@ int __must_check amd_iommu_unmap_page(st + unsigned int *flush_flags); + int __must_check amd_iommu_alloc_root(struct domain_iommu *hd); + int amd_iommu_reserve_domain_unity_map(struct domain *domain, +- paddr_t phys_addr, unsigned long size, +- int iw, int ir); ++ const struct ivrs_unity_map *map, ++ unsigned int flag); ++int amd_iommu_reserve_domain_unity_unmap(struct domain *d, ++ const struct ivrs_unity_map *map); + int __must_check amd_iommu_flush_iotlb_pages(struct domain *d, dfn_t dfn, + unsigned int page_count, + unsigned int flush_flags); +--- a/xen/drivers/passthrough/amd/iommu_map.c ++++ b/xen/drivers/passthrough/amd/iommu_map.c +@@ -420,38 +420,49 @@ int amd_iommu_flush_iotlb_all(struct dom + return 0; + } + +-int amd_iommu_reserve_domain_unity_map(struct domain *domain, +- paddr_t phys_addr, +- unsigned long size, int iw, int ir) ++int amd_iommu_reserve_domain_unity_map(struct domain *d, ++ const struct ivrs_unity_map *map, ++ unsigned int flag) + { +- unsigned long npages, i; +- unsigned long gfn; +- unsigned int flags = !!ir; +- unsigned int flush_flags = 0; +- int rt = 0; +- +- if ( iw ) +- flags |= IOMMUF_writable; +- +- npages = region_to_pages(phys_addr, size); +- gfn = phys_addr >> PAGE_SHIFT; +- for ( i = 0; i < npages; i++ ) ++ int rc; ++ ++ if ( d == dom_io ) ++ return 0; ++ ++ for ( rc = 0; !rc && map; map = map->next ) + { +- unsigned long frame = gfn + i; ++ p2m_access_t p2ma = p2m_access_n; ++ ++ if ( map->read ) ++ p2ma |= p2m_access_r; ++ if ( map->write ) ++ p2ma |= p2m_access_w; + +- rt = amd_iommu_map_page(domain, _dfn(frame), _mfn(frame), flags, +- &flush_flags); +- if ( rt != 0 ) +- break; ++ rc = iommu_identity_mapping(d, p2ma, map->addr, ++ map->addr + map->length - 1, flag); + } + +- /* Use while-break to avoid compiler warning */ +- while ( flush_flags && +- amd_iommu_flush_iotlb_pages(domain, _dfn(gfn), +- npages, flush_flags) ) +- break; ++ return rc; ++} ++ ++int amd_iommu_reserve_domain_unity_unmap(struct domain *d, ++ const struct ivrs_unity_map *map) ++{ ++ int rc; ++ ++ if ( d == dom_io ) ++ return 0; ++ ++ for ( rc = 0; map; map = map->next ) ++ { ++ int ret = iommu_identity_mapping(d, p2m_access_x, map->addr, ++ map->addr + map->length - 1, 0); ++ ++ if ( ret && ret != -ENOENT && !rc ) ++ rc = ret; ++ } + +- return rt; ++ return rc; + } + + int __init amd_iommu_quarantine_init(struct domain *d) +--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c ++++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c +@@ -327,6 +327,7 @@ static int reassign_device(struct domain + struct amd_iommu *iommu; + int bdf, rc; + struct domain_iommu *t = dom_iommu(target); ++ const struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(pdev->seg); + + bdf = PCI_BDF2(pdev->bus, pdev->devfn); + iommu = find_iommu_for_device(pdev->seg, bdf); +@@ -341,10 +342,24 @@ static int reassign_device(struct domain + + amd_iommu_disable_domain_device(source, iommu, devfn, pdev); + +- if ( devfn == pdev->devfn ) ++ /* ++ * If the device belongs to the hardware domain, and it has a unity mapping, ++ * don't remove it from the hardware domain, because BIOS may reference that ++ * mapping. ++ */ ++ if ( !is_hardware_domain(source) ) ++ { ++ rc = amd_iommu_reserve_domain_unity_unmap( ++ source, ++ ivrs_mappings[get_dma_requestor_id(pdev->seg, bdf)].unity_map); ++ if ( rc ) ++ return rc; ++ } ++ ++ if ( devfn == pdev->devfn && pdev->domain != dom_io ) + { +- list_move(&pdev->domain_list, &target->pdev_list); +- pdev->domain = target; ++ list_move(&pdev->domain_list, &dom_io->pdev_list); ++ pdev->domain = dom_io; + } + + rc = allocate_domain_resources(t); +@@ -356,6 +371,12 @@ static int reassign_device(struct domain + pdev->seg, pdev->bus, PCI_SLOT(devfn), PCI_FUNC(devfn), + source->domain_id, target->domain_id); + ++ if ( devfn == pdev->devfn && pdev->domain != target ) ++ { ++ list_move(&pdev->domain_list, &target->pdev_list); ++ pdev->domain = target; ++ } ++ + return 0; + } + +@@ -366,20 +387,28 @@ static int amd_iommu_assign_device(struc + struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(pdev->seg); + int bdf = PCI_BDF2(pdev->bus, devfn); + int req_id = get_dma_requestor_id(pdev->seg, bdf); +- const struct ivrs_unity_map *unity_map; ++ int rc = amd_iommu_reserve_domain_unity_map( ++ d, ivrs_mappings[req_id].unity_map, flag); ++ ++ if ( !rc ) ++ rc = reassign_device(pdev->domain, d, devfn, pdev); + +- for ( unity_map = ivrs_mappings[req_id].unity_map; unity_map; +- unity_map = unity_map->next ) ++ if ( rc && !is_hardware_domain(d) ) + { +- int rc = amd_iommu_reserve_domain_unity_map( +- d, unity_map->addr, unity_map->length, +- unity_map->write, unity_map->read); ++ int ret = amd_iommu_reserve_domain_unity_unmap( ++ d, ivrs_mappings[req_id].unity_map); + +- if ( rc ) +- return rc; ++ if ( ret ) ++ { ++ printk(XENLOG_ERR "AMD-Vi: " ++ "unity-unmap for %pd/%04x:%02x:%02x.%u failed (%d)\n", ++ d, pdev->seg, pdev->bus, ++ PCI_SLOT(devfn), PCI_FUNC(devfn), ret); ++ domain_crash(d); ++ } + } + +- return reassign_device(pdev->domain, d, devfn, pdev); ++ return rc; + } + + static void deallocate_next_page_table(struct page_info *pg, int level) +@@ -438,6 +467,7 @@ static void deallocate_iommu_page_tables + + static void amd_iommu_domain_destroy(struct domain *d) + { ++ iommu_identity_map_teardown(d); + deallocate_iommu_page_tables(d); + amd_iommu_flush_all_pages(d); + } diff --git a/xsa378-4.14-6.patch b/xsa378-4.14-6.patch new file mode 100644 index 0000000..ccecb97 --- /dev/null +++ b/xsa378-4.14-6.patch @@ -0,0 +1,411 @@ +From: Jan Beulich +Subject: AMD/IOMMU: re-arrange exclusion range and unity map recording + +The spec makes no provisions for OS behavior here to depend on the +amount of RAM found on the system. While the spec may not sufficiently +clearly distinguish both kinds of regions, they are surely meant to be +separate things: Only regions with ACPI_IVMD_EXCLUSION_RANGE set should +be candidates for putting in the exclusion range registers. (As there's +only a single such pair of registers per IOMMU, secondary non-adjacent +regions with the flag set already get converted to unity mapped +regions.) + +First of all, drop the dependency on max_page. With commit b4f042236ae0 +("AMD/IOMMU: Cease using a dynamic height for the IOMMU pagetables") the +use of it here was stale anyway; it was bogus already before, as it +didn't account for max_page getting increased later on. Simply try an +exclusion range registration first, and if it fails (for being +unsuitable or non-mergeable), register a unity mapping range. + +With this various local variables become unnecessary and hence get +dropped at the same time. + +With the max_page boundary dropped for using unity maps, the minimum +page table tree height now needs both recording and enforcing in +amd_iommu_domain_init(). Since we can't predict which devices may get +assigned to a domain, our only option is to uniformly force at least +that height for all domains, now that the height isn't dynamic anymore. + +Further don't make use of the exclusion range unless ACPI data says so. + +Note that exclusion range registration in +register_range_for_all_devices() is on a best effort basis. Hence unity +map entries also registered are redundant when the former succeeded, but +they also do no harm. Improvements in this area can be done later imo. + +Also adjust types where suitable without touching extra lines. + +This is part of XSA-378. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/amd/iommu.h ++++ b/xen/drivers/passthrough/amd/iommu.h +@@ -308,6 +308,8 @@ extern struct hpet_sbdf { + } init; + } hpet_sbdf; + ++extern int amd_iommu_min_paging_mode; ++ + extern void *shared_intremap_table; + extern unsigned long *shared_intremap_inuse; + +--- a/xen/drivers/passthrough/amd/iommu_acpi.c ++++ b/xen/drivers/passthrough/amd/iommu_acpi.c +@@ -118,12 +118,8 @@ static struct amd_iommu * __init find_io + } + + static int __init reserve_iommu_exclusion_range( +- struct amd_iommu *iommu, uint64_t base, uint64_t limit, +- bool all, bool iw, bool ir) ++ struct amd_iommu *iommu, paddr_t base, paddr_t limit, bool all) + { +- if ( !ir || !iw ) +- return -EPERM; +- + /* need to extend exclusion range? */ + if ( iommu->exclusion_enable ) + { +@@ -152,14 +148,18 @@ static int __init reserve_unity_map_for_ + { + struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(seg); + struct ivrs_unity_map *unity_map = ivrs_mappings[bdf].unity_map; ++ int paging_mode = amd_iommu_get_paging_mode(PFN_UP(base + length)); ++ ++ if ( paging_mode < 0 ) ++ return paging_mode; + + /* Check for overlaps. */ + for ( ; unity_map; unity_map = unity_map->next ) + { + /* + * Exact matches are okay. This can in particular happen when +- * register_exclusion_range_for_device() calls here twice for the +- * same (s,b,d,f). ++ * register_range_for_device() calls here twice for the same ++ * (s,b,d,f). + */ + if ( base == unity_map->addr && length == unity_map->length && + ir == unity_map->read && iw == unity_map->write ) +@@ -187,55 +187,52 @@ static int __init reserve_unity_map_for_ + unity_map->next = ivrs_mappings[bdf].unity_map; + ivrs_mappings[bdf].unity_map = unity_map; + ++ if ( paging_mode > amd_iommu_min_paging_mode ) ++ amd_iommu_min_paging_mode = paging_mode; ++ + return 0; + } + +-static int __init register_exclusion_range_for_all_devices( +- unsigned long base, unsigned long limit, u8 iw, u8 ir) ++static int __init register_range_for_all_devices( ++ paddr_t base, paddr_t limit, bool iw, bool ir, bool exclusion) + { + int seg = 0; /* XXX */ +- unsigned long range_top, iommu_top, length; + struct amd_iommu *iommu; +- unsigned int bdf; + int rc = 0; + + /* is part of exclusion range inside of IOMMU virtual address space? */ + /* note: 'limit' parameter is assumed to be page-aligned */ +- range_top = limit + PAGE_SIZE; +- iommu_top = max_page * PAGE_SIZE; +- if ( base < iommu_top ) +- { +- if ( range_top > iommu_top ) +- range_top = iommu_top; +- length = range_top - base; +- /* reserve r/w unity-mapped page entries for devices */ +- /* note: these entries are part of the exclusion range */ +- for ( bdf = 0; !rc && bdf < ivrs_bdf_entries; bdf++ ) +- rc = reserve_unity_map_for_device(seg, bdf, base, length, iw, ir); +- /* push 'base' just outside of virtual address space */ +- base = iommu_top; +- } +- /* register IOMMU exclusion range settings */ +- if ( !rc && limit >= iommu_top ) ++ if ( exclusion ) + { + for_each_amd_iommu( iommu ) + { +- rc = reserve_iommu_exclusion_range(iommu, base, limit, +- true /* all */, iw, ir); +- if ( rc ) +- break; ++ int ret = reserve_iommu_exclusion_range(iommu, base, limit, ++ true /* all */); ++ ++ if ( ret && !rc ) ++ rc = ret; + } + } + ++ if ( !exclusion || rc ) ++ { ++ paddr_t length = limit + PAGE_SIZE - base; ++ unsigned int bdf; ++ ++ /* reserve r/w unity-mapped page entries for devices */ ++ for ( bdf = rc = 0; !rc && bdf < ivrs_bdf_entries; bdf++ ) ++ rc = reserve_unity_map_for_device(seg, bdf, base, length, iw, ir); ++ } ++ + return rc; + } + +-static int __init register_exclusion_range_for_device( +- u16 bdf, unsigned long base, unsigned long limit, u8 iw, u8 ir) ++static int __init register_range_for_device( ++ unsigned int bdf, paddr_t base, paddr_t limit, ++ bool iw, bool ir, bool exclusion) + { + int seg = 0; /* XXX */ + struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(seg); +- unsigned long range_top, iommu_top, length; + struct amd_iommu *iommu; + u16 req; + int rc = 0; +@@ -249,27 +246,19 @@ static int __init register_exclusion_ran + req = ivrs_mappings[bdf].dte_requestor_id; + + /* note: 'limit' parameter is assumed to be page-aligned */ +- range_top = limit + PAGE_SIZE; +- iommu_top = max_page * PAGE_SIZE; +- if ( base < iommu_top ) +- { +- if ( range_top > iommu_top ) +- range_top = iommu_top; +- length = range_top - base; ++ if ( exclusion ) ++ rc = reserve_iommu_exclusion_range(iommu, base, limit, ++ false /* all */); ++ if ( !exclusion || rc ) ++ { ++ paddr_t length = limit + PAGE_SIZE - base; ++ + /* reserve unity-mapped page entries for device */ +- /* note: these entries are part of the exclusion range */ + rc = reserve_unity_map_for_device(seg, bdf, base, length, iw, ir) ?: + reserve_unity_map_for_device(seg, req, base, length, iw, ir); +- +- /* push 'base' just outside of virtual address space */ +- base = iommu_top; + } +- +- /* register IOMMU exclusion range settings for device */ +- if ( !rc && limit >= iommu_top ) ++ else + { +- rc = reserve_iommu_exclusion_range(iommu, base, limit, +- false /* all */, iw, ir); + ivrs_mappings[bdf].dte_allow_exclusion = true; + ivrs_mappings[req].dte_allow_exclusion = true; + } +@@ -277,53 +266,42 @@ static int __init register_exclusion_ran + return rc; + } + +-static int __init register_exclusion_range_for_iommu_devices( +- struct amd_iommu *iommu, +- unsigned long base, unsigned long limit, u8 iw, u8 ir) ++static int __init register_range_for_iommu_devices( ++ struct amd_iommu *iommu, paddr_t base, paddr_t limit, ++ bool iw, bool ir, bool exclusion) + { +- unsigned long range_top, iommu_top, length; ++ /* note: 'limit' parameter is assumed to be page-aligned */ ++ paddr_t length = limit + PAGE_SIZE - base; + unsigned int bdf; + u16 req; +- int rc = 0; ++ int rc; + +- /* is part of exclusion range inside of IOMMU virtual address space? */ +- /* note: 'limit' parameter is assumed to be page-aligned */ +- range_top = limit + PAGE_SIZE; +- iommu_top = max_page * PAGE_SIZE; +- if ( base < iommu_top ) +- { +- if ( range_top > iommu_top ) +- range_top = iommu_top; +- length = range_top - base; +- /* reserve r/w unity-mapped page entries for devices */ +- /* note: these entries are part of the exclusion range */ +- for ( bdf = 0; !rc && bdf < ivrs_bdf_entries; bdf++ ) +- { +- if ( iommu == find_iommu_for_device(iommu->seg, bdf) ) +- { +- req = get_ivrs_mappings(iommu->seg)[bdf].dte_requestor_id; +- rc = reserve_unity_map_for_device(iommu->seg, bdf, base, length, +- iw, ir) ?: +- reserve_unity_map_for_device(iommu->seg, req, base, length, +- iw, ir); +- } +- } +- +- /* push 'base' just outside of virtual address space */ +- base = iommu_top; ++ if ( exclusion ) ++ { ++ rc = reserve_iommu_exclusion_range(iommu, base, limit, true /* all */); ++ if ( !rc ) ++ return 0; + } + +- /* register IOMMU exclusion range settings */ +- if ( !rc && limit >= iommu_top ) +- rc = reserve_iommu_exclusion_range(iommu, base, limit, +- true /* all */, iw, ir); ++ /* reserve unity-mapped page entries for devices */ ++ for ( bdf = rc = 0; !rc && bdf < ivrs_bdf_entries; bdf++ ) ++ { ++ if ( iommu != find_iommu_for_device(iommu->seg, bdf) ) ++ continue; ++ ++ req = get_ivrs_mappings(iommu->seg)[bdf].dte_requestor_id; ++ rc = reserve_unity_map_for_device(iommu->seg, bdf, base, length, ++ iw, ir) ?: ++ reserve_unity_map_for_device(iommu->seg, req, base, length, ++ iw, ir); ++ } + + return rc; + } + + static int __init parse_ivmd_device_select( + const struct acpi_ivrs_memory *ivmd_block, +- unsigned long base, unsigned long limit, u8 iw, u8 ir) ++ paddr_t base, paddr_t limit, bool iw, bool ir, bool exclusion) + { + u16 bdf; + +@@ -334,12 +312,12 @@ static int __init parse_ivmd_device_sele + return -ENODEV; + } + +- return register_exclusion_range_for_device(bdf, base, limit, iw, ir); ++ return register_range_for_device(bdf, base, limit, iw, ir, exclusion); + } + + static int __init parse_ivmd_device_range( + const struct acpi_ivrs_memory *ivmd_block, +- unsigned long base, unsigned long limit, u8 iw, u8 ir) ++ paddr_t base, paddr_t limit, bool iw, bool ir, bool exclusion) + { + unsigned int first_bdf, last_bdf, bdf; + int error; +@@ -361,15 +339,15 @@ static int __init parse_ivmd_device_rang + } + + for ( bdf = first_bdf, error = 0; (bdf <= last_bdf) && !error; bdf++ ) +- error = register_exclusion_range_for_device( +- bdf, base, limit, iw, ir); ++ error = register_range_for_device( ++ bdf, base, limit, iw, ir, exclusion); + + return error; + } + + static int __init parse_ivmd_device_iommu( + const struct acpi_ivrs_memory *ivmd_block, +- unsigned long base, unsigned long limit, u8 iw, u8 ir) ++ paddr_t base, paddr_t limit, bool iw, bool ir, bool exclusion) + { + int seg = 0; /* XXX */ + struct amd_iommu *iommu; +@@ -384,14 +362,14 @@ static int __init parse_ivmd_device_iomm + return -ENODEV; + } + +- return register_exclusion_range_for_iommu_devices( +- iommu, base, limit, iw, ir); ++ return register_range_for_iommu_devices( ++ iommu, base, limit, iw, ir, exclusion); + } + + static int __init parse_ivmd_block(const struct acpi_ivrs_memory *ivmd_block) + { + unsigned long start_addr, mem_length, base, limit; +- u8 iw, ir; ++ bool iw = true, ir = true, exclusion = false; + + if ( ivmd_block->header.length < sizeof(*ivmd_block) ) + { +@@ -408,13 +386,11 @@ static int __init parse_ivmd_block(const + ivmd_block->header.type, start_addr, mem_length); + + if ( ivmd_block->header.flags & ACPI_IVMD_EXCLUSION_RANGE ) +- iw = ir = IOMMU_CONTROL_ENABLED; ++ exclusion = true; + else if ( ivmd_block->header.flags & ACPI_IVMD_UNITY ) + { +- iw = ivmd_block->header.flags & ACPI_IVMD_READ ? +- IOMMU_CONTROL_ENABLED : IOMMU_CONTROL_DISABLED; +- ir = ivmd_block->header.flags & ACPI_IVMD_WRITE ? +- IOMMU_CONTROL_ENABLED : IOMMU_CONTROL_DISABLED; ++ iw = ivmd_block->header.flags & ACPI_IVMD_READ; ++ ir = ivmd_block->header.flags & ACPI_IVMD_WRITE; + } + else + { +@@ -425,20 +401,20 @@ static int __init parse_ivmd_block(const + switch( ivmd_block->header.type ) + { + case ACPI_IVRS_TYPE_MEMORY_ALL: +- return register_exclusion_range_for_all_devices( +- base, limit, iw, ir); ++ return register_range_for_all_devices( ++ base, limit, iw, ir, exclusion); + + case ACPI_IVRS_TYPE_MEMORY_ONE: +- return parse_ivmd_device_select(ivmd_block, +- base, limit, iw, ir); ++ return parse_ivmd_device_select(ivmd_block, base, limit, ++ iw, ir, exclusion); + + case ACPI_IVRS_TYPE_MEMORY_RANGE: +- return parse_ivmd_device_range(ivmd_block, +- base, limit, iw, ir); ++ return parse_ivmd_device_range(ivmd_block, base, limit, ++ iw, ir, exclusion); + + case ACPI_IVRS_TYPE_MEMORY_IOMMU: +- return parse_ivmd_device_iommu(ivmd_block, +- base, limit, iw, ir); ++ return parse_ivmd_device_iommu(ivmd_block, base, limit, ++ iw, ir, exclusion); + + default: + AMD_IOMMU_DEBUG("IVMD Error: Invalid Block Type!\n"); +--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c ++++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c +@@ -231,6 +231,8 @@ static int __must_check allocate_domain_ + return rc; + } + ++int __read_mostly amd_iommu_min_paging_mode = 1; ++ + static int amd_iommu_domain_init(struct domain *d) + { + struct domain_iommu *hd = dom_iommu(d); +@@ -242,11 +244,13 @@ static int amd_iommu_domain_init(struct + * - HVM could in principle use 3 or 4 depending on how much guest + * physical address space we give it, but this isn't known yet so use 4 + * unilaterally. ++ * - Unity maps may require an even higher number. + */ +- hd->arch.paging_mode = amd_iommu_get_paging_mode( +- is_hvm_domain(d) +- ? 1ul << (DEFAULT_DOMAIN_ADDRESS_WIDTH - PAGE_SHIFT) +- : get_upper_mfn_bound() + 1); ++ hd->arch.paging_mode = max(amd_iommu_get_paging_mode( ++ is_hvm_domain(d) ++ ? 1ul << (DEFAULT_DOMAIN_ADDRESS_WIDTH - PAGE_SHIFT) ++ : get_upper_mfn_bound() + 1), ++ amd_iommu_min_paging_mode); + + return 0; + } diff --git a/xsa378-4.14-7.patch b/xsa378-4.14-7.patch new file mode 100644 index 0000000..6548b7b --- /dev/null +++ b/xsa378-4.14-7.patch @@ -0,0 +1,88 @@ +From: Jan Beulich +Subject: x86/p2m: introduce p2m_is_special() + +Seeing the similarity of grant, foreign, and (subsequently) direct-MMIO +handling, introduce a new P2M type group named "special" (as in "needing +special accessors to create/destroy"). + +Also use -EPERM instead of other error codes on the two domain_crash() +paths touched. + +This is part of XSA-378. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/arch/x86/mm/p2m.c ++++ b/xen/arch/x86/mm/p2m.c +@@ -807,7 +807,7 @@ p2m_remove_page(struct p2m_domain *p2m, + for ( i = 0; i < (1UL << page_order); i++ ) + { + p2m->get_entry(p2m, gfn_add(gfn, i), &t, &a, 0, NULL, NULL); +- if ( !p2m_is_grant(t) && !p2m_is_shared(t) && !p2m_is_foreign(t) ) ++ if ( !p2m_is_special(t) && !p2m_is_shared(t) ) + set_gpfn_from_mfn(mfn_x(mfn) + i, INVALID_M2P_ENTRY); + } + } +@@ -935,13 +935,13 @@ guest_physmap_add_entry(struct domain *d + &ot, &a, 0, NULL, NULL); + ASSERT(!p2m_is_shared(ot)); + } +- if ( p2m_is_grant(ot) || p2m_is_foreign(ot) ) ++ if ( p2m_is_special(ot) ) + { +- /* Really shouldn't be unmapping grant/foreign maps this way */ ++ /* Don't permit unmapping grant/foreign this way. */ + domain_crash(d); + p2m_unlock(p2m); + +- return -EINVAL; ++ return -EPERM; + } + else if ( p2m_is_ram(ot) && !p2m_is_paged(ot) ) + { +@@ -1035,8 +1035,7 @@ int p2m_change_type_one(struct domain *d + struct p2m_domain *p2m = p2m_get_hostp2m(d); + int rc; + +- BUG_ON(p2m_is_grant(ot) || p2m_is_grant(nt)); +- BUG_ON(p2m_is_foreign(ot) || p2m_is_foreign(nt)); ++ BUG_ON(p2m_is_special(ot) || p2m_is_special(nt)); + + gfn_lock(p2m, gfn, 0); + +@@ -1283,11 +1282,11 @@ static int set_typed_p2m_entry(struct do + gfn_unlock(p2m, gfn, order); + return cur_order + 1; + } +- if ( p2m_is_grant(ot) || p2m_is_foreign(ot) ) ++ if ( p2m_is_special(ot) ) + { + gfn_unlock(p2m, gfn, order); + domain_crash(d); +- return -ENOENT; ++ return -EPERM; + } + else if ( p2m_is_ram(ot) ) + { +--- a/xen/include/asm-x86/p2m.h ++++ b/xen/include/asm-x86/p2m.h +@@ -141,6 +141,10 @@ typedef unsigned int p2m_query_t; + | p2m_to_mask(p2m_ram_logdirty) ) + #define P2M_SHARED_TYPES (p2m_to_mask(p2m_ram_shared)) + ++/* Types established/cleaned up via special accessors. */ ++#define P2M_SPECIAL_TYPES (P2M_GRANT_TYPES | \ ++ p2m_to_mask(p2m_map_foreign)) ++ + /* Valid types not necessarily associated with a (valid) MFN. */ + #define P2M_INVALID_MFN_TYPES (P2M_POD_TYPES \ + | p2m_to_mask(p2m_mmio_direct) \ +@@ -169,6 +173,7 @@ typedef unsigned int p2m_query_t; + #define p2m_is_paged(_t) (p2m_to_mask(_t) & P2M_PAGED_TYPES) + #define p2m_is_sharable(_t) (p2m_to_mask(_t) & P2M_SHARABLE_TYPES) + #define p2m_is_shared(_t) (p2m_to_mask(_t) & P2M_SHARED_TYPES) ++#define p2m_is_special(_t) (p2m_to_mask(_t) & P2M_SPECIAL_TYPES) + #define p2m_is_broken(_t) (p2m_to_mask(_t) & P2M_BROKEN_TYPES) + #define p2m_is_foreign(_t) (p2m_to_mask(_t) & p2m_to_mask(p2m_map_foreign)) + diff --git a/xsa378-4.14-8.patch b/xsa378-4.14-8.patch new file mode 100644 index 0000000..cb12394 --- /dev/null +++ b/xsa378-4.14-8.patch @@ -0,0 +1,157 @@ +From: Jan Beulich +Subject: x86/p2m: guard (in particular) identity mapping entries + +Such entries, created by set_identity_p2m_entry(), should only be +destroyed by clear_identity_p2m_entry(). However, similarly, entries +created by set_mmio_p2m_entry() should only be torn down by +clear_mmio_p2m_entry(), so the logic gets based upon p2m_mmio_direct as +the entry type (separation between "ordinary" and 1:1 mappings would +require a further indicator to tell apart the two). + +As to the guest_remove_page() change, commit 48dfb297a20a ("x86/PVH: +allow guest_remove_page to remove p2m_mmio_direct pages"), which +introduced the call to clear_mmio_p2m_entry(), claimed this was done for +hwdom only without this actually having been the case. However, this +code shouldn't be there in the first place, as MMIO entries shouldn't be +dropped this way. Avoid triggering the warning again that 48dfb297a20a +silenced by an adjustment to xenmem_add_to_physmap_one() instead. + +Note that guest_physmap_mark_populate_on_demand() gets tightened beyond +the immediate purpose of this change. + +Note also that I didn't inspect code which isn't security supported, +e.g. sharing, paging, or altp2m. + +This is CVE-2021-28694 / part of XSA-378. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -4652,7 +4652,9 @@ int xenmem_add_to_physmap_one( + + /* Remove previously mapped page if it was present. */ + prev_mfn = get_gfn(d, gfn_x(gpfn), &p2mt); +- if ( mfn_valid(prev_mfn) ) ++ if ( p2mt == p2m_mmio_direct ) ++ rc = -EPERM; ++ else if ( mfn_valid(prev_mfn) ) + { + if ( is_special_page(mfn_to_page(prev_mfn)) ) + /* Special pages are simply unhooked from this phys slot. */ +--- a/xen/arch/x86/mm/p2m.c ++++ b/xen/arch/x86/mm/p2m.c +@@ -795,7 +795,8 @@ p2m_remove_page(struct p2m_domain *p2m, + &cur_order, NULL); + + if ( p2m_is_valid(t) && +- (!mfn_valid(mfn) || !mfn_eq(mfn_add(mfn, i), mfn_return)) ) ++ (!mfn_valid(mfn) || t == p2m_mmio_direct || ++ !mfn_eq(mfn_add(mfn, i), mfn_return)) ) + return -EILSEQ; + + i += (1UL << cur_order) - +@@ -893,7 +894,7 @@ guest_physmap_add_entry(struct domain *d + if ( p2m_is_foreign(t) ) + return -EINVAL; + +- if ( !mfn_valid(mfn) ) ++ if ( !mfn_valid(mfn) || t == p2m_mmio_direct ) + { + ASSERT_UNREACHABLE(); + return -EINVAL; +@@ -937,7 +938,7 @@ guest_physmap_add_entry(struct domain *d + } + if ( p2m_is_special(ot) ) + { +- /* Don't permit unmapping grant/foreign this way. */ ++ /* Don't permit unmapping grant/foreign/direct-MMIO this way. */ + domain_crash(d); + p2m_unlock(p2m); + +@@ -1387,8 +1388,8 @@ int set_identity_p2m_entry(struct domain + * order+1 for caller to retry with order (guaranteed smaller than + * the order value passed in) + */ +-int clear_mmio_p2m_entry(struct domain *d, unsigned long gfn_l, mfn_t mfn, +- unsigned int order) ++static int clear_mmio_p2m_entry(struct domain *d, unsigned long gfn_l, ++ mfn_t mfn, unsigned int order) + { + int rc = -EINVAL; + gfn_t gfn = _gfn(gfn_l); +--- a/xen/arch/x86/mm/p2m-pod.c ++++ b/xen/arch/x86/mm/p2m-pod.c +@@ -1292,17 +1292,17 @@ guest_physmap_mark_populate_on_demand(st + + p2m->get_entry(p2m, gfn_add(gfn, i), &ot, &a, 0, &cur_order, NULL); + n = 1UL << min(order, cur_order); +- if ( p2m_is_ram(ot) ) ++ if ( ot == p2m_populate_on_demand ) ++ { ++ /* Count how many PoD entries we'll be replacing if successful */ ++ pod_count += n; ++ } ++ else if ( ot != p2m_invalid && ot != p2m_mmio_dm ) + { + P2M_DEBUG("gfn_to_mfn returned type %d!\n", ot); + rc = -EBUSY; + goto out; + } +- else if ( ot == p2m_populate_on_demand ) +- { +- /* Count how man PoD entries we'll be replacing if successful */ +- pod_count += n; +- } + } + + /* Now, actually do the two-way mapping */ +--- a/xen/common/memory.c ++++ b/xen/common/memory.c +@@ -329,7 +329,7 @@ int guest_remove_page(struct domain *d, + } + if ( p2mt == p2m_mmio_direct ) + { +- rc = clear_mmio_p2m_entry(d, gmfn, mfn, PAGE_ORDER_4K); ++ rc = -EPERM; + goto out_put_gfn; + } + #else +@@ -1721,6 +1721,15 @@ int check_get_page_from_gfn(struct domai + return -EAGAIN; + } + #endif ++#ifdef CONFIG_X86 ++ if ( p2mt == p2m_mmio_direct ) ++ { ++ if ( page ) ++ put_page(page); ++ ++ return -EPERM; ++ } ++#endif + + if ( !page ) + return -EINVAL; +--- a/xen/include/asm-x86/p2m.h ++++ b/xen/include/asm-x86/p2m.h +@@ -143,7 +143,8 @@ typedef unsigned int p2m_query_t; + + /* Types established/cleaned up via special accessors. */ + #define P2M_SPECIAL_TYPES (P2M_GRANT_TYPES | \ +- p2m_to_mask(p2m_map_foreign)) ++ p2m_to_mask(p2m_map_foreign) | \ ++ p2m_to_mask(p2m_mmio_direct)) + + /* Valid types not necessarily associated with a (valid) MFN. */ + #define P2M_INVALID_MFN_TYPES (P2M_POD_TYPES \ +@@ -645,8 +646,6 @@ int set_foreign_p2m_entry(struct domain + /* Set mmio addresses in the p2m table (for pass-through) */ + int set_mmio_p2m_entry(struct domain *d, gfn_t gfn, mfn_t mfn, + unsigned int order); +-int clear_mmio_p2m_entry(struct domain *d, unsigned long gfn, mfn_t mfn, +- unsigned int order); + + /* Set identity addresses in the p2m table (for pass-through) */ + int set_identity_p2m_entry(struct domain *d, unsigned long gfn, diff --git a/xsa379-4.14.patch b/xsa379-4.14.patch new file mode 100644 index 0000000..25894b3 --- /dev/null +++ b/xsa379-4.14.patch @@ -0,0 +1,77 @@ +From: Jan Beulich +Subject: x86/mm: widen locked region in xenmem_add_to_physmap_one() + +For pages which can be made part of the P2M by the guest, but which can +also later be de-allocated (grant table v2 status pages being the +present example), it is imperative that they be mapped at no more than a +single GFN. We therefore need to make sure that of two parallel +XENMAPSPACE_grant_table requests for the same status page one completes +before the second checks at which other GFN the underlying MFN is +presently mapped. + +Push down the respective put_gfn(). This leverages that gfn_lock() +really aliases p2m_lock(), but the function makes this assumption +already anyway: In the XENMAPSPACE_gmfn case lock nesting constraints +for both involved GFNs would otherwise need to be enforced to avoid ABBA +deadlocks. + +This is CVE-2021-28697 / XSA-379. + +Signed-off-by: Jan Beulich +Reviewed-by: Julien Grall + +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -4650,8 +4650,20 @@ int xenmem_add_to_physmap_one( + goto put_both; + } + +- /* Remove previously mapped page if it was present. */ ++ /* ++ * Note that we're (ab)using GFN locking (to really be locking of the ++ * entire P2M) here in (at least) two ways: Finer grained locking would ++ * expose lock order violations in the XENMAPSPACE_gmfn case (due to the ++ * earlier get_gfn_unshare() above). Plus at the very least for the grant ++ * table v2 status page case we need to guarantee that the same page can ++ * only appear at a single GFN. While this is a property we want in ++ * general, for pages which can subsequently be freed this imperative: ++ * Upon freeing we wouldn't be able to find other mappings in the P2M ++ * (unless we did a brute force search). ++ */ + prev_mfn = get_gfn(d, gfn_x(gpfn), &p2mt); ++ ++ /* Remove previously mapped page if it was present. */ + if ( p2mt == p2m_mmio_direct ) + rc = -EPERM; + else if ( mfn_valid(prev_mfn) ) +@@ -4663,27 +4675,21 @@ int xenmem_add_to_physmap_one( + /* Normal domain memory is freed, to avoid leaking memory. */ + rc = guest_remove_page(d, gfn_x(gpfn)); + } +- /* In the XENMAPSPACE_gmfn case we still hold a ref on the old page. */ +- put_gfn(d, gfn_x(gpfn)); +- +- if ( rc ) +- goto put_both; + + /* Unmap from old location, if any. */ + old_gpfn = get_gpfn_from_mfn(mfn_x(mfn)); + ASSERT(!SHARED_M2P(old_gpfn)); + if ( space == XENMAPSPACE_gmfn && old_gpfn != gfn ) +- { + rc = -EXDEV; +- goto put_both; +- } +- if ( old_gpfn != INVALID_M2P_ENTRY ) ++ else if ( !rc && old_gpfn != INVALID_M2P_ENTRY ) + rc = guest_physmap_remove_page(d, _gfn(old_gpfn), mfn, PAGE_ORDER_4K); + + /* Map at new location. */ + if ( !rc ) + rc = guest_physmap_add_page(d, gpfn, mfn, PAGE_ORDER_4K); + ++ put_gfn(d, gfn_x(gpfn)); ++ + put_both: + /* + * In the XENMAPSPACE_gmfn case, we took a ref of the gfn at the top. diff --git a/xsa380-4.14-1.patch b/xsa380-4.14-1.patch new file mode 100644 index 0000000..7221248 --- /dev/null +++ b/xsa380-4.14-1.patch @@ -0,0 +1,148 @@ +From: Jan Beulich +Subject: gnttab: add preemption check to gnttab_release_mappings() + +A guest may die with many grant mappings still in place, or simply with +a large maptrack table. Iterating through this may take more time than +is reasonable without intermediate preemption (to run softirqs and +perhaps the scheduler). + +Move the invocation of the function to the section where other +restartable functions get invoked, and have the function itself check +for preemption every once in a while. Have it iterate the table +backwards, such that decreasing the maptrack limit is all it takes to +convey restart information. + +In domain_teardown() introduce PROG_none such that inserting at the +front will be easier going forward. + +This is part of CVE-2021-28698 / XSA-380. + +Reported-by: Andrew Cooper +Signed-off-by: Jan Beulich +Reviewed-by: Julien Grall + +--- a/xen/common/domain.c ++++ b/xen/common/domain.c +@@ -721,11 +721,13 @@ int domain_kill(struct domain *d) + return domain_kill(d); + d->is_dying = DOMDYING_dying; + argo_destroy(d); +- gnttab_release_mappings(d); + vnuma_destroy(d->vnuma); + domain_set_outstanding_pages(d, 0); + /* fallthrough */ + case DOMDYING_dying: ++ rc = gnttab_release_mappings(d); ++ if ( rc ) ++ break; + rc = evtchn_destroy(d); + if ( rc ) + break; +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -64,7 +64,13 @@ struct grant_table { + unsigned int nr_grant_frames; + /* Number of grant status frames shared with guest (for version 2) */ + unsigned int nr_status_frames; +- /* Number of available maptrack entries. */ ++ /* ++ * Number of available maptrack entries. For cleanup purposes it is ++ * important to realize that this field and @maptrack further down will ++ * only ever be accessed by the local domain. Thus it is okay to clean ++ * up early, and to shrink the limit for the purpose of tracking cleanup ++ * progress. ++ */ + unsigned int maptrack_limit; + /* Shared grant table (see include/public/grant_table.h). */ + union { +@@ -3708,9 +3714,7 @@ do_grant_table_op( + #include "compat/grant_table.c" + #endif + +-void +-gnttab_release_mappings( +- struct domain *d) ++int gnttab_release_mappings(struct domain *d) + { + struct grant_table *gt = d->grant_table, *rgt; + struct grant_mapping *map; +@@ -3724,10 +3728,34 @@ gnttab_release_mappings( + + BUG_ON(!d->is_dying); + +- for ( handle = 0; handle < gt->maptrack_limit; handle++ ) ++ if ( !gt || !gt->maptrack ) ++ return 0; ++ ++ for ( handle = gt->maptrack_limit; handle; ) + { + unsigned int clear_flags = 0; + ++ /* ++ * Deal with full pages such that their freeing (in the body of the ++ * if()) remains simple. ++ */ ++ if ( handle < gt->maptrack_limit && !(handle % MAPTRACK_PER_PAGE) ) ++ { ++ /* ++ * Changing maptrack_limit alters nr_maptrack_frames()'es return ++ * value. Free the then excess trailing page right here, rather ++ * than leaving it to grant_table_destroy() (and in turn requiring ++ * to leave gt->maptrack_limit unaltered). ++ */ ++ gt->maptrack_limit = handle; ++ FREE_XENHEAP_PAGE(gt->maptrack[nr_maptrack_frames(gt)]); ++ ++ if ( hypercall_preempt_check() ) ++ return -ERESTART; ++ } ++ ++ --handle; ++ + map = &maptrack_entry(gt, handle); + if ( !(map->flags & (GNTMAP_device_map|GNTMAP_host_map)) ) + continue; +@@ -3818,6 +3846,11 @@ gnttab_release_mappings( + + map->flags = 0; + } ++ ++ gt->maptrack_limit = 0; ++ FREE_XENHEAP_PAGE(gt->maptrack[0]); ++ ++ return 0; + } + + void grant_table_warn_active_grants(struct domain *d) +@@ -3881,8 +3914,7 @@ grant_table_destroy( + free_xenheap_page(t->shared_raw[i]); + xfree(t->shared_raw); + +- for ( i = 0; i < nr_maptrack_frames(t); i++ ) +- free_xenheap_page(t->maptrack[i]); ++ ASSERT(!t->maptrack_limit); + vfree(t->maptrack); + + for ( i = 0; i < nr_active_grant_frames(t); i++ ) +--- a/xen/include/xen/grant_table.h ++++ b/xen/include/xen/grant_table.h +@@ -47,9 +47,7 @@ void grant_table_init_vcpu(struct vcpu * + void grant_table_warn_active_grants(struct domain *d); + + /* Domain death release of granted mappings of other domains' memory. */ +-void +-gnttab_release_mappings( +- struct domain *d); ++int gnttab_release_mappings(struct domain *d); + + int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref, + gfn_t *gfn, uint16_t *status); +@@ -78,7 +76,7 @@ static inline void grant_table_init_vcpu + + static inline void grant_table_warn_active_grants(struct domain *d) {} + +-static inline void gnttab_release_mappings(struct domain *d) {} ++static inline int gnttab_release_mappings(struct domain *d) { return 0; } + + static inline int mem_sharing_gref_to_gfn(struct grant_table *gt, + grant_ref_t ref, diff --git a/xsa380-4.14-2.patch b/xsa380-4.14-2.patch new file mode 100644 index 0000000..d8b38b9 --- /dev/null +++ b/xsa380-4.14-2.patch @@ -0,0 +1,383 @@ +From: Jan Beulich +Subject: gnttab: replace mapkind() + +mapkind() doesn't scale very well with larger maptrack entry counts, +using a brute force linear search through all entries, with the only +option of an early loop exit if a matching writable entry was found. +Introduce a radix tree alongside the main maptrack table, thus +allowing much faster MFN-based lookup. To avoid the need to actually +allocate space for the individual nodes, encode the two counters in the +node pointers themselves, thus limiting the number of permitted +simultaneous r/o and r/w mappings of the same MFN to 2³¹-1 (64-bit) / +2¹⁵-1 (32-bit) each. + +To avoid enforcing an unnecessarily low bound on the number of +simultaneous mappings of a single MFN, introduce +radix_tree_{ulong_to_ptr,ptr_to_ulong} paralleling +radix_tree_{int_to_ptr,ptr_to_int}. + +As a consequence locking changes are also applicable: With there no +longer being any inspection of the remote domain's active entries, +there's also no need anymore to hold the remote domain's grant table +lock. And since we're no longer iterating over the local domain's map +track table, the lock in map_grant_ref() can also be dropped before the +new maptrack entry actually gets populated. + +As a nice side effect this also reduces the number of IOMMU operations +in unmap_common(): Previously we would have "established" a readable +mapping whenever we didn't find a writable entry anymore (yet, of +course, at least one readable one). But we only need to do this if we +actually dropped the last writable entry, not if there were none already +before. + +This is part of CVE-2021-28698 / XSA-380. + +Signed-off-by: Jan Beulich +Reviewed-by: Julien Grall + +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -37,6 +37,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -82,8 +83,13 @@ struct grant_table { + grant_status_t **status; + /* Active grant table. */ + struct active_grant_entry **active; +- /* Mapping tracking table per vcpu. */ ++ /* Handle-indexed tracking table of mappings. */ + struct grant_mapping **maptrack; ++ /* ++ * MFN-indexed tracking tree of mappings, if needed. Note that this is ++ * protected by @lock, not @maptrack_lock. ++ */ ++ struct radix_tree_root maptrack_tree; + + /* Domain to which this struct grant_table belongs. */ + const struct domain *domain; +@@ -501,34 +507,6 @@ static int get_paged_frame(unsigned long + return GNTST_okay; + } + +-static inline void +-double_gt_lock(struct grant_table *lgt, struct grant_table *rgt) +-{ +- /* +- * See mapkind() for why the write lock is also required for the +- * remote domain. +- */ +- if ( lgt < rgt ) +- { +- grant_write_lock(lgt); +- grant_write_lock(rgt); +- } +- else +- { +- if ( lgt != rgt ) +- grant_write_lock(rgt); +- grant_write_lock(lgt); +- } +-} +- +-static inline void +-double_gt_unlock(struct grant_table *lgt, struct grant_table *rgt) +-{ +- grant_write_unlock(lgt); +- if ( lgt != rgt ) +- grant_write_unlock(rgt); +-} +- + #define INVALID_MAPTRACK_HANDLE UINT_MAX + + static inline grant_handle_t +@@ -948,41 +926,17 @@ static struct active_grant_entry *grant_ + return ERR_PTR(-EINVAL); + } + +-#define MAPKIND_READ 1 +-#define MAPKIND_WRITE 2 +-static unsigned int mapkind( +- struct grant_table *lgt, const struct domain *rd, mfn_t mfn) +-{ +- struct grant_mapping *map; +- grant_handle_t handle, limit = lgt->maptrack_limit; +- unsigned int kind = 0; +- +- /* +- * Must have the local domain's grant table write lock when +- * iterating over its maptrack entries. +- */ +- ASSERT(percpu_rw_is_write_locked(&lgt->lock)); +- /* +- * Must have the remote domain's grant table write lock while +- * counting its active entries. +- */ +- ASSERT(percpu_rw_is_write_locked(&rd->grant_table->lock)); +- +- smp_rmb(); +- +- for ( handle = 0; !(kind & MAPKIND_WRITE) && handle < limit; handle++ ) +- { +- map = &maptrack_entry(lgt, handle); +- if ( !(map->flags & (GNTMAP_device_map|GNTMAP_host_map)) || +- map->domid != rd->domain_id ) +- continue; +- if ( mfn_eq(_active_entry(rd->grant_table, map->ref).mfn, mfn) ) +- kind |= map->flags & GNTMAP_readonly ? +- MAPKIND_READ : MAPKIND_WRITE; +- } +- +- return kind; +-} ++union maptrack_node { ++ struct { ++ /* Radix tree slot pointers use two of the bits. */ ++#ifdef __BIG_ENDIAN_BITFIELD ++ unsigned long : 2; ++#endif ++ unsigned long rd : BITS_PER_LONG / 2 - 1; ++ unsigned long wr : BITS_PER_LONG / 2 - 1; ++ } cnt; ++ unsigned long raw; ++}; + + static void + map_grant_ref( +@@ -1001,7 +955,6 @@ map_grant_ref( + struct grant_mapping *mt; + grant_entry_header_t *shah; + uint16_t *status; +- bool_t need_iommu; + + ld = current->domain; + +@@ -1220,31 +1173,75 @@ map_grant_ref( + * as mem-sharing and IOMMU use are incompatible). The dom_io case would + * need checking separately if we compared against owner here. + */ +- need_iommu = ld != rd && gnttab_need_iommu_mapping(ld); +- if ( need_iommu ) ++ if ( ld != rd && gnttab_need_iommu_mapping(ld) ) + { ++ union maptrack_node node = { ++ .cnt.rd = !!(op->flags & GNTMAP_readonly), ++ .cnt.wr = !(op->flags & GNTMAP_readonly), ++ }; ++ int err; ++ void **slot = NULL; + unsigned int kind; + +- double_gt_lock(lgt, rgt); ++ grant_write_lock(lgt); ++ ++ err = radix_tree_insert(&lgt->maptrack_tree, mfn_x(mfn), ++ radix_tree_ulong_to_ptr(node.raw)); ++ if ( err == -EEXIST ) ++ { ++ slot = radix_tree_lookup_slot(&lgt->maptrack_tree, mfn_x(mfn)); ++ if ( likely(slot) ) ++ { ++ node.raw = radix_tree_ptr_to_ulong(*slot); ++ err = -EBUSY; ++ ++ /* Update node only when refcount doesn't overflow. */ ++ if ( op->flags & GNTMAP_readonly ? ++node.cnt.rd ++ : ++node.cnt.wr ) ++ { ++ radix_tree_replace_slot(slot, ++ radix_tree_ulong_to_ptr(node.raw)); ++ err = 0; ++ } ++ } ++ else ++ ASSERT_UNREACHABLE(); ++ } + + /* + * We're not translated, so we know that dfns and mfns are + * the same things, so the IOMMU entry is always 1-to-1. + */ +- kind = mapkind(lgt, rd, mfn); +- if ( !(op->flags & GNTMAP_readonly) && +- !(kind & MAPKIND_WRITE) ) ++ if ( !(op->flags & GNTMAP_readonly) && node.cnt.wr == 1 ) + kind = IOMMUF_readable | IOMMUF_writable; +- else if ( !kind ) ++ else if ( (op->flags & GNTMAP_readonly) && ++ node.cnt.rd == 1 && !node.cnt.wr ) + kind = IOMMUF_readable; + else + kind = 0; +- if ( kind && iommu_legacy_map(ld, _dfn(mfn_x(mfn)), mfn, 0, kind) ) ++ if ( err || ++ (kind && iommu_legacy_map(ld, _dfn(mfn_x(mfn)), mfn, 0, kind)) ) + { +- double_gt_unlock(lgt, rgt); ++ if ( !err ) ++ { ++ if ( slot ) ++ { ++ op->flags & GNTMAP_readonly ? node.cnt.rd-- ++ : node.cnt.wr--; ++ radix_tree_replace_slot(slot, ++ radix_tree_ulong_to_ptr(node.raw)); ++ } ++ else ++ radix_tree_delete(&lgt->maptrack_tree, mfn_x(mfn)); ++ } ++ + rc = GNTST_general_error; +- goto undo_out; + } ++ ++ grant_write_unlock(lgt); ++ ++ if ( rc != GNTST_okay ) ++ goto undo_out; + } + + TRACE_1D(TRC_MEM_PAGE_GRANT_MAP, op->dom); +@@ -1252,10 +1249,6 @@ map_grant_ref( + /* + * All maptrack entry users check mt->flags first before using the + * other fields so just ensure the flags field is stored last. +- * +- * However, if gnttab_need_iommu_mapping() then this would race +- * with a concurrent mapkind() call (on an unmap, for example) +- * and a lock is required. + */ + mt = &maptrack_entry(lgt, handle); + mt->domid = op->dom; +@@ -1263,9 +1256,6 @@ map_grant_ref( + smp_wmb(); + write_atomic(&mt->flags, op->flags); + +- if ( need_iommu ) +- double_gt_unlock(lgt, rgt); +- + op->dev_bus_addr = mfn_to_maddr(mfn); + op->handle = handle; + op->status = GNTST_okay; +@@ -1487,19 +1477,34 @@ unmap_common( + /* See the respective comment in map_grant_ref(). */ + if ( rc == GNTST_okay && ld != rd && gnttab_need_iommu_mapping(ld) ) + { +- unsigned int kind; ++ void **slot; ++ union maptrack_node node; + int err = 0; + +- double_gt_lock(lgt, rgt); ++ grant_write_lock(lgt); ++ slot = radix_tree_lookup_slot(&lgt->maptrack_tree, mfn_x(op->mfn)); ++ node.raw = likely(slot) ? radix_tree_ptr_to_ulong(*slot) : 0; ++ ++ /* Refcount must not underflow. */ ++ if ( !(flags & GNTMAP_readonly ? node.cnt.rd-- ++ : node.cnt.wr--) ) ++ BUG(); + +- kind = mapkind(lgt, rd, op->mfn); +- if ( !kind ) ++ if ( !node.raw ) + err = iommu_legacy_unmap(ld, _dfn(mfn_x(op->mfn)), 0); +- else if ( !(kind & MAPKIND_WRITE) ) ++ else if ( !(flags & GNTMAP_readonly) && !node.cnt.wr ) + err = iommu_legacy_map(ld, _dfn(mfn_x(op->mfn)), op->mfn, 0, + IOMMUF_readable); + +- double_gt_unlock(lgt, rgt); ++ if ( err ) ++ ; ++ else if ( !node.raw ) ++ radix_tree_delete(&lgt->maptrack_tree, mfn_x(op->mfn)); ++ else ++ radix_tree_replace_slot(slot, ++ radix_tree_ulong_to_ptr(node.raw)); ++ ++ grant_write_unlock(lgt); + + if ( err ) + rc = GNTST_general_error; +@@ -1951,6 +1956,8 @@ int grant_table_init(struct domain *d, i + gt->maptrack = vzalloc(gt->max_maptrack_frames * sizeof(*gt->maptrack)); + if ( gt->maptrack == NULL ) + goto out; ++ ++ radix_tree_init(>->maptrack_tree); + } + + /* Shared grant table. */ +@@ -3734,6 +3741,7 @@ int gnttab_release_mappings(struct domai + for ( handle = gt->maptrack_limit; handle; ) + { + unsigned int clear_flags = 0; ++ mfn_t mfn; + + /* + * Deal with full pages such that their freeing (in the body of the +@@ -3839,17 +3847,31 @@ int gnttab_release_mappings(struct domai + if ( clear_flags ) + gnttab_clear_flags(rd, clear_flags, status); + ++ mfn = act->mfn; ++ + active_entry_release(act); + grant_read_unlock(rgt); + + rcu_unlock_domain(rd); + + map->flags = 0; ++ ++ /* ++ * This is excessive in that a single such call would suffice per ++ * mapped MFN (or none at all, if no entry was ever inserted). But it ++ * should be the common case for an MFN to be mapped just once, and ++ * this way we don't need to further maintain the counters. We also ++ * don't want to leave cleaning up of the tree as a whole to the end ++ * of the function, as this could take quite some time. ++ */ ++ radix_tree_delete(>->maptrack_tree, mfn_x(mfn)); + } + + gt->maptrack_limit = 0; + FREE_XENHEAP_PAGE(gt->maptrack[0]); + ++ radix_tree_destroy(>->maptrack_tree, NULL); ++ + return 0; + } + +--- a/xen/include/xen/radix-tree.h ++++ b/xen/include/xen/radix-tree.h +@@ -190,6 +190,25 @@ static inline int radix_tree_ptr_to_int( + return (int)((long)ptr >> 2); + } + ++/** ++ * radix_tree_{ulong_to_ptr,ptr_to_ulong}: ++ * ++ * Same for unsigned long values. Beware though that only BITS_PER_LONG-2 ++ * bits are actually usable for the value. ++ */ ++static inline void *radix_tree_ulong_to_ptr(unsigned long val) ++{ ++ unsigned long ptr = (val << 2) | 0x2; ++ ASSERT((ptr >> 2) == val); ++ return (void *)ptr; ++} ++ ++static inline unsigned long radix_tree_ptr_to_ulong(void *ptr) ++{ ++ ASSERT(((unsigned long)ptr & 0x3) == 0x2); ++ return (unsigned long)ptr >> 2; ++} ++ + int radix_tree_insert(struct radix_tree_root *, unsigned long, void *); + void *radix_tree_lookup(struct radix_tree_root *, unsigned long); + void **radix_tree_lookup_slot(struct radix_tree_root *, unsigned long); diff --git a/xsa382.patch b/xsa382.patch new file mode 100644 index 0000000..936c2de --- /dev/null +++ b/xsa382.patch @@ -0,0 +1,34 @@ +From: Jan Beulich +Subject: gnttab: fix array capacity check in gnttab_get_status_frames() + +The number of grant frames is of no interest here; converting the passed +in op.nr_frames this way means we allow for 8 times as many GFNs to be +written as actually fit in the array. We would corrupt xlat areas of +higher vCPU-s (after having faulted many times while trying to write to +the guard pages between any two areas) for 32-bit PV guests. For HVM +guests we'd simply crash as soon as we hit the first guard page, as +accesses to the xlat area are simply memcpy() there. + +This is CVE-2021-28699 / XSA-382. + +Fixes: 18b1be5e324b ("gnttab: make resource limits per domain") +Signed-off-by: Jan Beulich + +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -3243,12 +3243,11 @@ gnttab_get_status_frames(XEN_GUEST_HANDL + goto unlock; + } + +- if ( unlikely(limit_max < grant_to_status_frames(op.nr_frames)) ) ++ if ( unlikely(limit_max < op.nr_frames) ) + { + gdprintk(XENLOG_WARNING, +- "grant_to_status_frames(%u) for d%d is too large (%u,%u)\n", +- op.nr_frames, d->domain_id, +- grant_to_status_frames(op.nr_frames), limit_max); ++ "nr_status_frames for %pd is too large (%u,%u)\n", ++ d, op.nr_frames, limit_max); + op.status = GNTST_general_error; + goto unlock; + } diff --git a/xsa383.patch b/xsa383.patch new file mode 100644 index 0000000..9ab5eb3 --- /dev/null +++ b/xsa383.patch @@ -0,0 +1,55 @@ +From: Julien Grall +Date: Sat, 3 Jul 2021 14:03:36 +0100 +Subject: [PATCH] xen/arm: Restrict the amount of memory that dom0less domU and + dom0 can allocate + +Currently, both dom0less domUs and dom0 can allocate an "unlimited" +amount of memory because d->max_pages is set to ~0U. + +In particular, the former are meant to be unprivileged. Therefore the +memory they could allocate should be bounded. As the domain are not yet +officially aware of Xen (we don't expose advertise it in the DT, yet +the hypercalls are accessible), they should not need to allocate more +than the initial amount. So cap set d->max_pages directly the amount of +memory we are meant to allocate. + +Take the opportunity to also restrict the memory for dom0 as the +domain is direct mapped (e.g. MFN == GFN) and therefore cannot +allocate outside of the pre-allocated region. + +This is CVE-2021-28700 / XSA-383. + +Reported-by: Julien Grall +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +Tested-by: Stefano Stabellini +--- + xen/arch/arm/domain_build.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c +index 6c86d527810f..206038d1c022 100644 +--- a/xen/arch/arm/domain_build.c ++++ b/xen/arch/arm/domain_build.c +@@ -2440,7 +2440,8 @@ static int __init construct_domU(struct domain *d, + + if ( vcpu_create(d, 0) == NULL ) + return -ENOMEM; +- d->max_pages = ~0U; ++ ++ d->max_pages = ((paddr_t)mem * SZ_1K) >> PAGE_SHIFT; + + kinfo.d = d; + +@@ -2546,7 +2547,7 @@ static int __init construct_dom0(struct domain *d) + + iommu_hwdom_init(d); + +- d->max_pages = ~0U; ++ d->max_pages = dom0_mem >> PAGE_SHIFT; + + kinfo.unassigned_mem = dom0_mem; + kinfo.d = d; +-- +2.17.1 + From 3b4e0888e191d8f3c8df4757da36f012338bdb44 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 8 Sep 2021 20:58:27 +0100 Subject: [PATCH 017/137] Another race in XENMAPSPACE_grant_table handling [XSA-384, CVE-2021-28701] bugfix for XSA-380 stop editing grub files in /boot/efi/EFI/fedora --- xen.spec | 20 ++- xsa380-3.patch | 74 +++++++++ xsa380-4.14-1.patch | 148 ----------------- xsa380-4.14-2.patch | 383 -------------------------------------------- xsa384.patch | 81 ++++++++++ 5 files changed, 167 insertions(+), 539 deletions(-) create mode 100644 xsa380-3.patch delete mode 100644 xsa380-4.14-1.patch delete mode 100644 xsa380-4.14-2.patch create mode 100644 xsa384.patch diff --git a/xen.spec b/xen.spec index 93276be..995296e 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.15.0 -Release: 6%{?dist} +Release: 7%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -137,6 +137,8 @@ Patch65: xsa380-1.patch Patch66: xsa380-2.patch Patch67: xsa382.patch Patch68: xsa383.patch +Patch69: xsa380-3.patch +Patch70: xsa384.patch %if %build_qemutrad @@ -370,6 +372,8 @@ manage Xen virtual machines. %patch66 -p1 %patch67 -p1 %patch68 -p1 +%patch69 -p1 +%patch70 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -656,7 +660,7 @@ do_it() { fi } if [ $1 == 1 -a -f /sbin/grub2-mkconfig ]; then - for f in /boot/grub2/grub.cfg /boot/efi/EFI/fedora/grub.cfg; do + for f in /boot/grub2/grub.cfg; do if [ -f $f ]; then /sbin/grub2-mkconfig -o $f sed -i -e '/insmod module2/d' $f @@ -672,16 +676,11 @@ if [ -f /sbin/grub2-mkconfig ]; then TARGET=/boot/grub2/x86_64-efi do_it $DIR $TARGET fi - if [ -f /boot/efi/EFI/fedora/grub.cfg ]; then - DIR=/usr/lib/grub/x86_64-efi - TARGET=/boot/efi/EFI/fedora/x86_64-efi - do_it $DIR $TARGET - fi fi %postun hypervisor if [ -f /sbin/grub2-mkconfig ]; then - for f in /boot/grub2/grub.cfg /boot/efi/EFI/fedora/grub.cfg; do + for f in /boot/grub2/grub.cfg; do if [ -f $f ]; then /sbin/grub2-mkconfig -o $f sed -i -e '/insmod module2/d' $f @@ -970,6 +969,11 @@ fi %endif %changelog +* Wed Sep 08 2021 Michael Young - 4.15.0-7 +- Another race in XENMAPSPACE_grant_table handling [XSA-384, CVE-2021-28701] +- bugfix for XSA-380 +- stop editing grub files in /boot/efi/EFI/fedora + * Wed Aug 25 2021 Michael Young - 4.15.0-6 - IOMMU page mapping issues on x86 [XSA-378, CVE-2021-28694, CVE-2021-28695, CVE-2021-28696] (#1997531) (#1997568) diff --git a/xsa380-3.patch b/xsa380-3.patch new file mode 100644 index 0000000..1517249 --- /dev/null +++ b/xsa380-3.patch @@ -0,0 +1,74 @@ +From: Jan Beulich +Subject: gnttab: avoid triggering assertion in radix_tree_ulong_to_ptr() + +Relevant quotes from the C11 standard: + +"Except where explicitly stated otherwise, for the purposes of this + subclause unnamed members of objects of structure and union type do not + participate in initialization. Unnamed members of structure objects + have indeterminate value even after initialization." + +"If there are fewer initializers in a brace-enclosed list than there are + elements or members of an aggregate, [...], the remainder of the + aggregate shall be initialized implicitly the same as objects that have + static storage duration." + +"If an object that has static or thread storage duration is not + initialized explicitly, then: + [...] + — if it is an aggregate, every member is initialized (recursively) + according to these rules, and any padding is initialized to zero + bits; + [...]" + +"A bit-field declaration with no declarator, but only a colon and a + width, indicates an unnamed bit-field." Footnote: "An unnamed bit-field + structure member is useful for padding to conform to externally imposed + layouts." + +"There may be unnamed padding within a structure object, but not at its + beginning." + +Which makes me conclude: +- Whether an unnamed bit-field member is an unnamed member or padding is + unclear, and hence also whether the last quote above would render the + big endian case of the structure declaration invalid. +- Whether the number of members of an aggregate includes unnamed ones is + also not really clear. +- The initializer in map_grant_ref() initializes all fields of the "cnt" + sub-structure of the union, so assuming the second quote above applies + here (indirectly), the compiler isn't required to implicitly + initialize the rest (i.e. in particular any padding) like would happen + for static storage duration objects. + +Gcc 7.4.1 can be observed (apparently in debug builds only) to translate +aforementioned initializer to a read-modify-write operation of a stack +variable, leaving unchanged the top two bits of whatever was previously +in that stack slot. Clearly if either of the two bits were set, +radix_tree_ulong_to_ptr()'s assertion would trigger. + +Therefore, to be on the safe side, add an explicit padding field for the +non-big-endian-bitfields case and give a dummy name to both padding +fields. + +Fixes: 9781b51efde2 ("gnttab: replace mapkind()") +Signed-off-by: Jan Beulich +Acked-by: Andrew Cooper + +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -952,10 +952,13 @@ union maptrack_node { + struct { + /* Radix tree slot pointers use two of the bits. */ + #ifdef __BIG_ENDIAN_BITFIELD +- unsigned long : 2; ++ unsigned long _0 : 2; + #endif + unsigned long rd : BITS_PER_LONG / 2 - 1; + unsigned long wr : BITS_PER_LONG / 2 - 1; ++#ifndef __BIG_ENDIAN_BITFIELD ++ unsigned long _0 : 2; ++#endif + } cnt; + unsigned long raw; + }; diff --git a/xsa380-4.14-1.patch b/xsa380-4.14-1.patch deleted file mode 100644 index 7221248..0000000 --- a/xsa380-4.14-1.patch +++ /dev/null @@ -1,148 +0,0 @@ -From: Jan Beulich -Subject: gnttab: add preemption check to gnttab_release_mappings() - -A guest may die with many grant mappings still in place, or simply with -a large maptrack table. Iterating through this may take more time than -is reasonable without intermediate preemption (to run softirqs and -perhaps the scheduler). - -Move the invocation of the function to the section where other -restartable functions get invoked, and have the function itself check -for preemption every once in a while. Have it iterate the table -backwards, such that decreasing the maptrack limit is all it takes to -convey restart information. - -In domain_teardown() introduce PROG_none such that inserting at the -front will be easier going forward. - -This is part of CVE-2021-28698 / XSA-380. - -Reported-by: Andrew Cooper -Signed-off-by: Jan Beulich -Reviewed-by: Julien Grall - ---- a/xen/common/domain.c -+++ b/xen/common/domain.c -@@ -721,11 +721,13 @@ int domain_kill(struct domain *d) - return domain_kill(d); - d->is_dying = DOMDYING_dying; - argo_destroy(d); -- gnttab_release_mappings(d); - vnuma_destroy(d->vnuma); - domain_set_outstanding_pages(d, 0); - /* fallthrough */ - case DOMDYING_dying: -+ rc = gnttab_release_mappings(d); -+ if ( rc ) -+ break; - rc = evtchn_destroy(d); - if ( rc ) - break; ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -64,7 +64,13 @@ struct grant_table { - unsigned int nr_grant_frames; - /* Number of grant status frames shared with guest (for version 2) */ - unsigned int nr_status_frames; -- /* Number of available maptrack entries. */ -+ /* -+ * Number of available maptrack entries. For cleanup purposes it is -+ * important to realize that this field and @maptrack further down will -+ * only ever be accessed by the local domain. Thus it is okay to clean -+ * up early, and to shrink the limit for the purpose of tracking cleanup -+ * progress. -+ */ - unsigned int maptrack_limit; - /* Shared grant table (see include/public/grant_table.h). */ - union { -@@ -3708,9 +3714,7 @@ do_grant_table_op( - #include "compat/grant_table.c" - #endif - --void --gnttab_release_mappings( -- struct domain *d) -+int gnttab_release_mappings(struct domain *d) - { - struct grant_table *gt = d->grant_table, *rgt; - struct grant_mapping *map; -@@ -3724,10 +3728,34 @@ gnttab_release_mappings( - - BUG_ON(!d->is_dying); - -- for ( handle = 0; handle < gt->maptrack_limit; handle++ ) -+ if ( !gt || !gt->maptrack ) -+ return 0; -+ -+ for ( handle = gt->maptrack_limit; handle; ) - { - unsigned int clear_flags = 0; - -+ /* -+ * Deal with full pages such that their freeing (in the body of the -+ * if()) remains simple. -+ */ -+ if ( handle < gt->maptrack_limit && !(handle % MAPTRACK_PER_PAGE) ) -+ { -+ /* -+ * Changing maptrack_limit alters nr_maptrack_frames()'es return -+ * value. Free the then excess trailing page right here, rather -+ * than leaving it to grant_table_destroy() (and in turn requiring -+ * to leave gt->maptrack_limit unaltered). -+ */ -+ gt->maptrack_limit = handle; -+ FREE_XENHEAP_PAGE(gt->maptrack[nr_maptrack_frames(gt)]); -+ -+ if ( hypercall_preempt_check() ) -+ return -ERESTART; -+ } -+ -+ --handle; -+ - map = &maptrack_entry(gt, handle); - if ( !(map->flags & (GNTMAP_device_map|GNTMAP_host_map)) ) - continue; -@@ -3818,6 +3846,11 @@ gnttab_release_mappings( - - map->flags = 0; - } -+ -+ gt->maptrack_limit = 0; -+ FREE_XENHEAP_PAGE(gt->maptrack[0]); -+ -+ return 0; - } - - void grant_table_warn_active_grants(struct domain *d) -@@ -3881,8 +3914,7 @@ grant_table_destroy( - free_xenheap_page(t->shared_raw[i]); - xfree(t->shared_raw); - -- for ( i = 0; i < nr_maptrack_frames(t); i++ ) -- free_xenheap_page(t->maptrack[i]); -+ ASSERT(!t->maptrack_limit); - vfree(t->maptrack); - - for ( i = 0; i < nr_active_grant_frames(t); i++ ) ---- a/xen/include/xen/grant_table.h -+++ b/xen/include/xen/grant_table.h -@@ -47,9 +47,7 @@ void grant_table_init_vcpu(struct vcpu * - void grant_table_warn_active_grants(struct domain *d); - - /* Domain death release of granted mappings of other domains' memory. */ --void --gnttab_release_mappings( -- struct domain *d); -+int gnttab_release_mappings(struct domain *d); - - int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref, - gfn_t *gfn, uint16_t *status); -@@ -78,7 +76,7 @@ static inline void grant_table_init_vcpu - - static inline void grant_table_warn_active_grants(struct domain *d) {} - --static inline void gnttab_release_mappings(struct domain *d) {} -+static inline int gnttab_release_mappings(struct domain *d) { return 0; } - - static inline int mem_sharing_gref_to_gfn(struct grant_table *gt, - grant_ref_t ref, diff --git a/xsa380-4.14-2.patch b/xsa380-4.14-2.patch deleted file mode 100644 index d8b38b9..0000000 --- a/xsa380-4.14-2.patch +++ /dev/null @@ -1,383 +0,0 @@ -From: Jan Beulich -Subject: gnttab: replace mapkind() - -mapkind() doesn't scale very well with larger maptrack entry counts, -using a brute force linear search through all entries, with the only -option of an early loop exit if a matching writable entry was found. -Introduce a radix tree alongside the main maptrack table, thus -allowing much faster MFN-based lookup. To avoid the need to actually -allocate space for the individual nodes, encode the two counters in the -node pointers themselves, thus limiting the number of permitted -simultaneous r/o and r/w mappings of the same MFN to 2³¹-1 (64-bit) / -2¹⁵-1 (32-bit) each. - -To avoid enforcing an unnecessarily low bound on the number of -simultaneous mappings of a single MFN, introduce -radix_tree_{ulong_to_ptr,ptr_to_ulong} paralleling -radix_tree_{int_to_ptr,ptr_to_int}. - -As a consequence locking changes are also applicable: With there no -longer being any inspection of the remote domain's active entries, -there's also no need anymore to hold the remote domain's grant table -lock. And since we're no longer iterating over the local domain's map -track table, the lock in map_grant_ref() can also be dropped before the -new maptrack entry actually gets populated. - -As a nice side effect this also reduces the number of IOMMU operations -in unmap_common(): Previously we would have "established" a readable -mapping whenever we didn't find a writable entry anymore (yet, of -course, at least one readable one). But we only need to do this if we -actually dropped the last writable entry, not if there were none already -before. - -This is part of CVE-2021-28698 / XSA-380. - -Signed-off-by: Jan Beulich -Reviewed-by: Julien Grall - ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -37,6 +37,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -82,8 +83,13 @@ struct grant_table { - grant_status_t **status; - /* Active grant table. */ - struct active_grant_entry **active; -- /* Mapping tracking table per vcpu. */ -+ /* Handle-indexed tracking table of mappings. */ - struct grant_mapping **maptrack; -+ /* -+ * MFN-indexed tracking tree of mappings, if needed. Note that this is -+ * protected by @lock, not @maptrack_lock. -+ */ -+ struct radix_tree_root maptrack_tree; - - /* Domain to which this struct grant_table belongs. */ - const struct domain *domain; -@@ -501,34 +507,6 @@ static int get_paged_frame(unsigned long - return GNTST_okay; - } - --static inline void --double_gt_lock(struct grant_table *lgt, struct grant_table *rgt) --{ -- /* -- * See mapkind() for why the write lock is also required for the -- * remote domain. -- */ -- if ( lgt < rgt ) -- { -- grant_write_lock(lgt); -- grant_write_lock(rgt); -- } -- else -- { -- if ( lgt != rgt ) -- grant_write_lock(rgt); -- grant_write_lock(lgt); -- } --} -- --static inline void --double_gt_unlock(struct grant_table *lgt, struct grant_table *rgt) --{ -- grant_write_unlock(lgt); -- if ( lgt != rgt ) -- grant_write_unlock(rgt); --} -- - #define INVALID_MAPTRACK_HANDLE UINT_MAX - - static inline grant_handle_t -@@ -948,41 +926,17 @@ static struct active_grant_entry *grant_ - return ERR_PTR(-EINVAL); - } - --#define MAPKIND_READ 1 --#define MAPKIND_WRITE 2 --static unsigned int mapkind( -- struct grant_table *lgt, const struct domain *rd, mfn_t mfn) --{ -- struct grant_mapping *map; -- grant_handle_t handle, limit = lgt->maptrack_limit; -- unsigned int kind = 0; -- -- /* -- * Must have the local domain's grant table write lock when -- * iterating over its maptrack entries. -- */ -- ASSERT(percpu_rw_is_write_locked(&lgt->lock)); -- /* -- * Must have the remote domain's grant table write lock while -- * counting its active entries. -- */ -- ASSERT(percpu_rw_is_write_locked(&rd->grant_table->lock)); -- -- smp_rmb(); -- -- for ( handle = 0; !(kind & MAPKIND_WRITE) && handle < limit; handle++ ) -- { -- map = &maptrack_entry(lgt, handle); -- if ( !(map->flags & (GNTMAP_device_map|GNTMAP_host_map)) || -- map->domid != rd->domain_id ) -- continue; -- if ( mfn_eq(_active_entry(rd->grant_table, map->ref).mfn, mfn) ) -- kind |= map->flags & GNTMAP_readonly ? -- MAPKIND_READ : MAPKIND_WRITE; -- } -- -- return kind; --} -+union maptrack_node { -+ struct { -+ /* Radix tree slot pointers use two of the bits. */ -+#ifdef __BIG_ENDIAN_BITFIELD -+ unsigned long : 2; -+#endif -+ unsigned long rd : BITS_PER_LONG / 2 - 1; -+ unsigned long wr : BITS_PER_LONG / 2 - 1; -+ } cnt; -+ unsigned long raw; -+}; - - static void - map_grant_ref( -@@ -1001,7 +955,6 @@ map_grant_ref( - struct grant_mapping *mt; - grant_entry_header_t *shah; - uint16_t *status; -- bool_t need_iommu; - - ld = current->domain; - -@@ -1220,31 +1173,75 @@ map_grant_ref( - * as mem-sharing and IOMMU use are incompatible). The dom_io case would - * need checking separately if we compared against owner here. - */ -- need_iommu = ld != rd && gnttab_need_iommu_mapping(ld); -- if ( need_iommu ) -+ if ( ld != rd && gnttab_need_iommu_mapping(ld) ) - { -+ union maptrack_node node = { -+ .cnt.rd = !!(op->flags & GNTMAP_readonly), -+ .cnt.wr = !(op->flags & GNTMAP_readonly), -+ }; -+ int err; -+ void **slot = NULL; - unsigned int kind; - -- double_gt_lock(lgt, rgt); -+ grant_write_lock(lgt); -+ -+ err = radix_tree_insert(&lgt->maptrack_tree, mfn_x(mfn), -+ radix_tree_ulong_to_ptr(node.raw)); -+ if ( err == -EEXIST ) -+ { -+ slot = radix_tree_lookup_slot(&lgt->maptrack_tree, mfn_x(mfn)); -+ if ( likely(slot) ) -+ { -+ node.raw = radix_tree_ptr_to_ulong(*slot); -+ err = -EBUSY; -+ -+ /* Update node only when refcount doesn't overflow. */ -+ if ( op->flags & GNTMAP_readonly ? ++node.cnt.rd -+ : ++node.cnt.wr ) -+ { -+ radix_tree_replace_slot(slot, -+ radix_tree_ulong_to_ptr(node.raw)); -+ err = 0; -+ } -+ } -+ else -+ ASSERT_UNREACHABLE(); -+ } - - /* - * We're not translated, so we know that dfns and mfns are - * the same things, so the IOMMU entry is always 1-to-1. - */ -- kind = mapkind(lgt, rd, mfn); -- if ( !(op->flags & GNTMAP_readonly) && -- !(kind & MAPKIND_WRITE) ) -+ if ( !(op->flags & GNTMAP_readonly) && node.cnt.wr == 1 ) - kind = IOMMUF_readable | IOMMUF_writable; -- else if ( !kind ) -+ else if ( (op->flags & GNTMAP_readonly) && -+ node.cnt.rd == 1 && !node.cnt.wr ) - kind = IOMMUF_readable; - else - kind = 0; -- if ( kind && iommu_legacy_map(ld, _dfn(mfn_x(mfn)), mfn, 0, kind) ) -+ if ( err || -+ (kind && iommu_legacy_map(ld, _dfn(mfn_x(mfn)), mfn, 0, kind)) ) - { -- double_gt_unlock(lgt, rgt); -+ if ( !err ) -+ { -+ if ( slot ) -+ { -+ op->flags & GNTMAP_readonly ? node.cnt.rd-- -+ : node.cnt.wr--; -+ radix_tree_replace_slot(slot, -+ radix_tree_ulong_to_ptr(node.raw)); -+ } -+ else -+ radix_tree_delete(&lgt->maptrack_tree, mfn_x(mfn)); -+ } -+ - rc = GNTST_general_error; -- goto undo_out; - } -+ -+ grant_write_unlock(lgt); -+ -+ if ( rc != GNTST_okay ) -+ goto undo_out; - } - - TRACE_1D(TRC_MEM_PAGE_GRANT_MAP, op->dom); -@@ -1252,10 +1249,6 @@ map_grant_ref( - /* - * All maptrack entry users check mt->flags first before using the - * other fields so just ensure the flags field is stored last. -- * -- * However, if gnttab_need_iommu_mapping() then this would race -- * with a concurrent mapkind() call (on an unmap, for example) -- * and a lock is required. - */ - mt = &maptrack_entry(lgt, handle); - mt->domid = op->dom; -@@ -1263,9 +1256,6 @@ map_grant_ref( - smp_wmb(); - write_atomic(&mt->flags, op->flags); - -- if ( need_iommu ) -- double_gt_unlock(lgt, rgt); -- - op->dev_bus_addr = mfn_to_maddr(mfn); - op->handle = handle; - op->status = GNTST_okay; -@@ -1487,19 +1477,34 @@ unmap_common( - /* See the respective comment in map_grant_ref(). */ - if ( rc == GNTST_okay && ld != rd && gnttab_need_iommu_mapping(ld) ) - { -- unsigned int kind; -+ void **slot; -+ union maptrack_node node; - int err = 0; - -- double_gt_lock(lgt, rgt); -+ grant_write_lock(lgt); -+ slot = radix_tree_lookup_slot(&lgt->maptrack_tree, mfn_x(op->mfn)); -+ node.raw = likely(slot) ? radix_tree_ptr_to_ulong(*slot) : 0; -+ -+ /* Refcount must not underflow. */ -+ if ( !(flags & GNTMAP_readonly ? node.cnt.rd-- -+ : node.cnt.wr--) ) -+ BUG(); - -- kind = mapkind(lgt, rd, op->mfn); -- if ( !kind ) -+ if ( !node.raw ) - err = iommu_legacy_unmap(ld, _dfn(mfn_x(op->mfn)), 0); -- else if ( !(kind & MAPKIND_WRITE) ) -+ else if ( !(flags & GNTMAP_readonly) && !node.cnt.wr ) - err = iommu_legacy_map(ld, _dfn(mfn_x(op->mfn)), op->mfn, 0, - IOMMUF_readable); - -- double_gt_unlock(lgt, rgt); -+ if ( err ) -+ ; -+ else if ( !node.raw ) -+ radix_tree_delete(&lgt->maptrack_tree, mfn_x(op->mfn)); -+ else -+ radix_tree_replace_slot(slot, -+ radix_tree_ulong_to_ptr(node.raw)); -+ -+ grant_write_unlock(lgt); - - if ( err ) - rc = GNTST_general_error; -@@ -1951,6 +1956,8 @@ int grant_table_init(struct domain *d, i - gt->maptrack = vzalloc(gt->max_maptrack_frames * sizeof(*gt->maptrack)); - if ( gt->maptrack == NULL ) - goto out; -+ -+ radix_tree_init(>->maptrack_tree); - } - - /* Shared grant table. */ -@@ -3734,6 +3741,7 @@ int gnttab_release_mappings(struct domai - for ( handle = gt->maptrack_limit; handle; ) - { - unsigned int clear_flags = 0; -+ mfn_t mfn; - - /* - * Deal with full pages such that their freeing (in the body of the -@@ -3839,17 +3847,31 @@ int gnttab_release_mappings(struct domai - if ( clear_flags ) - gnttab_clear_flags(rd, clear_flags, status); - -+ mfn = act->mfn; -+ - active_entry_release(act); - grant_read_unlock(rgt); - - rcu_unlock_domain(rd); - - map->flags = 0; -+ -+ /* -+ * This is excessive in that a single such call would suffice per -+ * mapped MFN (or none at all, if no entry was ever inserted). But it -+ * should be the common case for an MFN to be mapped just once, and -+ * this way we don't need to further maintain the counters. We also -+ * don't want to leave cleaning up of the tree as a whole to the end -+ * of the function, as this could take quite some time. -+ */ -+ radix_tree_delete(>->maptrack_tree, mfn_x(mfn)); - } - - gt->maptrack_limit = 0; - FREE_XENHEAP_PAGE(gt->maptrack[0]); - -+ radix_tree_destroy(>->maptrack_tree, NULL); -+ - return 0; - } - ---- a/xen/include/xen/radix-tree.h -+++ b/xen/include/xen/radix-tree.h -@@ -190,6 +190,25 @@ static inline int radix_tree_ptr_to_int( - return (int)((long)ptr >> 2); - } - -+/** -+ * radix_tree_{ulong_to_ptr,ptr_to_ulong}: -+ * -+ * Same for unsigned long values. Beware though that only BITS_PER_LONG-2 -+ * bits are actually usable for the value. -+ */ -+static inline void *radix_tree_ulong_to_ptr(unsigned long val) -+{ -+ unsigned long ptr = (val << 2) | 0x2; -+ ASSERT((ptr >> 2) == val); -+ return (void *)ptr; -+} -+ -+static inline unsigned long radix_tree_ptr_to_ulong(void *ptr) -+{ -+ ASSERT(((unsigned long)ptr & 0x3) == 0x2); -+ return (unsigned long)ptr >> 2; -+} -+ - int radix_tree_insert(struct radix_tree_root *, unsigned long, void *); - void *radix_tree_lookup(struct radix_tree_root *, unsigned long); - void **radix_tree_lookup_slot(struct radix_tree_root *, unsigned long); diff --git a/xsa384.patch b/xsa384.patch new file mode 100644 index 0000000..4f155ac --- /dev/null +++ b/xsa384.patch @@ -0,0 +1,81 @@ +From: Jan Beulich +Subject: gnttab: deal with status frame mapping race + +Once gnttab_map_frame() drops the grant table lock, the MFN it reports +back to its caller is free to other manipulation. In particular +gnttab_unpopulate_status_frames() might free it, by a racing request on +another CPU, thus resulting in a reference to a deallocated page getting +added to a domain's P2M. + +Obtain a page reference in gnttab_map_frame() to prevent freeing of the +page until xenmem_add_to_physmap_one() has actually completed its acting +on the page. Do so uniformly, even if only strictly required for v2 +status pages, to avoid extra conditionals (which then would all need to +be kept in sync going forward). + +This is CVE-2021-28701 / XSA-384. + +Reported-by: Julien Grall +Signed-off-by: Jan Beulich +Reviewed-by: Julien Grall +--- +v2: Pull get_page() earlier and fold if()s. + +--- a/xen/arch/arm/mm.c ++++ b/xen/arch/arm/mm.c +@@ -1420,6 +1420,8 @@ int xenmem_add_to_physmap_one( + if ( rc ) + return rc; + ++ /* Need to take care of the reference obtained in gnttab_map_frame(). */ ++ page = mfn_to_page(mfn); + t = p2m_ram_rw; + + break; +@@ -1487,9 +1489,12 @@ int xenmem_add_to_physmap_one( + /* Map at new location. */ + rc = guest_physmap_add_entry(d, gfn, mfn, 0, t); + +- /* If we fail to add the mapping, we need to drop the reference we +- * took earlier on foreign pages */ +- if ( rc && space == XENMAPSPACE_gmfn_foreign ) ++ /* ++ * For XENMAPSPACE_gmfn_foreign if we failed to add the mapping, we need ++ * to drop the reference we took earlier. In all other cases we need to ++ * drop any reference we took earlier (perhaps indirectly). ++ */ ++ if ( space == XENMAPSPACE_gmfn_foreign ? rc : page != NULL ) + { + ASSERT(page != NULL); + put_page(page); +--- a/xen/arch/x86/mm/p2m.c ++++ b/xen/arch/x86/mm/p2m.c +@@ -2726,6 +2726,8 @@ int xenmem_add_to_physmap_one( + rc = gnttab_map_frame(d, idx, gpfn, &mfn); + if ( rc ) + return rc; ++ /* Need to take care of the reference obtained in gnttab_map_frame(). */ ++ page = mfn_to_page(mfn); + break; + + case XENMAPSPACE_gmfn: +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -4097,7 +4097,16 @@ int gnttab_map_frame(struct domain *d, u + } + + if ( !rc ) +- gnttab_set_frame_gfn(gt, status, idx, gfn); ++ { ++ /* ++ * Make sure gnttab_unpopulate_status_frames() won't (successfully) ++ * free the page until our caller has completed its operation. ++ */ ++ if ( get_page(mfn_to_page(*mfn), d) ) ++ gnttab_set_frame_gfn(gt, status, idx, gfn); ++ else ++ rc = -EBUSY; ++ } + + grant_write_unlock(gt); + From 3c3686c2d67a495976b4388e8ee3ac43e46540eb Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 8 Sep 2021 21:54:13 +0100 Subject: [PATCH 018/137] Another race in XENMAPSPACE_grant_table handling [XSA-384, CVE-2021-28701] bugfix for XSA-380 stop editing grub files in /boot/efi/EFI/fedora on Fedora 34 --- xen.spec | 21 ++++++++++++- xsa380-3.patch | 74 ++++++++++++++++++++++++++++++++++++++++++++ xsa384-4.14.patch | 79 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 xsa380-3.patch create mode 100644 xsa384-4.14.patch diff --git a/xen.spec b/xen.spec index 27c2825..998f5df 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.14.2 -Release: 3%{?dist} +Release: 4%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -141,6 +141,8 @@ Patch70: xsa380-4.14-1.patch Patch71: xsa380-4.14-2.patch Patch72: xsa382.patch Patch73: xsa383.patch +Patch74: xsa380-3.patch +Patch75: xsa384-4.14.patch %if %build_qemutrad @@ -378,6 +380,8 @@ manage Xen virtual machines. %patch71 -p1 %patch72 -p1 %patch73 -p1 +%patch74 -p1 +%patch75 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -664,7 +668,11 @@ do_it() { fi } if [ $1 == 1 -a -f /sbin/grub2-mkconfig ]; then +%if "%dist" <= ".fc33" for f in /boot/grub2/grub.cfg /boot/efi/EFI/fedora/grub.cfg; do +%else + for f in /boot/grub2/grub.cfg; do +%endif if [ -f $f ]; then /sbin/grub2-mkconfig -o $f sed -i -e '/insmod module2/d' $f @@ -680,16 +688,22 @@ if [ -f /sbin/grub2-mkconfig ]; then TARGET=/boot/grub2/x86_64-efi do_it $DIR $TARGET fi +%if "%dist" <= ".fc33" if [ -f /boot/efi/EFI/fedora/grub.cfg ]; then DIR=/usr/lib/grub/x86_64-efi TARGET=/boot/efi/EFI/fedora/x86_64-efi do_it $DIR $TARGET fi +%endif fi %postun hypervisor if [ -f /sbin/grub2-mkconfig ]; then +%if "%dist" <= ".fc33" for f in /boot/grub2/grub.cfg /boot/efi/EFI/fedora/grub.cfg; do +%else + for f in /boot/grub2/grub.cfg; do +%endif if [ -f $f ]; then /sbin/grub2-mkconfig -o $f sed -i -e '/insmod module2/d' $f @@ -974,6 +988,11 @@ fi %endif %changelog +* Wed Sep 08 2021 Michael Young - 4.14.2-4 +- Another race in XENMAPSPACE_grant_table handling [XSA-384, CVE-2021-28701] +- bugfix for XSA-380 +- stop editing grub files in /boot/efi/EFI/fedora on Fedora 34 + * Sat Aug 28 2021 Michael Young - 4.14.2-3 - IOMMU page mapping issues on x86 [XSA-378, CVE-2021-28694, CVE-2021-28695, CVE-2021-28696] (#1997531) (#1997568) diff --git a/xsa380-3.patch b/xsa380-3.patch new file mode 100644 index 0000000..1517249 --- /dev/null +++ b/xsa380-3.patch @@ -0,0 +1,74 @@ +From: Jan Beulich +Subject: gnttab: avoid triggering assertion in radix_tree_ulong_to_ptr() + +Relevant quotes from the C11 standard: + +"Except where explicitly stated otherwise, for the purposes of this + subclause unnamed members of objects of structure and union type do not + participate in initialization. Unnamed members of structure objects + have indeterminate value even after initialization." + +"If there are fewer initializers in a brace-enclosed list than there are + elements or members of an aggregate, [...], the remainder of the + aggregate shall be initialized implicitly the same as objects that have + static storage duration." + +"If an object that has static or thread storage duration is not + initialized explicitly, then: + [...] + — if it is an aggregate, every member is initialized (recursively) + according to these rules, and any padding is initialized to zero + bits; + [...]" + +"A bit-field declaration with no declarator, but only a colon and a + width, indicates an unnamed bit-field." Footnote: "An unnamed bit-field + structure member is useful for padding to conform to externally imposed + layouts." + +"There may be unnamed padding within a structure object, but not at its + beginning." + +Which makes me conclude: +- Whether an unnamed bit-field member is an unnamed member or padding is + unclear, and hence also whether the last quote above would render the + big endian case of the structure declaration invalid. +- Whether the number of members of an aggregate includes unnamed ones is + also not really clear. +- The initializer in map_grant_ref() initializes all fields of the "cnt" + sub-structure of the union, so assuming the second quote above applies + here (indirectly), the compiler isn't required to implicitly + initialize the rest (i.e. in particular any padding) like would happen + for static storage duration objects. + +Gcc 7.4.1 can be observed (apparently in debug builds only) to translate +aforementioned initializer to a read-modify-write operation of a stack +variable, leaving unchanged the top two bits of whatever was previously +in that stack slot. Clearly if either of the two bits were set, +radix_tree_ulong_to_ptr()'s assertion would trigger. + +Therefore, to be on the safe side, add an explicit padding field for the +non-big-endian-bitfields case and give a dummy name to both padding +fields. + +Fixes: 9781b51efde2 ("gnttab: replace mapkind()") +Signed-off-by: Jan Beulich +Acked-by: Andrew Cooper + +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -952,10 +952,13 @@ union maptrack_node { + struct { + /* Radix tree slot pointers use two of the bits. */ + #ifdef __BIG_ENDIAN_BITFIELD +- unsigned long : 2; ++ unsigned long _0 : 2; + #endif + unsigned long rd : BITS_PER_LONG / 2 - 1; + unsigned long wr : BITS_PER_LONG / 2 - 1; ++#ifndef __BIG_ENDIAN_BITFIELD ++ unsigned long _0 : 2; ++#endif + } cnt; + unsigned long raw; + }; diff --git a/xsa384-4.14.patch b/xsa384-4.14.patch new file mode 100644 index 0000000..9efe1b0 --- /dev/null +++ b/xsa384-4.14.patch @@ -0,0 +1,79 @@ +From: Jan Beulich +Subject: gnttab: deal with status frame mapping race + +Once gnttab_map_frame() drops the grant table lock, the MFN it reports +back to its caller is free to other manipulation. In particular +gnttab_unpopulate_status_frames() might free it, by a racing request on +another CPU, thus resulting in a reference to a deallocated page getting +added to a domain's P2M. + +Obtain a page reference in gnttab_map_frame() to prevent freeing of the +page until xenmem_add_to_physmap_one() has actually completed its acting +on the page. Do so uniformly, even if only strictly required for v2 +status pages, to avoid extra conditionals (which then would all need to +be kept in sync going forward). + +This is CVE-2021-28701 / XSA-384. + +Reported-by: Julien Grall +Signed-off-by: Jan Beulich +Reviewed-by: Julien Grall + +--- a/xen/arch/arm/mm.c ++++ b/xen/arch/arm/mm.c +@@ -1423,6 +1423,8 @@ int xenmem_add_to_physmap_one( + if ( rc ) + return rc; + ++ /* Need to take care of the reference obtained in gnttab_map_frame(). */ ++ page = mfn_to_page(mfn); + t = p2m_ram_rw; + + break; +@@ -1490,9 +1492,12 @@ int xenmem_add_to_physmap_one( + /* Map at new location. */ + rc = guest_physmap_add_entry(d, gfn, mfn, 0, t); + +- /* If we fail to add the mapping, we need to drop the reference we +- * took earlier on foreign pages */ +- if ( rc && space == XENMAPSPACE_gmfn_foreign ) ++ /* ++ * For XENMAPSPACE_gmfn_foreign if we failed to add the mapping, we need ++ * to drop the reference we took earlier. In all other cases we need to ++ * drop any reference we took earlier (perhaps indirectly). ++ */ ++ if ( space == XENMAPSPACE_gmfn_foreign ? rc : page != NULL ) + { + ASSERT(page != NULL); + put_page(page); +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -4620,6 +4620,8 @@ int xenmem_add_to_physmap_one( + rc = gnttab_map_frame(d, idx, gpfn, &mfn); + if ( rc ) + return rc; ++ /* Need to take care of the ref obtained in gnttab_map_frame(). */ ++ page = mfn_to_page(mfn); + break; + case XENMAPSPACE_gmfn: + { +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -4102,7 +4102,16 @@ int gnttab_map_frame(struct domain *d, u + } + + if ( !rc ) +- gnttab_set_frame_gfn(gt, status, idx, gfn); ++ { ++ /* ++ * Make sure gnttab_unpopulate_status_frames() won't (successfully) ++ * free the page until our caller has completed its operation. ++ */ ++ if ( get_page(mfn_to_page(*mfn), d) ) ++ gnttab_set_frame_gfn(gt, status, idx, gfn); ++ else ++ rc = -EBUSY; ++ } + + grant_write_unlock(gt); + From 84657107f472f5703c2699d1ce62b1a7192ffab0 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 9 Sep 2021 20:04:03 +0100 Subject: [PATCH 019/137] add bug reference --- xen.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/xen.spec b/xen.spec index 995296e..3a85379 100644 --- a/xen.spec +++ b/xen.spec @@ -971,6 +971,7 @@ fi %changelog * Wed Sep 08 2021 Michael Young - 4.15.0-7 - Another race in XENMAPSPACE_grant_table handling [XSA-384, CVE-2021-28701] + (#2002786) - bugfix for XSA-380 - stop editing grub files in /boot/efi/EFI/fedora From 2334838f4e2fb08a45f1eb14af61cdba9c6dec21 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 9 Sep 2021 20:06:46 +0100 Subject: [PATCH 020/137] add bug reference --- xen.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/xen.spec b/xen.spec index 998f5df..8f7137a 100644 --- a/xen.spec +++ b/xen.spec @@ -990,6 +990,7 @@ fi %changelog * Wed Sep 08 2021 Michael Young - 4.14.2-4 - Another race in XENMAPSPACE_grant_table handling [XSA-384, CVE-2021-28701] + (#2002786) - bugfix for XSA-380 - stop editing grub files in /boot/efi/EFI/fedora on Fedora 34 From 7724e33e7340b2beac564efcd2629b3c7a1939f2 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Sun, 12 Sep 2021 16:19:18 +0100 Subject: [PATCH 021/137] update to xen-4.15.1 --- .gitignore | 2 +- sources | 2 +- xen.gcc11.fixes.patch | 39 -- ...714729cf84df50d6831c4da076e21587408c.patch | 117 ----- ...654ebce7ccb272a3f4f3482c03aaad850d31.patch | 36 -- xen.spec | 63 +-- ...en-arm-Create-dom0less-domUs-earlier.patch | 85 ---- ...ules-should-always-be-scrubbed-if-bo.patch | 59 --- xsa373-4.15-1.patch | 120 ----- xsa373-4.15-2.patch | 102 ----- xsa373-4.15-3.patch | 163 ------- xsa373-4.15-4.patch | 79 ---- xsa373-4.15-5.patch | 141 ------ xsa375.patch | 50 --- xsa377.patch | 27 -- xsa378-4.15-1.patch | 142 ------ xsa378-4.15-2.patch | 218 ---------- xsa378-4.15-3.patch | 102 ----- xsa378-4.15-4.patch | 399 ----------------- xsa378-4.15-5.patch | 208 --------- xsa378-4.15-6.patch | 411 ------------------ xsa378-4.15-7.patch | 88 ---- xsa378-4.15-8.patch | 155 ------- xsa379-4.15.patch | 82 ---- xsa380-1.patch | 178 -------- xsa380-2.patch | 410 ----------------- xsa380-3.patch | 74 ---- xsa382.patch | 34 -- xsa383.patch | 55 --- xsa384.patch | 81 ---- 30 files changed, 10 insertions(+), 3712 deletions(-) delete mode 100644 xen.git-3581714729cf84df50d6831c4da076e21587408c.patch delete mode 100644 xen.git-58ad654ebce7ccb272a3f4f3482c03aaad850d31.patch delete mode 100644 xsa372-4.15-0001-xen-arm-Create-dom0less-domUs-earlier.patch delete mode 100644 xsa372-4.15-0002-xen-arm-Boot-modules-should-always-be-scrubbed-if-bo.patch delete mode 100644 xsa373-4.15-1.patch delete mode 100644 xsa373-4.15-2.patch delete mode 100644 xsa373-4.15-3.patch delete mode 100644 xsa373-4.15-4.patch delete mode 100644 xsa373-4.15-5.patch delete mode 100644 xsa375.patch delete mode 100644 xsa377.patch delete mode 100644 xsa378-4.15-1.patch delete mode 100644 xsa378-4.15-2.patch delete mode 100644 xsa378-4.15-3.patch delete mode 100644 xsa378-4.15-4.patch delete mode 100644 xsa378-4.15-5.patch delete mode 100644 xsa378-4.15-6.patch delete mode 100644 xsa378-4.15-7.patch delete mode 100644 xsa378-4.15-8.patch delete mode 100644 xsa379-4.15.patch delete mode 100644 xsa380-1.patch delete mode 100644 xsa380-2.patch delete mode 100644 xsa380-3.patch delete mode 100644 xsa382.patch delete mode 100644 xsa383.patch delete mode 100644 xsa384.patch diff --git a/.gitignore b/.gitignore index 794101c..10627f5 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.15.0.tar.gz +/xen-4.15.1.tar.gz diff --git a/sources b/sources index 875f44f..977c8aa 100644 --- a/sources +++ b/sources @@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.15.0.tar.gz) = 93683b8a97387ca5f003c635a11d163e61c87dbdc9a03081f9155fe87b49f1dfa74ce243fcd5e04dc009353a36e2375b786f1ebde828b5951a094cd64197b4c7 +SHA512 (xen-4.15.1.tar.gz) = 8d3cbdf708f46477e32ee7cbd16a490c82efa855cecd84ee712b8680df4d69c987ba9ab00ff3851f627b98a8ebbc5dab71f92f142ed958ee2bc538bc792cd4b9 diff --git a/xen.gcc11.fixes.patch b/xen.gcc11.fixes.patch index a075a6f..31db0cb 100644 --- a/xen.gcc11.fixes.patch +++ b/xen.gcc11.fixes.patch @@ -9,28 +9,6 @@ unsigned int mbytes, vmac_ctx_t *ctx); ---- xen-4.14.0/tools/libs/foreignmemory/linux.c.orig 2020-07-23 16:07:51.000000000 +0100 -+++ xen-4.14.0/tools/libs/foreignmemory/linux.c 2020-10-25 21:36:00.982040566 +0000 -@@ -162,7 +162,7 @@ - void *osdep_xenforeignmemory_map(xenforeignmemory_handle *fmem, - uint32_t dom, void *addr, - int prot, int flags, size_t num, -- const xen_pfn_t arr[/*num*/], int err[/*num*/]) -+ const xen_pfn_t arr[num], int err[num]) - { - int fd = fmem->fd; - privcmd_mmapbatch_v2_t ioctlx; ---- xen-4.14.0/tools/libs/foreignmemory/minios.c.orig 2020-07-23 16:07:51.000000000 +0100 -+++ xen-4.14.0/tools/libs/foreignmemory/minios.c 2020-10-26 22:36:12.423883688 +0000 -@@ -42,7 +42,7 @@ - void *osdep_xenforeignmemory_map(xenforeignmemory_handle *fmem, - uint32_t dom, void *addr, - int prot, int flags, size_t num, -- const xen_pfn_t arr[/*num*/], int err[/*num*/]) -+ const xen_pfn_t arr[num], int err[num]) - { - unsigned long pt_prot = 0; - if (prot & PROT_READ) diff --git a/xen/arch/x86/tboot.c b/xen/arch/x86/tboot.c index 320e06f..618ae92 100644 --- a/xen/arch/x86/tboot.c @@ -44,20 +22,3 @@ index 320e06f..618ae92 100644 /* Look for valid page-aligned address for shared page. */ if ( !opt_tboot_pa || (opt_tboot_pa & ~PAGE_MASK) ) -diff --git a/xen/arch/x86/x86_emulate/x86_emulate.c b/xen/arch/x86/x86_emulate/x86_emulate.c -index 84bb8e0..6ecf5db 100644 ---- a/xen/arch/x86/x86_emulate/x86_emulate.c -+++ b/xen/arch/x86/x86_emulate/x86_emulate.c -@@ -725,9 +725,9 @@ union vex { - #define copy_VEX(ptr, vex) ({ \ - if ( !mode_64bit() ) \ - (vex).reg |= 8; \ -- (ptr)[0 - PFX_BYTES] = ext < ext_8f08 ? 0xc4 : 0x8f; \ -- (ptr)[1 - PFX_BYTES] = (vex).raw[0]; \ -- (ptr)[2 - PFX_BYTES] = (vex).raw[1]; \ -+ ((volatile uint8_t *)ptr)[0 - PFX_BYTES] = ext < ext_8f08 ? 0xc4 : 0x8f; \ -+ ((volatile uint8_t *)ptr)[1 - PFX_BYTES] = (vex).raw[0]; \ -+ ((volatile uint8_t *)ptr)[2 - PFX_BYTES] = (vex).raw[1]; \ - container_of((ptr) + 1 - PFX_BYTES, typeof(vex), raw[0]); \ - }) - diff --git a/xen.git-3581714729cf84df50d6831c4da076e21587408c.patch b/xen.git-3581714729cf84df50d6831c4da076e21587408c.patch deleted file mode 100644 index 6eb4f71..0000000 --- a/xen.git-3581714729cf84df50d6831c4da076e21587408c.patch +++ /dev/null @@ -1,117 +0,0 @@ -From 3581714729cf84df50d6831c4da076e21587408c Mon Sep 17 00:00:00 2001 -From: Jan Beulich -Date: Thu, 15 Jul 2021 09:21:04 +0200 -Subject: [PATCH] VT-d: adjust domid map updating when unmapping context - -When an earlier error occurred, cleaning up the domid mapping data is -wrong, as references likely still exist. The only exception to this is -when the actual unmapping worked, but some flush failed (supposedly -impossible after XSA-373). The guest will get crashed in such a case -though, so add fallback cleanup to domain destruction to cover this -case. This in turn makes it desirable to silence the dprintk() in -domain_iommu_domid(). - -Note that no error will be returned anymore when the lookup fails - in -the common case lookup failure would already have caused -domain_context_unmap_one() to fail, yet even from a more general -perspective it doesn't look right to fail domain_context_unmap() in such -a case when this was the last device, but not when any earlier unmap was -otherwise successful. - -Signed-off-by: Jan Beulich -Reviewed-by: Kevin Tian -master commit: 32655880057ce2829f962d46916ea6cec60f98d3 -master date: 2021-06-24 16:29:13 +0200 ---- - xen/drivers/passthrough/vtd/iommu.c | 39 ++++++++++++++++++----------- - 1 file changed, 24 insertions(+), 15 deletions(-) - -diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c -index c0070f1c99..2b3609dae2 100644 ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -80,9 +80,11 @@ static int domain_iommu_domid(struct domain *d, - i = find_next_bit(iommu->domid_bitmap, nr_dom, i+1); - } - -- dprintk(XENLOG_ERR VTDPREFIX, -- "Cannot get valid iommu domid: domid=%d iommu->index=%d\n", -- d->domain_id, iommu->index); -+ if ( !d->is_dying ) -+ dprintk(XENLOG_ERR VTDPREFIX, -+ "Cannot get valid iommu %u domid: %pd\n", -+ iommu->index, d); -+ - return -1; - } - -@@ -147,6 +149,17 @@ static int context_get_domain_id(struct context_entry *context, - return domid; - } - -+static void cleanup_domid_map(struct domain *domain, struct vtd_iommu *iommu) -+{ -+ int iommu_domid = domain_iommu_domid(domain, iommu); -+ -+ if ( iommu_domid >= 0 ) -+ { -+ clear_bit(iommu_domid, iommu->domid_bitmap); -+ iommu->domid_map[iommu_domid] = 0; -+ } -+} -+ - static void sync_cache(const void *addr, unsigned int size) - { - static unsigned long clflush_size = 0; -@@ -1732,6 +1745,9 @@ static int domain_context_unmap(struct domain *domain, u8 devfn, - goto out; - } - -+ if ( ret ) -+ goto out; -+ - /* - * if no other devices under the same iommu owned by this domain, - * clear iommu in iommu_bitmap and clear domain_id in domid_bitmp -@@ -1751,19 +1767,8 @@ static int domain_context_unmap(struct domain *domain, u8 devfn, - - if ( found == 0 ) - { -- int iommu_domid; -- - clear_bit(iommu->index, &dom_iommu(domain)->arch.vtd.iommu_bitmap); -- -- iommu_domid = domain_iommu_domid(domain, iommu); -- if ( iommu_domid == -1 ) -- { -- ret = -EINVAL; -- goto out; -- } -- -- clear_bit(iommu_domid, iommu->domid_bitmap); -- iommu->domid_map[iommu_domid] = 0; -+ cleanup_domid_map(domain, iommu); - } - - out: -@@ -1783,6 +1788,7 @@ static void iommu_domain_teardown(struct domain *d) - { - struct domain_iommu *hd = dom_iommu(d); - struct mapped_rmrr *mrmrr, *tmp; -+ const struct acpi_drhd_unit *drhd; - - if ( list_empty(&acpi_drhd_units) ) - return; -@@ -1794,6 +1800,9 @@ static void iommu_domain_teardown(struct domain *d) - } - - ASSERT(!hd->arch.vtd.pgd_maddr); -+ -+ for_each_drhd_unit ( drhd ) -+ cleanup_domid_map(d, drhd->iommu); - } - - static int __must_check intel_iommu_map_page(struct domain *d, dfn_t dfn, --- -2.30.2 - diff --git a/xen.git-58ad654ebce7ccb272a3f4f3482c03aaad850d31.patch b/xen.git-58ad654ebce7ccb272a3f4f3482c03aaad850d31.patch deleted file mode 100644 index 7229049..0000000 --- a/xen.git-58ad654ebce7ccb272a3f4f3482c03aaad850d31.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 58ad654ebce7ccb272a3f4f3482c03aaad850d31 Mon Sep 17 00:00:00 2001 -From: Jan Beulich -Date: Thu, 22 Jul 2021 11:20:38 +0200 -Subject: [PATCH] x86: work around build issue with GNU ld 2.37 - -I suspect it is commit 40726f16a8d7 ("ld script expression parsing") -which broke the hypervisor build, by no longer accepting section names -with a dash in them inside ADDR() (and perhaps other script directives -expecting just a section name, not an expression): .note.gnu.build-id -is such a section. - -Quoting all section names passed to ADDR() via DECL_SECTION() works -around the regression. - -Signed-off-by: Jan Beulich -Acked-by: Andrew Cooper ---- - xen/arch/x86/xen.lds.S | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S -index 9c6c1c8005..955d5cf4a0 100644 ---- a/xen/arch/x86/xen.lds.S -+++ b/xen/arch/x86/xen.lds.S -@@ -18,7 +18,7 @@ ENTRY(efi_start) - #else /* !EFI */ - - #define FORMAT "elf64-x86-64" --#define DECL_SECTION(x) x : AT(ADDR(x) - __XEN_VIRT_START) -+#define DECL_SECTION(x) x : AT(ADDR(#x) - __XEN_VIRT_START) - - ENTRY(start_pa) - --- -2.30.2 - diff --git a/xen.spec b/xen.spec index 3a85379..9b0c237 100644 --- a/xen.spec +++ b/xen.spec @@ -57,8 +57,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.15.0 -Release: 7%{?dist} +Version: 4.15.1 +Release: 1%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -113,32 +113,6 @@ Patch41: xen.gcc9.fixes.patch Patch42: xen.gcc10.fixes.patch Patch43: xen.gcc11.fixes.patch Patch44: xen.ocaml.4.12.fixes.patch -Patch45: xsa372-4.15-0001-xen-arm-Create-dom0less-domUs-earlier.patch -Patch46: xsa372-4.15-0002-xen-arm-Boot-modules-should-always-be-scrubbed-if-bo.patch -Patch47: xsa373-4.15-1.patch -Patch48: xsa373-4.15-2.patch -Patch49: xsa373-4.15-3.patch -Patch50: xsa373-4.15-4.patch -Patch51: xsa373-4.15-5.patch -Patch52: xsa375.patch -Patch53: xsa377.patch -Patch54: xen.git-58ad654ebce7ccb272a3f4f3482c03aaad850d31.patch -Patch55: xen.git-3581714729cf84df50d6831c4da076e21587408c.patch -Patch56: xsa378-4.15-1.patch -Patch57: xsa378-4.15-2.patch -Patch58: xsa378-4.15-3.patch -Patch59: xsa378-4.15-4.patch -Patch60: xsa378-4.15-5.patch -Patch61: xsa378-4.15-6.patch -Patch62: xsa378-4.15-7.patch -Patch63: xsa378-4.15-8.patch -Patch64: xsa379-4.15.patch -Patch65: xsa380-1.patch -Patch66: xsa380-2.patch -Patch67: xsa382.patch -Patch68: xsa383.patch -Patch69: xsa380-3.patch -Patch70: xsa384.patch %if %build_qemutrad @@ -348,32 +322,6 @@ manage Xen virtual machines. %patch42 -p1 %patch43 -p1 %patch44 -p1 -%patch45 -p1 -%patch46 -p1 -%patch47 -p1 -%patch48 -p1 -%patch49 -p1 -%patch50 -p1 -%patch51 -p1 -%patch52 -p1 -%patch53 -p1 -%patch54 -p1 -%patch55 -p1 -%patch56 -p1 -%patch57 -p1 -%patch58 -p1 -%patch59 -p1 -%patch60 -p1 -%patch61 -p1 -%patch62 -p1 -%patch63 -p1 -%patch64 -p1 -%patch65 -p1 -%patch66 -p1 -%patch67 -p1 -%patch68 -p1 -%patch69 -p1 -%patch70 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -726,7 +674,7 @@ fi %files libs %{_libdir}/libxencall.so.1 -%{_libdir}/libxencall.so.1.2 +%{_libdir}/libxencall.so.1.3 %{_libdir}/libxenctrl.so.4.* %{_libdir}/libxendevicemodel.so.1 %{_libdir}/libxendevicemodel.so.1.4 @@ -969,6 +917,11 @@ fi %endif %changelog +* Sun Sep 12 2021 Michael Young - 4.15.1-1 +- update to xen-4.15.1 + remove or adjust patches now included or superceded upstream + update libxencall version + * Wed Sep 08 2021 Michael Young - 4.15.0-7 - Another race in XENMAPSPACE_grant_table handling [XSA-384, CVE-2021-28701] (#2002786) diff --git a/xsa372-4.15-0001-xen-arm-Create-dom0less-domUs-earlier.patch b/xsa372-4.15-0001-xen-arm-Create-dom0less-domUs-earlier.patch deleted file mode 100644 index a21dba4..0000000 --- a/xsa372-4.15-0001-xen-arm-Create-dom0less-domUs-earlier.patch +++ /dev/null @@ -1,85 +0,0 @@ -From b1e5a89f19d9919c3eae17ab9c6a663b0801ad9c Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 17 May 2021 17:47:13 +0100 -Subject: [PATCH 1/2] xen/arm: Create dom0less domUs earlier - -In a follow-up patch we will need to unallocate the boot modules -before heap_init_late() is called. - -The modules will contain the domUs kernel and initramfs. Therefore Xen -will need to create extra domUs (used by dom0less) before heap_init_late(). - -This has two consequences on dom0less: - 1) Domains will not be unpaused as soon as they are created but - once all have been created. However, Xen doesn't guarantee an order - to unpause, so this is not something one could rely on. - - 2) The memory allocated for a domU will not be scrubbed anymore when an - admin select bootscrub=on. This is not something we advertised, but if - this is a concern we can introduce either force scrub for all domUs or - a per-domain flag in the DT. The behavior for bootscrub=off and - bootscrub=idle (default) has not changed. - -This is part of XSA-372 / CVE-2021-28693. - -Signed-off-by: Julien Grall -Reviewed-by: Jan Beulich -Reviewed-by: Stefano Stabellini -Tested-by: Stefano Stabellini ---- - xen/arch/arm/domain_build.c | 2 -- - xen/arch/arm/setup.c | 11 ++++++----- - 2 files changed, 6 insertions(+), 7 deletions(-) - -diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c -index 374bf655ee34..4203ddcca0e3 100644 ---- a/xen/arch/arm/domain_build.c -+++ b/xen/arch/arm/domain_build.c -@@ -2515,8 +2515,6 @@ void __init create_domUs(void) - - if ( construct_domU(d, node) != 0 ) - panic("Could not set up domain %s\n", dt_node_name(node)); -- -- domain_unpause_by_systemcontroller(d); - } - } - -diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c -index 2532ec973913..441e0e16e9f0 100644 ---- a/xen/arch/arm/setup.c -+++ b/xen/arch/arm/setup.c -@@ -804,7 +804,7 @@ void __init start_xen(unsigned long boot_phys_offset, - int cpus, i; - const char *cmdline; - struct bootmodule *xen_bootmodule; -- struct domain *dom0; -+ struct domain *dom0, *d; - struct xen_domctl_createdomain dom0_cfg = { - .flags = XEN_DOMCTL_CDF_hvm | XEN_DOMCTL_CDF_hap, - .max_evtchn_port = -1, -@@ -987,6 +987,9 @@ void __init start_xen(unsigned long boot_phys_offset, - if ( construct_dom0(dom0) != 0) - panic("Could not set up DOM0 guest OS\n"); - -+ if ( acpi_disabled ) -+ create_domUs(); -+ - heap_init_late(); - - init_trace_bufs(); -@@ -1000,10 +1003,8 @@ void __init start_xen(unsigned long boot_phys_offset, - - system_state = SYS_STATE_active; - -- if ( acpi_disabled ) -- create_domUs(); -- -- domain_unpause_by_systemcontroller(dom0); -+ for_each_domain( d ) -+ domain_unpause_by_systemcontroller(d); - - /* Switch on to the dynamically allocated stack for the idle vcpu - * since the static one we're running on is about to be freed. */ --- -2.17.1 - diff --git a/xsa372-4.15-0002-xen-arm-Boot-modules-should-always-be-scrubbed-if-bo.patch b/xsa372-4.15-0002-xen-arm-Boot-modules-should-always-be-scrubbed-if-bo.patch deleted file mode 100644 index 9c322b1..0000000 --- a/xsa372-4.15-0002-xen-arm-Boot-modules-should-always-be-scrubbed-if-bo.patch +++ /dev/null @@ -1,59 +0,0 @@ -From 09bb28bdef3fb5e7d08bdd641601ca0c0d4d82b4 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Sat, 17 Apr 2021 17:38:28 +0100 -Subject: [PATCH 2/2] xen/arm: Boot modules should always be scrubbed if - bootscrub={on, idle} - -The function to initialize the pages (see init_heap_pages()) will request -scrub when the admin request idle bootscrub (default) and state == -SYS_STATE_active. When bootscrub=on, Xen will scrub any free pages in -heap_init_late(). - -Currently, the boot modules (e.g. kernels, initramfs) will be discarded/ -freed after heap_init_late() is called and system_state switched to -SYS_STATE_active. This means the pages associated with the boot modules -will not get scrubbed before getting re-purposed. - -If the memory is assigned to an untrusted domU, it may be able to -retrieve secrets from the modules. - -This is part of XSA-372 / CVE-2021-28693. - -Fixes: 1774e9b1df27 ("xen/arm: introduce create_domUs") -Signed-off-by: Julien Grall -Reviewed-by: Jan Beulich -Reviewed-by: Stefano Stabellini -Tested-by: Stefano Stabellini ---- - xen/arch/arm/setup.c | 8 ++++++-- - 1 file changed, 6 insertions(+), 2 deletions(-) - -diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c -index 441e0e16e9f0..8afb78f2c985 100644 ---- a/xen/arch/arm/setup.c -+++ b/xen/arch/arm/setup.c -@@ -72,8 +72,6 @@ domid_t __read_mostly max_init_domid; - - static __used void init_done(void) - { -- discard_initial_modules(); -- - /* Must be done past setting system_state. */ - unregister_init_virtual_region(); - -@@ -990,6 +988,12 @@ void __init start_xen(unsigned long boot_phys_offset, - if ( acpi_disabled ) - create_domUs(); - -+ /* -+ * This needs to be called **before** heap_init_late() so modules -+ * will be scrubbed (unless suppressed). -+ */ -+ discard_initial_modules(); -+ - heap_init_late(); - - init_trace_bufs(); --- -2.17.1 - diff --git a/xsa373-4.15-1.patch b/xsa373-4.15-1.patch deleted file mode 100644 index ee5229a..0000000 --- a/xsa373-4.15-1.patch +++ /dev/null @@ -1,120 +0,0 @@ -From: Jan Beulich -Subject: VT-d: size qinval queue dynamically - -With the present synchronous model, we need two slots for every -operation (the operation itself and a wait descriptor). There can be -one such pair of requests pending per CPU. To ensure that under all -normal circumstances a slot is always available when one is requested, -size the queue ring according to the number of present CPUs. - -This is part of XSA-373 / CVE-2021-28692. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/vtd/iommu.h -+++ b/xen/drivers/passthrough/vtd/iommu.h -@@ -450,17 +450,9 @@ struct qinval_entry { - }q; - }; - --/* Order of queue invalidation pages(max is 8) */ --#define QINVAL_PAGE_ORDER 2 -- --#define QINVAL_ARCH_PAGE_ORDER (QINVAL_PAGE_ORDER + PAGE_SHIFT_4K - PAGE_SHIFT) --#define QINVAL_ARCH_PAGE_NR ( QINVAL_ARCH_PAGE_ORDER < 0 ? \ -- 1 : \ -- 1 << QINVAL_ARCH_PAGE_ORDER ) -- - /* Each entry is 16 bytes, so 2^8 entries per page */ - #define QINVAL_ENTRY_ORDER ( PAGE_SHIFT - 4 ) --#define QINVAL_ENTRY_NR (1 << (QINVAL_PAGE_ORDER + 8)) -+#define QINVAL_MAX_ENTRY_NR (1u << (7 + QINVAL_ENTRY_ORDER)) - - /* Status data flag */ - #define QINVAL_STAT_INIT 0 ---- a/xen/drivers/passthrough/vtd/qinval.c -+++ b/xen/drivers/passthrough/vtd/qinval.c -@@ -31,6 +31,9 @@ - - #define VTD_QI_TIMEOUT 1 - -+static unsigned int __read_mostly qi_pg_order; -+static unsigned int __read_mostly qi_entry_nr; -+ - static int __must_check invalidate_sync(struct vtd_iommu *iommu); - - static void print_qi_regs(struct vtd_iommu *iommu) -@@ -55,7 +58,7 @@ static unsigned int qinval_next_index(st - tail >>= QINVAL_INDEX_SHIFT; - - /* (tail+1 == head) indicates a full queue, wait for HW */ -- while ( ( tail + 1 ) % QINVAL_ENTRY_NR == -+ while ( ((tail + 1) & (qi_entry_nr - 1)) == - ( dmar_readq(iommu->reg, DMAR_IQH_REG) >> QINVAL_INDEX_SHIFT ) ) - cpu_relax(); - -@@ -68,7 +71,7 @@ static void qinval_update_qtail(struct v - - /* Need hold register lock when update tail */ - ASSERT( spin_is_locked(&iommu->register_lock) ); -- val = (index + 1) % QINVAL_ENTRY_NR; -+ val = (index + 1) & (qi_entry_nr - 1); - dmar_writeq(iommu->reg, DMAR_IQT_REG, (val << QINVAL_INDEX_SHIFT)); - } - -@@ -403,8 +406,28 @@ int enable_qinval(struct vtd_iommu *iomm - - if ( iommu->qinval_maddr == 0 ) - { -- iommu->qinval_maddr = alloc_pgtable_maddr(QINVAL_ARCH_PAGE_NR, -- iommu->node); -+ if ( !qi_entry_nr ) -+ { -+ /* -+ * With the present synchronous model, we need two slots for every -+ * operation (the operation itself and a wait descriptor). There -+ * can be one such pair of requests pending per CPU. One extra -+ * entry is needed as the ring is considered full when there's -+ * only one entry left. -+ */ -+ BUILD_BUG_ON(CONFIG_NR_CPUS * 2 >= QINVAL_MAX_ENTRY_NR); -+ qi_pg_order = get_order_from_bytes((num_present_cpus() * 2 + 1) << -+ (PAGE_SHIFT - -+ QINVAL_ENTRY_ORDER)); -+ qi_entry_nr = 1u << (qi_pg_order + QINVAL_ENTRY_ORDER); -+ -+ dprintk(XENLOG_INFO VTDPREFIX, -+ "QI: using %u-entry ring(s)\n", qi_entry_nr); -+ } -+ -+ iommu->qinval_maddr = -+ alloc_pgtable_maddr(qi_entry_nr >> QINVAL_ENTRY_ORDER, -+ iommu->node); - if ( iommu->qinval_maddr == 0 ) - { - dprintk(XENLOG_WARNING VTDPREFIX, -@@ -418,15 +441,16 @@ int enable_qinval(struct vtd_iommu *iomm - - spin_lock_irqsave(&iommu->register_lock, flags); - -- /* Setup Invalidation Queue Address(IQA) register with the -- * address of the page we just allocated. QS field at -- * bits[2:0] to indicate size of queue is one 4KB page. -- * That's 256 entries. Queued Head (IQH) and Queue Tail (IQT) -- * registers are automatically reset to 0 with write -- * to IQA register. -+ /* -+ * Setup Invalidation Queue Address (IQA) register with the address of the -+ * pages we just allocated. The QS field at bits[2:0] indicates the size -+ * (page order) of the queue. -+ * -+ * Queued Head (IQH) and Queue Tail (IQT) registers are automatically -+ * reset to 0 with write to IQA register. - */ - dmar_writeq(iommu->reg, DMAR_IQA_REG, -- iommu->qinval_maddr | QINVAL_PAGE_ORDER); -+ iommu->qinval_maddr | qi_pg_order); - - dmar_writeq(iommu->reg, DMAR_IQT_REG, 0); - diff --git a/xsa373-4.15-2.patch b/xsa373-4.15-2.patch deleted file mode 100644 index d61a3b4..0000000 --- a/xsa373-4.15-2.patch +++ /dev/null @@ -1,102 +0,0 @@ -From: Jan Beulich -Subject: AMD/IOMMU: size command buffer dynamically - -With the present synchronous model, we need two slots for every -operation (the operation itself and a wait command). There can be one -such pair of commands pending per CPU. To ensure that under all normal -circumstances a slot is always available when one is requested, size the -command ring according to the number of present CPUs. - -This is part of XSA-373 / CVE-2021-28692. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/amd/iommu-defs.h -+++ b/xen/drivers/passthrough/amd/iommu-defs.h -@@ -20,9 +20,6 @@ - #ifndef AMD_IOMMU_DEFS_H - #define AMD_IOMMU_DEFS_H - --/* IOMMU Command Buffer entries: in power of 2 increments, minimum of 256 */ --#define IOMMU_CMD_BUFFER_DEFAULT_ENTRIES 512 -- - /* IOMMU Event Log entries: in power of 2 increments, minimum of 256 */ - #define IOMMU_EVENT_LOG_DEFAULT_ENTRIES 512 - -@@ -164,8 +161,8 @@ struct amd_iommu_dte { - #define IOMMU_CMD_BUFFER_LENGTH_MASK 0x0F000000 - #define IOMMU_CMD_BUFFER_LENGTH_SHIFT 24 - --#define IOMMU_CMD_BUFFER_ENTRY_SIZE 16 --#define IOMMU_CMD_BUFFER_POWER_OF2_ENTRIES_PER_PAGE 8 -+#define IOMMU_CMD_BUFFER_ENTRY_ORDER 4 -+#define IOMMU_CMD_BUFFER_MAX_ENTRIES (1u << 15) - - #define IOMMU_CMD_OPCODE_MASK 0xF0000000 - #define IOMMU_CMD_OPCODE_SHIFT 28 ---- a/xen/drivers/passthrough/amd/iommu_cmd.c -+++ b/xen/drivers/passthrough/amd/iommu_cmd.c -@@ -24,7 +24,7 @@ static int queue_iommu_command(struct am - { - uint32_t tail, head; - -- tail = iommu->cmd_buffer.tail + IOMMU_CMD_BUFFER_ENTRY_SIZE; -+ tail = iommu->cmd_buffer.tail + sizeof(cmd_entry_t); - if ( tail == iommu->cmd_buffer.size ) - tail = 0; - -@@ -33,7 +33,7 @@ static int queue_iommu_command(struct am - if ( head != tail ) - { - memcpy(iommu->cmd_buffer.buffer + iommu->cmd_buffer.tail, -- cmd, IOMMU_CMD_BUFFER_ENTRY_SIZE); -+ cmd, sizeof(cmd_entry_t)); - - iommu->cmd_buffer.tail = tail; - return 1; ---- a/xen/drivers/passthrough/amd/iommu_init.c -+++ b/xen/drivers/passthrough/amd/iommu_init.c -@@ -118,7 +118,7 @@ static void register_iommu_cmd_buffer_in - writel(entry, iommu->mmio_base + IOMMU_CMD_BUFFER_BASE_LOW_OFFSET); - - power_of2_entries = get_order_from_bytes(iommu->cmd_buffer.size) + -- IOMMU_CMD_BUFFER_POWER_OF2_ENTRIES_PER_PAGE; -+ PAGE_SHIFT - IOMMU_CMD_BUFFER_ENTRY_ORDER; - - entry = 0; - iommu_set_addr_hi_to_reg(&entry, addr_hi); -@@ -1018,9 +1018,31 @@ static void *__init allocate_ring_buffer - static void * __init allocate_cmd_buffer(struct amd_iommu *iommu) - { - /* allocate 'command buffer' in power of 2 increments of 4K */ -+ static unsigned int __read_mostly nr_ents; -+ -+ if ( !nr_ents ) -+ { -+ unsigned int order; -+ -+ /* -+ * With the present synchronous model, we need two slots for every -+ * operation (the operation itself and a wait command). There can be -+ * one such pair of requests pending per CPU. One extra entry is -+ * needed as the ring is considered full when there's only one entry -+ * left. -+ */ -+ BUILD_BUG_ON(CONFIG_NR_CPUS * 2 >= IOMMU_CMD_BUFFER_MAX_ENTRIES); -+ order = get_order_from_bytes((num_present_cpus() * 2 + 1) << -+ IOMMU_CMD_BUFFER_ENTRY_ORDER); -+ nr_ents = 1u << (order + PAGE_SHIFT - IOMMU_CMD_BUFFER_ENTRY_ORDER); -+ -+ AMD_IOMMU_DEBUG("using %u-entry cmd ring(s)\n", nr_ents); -+ } -+ -+ BUILD_BUG_ON(sizeof(cmd_entry_t) != (1u << IOMMU_CMD_BUFFER_ENTRY_ORDER)); -+ - return allocate_ring_buffer(&iommu->cmd_buffer, sizeof(cmd_entry_t), -- IOMMU_CMD_BUFFER_DEFAULT_ENTRIES, -- "Command Buffer", false); -+ nr_ents, "Command Buffer", false); - } - - static void * __init allocate_event_log(struct amd_iommu *iommu) diff --git a/xsa373-4.15-3.patch b/xsa373-4.15-3.patch deleted file mode 100644 index c7ddf5d..0000000 --- a/xsa373-4.15-3.patch +++ /dev/null @@ -1,163 +0,0 @@ -From: Jan Beulich -Subject: VT-d: eliminate flush related timeouts - -Leaving an in-progress operation pending when it appears to take too -long is problematic: If e.g. a QI command completed later, the write to -the "poll slot" may instead be understood to signal a subsequently -started command's completion. Also our accounting of the timeout period -was actually wrong: We included the time it took for the command to -actually make it to the front of the queue, which could be heavily -affected by guests other than the one for which the flush is being -performed. - -Do away with all timeout detection on all flush related code paths. -Log excessively long processing times (with a progressive threshold) to -have some indication of problems in this area. - -Additionally log (once) if qinval_next_index() didn't immediately find -an available slot. Together with the earlier change sizing the queue(s) -dynamically, we should now have a guarantee that with our fully -synchronous model any demand for slots can actually be satisfied. - -This is part of XSA-373 / CVE-2021-28692. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/vtd/dmar.h -+++ b/xen/drivers/passthrough/vtd/dmar.h -@@ -127,6 +127,34 @@ do { - } \ - } while (0) - -+#define IOMMU_FLUSH_WAIT(what, iommu, offset, op, cond, sts) \ -+do { \ -+ static unsigned int __read_mostly threshold = 1; \ -+ s_time_t start = NOW(); \ -+ s_time_t timeout = start + DMAR_OPERATION_TIMEOUT * threshold; \ -+ \ -+ for ( ; ; ) \ -+ { \ -+ sts = op(iommu->reg, offset); \ -+ if ( cond ) \ -+ break; \ -+ if ( timeout && NOW() > timeout ) \ -+ { \ -+ threshold |= threshold << 1; \ -+ printk(XENLOG_WARNING VTDPREFIX \ -+ " IOMMU#%u: %s flush taking too long\n", \ -+ iommu->index, what); \ -+ timeout = 0; \ -+ } \ -+ cpu_relax(); \ -+ } \ -+ \ -+ if ( !timeout ) \ -+ printk(XENLOG_WARNING VTDPREFIX \ -+ " IOMMU#%u: %s flush took %lums\n", \ -+ iommu->index, what, (NOW() - start) / 10000000); \ -+} while ( false ) -+ - int vtd_hw_check(void); - void disable_pmr(struct vtd_iommu *iommu); - int is_igd_drhd(struct acpi_drhd_unit *drhd); ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -373,8 +373,8 @@ static void iommu_flush_write_buffer(str - dmar_writel(iommu->reg, DMAR_GCMD_REG, val | DMA_GCMD_WBF); - - /* Make sure hardware complete it */ -- IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, dmar_readl, -- !(val & DMA_GSTS_WBFS), val); -+ IOMMU_FLUSH_WAIT("write buffer", iommu, DMAR_GSTS_REG, dmar_readl, -+ !(val & DMA_GSTS_WBFS), val); - - spin_unlock_irqrestore(&iommu->register_lock, flags); - } -@@ -423,8 +423,8 @@ int vtd_flush_context_reg(struct vtd_iom - dmar_writeq(iommu->reg, DMAR_CCMD_REG, val); - - /* Make sure hardware complete it */ -- IOMMU_WAIT_OP(iommu, DMAR_CCMD_REG, dmar_readq, -- !(val & DMA_CCMD_ICC), val); -+ IOMMU_FLUSH_WAIT("context", iommu, DMAR_CCMD_REG, dmar_readq, -+ !(val & DMA_CCMD_ICC), val); - - spin_unlock_irqrestore(&iommu->register_lock, flags); - /* flush context entry will implicitly flush write buffer */ -@@ -501,8 +501,8 @@ int vtd_flush_iotlb_reg(struct vtd_iommu - dmar_writeq(iommu->reg, tlb_offset + 8, val); - - /* Make sure hardware complete it */ -- IOMMU_WAIT_OP(iommu, (tlb_offset + 8), dmar_readq, -- !(val & DMA_TLB_IVT), val); -+ IOMMU_FLUSH_WAIT("iotlb", iommu, (tlb_offset + 8), dmar_readq, -+ !(val & DMA_TLB_IVT), val); - spin_unlock_irqrestore(&iommu->register_lock, flags); - - /* check IOTLB invalidation granularity */ ---- a/xen/drivers/passthrough/vtd/qinval.c -+++ b/xen/drivers/passthrough/vtd/qinval.c -@@ -29,8 +29,6 @@ - #include "extern.h" - #include "../ats.h" - --#define VTD_QI_TIMEOUT 1 -- - static unsigned int __read_mostly qi_pg_order; - static unsigned int __read_mostly qi_entry_nr; - -@@ -60,7 +58,11 @@ static unsigned int qinval_next_index(st - /* (tail+1 == head) indicates a full queue, wait for HW */ - while ( ((tail + 1) & (qi_entry_nr - 1)) == - ( dmar_readq(iommu->reg, DMAR_IQH_REG) >> QINVAL_INDEX_SHIFT ) ) -+ { -+ printk_once(XENLOG_ERR VTDPREFIX " IOMMU#%u: no QI slot available\n", -+ iommu->index); - cpu_relax(); -+ } - - return tail; - } -@@ -180,23 +182,32 @@ static int __must_check queue_invalidate - /* Now we don't support interrupt method */ - if ( sw ) - { -- s_time_t timeout; -- -- /* In case all wait descriptor writes to same addr with same data */ -- timeout = NOW() + MILLISECS(flush_dev_iotlb ? -- iommu_dev_iotlb_timeout : VTD_QI_TIMEOUT); -+ static unsigned int __read_mostly threshold = 1; -+ s_time_t start = NOW(); -+ s_time_t timeout = start + (flush_dev_iotlb -+ ? iommu_dev_iotlb_timeout -+ : 100) * MILLISECS(threshold); - - while ( ACCESS_ONCE(*this_poll_slot) != QINVAL_STAT_DONE ) - { -- if ( NOW() > timeout ) -+ if ( timeout && NOW() > timeout ) - { -- print_qi_regs(iommu); -+ threshold |= threshold << 1; - printk(XENLOG_WARNING VTDPREFIX -- " Queue invalidate wait descriptor timed out\n"); -- return -ETIMEDOUT; -+ " IOMMU#%u: QI%s wait descriptor taking too long\n", -+ iommu->index, flush_dev_iotlb ? " dev" : ""); -+ print_qi_regs(iommu); -+ timeout = 0; - } - cpu_relax(); - } -+ -+ if ( !timeout ) -+ printk(XENLOG_WARNING VTDPREFIX -+ " IOMMU#%u: QI%s wait descriptor took %lums\n", -+ iommu->index, flush_dev_iotlb ? " dev" : "", -+ (NOW() - start) / 10000000); -+ - return 0; - } - diff --git a/xsa373-4.15-4.patch b/xsa373-4.15-4.patch deleted file mode 100644 index 17592cb..0000000 --- a/xsa373-4.15-4.patch +++ /dev/null @@ -1,79 +0,0 @@ -From: Jan Beulich -Subject: AMD/IOMMU: wait for command slot to be available - -No caller cared about send_iommu_command() indicating unavailability of -a slot. Hence if a sufficient number prior commands timed out, we did -blindly assume that the requested command was submitted to the IOMMU -when really it wasn't. This could mean both a hanging system (waiting -for a command to complete that was never seen by the IOMMU) or blindly -propagating success back to callers, making them believe they're fine -to e.g. free previously unmapped pages. - -Fold the three involved functions into one, add spin waiting for an -available slot along the lines of VT-d's qinval_next_index(), and as a -consequence drop all error indicator return types/values. - -This is part of XSA-373 / CVE-2021-28692. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/amd/iommu_cmd.c -+++ b/xen/drivers/passthrough/amd/iommu_cmd.c -@@ -20,43 +20,30 @@ - #include "iommu.h" - #include "../ats.h" - --static int queue_iommu_command(struct amd_iommu *iommu, u32 cmd[]) -+static void send_iommu_command(struct amd_iommu *iommu, -+ const uint32_t cmd[4]) - { -- uint32_t tail, head; -+ uint32_t tail; - - tail = iommu->cmd_buffer.tail + sizeof(cmd_entry_t); - if ( tail == iommu->cmd_buffer.size ) - tail = 0; - -- head = readl(iommu->mmio_base + -- IOMMU_CMD_BUFFER_HEAD_OFFSET) & IOMMU_RING_BUFFER_PTR_MASK; -- if ( head != tail ) -+ while ( tail == (readl(iommu->mmio_base + -+ IOMMU_CMD_BUFFER_HEAD_OFFSET) & -+ IOMMU_RING_BUFFER_PTR_MASK) ) - { -- memcpy(iommu->cmd_buffer.buffer + iommu->cmd_buffer.tail, -- cmd, sizeof(cmd_entry_t)); -- -- iommu->cmd_buffer.tail = tail; -- return 1; -+ printk_once(XENLOG_ERR "AMD IOMMU %pp: no cmd slot available\n", -+ &PCI_SBDF2(iommu->seg, iommu->bdf)); -+ cpu_relax(); - } - -- return 0; --} -- --static void commit_iommu_command_buffer(struct amd_iommu *iommu) --{ -- writel(iommu->cmd_buffer.tail, -- iommu->mmio_base + IOMMU_CMD_BUFFER_TAIL_OFFSET); --} -+ memcpy(iommu->cmd_buffer.buffer + iommu->cmd_buffer.tail, -+ cmd, sizeof(cmd_entry_t)); - --static int send_iommu_command(struct amd_iommu *iommu, u32 cmd[]) --{ -- if ( queue_iommu_command(iommu, cmd) ) -- { -- commit_iommu_command_buffer(iommu); -- return 1; -- } -+ iommu->cmd_buffer.tail = tail; - -- return 0; -+ writel(tail, iommu->mmio_base + IOMMU_CMD_BUFFER_TAIL_OFFSET); - } - - static void flush_command_buffer(struct amd_iommu *iommu) diff --git a/xsa373-4.15-5.patch b/xsa373-4.15-5.patch deleted file mode 100644 index 0c6b1ea..0000000 --- a/xsa373-4.15-5.patch +++ /dev/null @@ -1,141 +0,0 @@ -From: Jan Beulich -Subject: AMD/IOMMU: drop command completion timeout - -First and foremost - such timeouts were not signaled to callers, making -them believe they're fine to e.g. free previously unmapped pages. - -Mirror VT-d's behavior: A fixed number of loop iterations is not a -suitable way to detect timeouts in an environment (CPU and bus speeds) -independent manner anyway. Furthermore, leaving an in-progress operation -pending when it appears to take too long is problematic: If a command -completed later, the signaling of its completion may instead be -understood to signal a subsequently started command's completion. - -Log excessively long processing times (with a progressive threshold) to -have some indication of problems in this area. Allow callers to specify -a non-default timeout bias for this logging, using the same values as -VT-d does, which in particular means a (by default) much larger value -for device IO TLB invalidation. - -This is part of XSA-373 / CVE-2021-28692. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/amd/iommu_cmd.c -+++ b/xen/drivers/passthrough/amd/iommu_cmd.c -@@ -46,10 +46,12 @@ static void send_iommu_command(struct am - writel(tail, iommu->mmio_base + IOMMU_CMD_BUFFER_TAIL_OFFSET); - } - --static void flush_command_buffer(struct amd_iommu *iommu) -+static void flush_command_buffer(struct amd_iommu *iommu, -+ unsigned int timeout_base) - { -- unsigned int cmd[4], status, loop_count; -- bool comp_wait; -+ uint32_t cmd[4]; -+ s_time_t start, timeout; -+ static unsigned int __read_mostly threshold = 1; - - /* RW1C 'ComWaitInt' in status register */ - writel(IOMMU_STATUS_COMP_WAIT_INT, -@@ -65,22 +67,29 @@ static void flush_command_buffer(struct - IOMMU_COMP_WAIT_I_FLAG_SHIFT, &cmd[0]); - send_iommu_command(iommu, cmd); - -- /* Make loop_count long enough for polling completion wait bit */ -- loop_count = 1000; -- do { -- status = readl(iommu->mmio_base + IOMMU_STATUS_MMIO_OFFSET); -- comp_wait = status & IOMMU_STATUS_COMP_WAIT_INT; -- --loop_count; -- } while ( !comp_wait && loop_count ); -- -- if ( comp_wait ) -+ start = NOW(); -+ timeout = start + (timeout_base ?: 100) * MILLISECS(threshold); -+ while ( !(readl(iommu->mmio_base + IOMMU_STATUS_MMIO_OFFSET) & -+ IOMMU_STATUS_COMP_WAIT_INT) ) - { -- /* RW1C 'ComWaitInt' in status register */ -- writel(IOMMU_STATUS_COMP_WAIT_INT, -- iommu->mmio_base + IOMMU_STATUS_MMIO_OFFSET); -- return; -+ if ( timeout && NOW() > timeout ) -+ { -+ threshold |= threshold << 1; -+ printk(XENLOG_WARNING -+ "AMD IOMMU %pp: %scompletion wait taking too long\n", -+ &PCI_SBDF2(iommu->seg, iommu->bdf), -+ timeout_base ? "iotlb " : ""); -+ timeout = 0; -+ } -+ cpu_relax(); - } -- AMD_IOMMU_DEBUG("Warning: ComWaitInt bit did not assert!\n"); -+ -+ if ( !timeout ) -+ printk(XENLOG_WARNING -+ "AMD IOMMU %pp: %scompletion wait took %lums\n", -+ &PCI_SBDF2(iommu->seg, iommu->bdf), -+ timeout_base ? "iotlb " : "", -+ (NOW() - start) / 10000000); - } - - /* Build low level iommu command messages */ -@@ -291,7 +300,7 @@ void amd_iommu_flush_iotlb(u8 devfn, con - /* send INVALIDATE_IOTLB_PAGES command */ - spin_lock_irqsave(&iommu->lock, flags); - invalidate_iotlb_pages(iommu, maxpend, 0, queueid, daddr, req_id, order); -- flush_command_buffer(iommu); -+ flush_command_buffer(iommu, iommu_dev_iotlb_timeout); - spin_unlock_irqrestore(&iommu->lock, flags); - } - -@@ -328,7 +337,7 @@ static void _amd_iommu_flush_pages(struc - { - spin_lock_irqsave(&iommu->lock, flags); - invalidate_iommu_pages(iommu, daddr, dom_id, order); -- flush_command_buffer(iommu); -+ flush_command_buffer(iommu, 0); - spin_unlock_irqrestore(&iommu->lock, flags); - } - -@@ -352,7 +361,7 @@ void amd_iommu_flush_device(struct amd_i - ASSERT( spin_is_locked(&iommu->lock) ); - - invalidate_dev_table_entry(iommu, bdf); -- flush_command_buffer(iommu); -+ flush_command_buffer(iommu, 0); - } - - void amd_iommu_flush_intremap(struct amd_iommu *iommu, uint16_t bdf) -@@ -360,7 +369,7 @@ void amd_iommu_flush_intremap(struct amd - ASSERT( spin_is_locked(&iommu->lock) ); - - invalidate_interrupt_table(iommu, bdf); -- flush_command_buffer(iommu); -+ flush_command_buffer(iommu, 0); - } - - void amd_iommu_flush_all_caches(struct amd_iommu *iommu) -@@ -368,7 +377,7 @@ void amd_iommu_flush_all_caches(struct a - ASSERT( spin_is_locked(&iommu->lock) ); - - invalidate_iommu_all(iommu); -- flush_command_buffer(iommu); -+ flush_command_buffer(iommu, 0); - } - - void amd_iommu_send_guest_cmd(struct amd_iommu *iommu, u32 cmd[]) -@@ -378,7 +387,8 @@ void amd_iommu_send_guest_cmd(struct amd - spin_lock_irqsave(&iommu->lock, flags); - - send_iommu_command(iommu, cmd); -- flush_command_buffer(iommu); -+ /* TBD: Timeout selection may require peeking into cmd[]. */ -+ flush_command_buffer(iommu, 0); - - spin_unlock_irqrestore(&iommu->lock, flags); - } diff --git a/xsa375.patch b/xsa375.patch deleted file mode 100644 index aa2e5ad..0000000 --- a/xsa375.patch +++ /dev/null @@ -1,50 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Protect against Speculative Code Store Bypass - -Modern x86 processors have far-better-than-architecturally-guaranteed self -modifying code detection. Typically, when a write hits an instruction in -flight, a Machine Clear occurs to flush stale content in the frontend and -backend. - -For self modifying code, before a write which hits an instruction in flight -retires, the frontend can speculatively decode and execute the old instruction -stream. Speculation of this form can suffer from type confusion in registers, -and potentially leak data. - -Furthermore, updates are typically byte-wise, rather than atomic. Depending -on timing, speculation can race ahead multiple times between individual -writes, and execute the transiently-malformed instruction stream. - -Xen has stubs which are used in certain cases for emulation purposes. Inhibit -speculation between updating the stub and executing it. - -This is XSA-375 / CVE-2021-0089. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c -index 8889509d2a..11467a1e3a 100644 ---- a/xen/arch/x86/pv/emul-priv-op.c -+++ b/xen/arch/x86/pv/emul-priv-op.c -@@ -138,6 +138,8 @@ static io_emul_stub_t *io_emul_stub_setup(struct priv_op_ctxt *ctxt, u8 opcode, - /* Runtime confirmation that we haven't clobbered an adjacent stub. */ - BUG_ON(STUB_BUF_SIZE / 2 < (p - ctxt->io_emul_stub)); - -+ block_speculation(); /* SCSB */ -+ - /* Handy function-typed pointer to the stub. */ - return (void *)stub_va; - -diff --git a/xen/arch/x86/x86_emulate/x86_emulate.c b/xen/arch/x86/x86_emulate/x86_emulate.c -index c25d88d0d8..f42ff2a837 100644 ---- a/xen/arch/x86/x86_emulate/x86_emulate.c -+++ b/xen/arch/x86/x86_emulate/x86_emulate.c -@@ -1257,6 +1257,7 @@ static inline int mkec(uint8_t e, int32_t ec, ...) - # define invoke_stub(pre, post, constraints...) do { \ - stub_exn.info = (union stub_exception_token) { .raw = ~0 }; \ - stub_exn.line = __LINE__; /* Utility outweighs livepatching cost */ \ -+ block_speculation(); /* SCSB */ \ - asm volatile ( pre "\n\tINDIRECT_CALL %[stub]\n\t" post "\n" \ - ".Lret%=:\n\t" \ - ".pushsection .fixup,\"ax\"\n" \ diff --git a/xsa377.patch b/xsa377.patch deleted file mode 100644 index 1a1887b..0000000 --- a/xsa377.patch +++ /dev/null @@ -1,27 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Mitigate TAA after S3 resume - -The user chosen setting for MSR_TSX_CTRL needs restoring after S3. - -All APs get the correct setting via start_secondary(), but the BSP was missed -out. - -This is XSA-377 / CVE-2021-28690. - -Fixes: 8c4330818f6 ("x86/spec-ctrl: Mitigate the TSX Asynchronous Abort sidechannel") -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/acpi/power.c b/xen/arch/x86/acpi/power.c -index 91a8c4d0bd..31a56f02d0 100644 ---- a/xen/arch/x86/acpi/power.c -+++ b/xen/arch/x86/acpi/power.c -@@ -288,6 +288,8 @@ static int enter_state(u32 state) - - microcode_update_one(); - -+ tsx_init(); /* Needs microcode. May change HLE/RTM feature bits. */ -+ - if ( !recheck_cpu_features(0) ) - panic("Missing previously available feature(s)\n"); - diff --git a/xsa378-4.15-1.patch b/xsa378-4.15-1.patch deleted file mode 100644 index 4b38f96..0000000 --- a/xsa378-4.15-1.patch +++ /dev/null @@ -1,142 +0,0 @@ -From: Jan Beulich -Subject: AMD/IOMMU: correct global exclusion range extending - -Besides unity mapping regions, the AMD IOMMU spec also provides for -exclusion ranges (areas of memory not to be subject to DMA translation) -to be specified by firmware in the ACPI tables. The spec does not put -any constraints on the number of such regions. - -Blindly assuming all addresses between any two such ranges should also -be excluded can't be right. Since hardware has room for just a single -such range (comprised of the Exclusion Base Register and the Exclusion -Range Limit Register), combine only adjacent or overlapping regions (for -now; this may require further adjustment in case table entries aren't -sorted by address) with matching exclusion_allow_all settings. This -requires bubbling up error indicators, such that IOMMU init can be -failed when concatenation wasn't possible. - -Furthermore, since the exclusion range specified in IOMMU registers -implies R/W access, reject requests asking for less permissions (this -will be brought closer to the spec by a subsequent change). - -This is part of XSA-378 / CVE-2021-28695. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/amd/iommu_acpi.c -+++ b/xen/drivers/passthrough/amd/iommu_acpi.c -@@ -116,12 +116,21 @@ static struct amd_iommu * __init find_io - return NULL; - } - --static void __init reserve_iommu_exclusion_range( -- struct amd_iommu *iommu, uint64_t base, uint64_t limit) -+static int __init reserve_iommu_exclusion_range( -+ struct amd_iommu *iommu, uint64_t base, uint64_t limit, -+ bool all, bool iw, bool ir) - { -+ if ( !ir || !iw ) -+ return -EPERM; -+ - /* need to extend exclusion range? */ - if ( iommu->exclusion_enable ) - { -+ if ( iommu->exclusion_limit + PAGE_SIZE < base || -+ limit + PAGE_SIZE < iommu->exclusion_base || -+ iommu->exclusion_allow_all != all ) -+ return -EBUSY; -+ - if ( iommu->exclusion_base < base ) - base = iommu->exclusion_base; - if ( iommu->exclusion_limit > limit ) -@@ -129,16 +138,11 @@ static void __init reserve_iommu_exclusi - } - - iommu->exclusion_enable = IOMMU_CONTROL_ENABLED; -+ iommu->exclusion_allow_all = all; - iommu->exclusion_base = base; - iommu->exclusion_limit = limit; --} - --static void __init reserve_iommu_exclusion_range_all( -- struct amd_iommu *iommu, -- unsigned long base, unsigned long limit) --{ -- reserve_iommu_exclusion_range(iommu, base, limit); -- iommu->exclusion_allow_all = IOMMU_CONTROL_ENABLED; -+ return 0; - } - - static void __init reserve_unity_map_for_device( -@@ -176,6 +180,7 @@ static int __init register_exclusion_ran - unsigned long range_top, iommu_top, length; - struct amd_iommu *iommu; - unsigned int bdf; -+ int rc = 0; - - /* is part of exclusion range inside of IOMMU virtual address space? */ - /* note: 'limit' parameter is assumed to be page-aligned */ -@@ -197,10 +202,15 @@ static int __init register_exclusion_ran - if ( limit >= iommu_top ) - { - for_each_amd_iommu( iommu ) -- reserve_iommu_exclusion_range_all(iommu, base, limit); -+ { -+ rc = reserve_iommu_exclusion_range(iommu, base, limit, -+ true /* all */, iw, ir); -+ if ( rc ) -+ break; -+ } - } - -- return 0; -+ return rc; - } - - static int __init register_exclusion_range_for_device( -@@ -211,6 +221,7 @@ static int __init register_exclusion_ran - unsigned long range_top, iommu_top, length; - struct amd_iommu *iommu; - u16 req; -+ int rc = 0; - - iommu = find_iommu_for_device(seg, bdf); - if ( !iommu ) -@@ -240,12 +251,13 @@ static int __init register_exclusion_ran - /* register IOMMU exclusion range settings for device */ - if ( limit >= iommu_top ) - { -- reserve_iommu_exclusion_range(iommu, base, limit); -+ rc = reserve_iommu_exclusion_range(iommu, base, limit, -+ false /* all */, iw, ir); - ivrs_mappings[bdf].dte_allow_exclusion = true; - ivrs_mappings[req].dte_allow_exclusion = true; - } - -- return 0; -+ return rc; - } - - static int __init register_exclusion_range_for_iommu_devices( -@@ -255,6 +267,7 @@ static int __init register_exclusion_ran - unsigned long range_top, iommu_top, length; - unsigned int bdf; - u16 req; -+ int rc = 0; - - /* is part of exclusion range inside of IOMMU virtual address space? */ - /* note: 'limit' parameter is assumed to be page-aligned */ -@@ -285,8 +298,10 @@ static int __init register_exclusion_ran - - /* register IOMMU exclusion range settings */ - if ( limit >= iommu_top ) -- reserve_iommu_exclusion_range_all(iommu, base, limit); -- return 0; -+ rc = reserve_iommu_exclusion_range(iommu, base, limit, -+ true /* all */, iw, ir); -+ -+ return rc; - } - - static int __init parse_ivmd_device_select( diff --git a/xsa378-4.15-2.patch b/xsa378-4.15-2.patch deleted file mode 100644 index aa19772..0000000 --- a/xsa378-4.15-2.patch +++ /dev/null @@ -1,218 +0,0 @@ -From: Jan Beulich -Subject: AMD/IOMMU: correct device unity map handling - -Blindly assuming all addresses between any two such ranges, specified by -firmware in the ACPI tables, should also be unity-mapped can't be right. -Nor can it be correct to merge ranges with differing permissions. Track -ranges individually; don't merge at all, but check for overlaps instead. -This requires bubbling up error indicators, such that IOMMU init can be -failed when allocation of a new tracking struct wasn't possible, or an -overlap was detected. - -At this occasion also stop ignoring -amd_iommu_reserve_domain_unity_map()'s return value. - -This is part of XSA-378 / CVE-2021-28695. - -Signed-off-by: Jan Beulich -Reviewed-by: George Dunlap -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/amd/iommu.h -+++ b/xen/drivers/passthrough/amd/iommu.h -@@ -107,20 +107,24 @@ struct amd_iommu { - struct list_head ats_devices; - }; - -+struct ivrs_unity_map { -+ bool read:1; -+ bool write:1; -+ paddr_t addr; -+ unsigned long length; -+ struct ivrs_unity_map *next; -+}; -+ - struct ivrs_mappings { - uint16_t dte_requestor_id; - bool valid:1; - bool dte_allow_exclusion:1; -- bool unity_map_enable:1; -- bool write_permission:1; -- bool read_permission:1; - - /* ivhd device data settings */ - uint8_t device_flags; - -- unsigned long addr_range_start; -- unsigned long addr_range_length; - struct amd_iommu *iommu; -+ struct ivrs_unity_map *unity_map; - - /* per device interrupt remapping table */ - void *intremap_table; ---- a/xen/drivers/passthrough/amd/iommu_acpi.c -+++ b/xen/drivers/passthrough/amd/iommu_acpi.c -@@ -145,32 +145,48 @@ static int __init reserve_iommu_exclusio - return 0; - } - --static void __init reserve_unity_map_for_device( -- u16 seg, u16 bdf, unsigned long base, -- unsigned long length, u8 iw, u8 ir) -+static int __init reserve_unity_map_for_device( -+ uint16_t seg, uint16_t bdf, unsigned long base, -+ unsigned long length, bool iw, bool ir) - { - struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(seg); -- unsigned long old_top, new_top; -+ struct ivrs_unity_map *unity_map = ivrs_mappings[bdf].unity_map; - -- /* need to extend unity-mapped range? */ -- if ( ivrs_mappings[bdf].unity_map_enable ) -+ /* Check for overlaps. */ -+ for ( ; unity_map; unity_map = unity_map->next ) - { -- old_top = ivrs_mappings[bdf].addr_range_start + -- ivrs_mappings[bdf].addr_range_length; -- new_top = base + length; -- if ( old_top > new_top ) -- new_top = old_top; -- if ( ivrs_mappings[bdf].addr_range_start < base ) -- base = ivrs_mappings[bdf].addr_range_start; -- length = new_top - base; -- } -- -- /* extend r/w permissioms and keep aggregate */ -- ivrs_mappings[bdf].write_permission = iw; -- ivrs_mappings[bdf].read_permission = ir; -- ivrs_mappings[bdf].unity_map_enable = true; -- ivrs_mappings[bdf].addr_range_start = base; -- ivrs_mappings[bdf].addr_range_length = length; -+ /* -+ * Exact matches are okay. This can in particular happen when -+ * register_exclusion_range_for_device() calls here twice for the -+ * same (s,b,d,f). -+ */ -+ if ( base == unity_map->addr && length == unity_map->length && -+ ir == unity_map->read && iw == unity_map->write ) -+ return 0; -+ -+ if ( unity_map->addr + unity_map->length > base && -+ base + length > unity_map->addr ) -+ { -+ AMD_IOMMU_DEBUG("IVMD Error: overlap [%lx,%lx) vs [%lx,%lx)\n", -+ base, base + length, unity_map->addr, -+ unity_map->addr + unity_map->length); -+ return -EPERM; -+ } -+ } -+ -+ /* Populate and insert a new unity map. */ -+ unity_map = xmalloc(struct ivrs_unity_map); -+ if ( !unity_map ) -+ return -ENOMEM; -+ -+ unity_map->read = ir; -+ unity_map->write = iw; -+ unity_map->addr = base; -+ unity_map->length = length; -+ unity_map->next = ivrs_mappings[bdf].unity_map; -+ ivrs_mappings[bdf].unity_map = unity_map; -+ -+ return 0; - } - - static int __init register_exclusion_range_for_all_devices( -@@ -193,13 +209,13 @@ static int __init register_exclusion_ran - length = range_top - base; - /* reserve r/w unity-mapped page entries for devices */ - /* note: these entries are part of the exclusion range */ -- for ( bdf = 0; bdf < ivrs_bdf_entries; bdf++ ) -- reserve_unity_map_for_device(seg, bdf, base, length, iw, ir); -+ for ( bdf = 0; !rc && bdf < ivrs_bdf_entries; bdf++ ) -+ rc = reserve_unity_map_for_device(seg, bdf, base, length, iw, ir); - /* push 'base' just outside of virtual address space */ - base = iommu_top; - } - /* register IOMMU exclusion range settings */ -- if ( limit >= iommu_top ) -+ if ( !rc && limit >= iommu_top ) - { - for_each_amd_iommu( iommu ) - { -@@ -241,15 +257,15 @@ static int __init register_exclusion_ran - length = range_top - base; - /* reserve unity-mapped page entries for device */ - /* note: these entries are part of the exclusion range */ -- reserve_unity_map_for_device(seg, bdf, base, length, iw, ir); -- reserve_unity_map_for_device(seg, req, base, length, iw, ir); -+ rc = reserve_unity_map_for_device(seg, bdf, base, length, iw, ir) ?: -+ reserve_unity_map_for_device(seg, req, base, length, iw, ir); - - /* push 'base' just outside of virtual address space */ - base = iommu_top; - } - - /* register IOMMU exclusion range settings for device */ -- if ( limit >= iommu_top ) -+ if ( !rc && limit >= iommu_top ) - { - rc = reserve_iommu_exclusion_range(iommu, base, limit, - false /* all */, iw, ir); -@@ -280,15 +296,15 @@ static int __init register_exclusion_ran - length = range_top - base; - /* reserve r/w unity-mapped page entries for devices */ - /* note: these entries are part of the exclusion range */ -- for ( bdf = 0; bdf < ivrs_bdf_entries; bdf++ ) -+ for ( bdf = 0; !rc && bdf < ivrs_bdf_entries; bdf++ ) - { - if ( iommu == find_iommu_for_device(iommu->seg, bdf) ) - { -- reserve_unity_map_for_device(iommu->seg, bdf, base, length, -- iw, ir); - req = get_ivrs_mappings(iommu->seg)[bdf].dte_requestor_id; -- reserve_unity_map_for_device(iommu->seg, req, base, length, -- iw, ir); -+ rc = reserve_unity_map_for_device(iommu->seg, bdf, base, length, -+ iw, ir) ?: -+ reserve_unity_map_for_device(iommu->seg, req, base, length, -+ iw, ir); - } - } - -@@ -297,7 +313,7 @@ static int __init register_exclusion_ran - } - - /* register IOMMU exclusion range settings */ -- if ( limit >= iommu_top ) -+ if ( !rc && limit >= iommu_top ) - rc = reserve_iommu_exclusion_range(iommu, base, limit, - true /* all */, iw, ir); - ---- a/xen/drivers/passthrough/amd/pci_amd_iommu.c -+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c -@@ -367,15 +367,17 @@ static int amd_iommu_assign_device(struc - struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(pdev->seg); - int bdf = PCI_BDF2(pdev->bus, devfn); - int req_id = get_dma_requestor_id(pdev->seg, bdf); -+ const struct ivrs_unity_map *unity_map; - -- if ( ivrs_mappings[req_id].unity_map_enable ) -+ for ( unity_map = ivrs_mappings[req_id].unity_map; unity_map; -+ unity_map = unity_map->next ) - { -- amd_iommu_reserve_domain_unity_map( -- d, -- ivrs_mappings[req_id].addr_range_start, -- ivrs_mappings[req_id].addr_range_length, -- ivrs_mappings[req_id].write_permission, -- ivrs_mappings[req_id].read_permission); -+ int rc = amd_iommu_reserve_domain_unity_map( -+ d, unity_map->addr, unity_map->length, -+ unity_map->write, unity_map->read); -+ -+ if ( rc ) -+ return rc; - } - - return reassign_device(pdev->domain, d, devfn, pdev); diff --git a/xsa378-4.15-3.patch b/xsa378-4.15-3.patch deleted file mode 100644 index 9e1bd18..0000000 --- a/xsa378-4.15-3.patch +++ /dev/null @@ -1,102 +0,0 @@ -From: Jan Beulich -Subject: IOMMU: also pass p2m_access_t to p2m_get_iommu_flags() - -A subsequent change will want to customize the IOMMU permissions based -on this. - -This is part of XSA-378. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/arch/x86/mm/p2m-ept.c -+++ b/xen/arch/x86/mm/p2m-ept.c -@@ -681,7 +681,7 @@ ept_set_entry(struct p2m_domain *p2m, gf - uint8_t ipat = 0; - bool_t need_modify_vtd_table = 1; - bool_t vtd_pte_present = 0; -- unsigned int iommu_flags = p2m_get_iommu_flags(p2mt, mfn); -+ unsigned int iommu_flags = p2m_get_iommu_flags(p2mt, p2ma, mfn); - bool_t needs_sync = 1; - ept_entry_t old_entry = { .epte = 0 }; - ept_entry_t new_entry = { .epte = 0 }; -@@ -809,8 +809,8 @@ ept_set_entry(struct p2m_domain *p2m, gf - - /* Safe to read-then-write because we hold the p2m lock */ - if ( ept_entry->mfn == new_entry.mfn && -- p2m_get_iommu_flags(ept_entry->sa_p2mt, _mfn(ept_entry->mfn)) == -- iommu_flags ) -+ p2m_get_iommu_flags(ept_entry->sa_p2mt, ept_entry->access, -+ _mfn(ept_entry->mfn)) == iommu_flags ) - need_modify_vtd_table = 0; - - ept_p2m_type_to_flags(p2m, &new_entry); ---- a/xen/arch/x86/mm/p2m-pt.c -+++ b/xen/arch/x86/mm/p2m-pt.c -@@ -545,6 +545,16 @@ int p2m_pt_handle_deferred_changes(uint6 - return rc; - } - -+/* Reconstruct a fake p2m_access_t from stored PTE flags. */ -+static p2m_access_t p2m_flags_to_access(unsigned int flags) -+{ -+ if ( flags & _PAGE_PRESENT ) -+ return p2m_access_n; -+ -+ /* No need to look at _PAGE_NX for now. */ -+ return flags & _PAGE_RW ? p2m_access_rw : p2m_access_r; -+} -+ - /* Checks only applicable to entries with order > PAGE_ORDER_4K */ - static void check_entry(mfn_t mfn, p2m_type_t new, p2m_type_t old, - unsigned int order) -@@ -579,7 +589,7 @@ p2m_pt_set_entry(struct p2m_domain *p2m, - l2_pgentry_t l2e_content; - l3_pgentry_t l3e_content; - int rc; -- unsigned int iommu_pte_flags = p2m_get_iommu_flags(p2mt, mfn); -+ unsigned int iommu_pte_flags = p2m_get_iommu_flags(p2mt, p2ma, mfn); - /* - * old_mfn and iommu_old_flags control possible flush/update needs on the - * IOMMU: We need to flush when MFN or flags (i.e. permissions) change. -@@ -642,6 +652,7 @@ p2m_pt_set_entry(struct p2m_domain *p2m, - old_mfn = l1e_get_pfn(*p2m_entry); - iommu_old_flags = - p2m_get_iommu_flags(p2m_flags_to_type(flags), -+ p2m_flags_to_access(flags), - _mfn(old_mfn)); - } - else -@@ -684,9 +695,10 @@ p2m_pt_set_entry(struct p2m_domain *p2m, - 0, L1_PAGETABLE_ENTRIES); - ASSERT(p2m_entry); - old_mfn = l1e_get_pfn(*p2m_entry); -+ flags = l1e_get_flags(*p2m_entry); - iommu_old_flags = -- p2m_get_iommu_flags(p2m_flags_to_type(l1e_get_flags(*p2m_entry)), -- _mfn(old_mfn)); -+ p2m_get_iommu_flags(p2m_flags_to_type(flags), -+ p2m_flags_to_access(flags), _mfn(old_mfn)); - - if ( mfn_valid(mfn) || p2m_allows_invalid_mfn(p2mt) ) - entry_content = p2m_l1e_from_pfn(mfn_x(mfn), -@@ -714,6 +726,7 @@ p2m_pt_set_entry(struct p2m_domain *p2m, - old_mfn = l1e_get_pfn(*p2m_entry); - iommu_old_flags = - p2m_get_iommu_flags(p2m_flags_to_type(flags), -+ p2m_flags_to_access(flags), - _mfn(old_mfn)); - } - else ---- a/xen/include/asm-x86/p2m.h -+++ b/xen/include/asm-x86/p2m.h -@@ -915,7 +915,8 @@ static inline void p2m_altp2m_check(stru - /* - * p2m type to IOMMU flags - */ --static inline unsigned int p2m_get_iommu_flags(p2m_type_t p2mt, mfn_t mfn) -+static inline unsigned int p2m_get_iommu_flags(p2m_type_t p2mt, -+ p2m_access_t p2ma, mfn_t mfn) - { - unsigned int flags; - diff --git a/xsa378-4.15-4.patch b/xsa378-4.15-4.patch deleted file mode 100644 index a1d86db..0000000 --- a/xsa378-4.15-4.patch +++ /dev/null @@ -1,399 +0,0 @@ -From: Jan Beulich -Subject: IOMMU: generalize VT-d's tracking of mapped RMRR regions - -In order to re-use it elsewhere, move the logic to vendor independent -code and strip it of RMRR specifics. - -Note that the prior "map" parameter gets folded into the new "p2ma" one -(which AMD IOMMU code will want to make use of), assigning alternative -meaning ("unmap") to p2m_access_x. Prepare set_identity_p2m_entry() and -p2m_get_iommu_flags() for getting passed access types other than -p2m_access_rw (in the latter case just for p2m_mmio_direct requests). - -Note also that, to be on the safe side, an overlap check gets added to -the main loop of iommu_identity_mapping(). - -This is part of XSA-378. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/arch/x86/mm/p2m.c -+++ b/xen/arch/x86/mm/p2m.c -@@ -1365,7 +1365,7 @@ int set_identity_p2m_entry(struct domain - return 0; - return iommu_legacy_map(d, _dfn(gfn_l), _mfn(gfn_l), - 1ul << PAGE_ORDER_4K, -- IOMMUF_readable | IOMMUF_writable); -+ p2m_access_to_iommu_flags(p2ma)); - } - - gfn_lock(p2m, gfn, 0); ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -42,12 +42,6 @@ - #include "vtd.h" - #include "../ats.h" - --struct mapped_rmrr { -- struct list_head list; -- u64 base, end; -- unsigned int count; --}; -- - /* Possible unfiltered LAPIC/MSI messages from untrusted sources? */ - bool __read_mostly untrusted_msi; - -@@ -1311,7 +1305,6 @@ static int intel_iommu_domain_init(struc - struct domain_iommu *hd = dom_iommu(d); - - hd->arch.vtd.agaw = width_to_agaw(DEFAULT_DOMAIN_ADDRESS_WIDTH); -- INIT_LIST_HEAD(&hd->arch.vtd.mapped_rmrrs); - - return 0; - } -@@ -1788,17 +1781,12 @@ static void iommu_clear_root_pgtable(str - static void iommu_domain_teardown(struct domain *d) - { - struct domain_iommu *hd = dom_iommu(d); -- struct mapped_rmrr *mrmrr, *tmp; - const struct acpi_drhd_unit *drhd; - - if ( list_empty(&acpi_drhd_units) ) - return; - -- list_for_each_entry_safe ( mrmrr, tmp, &hd->arch.vtd.mapped_rmrrs, list ) -- { -- list_del(&mrmrr->list); -- xfree(mrmrr); -- } -+ iommu_identity_map_teardown(d); - - ASSERT(!hd->arch.vtd.pgd_maddr); - -@@ -1946,74 +1934,6 @@ static int __init vtd_ept_page_compatibl - (ept_has_1gb(ept_cap) && opt_hap_1gb) <= cap_sps_1gb(vtd_cap); - } - --static int rmrr_identity_mapping(struct domain *d, bool_t map, -- const struct acpi_rmrr_unit *rmrr, -- u32 flag) --{ -- unsigned long base_pfn = rmrr->base_address >> PAGE_SHIFT_4K; -- unsigned long end_pfn = PAGE_ALIGN_4K(rmrr->end_address) >> PAGE_SHIFT_4K; -- struct mapped_rmrr *mrmrr; -- struct domain_iommu *hd = dom_iommu(d); -- -- ASSERT(pcidevs_locked()); -- ASSERT(rmrr->base_address < rmrr->end_address); -- -- /* -- * No need to acquire hd->arch.mapping_lock: Both insertion and removal -- * get done while holding pcidevs_lock. -- */ -- list_for_each_entry( mrmrr, &hd->arch.vtd.mapped_rmrrs, list ) -- { -- if ( mrmrr->base == rmrr->base_address && -- mrmrr->end == rmrr->end_address ) -- { -- int ret = 0; -- -- if ( map ) -- { -- ++mrmrr->count; -- return 0; -- } -- -- if ( --mrmrr->count ) -- return 0; -- -- while ( base_pfn < end_pfn ) -- { -- if ( clear_identity_p2m_entry(d, base_pfn) ) -- ret = -ENXIO; -- base_pfn++; -- } -- -- list_del(&mrmrr->list); -- xfree(mrmrr); -- return ret; -- } -- } -- -- if ( !map ) -- return -ENOENT; -- -- while ( base_pfn < end_pfn ) -- { -- int err = set_identity_p2m_entry(d, base_pfn, p2m_access_rw, flag); -- -- if ( err ) -- return err; -- base_pfn++; -- } -- -- mrmrr = xmalloc(struct mapped_rmrr); -- if ( !mrmrr ) -- return -ENOMEM; -- mrmrr->base = rmrr->base_address; -- mrmrr->end = rmrr->end_address; -- mrmrr->count = 1; -- list_add_tail(&mrmrr->list, &hd->arch.vtd.mapped_rmrrs); -- -- return 0; --} -- - static int intel_iommu_add_device(u8 devfn, struct pci_dev *pdev) - { - struct acpi_rmrr_unit *rmrr; -@@ -2045,7 +1965,9 @@ static int intel_iommu_add_device(u8 dev - * Since RMRRs are always reserved in the e820 map for the hardware - * domain, there shouldn't be a conflict. - */ -- ret = rmrr_identity_mapping(pdev->domain, 1, rmrr, 0); -+ ret = iommu_identity_mapping(pdev->domain, p2m_access_rw, -+ rmrr->base_address, rmrr->end_address, -+ 0); - if ( ret ) - dprintk(XENLOG_ERR VTDPREFIX, "d%d: RMRR mapping failed\n", - pdev->domain->domain_id); -@@ -2090,7 +2012,8 @@ static int intel_iommu_remove_device(u8 - * Any flag is nothing to clear these mappings but here - * its always safe and strict to set 0. - */ -- rmrr_identity_mapping(pdev->domain, 0, rmrr, 0); -+ iommu_identity_mapping(pdev->domain, p2m_access_x, rmrr->base_address, -+ rmrr->end_address, 0); - } - - return domain_context_unmap(pdev->domain, devfn, pdev); -@@ -2289,7 +2212,8 @@ static void __hwdom_init setup_hwdom_rmr - * domain, there shouldn't be a conflict. So its always safe and - * strict to set 0. - */ -- ret = rmrr_identity_mapping(d, 1, rmrr, 0); -+ ret = iommu_identity_mapping(d, p2m_access_rw, rmrr->base_address, -+ rmrr->end_address, 0); - if ( ret ) - dprintk(XENLOG_ERR VTDPREFIX, - "IOMMU: mapping reserved region failed\n"); -@@ -2460,7 +2384,9 @@ static int reassign_device_ownership( - * Any RMRR flag is always ignored when remove a device, - * but its always safe and strict to set 0. - */ -- ret = rmrr_identity_mapping(source, 0, rmrr, 0); -+ ret = iommu_identity_mapping(source, p2m_access_x, -+ rmrr->base_address, -+ rmrr->end_address, 0); - if ( ret != -ENOENT ) - return ret; - } -@@ -2556,7 +2482,8 @@ static int intel_iommu_assign_device( - PCI_BUS(bdf) == bus && - PCI_DEVFN2(bdf) == devfn ) - { -- ret = rmrr_identity_mapping(d, 1, rmrr, flag); -+ ret = iommu_identity_mapping(d, p2m_access_rw, rmrr->base_address, -+ rmrr->end_address, flag); - if ( ret ) - { - int rc; ---- a/xen/drivers/passthrough/x86/iommu.c -+++ b/xen/drivers/passthrough/x86/iommu.c -@@ -143,6 +143,7 @@ int arch_iommu_domain_init(struct domain - - INIT_PAGE_LIST_HEAD(&hd->arch.pgtables.list); - spin_lock_init(&hd->arch.pgtables.lock); -+ INIT_LIST_HEAD(&hd->arch.identity_maps); - - return 0; - } -@@ -158,6 +159,99 @@ void arch_iommu_domain_destroy(struct do - page_list_empty(&dom_iommu(d)->arch.pgtables.list)); - } - -+struct identity_map { -+ struct list_head list; -+ paddr_t base, end; -+ p2m_access_t access; -+ unsigned int count; -+}; -+ -+int iommu_identity_mapping(struct domain *d, p2m_access_t p2ma, -+ paddr_t base, paddr_t end, -+ unsigned int flag) -+{ -+ unsigned long base_pfn = base >> PAGE_SHIFT_4K; -+ unsigned long end_pfn = PAGE_ALIGN_4K(end) >> PAGE_SHIFT_4K; -+ struct identity_map *map; -+ struct domain_iommu *hd = dom_iommu(d); -+ -+ ASSERT(pcidevs_locked()); -+ ASSERT(base < end); -+ -+ /* -+ * No need to acquire hd->arch.mapping_lock: Both insertion and removal -+ * get done while holding pcidevs_lock. -+ */ -+ list_for_each_entry( map, &hd->arch.identity_maps, list ) -+ { -+ if ( map->base == base && map->end == end ) -+ { -+ int ret = 0; -+ -+ if ( p2ma != p2m_access_x ) -+ { -+ if ( map->access != p2ma ) -+ return -EADDRINUSE; -+ ++map->count; -+ return 0; -+ } -+ -+ if ( --map->count ) -+ return 0; -+ -+ while ( base_pfn < end_pfn ) -+ { -+ if ( clear_identity_p2m_entry(d, base_pfn) ) -+ ret = -ENXIO; -+ base_pfn++; -+ } -+ -+ list_del(&map->list); -+ xfree(map); -+ -+ return ret; -+ } -+ -+ if ( end >= map->base && map->end >= base ) -+ return -EADDRINUSE; -+ } -+ -+ if ( p2ma == p2m_access_x ) -+ return -ENOENT; -+ -+ while ( base_pfn < end_pfn ) -+ { -+ int err = set_identity_p2m_entry(d, base_pfn, p2ma, flag); -+ -+ if ( err ) -+ return err; -+ base_pfn++; -+ } -+ -+ map = xmalloc(struct identity_map); -+ if ( !map ) -+ return -ENOMEM; -+ map->base = base; -+ map->end = end; -+ map->access = p2ma; -+ map->count = 1; -+ list_add_tail(&map->list, &hd->arch.identity_maps); -+ -+ return 0; -+} -+ -+void iommu_identity_map_teardown(struct domain *d) -+{ -+ struct domain_iommu *hd = dom_iommu(d); -+ struct identity_map *map, *tmp; -+ -+ list_for_each_entry_safe ( map, tmp, &hd->arch.identity_maps, list ) -+ { -+ list_del(&map->list); -+ xfree(map); -+ } -+} -+ - static bool __hwdom_init hwdom_iommu_map(const struct domain *d, - unsigned long pfn, - unsigned long max_pfn) ---- a/xen/include/asm-x86/iommu.h -+++ b/xen/include/asm-x86/iommu.h -@@ -16,6 +16,7 @@ - - #include - #include -+#include - #include - #include - #include -@@ -51,13 +52,14 @@ struct arch_iommu - spinlock_t lock; - } pgtables; - -+ struct list_head identity_maps; -+ - union { - /* Intel VT-d */ - struct { - uint64_t pgd_maddr; /* io page directory machine address */ - unsigned int agaw; /* adjusted guest address width, 0 is level 2 30-bit */ - uint64_t iommu_bitmap; /* bitmap of iommu(s) that the domain uses */ -- struct list_head mapped_rmrrs; - } vtd; - /* AMD IOMMU */ - struct { -@@ -123,6 +125,11 @@ static inline void iommu_disable_x2apic( - iommu_ops.disable_x2apic(); - } - -+int iommu_identity_mapping(struct domain *d, p2m_access_t p2ma, -+ paddr_t base, paddr_t end, -+ unsigned int flag); -+void iommu_identity_map_teardown(struct domain *d); -+ - extern bool untrusted_msi; - - int pi_update_irte(const struct pi_desc *pi_desc, const struct pirq *pirq, ---- a/xen/include/asm-x86/p2m.h -+++ b/xen/include/asm-x86/p2m.h -@@ -912,6 +912,34 @@ struct p2m_domain *p2m_get_altp2m(struct - static inline void p2m_altp2m_check(struct vcpu *v, uint16_t idx) {} - #endif - -+/* p2m access to IOMMU flags */ -+static inline unsigned int p2m_access_to_iommu_flags(p2m_access_t p2ma) -+{ -+ switch ( p2ma ) -+ { -+ case p2m_access_rw: -+ case p2m_access_rwx: -+ return IOMMUF_readable | IOMMUF_writable; -+ -+ case p2m_access_r: -+ case p2m_access_rx: -+ case p2m_access_rx2rw: -+ return IOMMUF_readable; -+ -+ case p2m_access_w: -+ case p2m_access_wx: -+ return IOMMUF_writable; -+ -+ case p2m_access_n: -+ case p2m_access_x: -+ case p2m_access_n2rwx: -+ return 0; -+ } -+ -+ ASSERT_UNREACHABLE(); -+ return 0; -+} -+ - /* - * p2m type to IOMMU flags - */ -@@ -933,9 +961,10 @@ static inline unsigned int p2m_get_iommu - flags = IOMMUF_readable; - break; - case p2m_mmio_direct: -- flags = IOMMUF_readable; -- if ( !rangeset_contains_singleton(mmio_ro_ranges, mfn_x(mfn)) ) -- flags |= IOMMUF_writable; -+ flags = p2m_access_to_iommu_flags(p2ma); -+ if ( (flags & IOMMUF_writable) && -+ rangeset_contains_singleton(mmio_ro_ranges, mfn_x(mfn)) ) -+ flags &= ~IOMMUF_writable; - break; - default: - flags = 0; diff --git a/xsa378-4.15-5.patch b/xsa378-4.15-5.patch deleted file mode 100644 index 134f425..0000000 --- a/xsa378-4.15-5.patch +++ /dev/null @@ -1,208 +0,0 @@ -From: Jan Beulich -Subject: AMD/IOMMU: re-arrange/complete re-assignment handling - -Prior to the assignment step having completed successfully, devices -should not get associated with their new owner. Hand the device to DomIO -(perhaps temporarily), until after the de-assignment step has completed. - -De-assignment of a device (from other than Dom0) as well as failure of -reassign_device() during assignment should result in unity mappings -getting torn down. This in turn requires switching to a refcounted -mapping approach, as was already used by VT-d for its RMRRs, to prevent -unmapping a region used by multiple devices. - -This is CVE-2021-28696 / part of XSA-378. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/amd/iommu.h -+++ b/xen/drivers/passthrough/amd/iommu.h -@@ -232,8 +232,10 @@ int __must_check amd_iommu_unmap_page(st - unsigned int *flush_flags); - int __must_check amd_iommu_alloc_root(struct domain *d); - int amd_iommu_reserve_domain_unity_map(struct domain *domain, -- paddr_t phys_addr, unsigned long size, -- int iw, int ir); -+ const struct ivrs_unity_map *map, -+ unsigned int flag); -+int amd_iommu_reserve_domain_unity_unmap(struct domain *d, -+ const struct ivrs_unity_map *map); - int __must_check amd_iommu_flush_iotlb_pages(struct domain *d, dfn_t dfn, - unsigned long page_count, - unsigned int flush_flags); ---- a/xen/drivers/passthrough/amd/iommu_map.c -+++ b/xen/drivers/passthrough/amd/iommu_map.c -@@ -419,38 +419,49 @@ int amd_iommu_flush_iotlb_all(struct dom - return 0; - } - --int amd_iommu_reserve_domain_unity_map(struct domain *domain, -- paddr_t phys_addr, -- unsigned long size, int iw, int ir) -+int amd_iommu_reserve_domain_unity_map(struct domain *d, -+ const struct ivrs_unity_map *map, -+ unsigned int flag) - { -- unsigned long npages, i; -- unsigned long gfn; -- unsigned int flags = !!ir; -- unsigned int flush_flags = 0; -- int rt = 0; -- -- if ( iw ) -- flags |= IOMMUF_writable; -- -- npages = region_to_pages(phys_addr, size); -- gfn = phys_addr >> PAGE_SHIFT; -- for ( i = 0; i < npages; i++ ) -+ int rc; -+ -+ if ( d == dom_io ) -+ return 0; -+ -+ for ( rc = 0; !rc && map; map = map->next ) - { -- unsigned long frame = gfn + i; -+ p2m_access_t p2ma = p2m_access_n; -+ -+ if ( map->read ) -+ p2ma |= p2m_access_r; -+ if ( map->write ) -+ p2ma |= p2m_access_w; - -- rt = amd_iommu_map_page(domain, _dfn(frame), _mfn(frame), flags, -- &flush_flags); -- if ( rt != 0 ) -- break; -+ rc = iommu_identity_mapping(d, p2ma, map->addr, -+ map->addr + map->length - 1, flag); - } - -- /* Use while-break to avoid compiler warning */ -- while ( flush_flags && -- amd_iommu_flush_iotlb_pages(domain, _dfn(gfn), -- npages, flush_flags) ) -- break; -+ return rc; -+} -+ -+int amd_iommu_reserve_domain_unity_unmap(struct domain *d, -+ const struct ivrs_unity_map *map) -+{ -+ int rc; -+ -+ if ( d == dom_io ) -+ return 0; -+ -+ for ( rc = 0; map; map = map->next ) -+ { -+ int ret = iommu_identity_mapping(d, p2m_access_x, map->addr, -+ map->addr + map->length - 1, 0); -+ -+ if ( ret && ret != -ENOENT && !rc ) -+ rc = ret; -+ } - -- return rt; -+ return rc; - } - - int __init amd_iommu_quarantine_init(struct domain *d) ---- a/xen/drivers/passthrough/amd/pci_amd_iommu.c -+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c -@@ -329,6 +329,7 @@ static int reassign_device(struct domain - { - struct amd_iommu *iommu; - int bdf, rc; -+ const struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(pdev->seg); - - bdf = PCI_BDF2(pdev->bus, pdev->devfn); - iommu = find_iommu_for_device(pdev->seg, bdf); -@@ -343,10 +344,24 @@ static int reassign_device(struct domain - - amd_iommu_disable_domain_device(source, iommu, devfn, pdev); - -- if ( devfn == pdev->devfn ) -+ /* -+ * If the device belongs to the hardware domain, and it has a unity mapping, -+ * don't remove it from the hardware domain, because BIOS may reference that -+ * mapping. -+ */ -+ if ( !is_hardware_domain(source) ) - { -- list_move(&pdev->domain_list, &target->pdev_list); -- pdev->domain = target; -+ rc = amd_iommu_reserve_domain_unity_unmap( -+ source, -+ ivrs_mappings[get_dma_requestor_id(pdev->seg, bdf)].unity_map); -+ if ( rc ) -+ return rc; -+ } -+ -+ if ( devfn == pdev->devfn && pdev->domain != dom_io ) -+ { -+ list_move(&pdev->domain_list, &dom_io->pdev_list); -+ pdev->domain = dom_io; - } - - rc = allocate_domain_resources(target); -@@ -357,6 +372,12 @@ static int reassign_device(struct domain - AMD_IOMMU_DEBUG("Re-assign %pp from dom%d to dom%d\n", - &pdev->sbdf, source->domain_id, target->domain_id); - -+ if ( devfn == pdev->devfn && pdev->domain != target ) -+ { -+ list_move(&pdev->domain_list, &target->pdev_list); -+ pdev->domain = target; -+ } -+ - return 0; - } - -@@ -367,20 +388,28 @@ static int amd_iommu_assign_device(struc - struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(pdev->seg); - int bdf = PCI_BDF2(pdev->bus, devfn); - int req_id = get_dma_requestor_id(pdev->seg, bdf); -- const struct ivrs_unity_map *unity_map; -+ int rc = amd_iommu_reserve_domain_unity_map( -+ d, ivrs_mappings[req_id].unity_map, flag); - -- for ( unity_map = ivrs_mappings[req_id].unity_map; unity_map; -- unity_map = unity_map->next ) -+ if ( !rc ) -+ rc = reassign_device(pdev->domain, d, devfn, pdev); -+ -+ if ( rc && !is_hardware_domain(d) ) - { -- int rc = amd_iommu_reserve_domain_unity_map( -- d, unity_map->addr, unity_map->length, -- unity_map->write, unity_map->read); -+ int ret = amd_iommu_reserve_domain_unity_unmap( -+ d, ivrs_mappings[req_id].unity_map); - -- if ( rc ) -- return rc; -+ if ( ret ) -+ { -+ printk(XENLOG_ERR "AMD-Vi: " -+ "unity-unmap for %pd/%04x:%02x:%02x.%u failed (%d)\n", -+ d, pdev->seg, pdev->bus, -+ PCI_SLOT(devfn), PCI_FUNC(devfn), ret); -+ domain_crash(d); -+ } - } - -- return reassign_device(pdev->domain, d, devfn, pdev); -+ return rc; - } - - static void amd_iommu_clear_root_pgtable(struct domain *d) -@@ -394,6 +423,7 @@ static void amd_iommu_clear_root_pgtable - - static void amd_iommu_domain_destroy(struct domain *d) - { -+ iommu_identity_map_teardown(d); - ASSERT(!dom_iommu(d)->arch.amd.root_table); - } - diff --git a/xsa378-4.15-6.patch b/xsa378-4.15-6.patch deleted file mode 100644 index d6ebc2a..0000000 --- a/xsa378-4.15-6.patch +++ /dev/null @@ -1,411 +0,0 @@ -From: Jan Beulich -Subject: AMD/IOMMU: re-arrange exclusion range and unity map recording - -The spec makes no provisions for OS behavior here to depend on the -amount of RAM found on the system. While the spec may not sufficiently -clearly distinguish both kinds of regions, they are surely meant to be -separate things: Only regions with ACPI_IVMD_EXCLUSION_RANGE set should -be candidates for putting in the exclusion range registers. (As there's -only a single such pair of registers per IOMMU, secondary non-adjacent -regions with the flag set already get converted to unity mapped -regions.) - -First of all, drop the dependency on max_page. With commit b4f042236ae0 -("AMD/IOMMU: Cease using a dynamic height for the IOMMU pagetables") the -use of it here was stale anyway; it was bogus already before, as it -didn't account for max_page getting increased later on. Simply try an -exclusion range registration first, and if it fails (for being -unsuitable or non-mergeable), register a unity mapping range. - -With this various local variables become unnecessary and hence get -dropped at the same time. - -With the max_page boundary dropped for using unity maps, the minimum -page table tree height now needs both recording and enforcing in -amd_iommu_domain_init(). Since we can't predict which devices may get -assigned to a domain, our only option is to uniformly force at least -that height for all domains, now that the height isn't dynamic anymore. - -Further don't make use of the exclusion range unless ACPI data says so. - -Note that exclusion range registration in -register_range_for_all_devices() is on a best effort basis. Hence unity -map entries also registered are redundant when the former succeeded, but -they also do no harm. Improvements in this area can be done later imo. - -Also adjust types where suitable without touching extra lines. - -This is part of XSA-378. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/amd/iommu.h -+++ b/xen/drivers/passthrough/amd/iommu.h -@@ -304,6 +304,8 @@ extern struct hpet_sbdf { - } init; - } hpet_sbdf; - -+extern int amd_iommu_min_paging_mode; -+ - extern void *shared_intremap_table; - extern unsigned long *shared_intremap_inuse; - ---- a/xen/drivers/passthrough/amd/iommu_acpi.c -+++ b/xen/drivers/passthrough/amd/iommu_acpi.c -@@ -117,12 +117,8 @@ static struct amd_iommu * __init find_io - } - - static int __init reserve_iommu_exclusion_range( -- struct amd_iommu *iommu, uint64_t base, uint64_t limit, -- bool all, bool iw, bool ir) -+ struct amd_iommu *iommu, paddr_t base, paddr_t limit, bool all) - { -- if ( !ir || !iw ) -- return -EPERM; -- - /* need to extend exclusion range? */ - if ( iommu->exclusion_enable ) - { -@@ -151,14 +147,18 @@ static int __init reserve_unity_map_for_ - { - struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(seg); - struct ivrs_unity_map *unity_map = ivrs_mappings[bdf].unity_map; -+ int paging_mode = amd_iommu_get_paging_mode(PFN_UP(base + length)); -+ -+ if ( paging_mode < 0 ) -+ return paging_mode; - - /* Check for overlaps. */ - for ( ; unity_map; unity_map = unity_map->next ) - { - /* - * Exact matches are okay. This can in particular happen when -- * register_exclusion_range_for_device() calls here twice for the -- * same (s,b,d,f). -+ * register_range_for_device() calls here twice for the same -+ * (s,b,d,f). - */ - if ( base == unity_map->addr && length == unity_map->length && - ir == unity_map->read && iw == unity_map->write ) -@@ -186,55 +186,52 @@ static int __init reserve_unity_map_for_ - unity_map->next = ivrs_mappings[bdf].unity_map; - ivrs_mappings[bdf].unity_map = unity_map; - -+ if ( paging_mode > amd_iommu_min_paging_mode ) -+ amd_iommu_min_paging_mode = paging_mode; -+ - return 0; - } - --static int __init register_exclusion_range_for_all_devices( -- unsigned long base, unsigned long limit, u8 iw, u8 ir) -+static int __init register_range_for_all_devices( -+ paddr_t base, paddr_t limit, bool iw, bool ir, bool exclusion) - { - int seg = 0; /* XXX */ -- unsigned long range_top, iommu_top, length; - struct amd_iommu *iommu; -- unsigned int bdf; - int rc = 0; - - /* is part of exclusion range inside of IOMMU virtual address space? */ - /* note: 'limit' parameter is assumed to be page-aligned */ -- range_top = limit + PAGE_SIZE; -- iommu_top = max_page * PAGE_SIZE; -- if ( base < iommu_top ) -- { -- if ( range_top > iommu_top ) -- range_top = iommu_top; -- length = range_top - base; -- /* reserve r/w unity-mapped page entries for devices */ -- /* note: these entries are part of the exclusion range */ -- for ( bdf = 0; !rc && bdf < ivrs_bdf_entries; bdf++ ) -- rc = reserve_unity_map_for_device(seg, bdf, base, length, iw, ir); -- /* push 'base' just outside of virtual address space */ -- base = iommu_top; -- } -- /* register IOMMU exclusion range settings */ -- if ( !rc && limit >= iommu_top ) -+ if ( exclusion ) - { - for_each_amd_iommu( iommu ) - { -- rc = reserve_iommu_exclusion_range(iommu, base, limit, -- true /* all */, iw, ir); -- if ( rc ) -- break; -+ int ret = reserve_iommu_exclusion_range(iommu, base, limit, -+ true /* all */); -+ -+ if ( ret && !rc ) -+ rc = ret; - } - } - -+ if ( !exclusion || rc ) -+ { -+ paddr_t length = limit + PAGE_SIZE - base; -+ unsigned int bdf; -+ -+ /* reserve r/w unity-mapped page entries for devices */ -+ for ( bdf = rc = 0; !rc && bdf < ivrs_bdf_entries; bdf++ ) -+ rc = reserve_unity_map_for_device(seg, bdf, base, length, iw, ir); -+ } -+ - return rc; - } - --static int __init register_exclusion_range_for_device( -- u16 bdf, unsigned long base, unsigned long limit, u8 iw, u8 ir) -+static int __init register_range_for_device( -+ unsigned int bdf, paddr_t base, paddr_t limit, -+ bool iw, bool ir, bool exclusion) - { - int seg = 0; /* XXX */ - struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(seg); -- unsigned long range_top, iommu_top, length; - struct amd_iommu *iommu; - u16 req; - int rc = 0; -@@ -248,27 +245,19 @@ static int __init register_exclusion_ran - req = ivrs_mappings[bdf].dte_requestor_id; - - /* note: 'limit' parameter is assumed to be page-aligned */ -- range_top = limit + PAGE_SIZE; -- iommu_top = max_page * PAGE_SIZE; -- if ( base < iommu_top ) -- { -- if ( range_top > iommu_top ) -- range_top = iommu_top; -- length = range_top - base; -+ if ( exclusion ) -+ rc = reserve_iommu_exclusion_range(iommu, base, limit, -+ false /* all */); -+ if ( !exclusion || rc ) -+ { -+ paddr_t length = limit + PAGE_SIZE - base; -+ - /* reserve unity-mapped page entries for device */ -- /* note: these entries are part of the exclusion range */ - rc = reserve_unity_map_for_device(seg, bdf, base, length, iw, ir) ?: - reserve_unity_map_for_device(seg, req, base, length, iw, ir); -- -- /* push 'base' just outside of virtual address space */ -- base = iommu_top; - } -- -- /* register IOMMU exclusion range settings for device */ -- if ( !rc && limit >= iommu_top ) -+ else - { -- rc = reserve_iommu_exclusion_range(iommu, base, limit, -- false /* all */, iw, ir); - ivrs_mappings[bdf].dte_allow_exclusion = true; - ivrs_mappings[req].dte_allow_exclusion = true; - } -@@ -276,53 +265,42 @@ static int __init register_exclusion_ran - return rc; - } - --static int __init register_exclusion_range_for_iommu_devices( -- struct amd_iommu *iommu, -- unsigned long base, unsigned long limit, u8 iw, u8 ir) -+static int __init register_range_for_iommu_devices( -+ struct amd_iommu *iommu, paddr_t base, paddr_t limit, -+ bool iw, bool ir, bool exclusion) - { -- unsigned long range_top, iommu_top, length; -+ /* note: 'limit' parameter is assumed to be page-aligned */ -+ paddr_t length = limit + PAGE_SIZE - base; - unsigned int bdf; - u16 req; -- int rc = 0; -+ int rc; - -- /* is part of exclusion range inside of IOMMU virtual address space? */ -- /* note: 'limit' parameter is assumed to be page-aligned */ -- range_top = limit + PAGE_SIZE; -- iommu_top = max_page * PAGE_SIZE; -- if ( base < iommu_top ) -- { -- if ( range_top > iommu_top ) -- range_top = iommu_top; -- length = range_top - base; -- /* reserve r/w unity-mapped page entries for devices */ -- /* note: these entries are part of the exclusion range */ -- for ( bdf = 0; !rc && bdf < ivrs_bdf_entries; bdf++ ) -- { -- if ( iommu == find_iommu_for_device(iommu->seg, bdf) ) -- { -- req = get_ivrs_mappings(iommu->seg)[bdf].dte_requestor_id; -- rc = reserve_unity_map_for_device(iommu->seg, bdf, base, length, -- iw, ir) ?: -- reserve_unity_map_for_device(iommu->seg, req, base, length, -- iw, ir); -- } -- } -- -- /* push 'base' just outside of virtual address space */ -- base = iommu_top; -+ if ( exclusion ) -+ { -+ rc = reserve_iommu_exclusion_range(iommu, base, limit, true /* all */); -+ if ( !rc ) -+ return 0; - } - -- /* register IOMMU exclusion range settings */ -- if ( !rc && limit >= iommu_top ) -- rc = reserve_iommu_exclusion_range(iommu, base, limit, -- true /* all */, iw, ir); -+ /* reserve unity-mapped page entries for devices */ -+ for ( bdf = rc = 0; !rc && bdf < ivrs_bdf_entries; bdf++ ) -+ { -+ if ( iommu != find_iommu_for_device(iommu->seg, bdf) ) -+ continue; -+ -+ req = get_ivrs_mappings(iommu->seg)[bdf].dte_requestor_id; -+ rc = reserve_unity_map_for_device(iommu->seg, bdf, base, length, -+ iw, ir) ?: -+ reserve_unity_map_for_device(iommu->seg, req, base, length, -+ iw, ir); -+ } - - return rc; - } - - static int __init parse_ivmd_device_select( - const struct acpi_ivrs_memory *ivmd_block, -- unsigned long base, unsigned long limit, u8 iw, u8 ir) -+ paddr_t base, paddr_t limit, bool iw, bool ir, bool exclusion) - { - u16 bdf; - -@@ -333,12 +311,12 @@ static int __init parse_ivmd_device_sele - return -ENODEV; - } - -- return register_exclusion_range_for_device(bdf, base, limit, iw, ir); -+ return register_range_for_device(bdf, base, limit, iw, ir, exclusion); - } - - static int __init parse_ivmd_device_range( - const struct acpi_ivrs_memory *ivmd_block, -- unsigned long base, unsigned long limit, u8 iw, u8 ir) -+ paddr_t base, paddr_t limit, bool iw, bool ir, bool exclusion) - { - unsigned int first_bdf, last_bdf, bdf; - int error; -@@ -360,15 +338,15 @@ static int __init parse_ivmd_device_rang - } - - for ( bdf = first_bdf, error = 0; (bdf <= last_bdf) && !error; bdf++ ) -- error = register_exclusion_range_for_device( -- bdf, base, limit, iw, ir); -+ error = register_range_for_device( -+ bdf, base, limit, iw, ir, exclusion); - - return error; - } - - static int __init parse_ivmd_device_iommu( - const struct acpi_ivrs_memory *ivmd_block, -- unsigned long base, unsigned long limit, u8 iw, u8 ir) -+ paddr_t base, paddr_t limit, bool iw, bool ir, bool exclusion) - { - int seg = 0; /* XXX */ - struct amd_iommu *iommu; -@@ -383,14 +361,14 @@ static int __init parse_ivmd_device_iomm - return -ENODEV; - } - -- return register_exclusion_range_for_iommu_devices( -- iommu, base, limit, iw, ir); -+ return register_range_for_iommu_devices( -+ iommu, base, limit, iw, ir, exclusion); - } - - static int __init parse_ivmd_block(const struct acpi_ivrs_memory *ivmd_block) - { - unsigned long start_addr, mem_length, base, limit; -- u8 iw, ir; -+ bool iw = true, ir = true, exclusion = false; - - if ( ivmd_block->header.length < sizeof(*ivmd_block) ) - { -@@ -407,13 +385,11 @@ static int __init parse_ivmd_block(const - ivmd_block->header.type, start_addr, mem_length); - - if ( ivmd_block->header.flags & ACPI_IVMD_EXCLUSION_RANGE ) -- iw = ir = IOMMU_CONTROL_ENABLED; -+ exclusion = true; - else if ( ivmd_block->header.flags & ACPI_IVMD_UNITY ) - { -- iw = ivmd_block->header.flags & ACPI_IVMD_READ ? -- IOMMU_CONTROL_ENABLED : IOMMU_CONTROL_DISABLED; -- ir = ivmd_block->header.flags & ACPI_IVMD_WRITE ? -- IOMMU_CONTROL_ENABLED : IOMMU_CONTROL_DISABLED; -+ iw = ivmd_block->header.flags & ACPI_IVMD_READ; -+ ir = ivmd_block->header.flags & ACPI_IVMD_WRITE; - } - else - { -@@ -424,20 +400,20 @@ static int __init parse_ivmd_block(const - switch( ivmd_block->header.type ) - { - case ACPI_IVRS_TYPE_MEMORY_ALL: -- return register_exclusion_range_for_all_devices( -- base, limit, iw, ir); -+ return register_range_for_all_devices( -+ base, limit, iw, ir, exclusion); - - case ACPI_IVRS_TYPE_MEMORY_ONE: -- return parse_ivmd_device_select(ivmd_block, -- base, limit, iw, ir); -+ return parse_ivmd_device_select(ivmd_block, base, limit, -+ iw, ir, exclusion); - - case ACPI_IVRS_TYPE_MEMORY_RANGE: -- return parse_ivmd_device_range(ivmd_block, -- base, limit, iw, ir); -+ return parse_ivmd_device_range(ivmd_block, base, limit, -+ iw, ir, exclusion); - - case ACPI_IVRS_TYPE_MEMORY_IOMMU: -- return parse_ivmd_device_iommu(ivmd_block, -- base, limit, iw, ir); -+ return parse_ivmd_device_iommu(ivmd_block, base, limit, -+ iw, ir, exclusion); - - default: - AMD_IOMMU_DEBUG("IVMD Error: Invalid Block Type!\n"); ---- a/xen/drivers/passthrough/amd/pci_amd_iommu.c -+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c -@@ -234,6 +234,8 @@ static int __must_check allocate_domain_ - return rc; - } - -+int __read_mostly amd_iommu_min_paging_mode = 1; -+ - static int amd_iommu_domain_init(struct domain *d) - { - struct domain_iommu *hd = dom_iommu(d); -@@ -245,11 +247,13 @@ static int amd_iommu_domain_init(struct - * - HVM could in principle use 3 or 4 depending on how much guest - * physical address space we give it, but this isn't known yet so use 4 - * unilaterally. -+ * - Unity maps may require an even higher number. - */ -- hd->arch.amd.paging_mode = amd_iommu_get_paging_mode( -- is_hvm_domain(d) -- ? 1ul << (DEFAULT_DOMAIN_ADDRESS_WIDTH - PAGE_SHIFT) -- : get_upper_mfn_bound() + 1); -+ hd->arch.amd.paging_mode = max(amd_iommu_get_paging_mode( -+ is_hvm_domain(d) -+ ? 1ul << (DEFAULT_DOMAIN_ADDRESS_WIDTH - PAGE_SHIFT) -+ : get_upper_mfn_bound() + 1), -+ amd_iommu_min_paging_mode); - - return 0; - } diff --git a/xsa378-4.15-7.patch b/xsa378-4.15-7.patch deleted file mode 100644 index 0f59532..0000000 --- a/xsa378-4.15-7.patch +++ /dev/null @@ -1,88 +0,0 @@ -From: Jan Beulich -Subject: x86/p2m: introduce p2m_is_special() - -Seeing the similarity of grant, foreign, and (subsequently) direct-MMIO -handling, introduce a new P2M type group named "special" (as in "needing -special accessors to create/destroy"). - -Also use -EPERM instead of other error codes on the two domain_crash() -paths touched. - -This is part of XSA-378. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/arch/x86/mm/p2m.c -+++ b/xen/arch/x86/mm/p2m.c -@@ -811,7 +811,7 @@ p2m_remove_page(struct p2m_domain *p2m, - for ( i = 0; i < (1UL << page_order); i++ ) - { - p2m->get_entry(p2m, gfn_add(gfn, i), &t, &a, 0, NULL, NULL); -- if ( !p2m_is_grant(t) && !p2m_is_shared(t) && !p2m_is_foreign(t) ) -+ if ( !p2m_is_special(t) && !p2m_is_shared(t) ) - set_gpfn_from_mfn(mfn_x(mfn) + i, INVALID_M2P_ENTRY); - } - } -@@ -941,13 +941,13 @@ guest_physmap_add_entry(struct domain *d - &ot, &a, 0, NULL, NULL); - ASSERT(!p2m_is_shared(ot)); - } -- if ( p2m_is_grant(ot) || p2m_is_foreign(ot) ) -+ if ( p2m_is_special(ot) ) - { -- /* Really shouldn't be unmapping grant/foreign maps this way */ -+ /* Don't permit unmapping grant/foreign this way. */ - domain_crash(d); - p2m_unlock(p2m); - -- return -EINVAL; -+ return -EPERM; - } - else if ( p2m_is_ram(ot) && !p2m_is_paged(ot) ) - { -@@ -1041,8 +1041,7 @@ int p2m_change_type_one(struct domain *d - struct p2m_domain *p2m = p2m_get_hostp2m(d); - int rc; - -- BUG_ON(p2m_is_grant(ot) || p2m_is_grant(nt)); -- BUG_ON(p2m_is_foreign(ot) || p2m_is_foreign(nt)); -+ BUG_ON(p2m_is_special(ot) || p2m_is_special(nt)); - - gfn_lock(p2m, gfn, 0); - -@@ -1289,11 +1288,11 @@ static int set_typed_p2m_entry(struct do - gfn_unlock(p2m, gfn, order); - return cur_order + 1; - } -- if ( p2m_is_grant(ot) || p2m_is_foreign(ot) ) -+ if ( p2m_is_special(ot) ) - { - gfn_unlock(p2m, gfn, order); - domain_crash(d); -- return -ENOENT; -+ return -EPERM; - } - else if ( p2m_is_ram(ot) ) - { ---- a/xen/include/asm-x86/p2m.h -+++ b/xen/include/asm-x86/p2m.h -@@ -149,6 +149,10 @@ typedef unsigned int p2m_query_t; - | p2m_to_mask(p2m_ram_logdirty) ) - #define P2M_SHARED_TYPES (p2m_to_mask(p2m_ram_shared)) - -+/* Types established/cleaned up via special accessors. */ -+#define P2M_SPECIAL_TYPES (P2M_GRANT_TYPES | \ -+ p2m_to_mask(p2m_map_foreign)) -+ - /* Valid types not necessarily associated with a (valid) MFN. */ - #define P2M_INVALID_MFN_TYPES (P2M_POD_TYPES \ - | p2m_to_mask(p2m_mmio_direct) \ -@@ -177,6 +181,7 @@ typedef unsigned int p2m_query_t; - #define p2m_is_paged(_t) (p2m_to_mask(_t) & P2M_PAGED_TYPES) - #define p2m_is_sharable(_t) (p2m_to_mask(_t) & P2M_SHARABLE_TYPES) - #define p2m_is_shared(_t) (p2m_to_mask(_t) & P2M_SHARED_TYPES) -+#define p2m_is_special(_t) (p2m_to_mask(_t) & P2M_SPECIAL_TYPES) - #define p2m_is_broken(_t) (p2m_to_mask(_t) & P2M_BROKEN_TYPES) - #define p2m_is_foreign(_t) (p2m_to_mask(_t) & p2m_to_mask(p2m_map_foreign)) - diff --git a/xsa378-4.15-8.patch b/xsa378-4.15-8.patch deleted file mode 100644 index 391a0bc..0000000 --- a/xsa378-4.15-8.patch +++ /dev/null @@ -1,155 +0,0 @@ -From: Jan Beulich -Subject: x86/p2m: guard (in particular) identity mapping entries - -Such entries, created by set_identity_p2m_entry(), should only be -destroyed by clear_identity_p2m_entry(). However, similarly, entries -created by set_mmio_p2m_entry() should only be torn down by -clear_mmio_p2m_entry(), so the logic gets based upon p2m_mmio_direct as -the entry type (separation between "ordinary" and 1:1 mappings would -require a further indicator to tell apart the two). - -As to the guest_remove_page() change, commit 48dfb297a20a ("x86/PVH: -allow guest_remove_page to remove p2m_mmio_direct pages"), which -introduced the call to clear_mmio_p2m_entry(), claimed this was done for -hwdom only without this actually having been the case. However, this -code shouldn't be there in the first place, as MMIO entries shouldn't be -dropped this way. Avoid triggering the warning again that 48dfb297a20a -silenced by an adjustment to xenmem_add_to_physmap_one() instead. - -Note that guest_physmap_mark_populate_on_demand() gets tightened beyond -the immediate purpose of this change. - -Note also that I didn't inspect code which isn't security supported, -e.g. sharing, paging, or altp2m. - -This is CVE-2021-28694 / part of XSA-378. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/arch/x86/mm/p2m.c -+++ b/xen/arch/x86/mm/p2m.c -@@ -799,7 +799,8 @@ p2m_remove_page(struct p2m_domain *p2m, - &cur_order, NULL); - - if ( p2m_is_valid(t) && -- (!mfn_valid(mfn) || !mfn_eq(mfn_add(mfn, i), mfn_return)) ) -+ (!mfn_valid(mfn) || t == p2m_mmio_direct || -+ !mfn_eq(mfn_add(mfn, i), mfn_return)) ) - return -EILSEQ; - - i += (1UL << cur_order) - -@@ -899,7 +900,7 @@ guest_physmap_add_entry(struct domain *d - if ( p2m_is_foreign(t) ) - return -EINVAL; - -- if ( !mfn_valid(mfn) ) -+ if ( !mfn_valid(mfn) || t == p2m_mmio_direct ) - { - ASSERT_UNREACHABLE(); - return -EINVAL; -@@ -943,7 +944,7 @@ guest_physmap_add_entry(struct domain *d - } - if ( p2m_is_special(ot) ) - { -- /* Don't permit unmapping grant/foreign this way. */ -+ /* Don't permit unmapping grant/foreign/direct-MMIO this way. */ - domain_crash(d); - p2m_unlock(p2m); - -@@ -1399,8 +1400,8 @@ int set_identity_p2m_entry(struct domain - * order+1 for caller to retry with order (guaranteed smaller than - * the order value passed in) - */ --int clear_mmio_p2m_entry(struct domain *d, unsigned long gfn_l, mfn_t mfn, -- unsigned int order) -+static int clear_mmio_p2m_entry(struct domain *d, unsigned long gfn_l, -+ mfn_t mfn, unsigned int order) - { - int rc = -EINVAL; - gfn_t gfn = _gfn(gfn_l); -@@ -2731,7 +2732,9 @@ int xenmem_add_to_physmap_one( - - /* Remove previously mapped page if it was present. */ - prev_mfn = get_gfn(d, gfn_x(gpfn), &p2mt); -- if ( mfn_valid(prev_mfn) ) -+ if ( p2mt == p2m_mmio_direct ) -+ rc = -EPERM; -+ else if ( mfn_valid(prev_mfn) ) - { - if ( is_special_page(mfn_to_page(prev_mfn)) ) - /* Special pages are simply unhooked from this phys slot. */ ---- a/xen/arch/x86/mm/p2m-pod.c -+++ b/xen/arch/x86/mm/p2m-pod.c -@@ -1299,17 +1299,17 @@ guest_physmap_mark_populate_on_demand(st - - p2m->get_entry(p2m, gfn_add(gfn, i), &ot, &a, 0, &cur_order, NULL); - n = 1UL << min(order, cur_order); -- if ( p2m_is_ram(ot) ) -+ if ( ot == p2m_populate_on_demand ) -+ { -+ /* Count how many PoD entries we'll be replacing if successful */ -+ pod_count += n; -+ } -+ else if ( ot != p2m_invalid && ot != p2m_mmio_dm ) - { - P2M_DEBUG("gfn_to_mfn returned type %d!\n", ot); - rc = -EBUSY; - goto out; - } -- else if ( ot == p2m_populate_on_demand ) -- { -- /* Count how man PoD entries we'll be replacing if successful */ -- pod_count += n; -- } - } - - /* Now, actually do the two-way mapping */ ---- a/xen/common/memory.c -+++ b/xen/common/memory.c -@@ -330,7 +330,7 @@ int guest_remove_page(struct domain *d, - } - if ( p2mt == p2m_mmio_direct ) - { -- rc = clear_mmio_p2m_entry(d, gmfn, mfn, PAGE_ORDER_4K); -+ rc = -EPERM; - goto out_put_gfn; - } - #else -@@ -1875,6 +1875,15 @@ int check_get_page_from_gfn(struct domai - return -EAGAIN; - } - #endif -+#ifdef CONFIG_X86 -+ if ( p2mt == p2m_mmio_direct ) -+ { -+ if ( page ) -+ put_page(page); -+ -+ return -EPERM; -+ } -+#endif - - if ( !page ) - return -EINVAL; ---- a/xen/include/asm-x86/p2m.h -+++ b/xen/include/asm-x86/p2m.h -@@ -151,7 +151,8 @@ typedef unsigned int p2m_query_t; - - /* Types established/cleaned up via special accessors. */ - #define P2M_SPECIAL_TYPES (P2M_GRANT_TYPES | \ -- p2m_to_mask(p2m_map_foreign)) -+ p2m_to_mask(p2m_map_foreign) | \ -+ p2m_to_mask(p2m_mmio_direct)) - - /* Valid types not necessarily associated with a (valid) MFN. */ - #define P2M_INVALID_MFN_TYPES (P2M_POD_TYPES \ -@@ -666,8 +667,6 @@ int p2m_is_logdirty_range(struct p2m_dom - /* Set mmio addresses in the p2m table (for pass-through) */ - int set_mmio_p2m_entry(struct domain *d, gfn_t gfn, mfn_t mfn, - unsigned int order); --int clear_mmio_p2m_entry(struct domain *d, unsigned long gfn, mfn_t mfn, -- unsigned int order); - - /* Set identity addresses in the p2m table (for pass-through) */ - int set_identity_p2m_entry(struct domain *d, unsigned long gfn, diff --git a/xsa379-4.15.patch b/xsa379-4.15.patch deleted file mode 100644 index 004f56e..0000000 --- a/xsa379-4.15.patch +++ /dev/null @@ -1,82 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: widen locked region in xenmem_add_to_physmap_one() - -For pages which can be made part of the P2M by the guest, but which can -also later be de-allocated (grant table v2 status pages being the -present example), it is imperative that they be mapped at no more than a -single GFN. We therefore need to make sure that of two parallel -XENMAPSPACE_grant_table requests for the same status page one completes -before the second checks at which other GFN the underlying MFN is -presently mapped. - -Push down the respective put_gfn(). This leverages that gfn_lock() -really aliases p2m_lock(), but the function makes this assumption -already anyway: In the XENMAPSPACE_gmfn case lock nesting constraints -for both involved GFNs would otherwise need to be enforced to avoid ABBA -deadlocks. - -This is CVE-2021-28697 / XSA-379. - -Signed-off-by: Jan Beulich -Reviewed-by: Julien Grall ---- -Since there was some re-ordering of the checks in staging/master (the --EXDEV now sitting earlier there), I deemed it better to drop the -earlier "if ( rc )" and allow an earlier error to be overwritten by --EXDEV here. - ---- a/xen/arch/x86/mm/p2m.c -+++ b/xen/arch/x86/mm/p2m.c -@@ -2730,8 +2730,20 @@ int xenmem_add_to_physmap_one( - goto put_both; - } - -- /* Remove previously mapped page if it was present. */ -+ /* -+ * Note that we're (ab)using GFN locking (to really be locking of the -+ * entire P2M) here in (at least) two ways: Finer grained locking would -+ * expose lock order violations in the XENMAPSPACE_gmfn case (due to the -+ * earlier get_gfn_unshare() above). Plus at the very least for the grant -+ * table v2 status page case we need to guarantee that the same page can -+ * only appear at a single GFN. While this is a property we want in -+ * general, for pages which can subsequently be freed this imperative: -+ * Upon freeing we wouldn't be able to find other mappings in the P2M -+ * (unless we did a brute force search). -+ */ - prev_mfn = get_gfn(d, gfn_x(gpfn), &p2mt); -+ -+ /* Remove previously mapped page if it was present. */ - if ( p2mt == p2m_mmio_direct ) - rc = -EPERM; - else if ( mfn_valid(prev_mfn) ) -@@ -2743,27 +2755,21 @@ int xenmem_add_to_physmap_one( - /* Normal domain memory is freed, to avoid leaking memory. */ - rc = guest_remove_page(d, gfn_x(gpfn)); - } -- /* In the XENMAPSPACE_gmfn case we still hold a ref on the old page. */ -- put_gfn(d, gfn_x(gpfn)); -- -- if ( rc ) -- goto put_both; - - /* Unmap from old location, if any. */ - old_gpfn = get_gpfn_from_mfn(mfn_x(mfn)); - ASSERT(!SHARED_M2P(old_gpfn)); - if ( space == XENMAPSPACE_gmfn && old_gpfn != gfn ) -- { - rc = -EXDEV; -- goto put_both; -- } -- if ( old_gpfn != INVALID_M2P_ENTRY ) -+ else if ( !rc && old_gpfn != INVALID_M2P_ENTRY ) - rc = guest_physmap_remove_page(d, _gfn(old_gpfn), mfn, PAGE_ORDER_4K); - - /* Map at new location. */ - if ( !rc ) - rc = guest_physmap_add_page(d, gpfn, mfn, PAGE_ORDER_4K); - -+ put_gfn(d, gfn_x(gpfn)); -+ - put_both: - /* - * In the XENMAPSPACE_gmfn case, we took a ref of the gfn at the top. diff --git a/xsa380-1.patch b/xsa380-1.patch deleted file mode 100644 index 9212d73..0000000 --- a/xsa380-1.patch +++ /dev/null @@ -1,178 +0,0 @@ -From: Jan Beulich -Subject: gnttab: add preemption check to gnttab_release_mappings() - -A guest may die with many grant mappings still in place, or simply with -a large maptrack table. Iterating through this may take more time than -is reasonable without intermediate preemption (to run softirqs and -perhaps the scheduler). - -Move the invocation of the function to the section where other -restartable functions get invoked, and have the function itself check -for preemption every once in a while. Have it iterate the table -backwards, such that decreasing the maptrack limit is all it takes to -convey restart information. - -In domain_teardown() introduce PROG_none such that inserting at the -front will be easier going forward. - -This is part of CVE-2021-28698 / XSA-380. - -Reported-by: Andrew Cooper -Signed-off-by: Jan Beulich -Reviewed-by: Julien Grall ---- -While I consider removal of the freeing of t->maptrack[i] from -grant_table_destroy() an integral part of this change, also freeing -t->maptrack right in gnttab_release_mappings() would seem like an -unrelated change to me, so I'm not moving that one for now. If others -think it would better be moved, I'd be happy to do so. - -While in principle it would be nice to also eliminate the other loops -from grant_table_destroy() (which can all take long as well provided a -large enough max_grant_frames), ->maptrack[] really is special in that -it only gets accessed when processing requests by the domain itself. The -other arrays may all continue to be accessed as remote domains drop uses -of grants by the dying domain. ---- -v3: Add comment. -v2: Move gnttab_release_mappings() invocation into domain_teardown(). - Don't crash when cleaning up domain without maptrack table. Extend - comment next to maptrack_limit. - ---- a/xen/common/domain.c -+++ b/xen/common/domain.c -@@ -412,11 +412,18 @@ static int domain_teardown(struct domain - v = d->teardown.vcpu - - enum { -- PROG_vcpu_teardown = 1, -+ PROG_none, -+ PROG_gnttab_mappings, -+ PROG_vcpu_teardown, - PROG_done, - }; - -- case 0: -+ case PROG_none: -+ rc = gnttab_release_mappings(d); -+ if ( rc ) -+ return rc; -+ -+ PROGRESS(gnttab_mappings): - for_each_vcpu ( d, v ) - { - PROGRESS_VCPU(teardown); -@@ -908,7 +915,6 @@ int domain_kill(struct domain *d) - return domain_kill(d); - d->is_dying = DOMDYING_dying; - argo_destroy(d); -- gnttab_release_mappings(d); - vnuma_destroy(d->vnuma); - domain_set_outstanding_pages(d, 0); - /* fallthrough */ ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -64,7 +64,13 @@ struct grant_table { - unsigned int nr_grant_frames; - /* Number of grant status frames shared with guest (for version 2) */ - unsigned int nr_status_frames; -- /* Number of available maptrack entries. */ -+ /* -+ * Number of available maptrack entries. For cleanup purposes it is -+ * important to realize that this field and @maptrack further down will -+ * only ever be accessed by the local domain. Thus it is okay to clean -+ * up early, and to shrink the limit for the purpose of tracking cleanup -+ * progress. -+ */ - unsigned int maptrack_limit; - /* Shared grant table (see include/public/grant_table.h). */ - union { -@@ -3679,9 +3685,7 @@ do_grant_table_op( - #include "compat/grant_table.c" - #endif - --void --gnttab_release_mappings( -- struct domain *d) -+int gnttab_release_mappings(struct domain *d) - { - struct grant_table *gt = d->grant_table, *rgt; - struct grant_mapping *map; -@@ -3695,8 +3699,32 @@ gnttab_release_mappings( - - BUG_ON(!d->is_dying); - -- for ( handle = 0; handle < gt->maptrack_limit; handle++ ) -+ if ( !gt || !gt->maptrack ) -+ return 0; -+ -+ for ( handle = gt->maptrack_limit; handle; ) - { -+ /* -+ * Deal with full pages such that their freeing (in the body of the -+ * if()) remains simple. -+ */ -+ if ( handle < gt->maptrack_limit && !(handle % MAPTRACK_PER_PAGE) ) -+ { -+ /* -+ * Changing maptrack_limit alters nr_maptrack_frames()'es return -+ * value. Free the then excess trailing page right here, rather -+ * than leaving it to grant_table_destroy() (and in turn requiring -+ * to leave gt->maptrack_limit unaltered). -+ */ -+ gt->maptrack_limit = handle; -+ FREE_XENHEAP_PAGE(gt->maptrack[nr_maptrack_frames(gt)]); -+ -+ if ( hypercall_preempt_check() ) -+ return -ERESTART; -+ } -+ -+ --handle; -+ - map = &maptrack_entry(gt, handle); - if ( !(map->flags & (GNTMAP_device_map|GNTMAP_host_map)) ) - continue; -@@ -3780,6 +3808,11 @@ gnttab_release_mappings( - - map->flags = 0; - } -+ -+ gt->maptrack_limit = 0; -+ FREE_XENHEAP_PAGE(gt->maptrack[0]); -+ -+ return 0; - } - - void grant_table_warn_active_grants(struct domain *d) -@@ -3843,8 +3876,7 @@ grant_table_destroy( - free_xenheap_page(t->shared_raw[i]); - xfree(t->shared_raw); - -- for ( i = 0; i < nr_maptrack_frames(t); i++ ) -- free_xenheap_page(t->maptrack[i]); -+ ASSERT(!t->maptrack_limit); - vfree(t->maptrack); - - for ( i = 0; i < nr_active_grant_frames(t); i++ ) ---- a/xen/include/xen/grant_table.h -+++ b/xen/include/xen/grant_table.h -@@ -47,9 +47,7 @@ void grant_table_init_vcpu(struct vcpu * - void grant_table_warn_active_grants(struct domain *d); - - /* Domain death release of granted mappings of other domains' memory. */ --void --gnttab_release_mappings( -- struct domain *d); -+int gnttab_release_mappings(struct domain *d); - - int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref, - gfn_t *gfn, uint16_t *status); -@@ -80,7 +78,7 @@ static inline void grant_table_init_vcpu - - static inline void grant_table_warn_active_grants(struct domain *d) {} - --static inline void gnttab_release_mappings(struct domain *d) {} -+static inline int gnttab_release_mappings(struct domain *d) { return 0; } - - static inline int mem_sharing_gref_to_gfn(struct grant_table *gt, - grant_ref_t ref, diff --git a/xsa380-2.patch b/xsa380-2.patch deleted file mode 100644 index e5a134d..0000000 --- a/xsa380-2.patch +++ /dev/null @@ -1,410 +0,0 @@ -From: Jan Beulich -Subject: gnttab: replace mapkind() - -mapkind() doesn't scale very well with larger maptrack entry counts, -using a brute force linear search through all entries, with the only -option of an early loop exit if a matching writable entry was found. -Introduce a radix tree alongside the main maptrack table, thus -allowing much faster MFN-based lookup. To avoid the need to actually -allocate space for the individual nodes, encode the two counters in the -node pointers themselves, thus limiting the number of permitted -simultaneous r/o and r/w mappings of the same MFN to 2³¹-1 (64-bit) / -2¹⁵-1 (32-bit) each. - -To avoid enforcing an unnecessarily low bound on the number of -simultaneous mappings of a single MFN, introduce -radix_tree_{ulong_to_ptr,ptr_to_ulong} paralleling -radix_tree_{int_to_ptr,ptr_to_int}. - -As a consequence locking changes are also applicable: With there no -longer being any inspection of the remote domain's active entries, -there's also no need anymore to hold the remote domain's grant table -lock. And since we're no longer iterating over the local domain's map -track table, the lock in map_grant_ref() can also be dropped before the -new maptrack entry actually gets populated. - -As a nice side effect this also reduces the number of IOMMU operations -in unmap_common(): Previously we would have "established" a readable -mapping whenever we didn't find a writable entry anymore (yet, of -course, at least one readable one). But we only need to do this if we -actually dropped the last writable entry, not if there were none already -before. - -This is part of CVE-2021-28698 / XSA-380. - -Signed-off-by: Jan Beulich -Reviewed-by: Julien Grall ---- -I hope that limiting the map count to 32k on Arm32 is good enough. I -also hope it is out of question that 2G of mappings are enough on 64-bit -architectures. - -I'm using the grant table lock for synchronization to limit differences -in behavior to prior code. I think in principle the maptrack lock could -be used equally well. - -Shouldn't IOMMU insertions be limited anyway to GNTMAP_device_map -requests? This would further save on the number of radix tree nodes in -need of maintaining. - -I'm hesitant to introduce GNTST_* in a security patch, but being able to -tell allocation failure or counter overflow from other errors might be -worthwhile. - -I don't think adding anything to gnttab_usage_print() is useful: -radix_tree_gang_lookup() requires nodes to record their own indexes into -the tree, which we don't do to save space. Yet without indexes printing -node contents isn't very useful. Plus there's also no printing of the -main maptrack table contents. ---- -v3: Check for radix_tree_lookup_slot() returning NULL. Convert -EEXIST - to -EBUSY. Add comments. Re-base over comment addition in patch 1. -v2: New. - ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -37,6 +37,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -82,8 +83,13 @@ struct grant_table { - grant_status_t **status; - /* Active grant table. */ - struct active_grant_entry **active; -- /* Mapping tracking table per vcpu. */ -+ /* Handle-indexed tracking table of mappings. */ - struct grant_mapping **maptrack; -+ /* -+ * MFN-indexed tracking tree of mappings, if needed. Note that this is -+ * protected by @lock, not @maptrack_lock. -+ */ -+ struct radix_tree_root maptrack_tree; - - /* Domain to which this struct grant_table belongs. */ - const struct domain *domain; -@@ -516,34 +522,6 @@ static int get_paged_frame(unsigned long - return GNTST_okay; - } - --static inline void --double_gt_lock(struct grant_table *lgt, struct grant_table *rgt) --{ -- /* -- * See mapkind() for why the write lock is also required for the -- * remote domain. -- */ -- if ( lgt < rgt ) -- { -- grant_write_lock(lgt); -- grant_write_lock(rgt); -- } -- else -- { -- if ( lgt != rgt ) -- grant_write_lock(rgt); -- grant_write_lock(lgt); -- } --} -- --static inline void --double_gt_unlock(struct grant_table *lgt, struct grant_table *rgt) --{ -- grant_write_unlock(lgt); -- if ( lgt != rgt ) -- grant_write_unlock(rgt); --} -- - #define INVALID_MAPTRACK_HANDLE UINT_MAX - - static inline grant_handle_t -@@ -970,41 +948,17 @@ static struct active_grant_entry *grant_ - return ERR_PTR(-EINVAL); - } - --#define MAPKIND_READ 1 --#define MAPKIND_WRITE 2 --static unsigned int mapkind( -- struct grant_table *lgt, const struct domain *rd, mfn_t mfn) --{ -- struct grant_mapping *map; -- grant_handle_t handle, limit = lgt->maptrack_limit; -- unsigned int kind = 0; -- -- /* -- * Must have the local domain's grant table write lock when -- * iterating over its maptrack entries. -- */ -- ASSERT(percpu_rw_is_write_locked(&lgt->lock)); -- /* -- * Must have the remote domain's grant table write lock while -- * counting its active entries. -- */ -- ASSERT(percpu_rw_is_write_locked(&rd->grant_table->lock)); -- -- smp_rmb(); -- -- for ( handle = 0; !(kind & MAPKIND_WRITE) && handle < limit; handle++ ) -- { -- map = &maptrack_entry(lgt, handle); -- if ( !(map->flags & (GNTMAP_device_map|GNTMAP_host_map)) || -- map->domid != rd->domain_id ) -- continue; -- if ( mfn_eq(_active_entry(rd->grant_table, map->ref).mfn, mfn) ) -- kind |= map->flags & GNTMAP_readonly ? -- MAPKIND_READ : MAPKIND_WRITE; -- } -- -- return kind; --} -+union maptrack_node { -+ struct { -+ /* Radix tree slot pointers use two of the bits. */ -+#ifdef __BIG_ENDIAN_BITFIELD -+ unsigned long : 2; -+#endif -+ unsigned long rd : BITS_PER_LONG / 2 - 1; -+ unsigned long wr : BITS_PER_LONG / 2 - 1; -+ } cnt; -+ unsigned long raw; -+}; - - static void - map_grant_ref( -@@ -1023,7 +977,6 @@ map_grant_ref( - struct grant_mapping *mt; - grant_entry_header_t *shah; - uint16_t *status; -- bool_t need_iommu; - - ld = current->domain; - -@@ -1244,31 +1197,75 @@ map_grant_ref( - * as mem-sharing and IOMMU use are incompatible). The dom_io case would - * need checking separately if we compared against owner here. - */ -- need_iommu = ld != rd && gnttab_need_iommu_mapping(ld); -- if ( need_iommu ) -+ if ( ld != rd && gnttab_need_iommu_mapping(ld) ) - { -+ union maptrack_node node = { -+ .cnt.rd = !!(op->flags & GNTMAP_readonly), -+ .cnt.wr = !(op->flags & GNTMAP_readonly), -+ }; -+ int err; -+ void **slot = NULL; - unsigned int kind; - -- double_gt_lock(lgt, rgt); -+ grant_write_lock(lgt); -+ -+ err = radix_tree_insert(&lgt->maptrack_tree, mfn_x(mfn), -+ radix_tree_ulong_to_ptr(node.raw)); -+ if ( err == -EEXIST ) -+ { -+ slot = radix_tree_lookup_slot(&lgt->maptrack_tree, mfn_x(mfn)); -+ if ( likely(slot) ) -+ { -+ node.raw = radix_tree_ptr_to_ulong(*slot); -+ err = -EBUSY; -+ -+ /* Update node only when refcount doesn't overflow. */ -+ if ( op->flags & GNTMAP_readonly ? ++node.cnt.rd -+ : ++node.cnt.wr ) -+ { -+ radix_tree_replace_slot(slot, -+ radix_tree_ulong_to_ptr(node.raw)); -+ err = 0; -+ } -+ } -+ else -+ ASSERT_UNREACHABLE(); -+ } - - /* - * We're not translated, so we know that dfns and mfns are - * the same things, so the IOMMU entry is always 1-to-1. - */ -- kind = mapkind(lgt, rd, mfn); -- if ( !(op->flags & GNTMAP_readonly) && -- !(kind & MAPKIND_WRITE) ) -+ if ( !(op->flags & GNTMAP_readonly) && node.cnt.wr == 1 ) - kind = IOMMUF_readable | IOMMUF_writable; -- else if ( !kind ) -+ else if ( (op->flags & GNTMAP_readonly) && -+ node.cnt.rd == 1 && !node.cnt.wr ) - kind = IOMMUF_readable; - else - kind = 0; -- if ( kind && iommu_legacy_map(ld, _dfn(mfn_x(mfn)), mfn, 1, kind) ) -+ if ( err || -+ (kind && iommu_legacy_map(ld, _dfn(mfn_x(mfn)), mfn, 1, kind)) ) - { -- double_gt_unlock(lgt, rgt); -+ if ( !err ) -+ { -+ if ( slot ) -+ { -+ op->flags & GNTMAP_readonly ? node.cnt.rd-- -+ : node.cnt.wr--; -+ radix_tree_replace_slot(slot, -+ radix_tree_ulong_to_ptr(node.raw)); -+ } -+ else -+ radix_tree_delete(&lgt->maptrack_tree, mfn_x(mfn)); -+ } -+ - rc = GNTST_general_error; -- goto undo_out; - } -+ -+ grant_write_unlock(lgt); -+ -+ if ( rc != GNTST_okay ) -+ goto undo_out; - } - - TRACE_1D(TRC_MEM_PAGE_GRANT_MAP, op->dom); -@@ -1276,10 +1273,6 @@ map_grant_ref( - /* - * All maptrack entry users check mt->flags first before using the - * other fields so just ensure the flags field is stored last. -- * -- * However, if gnttab_need_iommu_mapping() then this would race -- * with a concurrent mapkind() call (on an unmap, for example) -- * and a lock is required. - */ - mt = &maptrack_entry(lgt, handle); - mt->domid = op->dom; -@@ -1287,9 +1280,6 @@ map_grant_ref( - smp_wmb(); - write_atomic(&mt->flags, op->flags); - -- if ( need_iommu ) -- double_gt_unlock(lgt, rgt); -- - op->dev_bus_addr = mfn_to_maddr(mfn); - op->handle = handle; - op->status = GNTST_okay; -@@ -1497,19 +1487,34 @@ unmap_common( - /* See the respective comment in map_grant_ref(). */ - if ( rc == GNTST_okay && ld != rd && gnttab_need_iommu_mapping(ld) ) - { -- unsigned int kind; -+ void **slot; -+ union maptrack_node node; - int err = 0; - -- double_gt_lock(lgt, rgt); -+ grant_write_lock(lgt); -+ slot = radix_tree_lookup_slot(&lgt->maptrack_tree, mfn_x(op->mfn)); -+ node.raw = likely(slot) ? radix_tree_ptr_to_ulong(*slot) : 0; -+ -+ /* Refcount must not underflow. */ -+ if ( !(flags & GNTMAP_readonly ? node.cnt.rd-- -+ : node.cnt.wr--) ) -+ BUG(); - -- kind = mapkind(lgt, rd, op->mfn); -- if ( !kind ) -+ if ( !node.raw ) - err = iommu_legacy_unmap(ld, _dfn(mfn_x(op->mfn)), 1); -- else if ( !(kind & MAPKIND_WRITE) ) -+ else if ( !(flags & GNTMAP_readonly) && !node.cnt.wr ) - err = iommu_legacy_map(ld, _dfn(mfn_x(op->mfn)), op->mfn, 1, - IOMMUF_readable); - -- double_gt_unlock(lgt, rgt); -+ if ( err ) -+ ; -+ else if ( !node.raw ) -+ radix_tree_delete(&lgt->maptrack_tree, mfn_x(op->mfn)); -+ else -+ radix_tree_replace_slot(slot, -+ radix_tree_ulong_to_ptr(node.raw)); -+ -+ grant_write_unlock(lgt); - - if ( err ) - rc = GNTST_general_error; -@@ -1956,6 +1961,8 @@ int grant_table_init(struct domain *d, i - gt->maptrack = vzalloc(gt->max_maptrack_frames * sizeof(*gt->maptrack)); - if ( gt->maptrack == NULL ) - goto out; -+ -+ radix_tree_init(>->maptrack_tree); - } - - /* Shared grant table. */ -@@ -3704,6 +3711,8 @@ int gnttab_release_mappings(struct domai - - for ( handle = gt->maptrack_limit; handle; ) - { -+ mfn_t mfn; -+ - /* - * Deal with full pages such that their freeing (in the body of the - * if()) remains simple. -@@ -3801,17 +3810,31 @@ int gnttab_release_mappings(struct domai - - reduce_status_for_pin(rd, act, status, map->flags & GNTMAP_readonly); - -+ mfn = act->mfn; -+ - active_entry_release(act); - grant_read_unlock(rgt); - - rcu_unlock_domain(rd); - - map->flags = 0; -+ -+ /* -+ * This is excessive in that a single such call would suffice per -+ * mapped MFN (or none at all, if no entry was ever inserted). But it -+ * should be the common case for an MFN to be mapped just once, and -+ * this way we don't need to further maintain the counters. We also -+ * don't want to leave cleaning up of the tree as a whole to the end -+ * of the function, as this could take quite some time. -+ */ -+ radix_tree_delete(>->maptrack_tree, mfn_x(mfn)); - } - - gt->maptrack_limit = 0; - FREE_XENHEAP_PAGE(gt->maptrack[0]); - -+ radix_tree_destroy(>->maptrack_tree, NULL); -+ - return 0; - } - ---- a/xen/include/xen/radix-tree.h -+++ b/xen/include/xen/radix-tree.h -@@ -190,6 +190,25 @@ static inline int radix_tree_ptr_to_int( - return (int)((long)ptr >> 2); - } - -+/** -+ * radix_tree_{ulong_to_ptr,ptr_to_ulong}: -+ * -+ * Same for unsigned long values. Beware though that only BITS_PER_LONG-2 -+ * bits are actually usable for the value. -+ */ -+static inline void *radix_tree_ulong_to_ptr(unsigned long val) -+{ -+ unsigned long ptr = (val << 2) | 0x2; -+ ASSERT((ptr >> 2) == val); -+ return (void *)ptr; -+} -+ -+static inline unsigned long radix_tree_ptr_to_ulong(void *ptr) -+{ -+ ASSERT(((unsigned long)ptr & 0x3) == 0x2); -+ return (unsigned long)ptr >> 2; -+} -+ - int radix_tree_insert(struct radix_tree_root *, unsigned long, void *); - void *radix_tree_lookup(struct radix_tree_root *, unsigned long); - void **radix_tree_lookup_slot(struct radix_tree_root *, unsigned long); diff --git a/xsa380-3.patch b/xsa380-3.patch deleted file mode 100644 index 1517249..0000000 --- a/xsa380-3.patch +++ /dev/null @@ -1,74 +0,0 @@ -From: Jan Beulich -Subject: gnttab: avoid triggering assertion in radix_tree_ulong_to_ptr() - -Relevant quotes from the C11 standard: - -"Except where explicitly stated otherwise, for the purposes of this - subclause unnamed members of objects of structure and union type do not - participate in initialization. Unnamed members of structure objects - have indeterminate value even after initialization." - -"If there are fewer initializers in a brace-enclosed list than there are - elements or members of an aggregate, [...], the remainder of the - aggregate shall be initialized implicitly the same as objects that have - static storage duration." - -"If an object that has static or thread storage duration is not - initialized explicitly, then: - [...] - — if it is an aggregate, every member is initialized (recursively) - according to these rules, and any padding is initialized to zero - bits; - [...]" - -"A bit-field declaration with no declarator, but only a colon and a - width, indicates an unnamed bit-field." Footnote: "An unnamed bit-field - structure member is useful for padding to conform to externally imposed - layouts." - -"There may be unnamed padding within a structure object, but not at its - beginning." - -Which makes me conclude: -- Whether an unnamed bit-field member is an unnamed member or padding is - unclear, and hence also whether the last quote above would render the - big endian case of the structure declaration invalid. -- Whether the number of members of an aggregate includes unnamed ones is - also not really clear. -- The initializer in map_grant_ref() initializes all fields of the "cnt" - sub-structure of the union, so assuming the second quote above applies - here (indirectly), the compiler isn't required to implicitly - initialize the rest (i.e. in particular any padding) like would happen - for static storage duration objects. - -Gcc 7.4.1 can be observed (apparently in debug builds only) to translate -aforementioned initializer to a read-modify-write operation of a stack -variable, leaving unchanged the top two bits of whatever was previously -in that stack slot. Clearly if either of the two bits were set, -radix_tree_ulong_to_ptr()'s assertion would trigger. - -Therefore, to be on the safe side, add an explicit padding field for the -non-big-endian-bitfields case and give a dummy name to both padding -fields. - -Fixes: 9781b51efde2 ("gnttab: replace mapkind()") -Signed-off-by: Jan Beulich -Acked-by: Andrew Cooper - ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -952,10 +952,13 @@ union maptrack_node { - struct { - /* Radix tree slot pointers use two of the bits. */ - #ifdef __BIG_ENDIAN_BITFIELD -- unsigned long : 2; -+ unsigned long _0 : 2; - #endif - unsigned long rd : BITS_PER_LONG / 2 - 1; - unsigned long wr : BITS_PER_LONG / 2 - 1; -+#ifndef __BIG_ENDIAN_BITFIELD -+ unsigned long _0 : 2; -+#endif - } cnt; - unsigned long raw; - }; diff --git a/xsa382.patch b/xsa382.patch deleted file mode 100644 index 936c2de..0000000 --- a/xsa382.patch +++ /dev/null @@ -1,34 +0,0 @@ -From: Jan Beulich -Subject: gnttab: fix array capacity check in gnttab_get_status_frames() - -The number of grant frames is of no interest here; converting the passed -in op.nr_frames this way means we allow for 8 times as many GFNs to be -written as actually fit in the array. We would corrupt xlat areas of -higher vCPU-s (after having faulted many times while trying to write to -the guard pages between any two areas) for 32-bit PV guests. For HVM -guests we'd simply crash as soon as we hit the first guard page, as -accesses to the xlat area are simply memcpy() there. - -This is CVE-2021-28699 / XSA-382. - -Fixes: 18b1be5e324b ("gnttab: make resource limits per domain") -Signed-off-by: Jan Beulich - ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -3243,12 +3243,11 @@ gnttab_get_status_frames(XEN_GUEST_HANDL - goto unlock; - } - -- if ( unlikely(limit_max < grant_to_status_frames(op.nr_frames)) ) -+ if ( unlikely(limit_max < op.nr_frames) ) - { - gdprintk(XENLOG_WARNING, -- "grant_to_status_frames(%u) for d%d is too large (%u,%u)\n", -- op.nr_frames, d->domain_id, -- grant_to_status_frames(op.nr_frames), limit_max); -+ "nr_status_frames for %pd is too large (%u,%u)\n", -+ d, op.nr_frames, limit_max); - op.status = GNTST_general_error; - goto unlock; - } diff --git a/xsa383.patch b/xsa383.patch deleted file mode 100644 index 9ab5eb3..0000000 --- a/xsa383.patch +++ /dev/null @@ -1,55 +0,0 @@ -From: Julien Grall -Date: Sat, 3 Jul 2021 14:03:36 +0100 -Subject: [PATCH] xen/arm: Restrict the amount of memory that dom0less domU and - dom0 can allocate - -Currently, both dom0less domUs and dom0 can allocate an "unlimited" -amount of memory because d->max_pages is set to ~0U. - -In particular, the former are meant to be unprivileged. Therefore the -memory they could allocate should be bounded. As the domain are not yet -officially aware of Xen (we don't expose advertise it in the DT, yet -the hypercalls are accessible), they should not need to allocate more -than the initial amount. So cap set d->max_pages directly the amount of -memory we are meant to allocate. - -Take the opportunity to also restrict the memory for dom0 as the -domain is direct mapped (e.g. MFN == GFN) and therefore cannot -allocate outside of the pre-allocated region. - -This is CVE-2021-28700 / XSA-383. - -Reported-by: Julien Grall -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini -Tested-by: Stefano Stabellini ---- - xen/arch/arm/domain_build.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c -index 6c86d527810f..206038d1c022 100644 ---- a/xen/arch/arm/domain_build.c -+++ b/xen/arch/arm/domain_build.c -@@ -2440,7 +2440,8 @@ static int __init construct_domU(struct domain *d, - - if ( vcpu_create(d, 0) == NULL ) - return -ENOMEM; -- d->max_pages = ~0U; -+ -+ d->max_pages = ((paddr_t)mem * SZ_1K) >> PAGE_SHIFT; - - kinfo.d = d; - -@@ -2546,7 +2547,7 @@ static int __init construct_dom0(struct domain *d) - - iommu_hwdom_init(d); - -- d->max_pages = ~0U; -+ d->max_pages = dom0_mem >> PAGE_SHIFT; - - kinfo.unassigned_mem = dom0_mem; - kinfo.d = d; --- -2.17.1 - diff --git a/xsa384.patch b/xsa384.patch deleted file mode 100644 index 4f155ac..0000000 --- a/xsa384.patch +++ /dev/null @@ -1,81 +0,0 @@ -From: Jan Beulich -Subject: gnttab: deal with status frame mapping race - -Once gnttab_map_frame() drops the grant table lock, the MFN it reports -back to its caller is free to other manipulation. In particular -gnttab_unpopulate_status_frames() might free it, by a racing request on -another CPU, thus resulting in a reference to a deallocated page getting -added to a domain's P2M. - -Obtain a page reference in gnttab_map_frame() to prevent freeing of the -page until xenmem_add_to_physmap_one() has actually completed its acting -on the page. Do so uniformly, even if only strictly required for v2 -status pages, to avoid extra conditionals (which then would all need to -be kept in sync going forward). - -This is CVE-2021-28701 / XSA-384. - -Reported-by: Julien Grall -Signed-off-by: Jan Beulich -Reviewed-by: Julien Grall ---- -v2: Pull get_page() earlier and fold if()s. - ---- a/xen/arch/arm/mm.c -+++ b/xen/arch/arm/mm.c -@@ -1420,6 +1420,8 @@ int xenmem_add_to_physmap_one( - if ( rc ) - return rc; - -+ /* Need to take care of the reference obtained in gnttab_map_frame(). */ -+ page = mfn_to_page(mfn); - t = p2m_ram_rw; - - break; -@@ -1487,9 +1489,12 @@ int xenmem_add_to_physmap_one( - /* Map at new location. */ - rc = guest_physmap_add_entry(d, gfn, mfn, 0, t); - -- /* If we fail to add the mapping, we need to drop the reference we -- * took earlier on foreign pages */ -- if ( rc && space == XENMAPSPACE_gmfn_foreign ) -+ /* -+ * For XENMAPSPACE_gmfn_foreign if we failed to add the mapping, we need -+ * to drop the reference we took earlier. In all other cases we need to -+ * drop any reference we took earlier (perhaps indirectly). -+ */ -+ if ( space == XENMAPSPACE_gmfn_foreign ? rc : page != NULL ) - { - ASSERT(page != NULL); - put_page(page); ---- a/xen/arch/x86/mm/p2m.c -+++ b/xen/arch/x86/mm/p2m.c -@@ -2726,6 +2726,8 @@ int xenmem_add_to_physmap_one( - rc = gnttab_map_frame(d, idx, gpfn, &mfn); - if ( rc ) - return rc; -+ /* Need to take care of the reference obtained in gnttab_map_frame(). */ -+ page = mfn_to_page(mfn); - break; - - case XENMAPSPACE_gmfn: ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -4097,7 +4097,16 @@ int gnttab_map_frame(struct domain *d, u - } - - if ( !rc ) -- gnttab_set_frame_gfn(gt, status, idx, gfn); -+ { -+ /* -+ * Make sure gnttab_unpopulate_status_frames() won't (successfully) -+ * free the page until our caller has completed its operation. -+ */ -+ if ( get_page(mfn_to_page(*mfn), d) ) -+ gnttab_set_frame_gfn(gt, status, idx, gfn); -+ else -+ rc = -EBUSY; -+ } - - grant_write_unlock(gt); - From a9eb426b07769f34c58f3ed813eb9d71ac3847bc Mon Sep 17 00:00:00 2001 From: Michael Young Date: Mon, 13 Sep 2021 20:57:20 +0100 Subject: [PATCH 022/137] update to xen-4.14.3 --- .gitignore | 2 +- sources | 2 +- xen.gcc11.fixes.patch | 39 -- ...0c113e0067b86186db94aabae9c91aa09f35.patch | 117 ----- xen.spec | 58 +-- ...en-arm-Create-dom0less-domUs-earlier.patch | 83 ---- ...ules-should-always-be-scrubbed-if-bo.patch | 58 --- xsa373-4.14-1.patch | 120 ----- xsa373-4.14-2.patch | 102 ----- xsa373-4.14-3.patch | 163 ------- xsa373-4.14-4.patch | 81 ---- xsa373-4.14-5.patch | 143 ------ xsa375.patch | 50 --- xsa377.patch | 27 -- xsa378-4.14-1.patch | 142 ------ xsa378-4.14-2.patch | 218 ---------- xsa378-4.14-3.patch | 102 ----- xsa378-4.14-4.patch | 385 ---------------- xsa378-4.14-5.patch | 208 --------- xsa378-4.14-6.patch | 411 ------------------ xsa378-4.14-7.patch | 88 ---- xsa378-4.14-8.patch | 157 ------- xsa379-4.14.patch | 77 ---- xsa380-3.patch | 74 ---- xsa380-4.14-1.patch | 148 ------- xsa380-4.14-2.patch | 383 ---------------- xsa382.patch | 34 -- xsa383.patch | 55 --- xsa384-4.14.patch | 79 ---- 29 files changed, 8 insertions(+), 3598 deletions(-) delete mode 100644 xen.git-e06d0c113e0067b86186db94aabae9c91aa09f35.patch delete mode 100644 xsa372-4.14-0001-xen-arm-Create-dom0less-domUs-earlier.patch delete mode 100644 xsa372-4.14-0002-xen-arm-Boot-modules-should-always-be-scrubbed-if-bo.patch delete mode 100644 xsa373-4.14-1.patch delete mode 100644 xsa373-4.14-2.patch delete mode 100644 xsa373-4.14-3.patch delete mode 100644 xsa373-4.14-4.patch delete mode 100644 xsa373-4.14-5.patch delete mode 100644 xsa375.patch delete mode 100644 xsa377.patch delete mode 100644 xsa378-4.14-1.patch delete mode 100644 xsa378-4.14-2.patch delete mode 100644 xsa378-4.14-3.patch delete mode 100644 xsa378-4.14-4.patch delete mode 100644 xsa378-4.14-5.patch delete mode 100644 xsa378-4.14-6.patch delete mode 100644 xsa378-4.14-7.patch delete mode 100644 xsa378-4.14-8.patch delete mode 100644 xsa379-4.14.patch delete mode 100644 xsa380-3.patch delete mode 100644 xsa380-4.14-1.patch delete mode 100644 xsa380-4.14-2.patch delete mode 100644 xsa382.patch delete mode 100644 xsa383.patch delete mode 100644 xsa384-4.14.patch diff --git a/.gitignore b/.gitignore index a217f2a..6b16b1e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.14.2.tar.gz +/xen-4.14.3.tar.gz diff --git a/sources b/sources index b24adf4..0c326a8 100644 --- a/sources +++ b/sources @@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.14.2.tar.gz) = 83c9333b70dbee3e29c6bf08e5ad030676e6c4a32b976f3f5e6a8f8d0dd9e4898bac88dd8e1c9d2ad3509cebb5d212e1745f9392a469d7afeb841d79801ccf39 +SHA512 (xen-4.14.3.tar.gz) = b462fcc1549f6e57f7f2a4fd10ce1e957a25a6a7c0319672b62699468f6c4330b9cd0cf2b0231b5cce94f4bb142a957eb8aa58aa0ffb5c85b37211d6b34ccf16 diff --git a/xen.gcc11.fixes.patch b/xen.gcc11.fixes.patch index a075a6f..31db0cb 100644 --- a/xen.gcc11.fixes.patch +++ b/xen.gcc11.fixes.patch @@ -9,28 +9,6 @@ unsigned int mbytes, vmac_ctx_t *ctx); ---- xen-4.14.0/tools/libs/foreignmemory/linux.c.orig 2020-07-23 16:07:51.000000000 +0100 -+++ xen-4.14.0/tools/libs/foreignmemory/linux.c 2020-10-25 21:36:00.982040566 +0000 -@@ -162,7 +162,7 @@ - void *osdep_xenforeignmemory_map(xenforeignmemory_handle *fmem, - uint32_t dom, void *addr, - int prot, int flags, size_t num, -- const xen_pfn_t arr[/*num*/], int err[/*num*/]) -+ const xen_pfn_t arr[num], int err[num]) - { - int fd = fmem->fd; - privcmd_mmapbatch_v2_t ioctlx; ---- xen-4.14.0/tools/libs/foreignmemory/minios.c.orig 2020-07-23 16:07:51.000000000 +0100 -+++ xen-4.14.0/tools/libs/foreignmemory/minios.c 2020-10-26 22:36:12.423883688 +0000 -@@ -42,7 +42,7 @@ - void *osdep_xenforeignmemory_map(xenforeignmemory_handle *fmem, - uint32_t dom, void *addr, - int prot, int flags, size_t num, -- const xen_pfn_t arr[/*num*/], int err[/*num*/]) -+ const xen_pfn_t arr[num], int err[num]) - { - unsigned long pt_prot = 0; - if (prot & PROT_READ) diff --git a/xen/arch/x86/tboot.c b/xen/arch/x86/tboot.c index 320e06f..618ae92 100644 --- a/xen/arch/x86/tboot.c @@ -44,20 +22,3 @@ index 320e06f..618ae92 100644 /* Look for valid page-aligned address for shared page. */ if ( !opt_tboot_pa || (opt_tboot_pa & ~PAGE_MASK) ) -diff --git a/xen/arch/x86/x86_emulate/x86_emulate.c b/xen/arch/x86/x86_emulate/x86_emulate.c -index 84bb8e0..6ecf5db 100644 ---- a/xen/arch/x86/x86_emulate/x86_emulate.c -+++ b/xen/arch/x86/x86_emulate/x86_emulate.c -@@ -725,9 +725,9 @@ union vex { - #define copy_VEX(ptr, vex) ({ \ - if ( !mode_64bit() ) \ - (vex).reg |= 8; \ -- (ptr)[0 - PFX_BYTES] = ext < ext_8f08 ? 0xc4 : 0x8f; \ -- (ptr)[1 - PFX_BYTES] = (vex).raw[0]; \ -- (ptr)[2 - PFX_BYTES] = (vex).raw[1]; \ -+ ((volatile uint8_t *)ptr)[0 - PFX_BYTES] = ext < ext_8f08 ? 0xc4 : 0x8f; \ -+ ((volatile uint8_t *)ptr)[1 - PFX_BYTES] = (vex).raw[0]; \ -+ ((volatile uint8_t *)ptr)[2 - PFX_BYTES] = (vex).raw[1]; \ - container_of((ptr) + 1 - PFX_BYTES, typeof(vex), raw[0]); \ - }) - diff --git a/xen.git-e06d0c113e0067b86186db94aabae9c91aa09f35.patch b/xen.git-e06d0c113e0067b86186db94aabae9c91aa09f35.patch deleted file mode 100644 index e36de31..0000000 --- a/xen.git-e06d0c113e0067b86186db94aabae9c91aa09f35.patch +++ /dev/null @@ -1,117 +0,0 @@ -From e06d0c113e0067b86186db94aabae9c91aa09f35 Mon Sep 17 00:00:00 2001 -From: Jan Beulich -Date: Thu, 15 Jul 2021 09:32:21 +0200 -Subject: [PATCH] VT-d: adjust domid map updating when unmapping context - -When an earlier error occurred, cleaning up the domid mapping data is -wrong, as references likely still exist. The only exception to this is -when the actual unmapping worked, but some flush failed (supposedly -impossible after XSA-373). The guest will get crashed in such a case -though, so add fallback cleanup to domain destruction to cover this -case. This in turn makes it desirable to silence the dprintk() in -domain_iommu_domid(). - -Note that no error will be returned anymore when the lookup fails - in -the common case lookup failure would already have caused -domain_context_unmap_one() to fail, yet even from a more general -perspective it doesn't look right to fail domain_context_unmap() in such -a case when this was the last device, but not when any earlier unmap was -otherwise successful. - -Signed-off-by: Jan Beulich -Reviewed-by: Kevin Tian -master commit: 32655880057ce2829f962d46916ea6cec60f98d3 -master date: 2021-06-24 16:29:13 +0200 ---- - xen/drivers/passthrough/vtd/iommu.c | 39 ++++++++++++++++++----------- - 1 file changed, 24 insertions(+), 15 deletions(-) - -diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c -index 7d1813a615..e4c0e4368e 100644 ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -79,9 +79,11 @@ static int domain_iommu_domid(struct domain *d, - i = find_next_bit(iommu->domid_bitmap, nr_dom, i+1); - } - -- dprintk(XENLOG_ERR VTDPREFIX, -- "Cannot get valid iommu domid: domid=%d iommu->index=%d\n", -- d->domain_id, iommu->index); -+ if ( !d->is_dying ) -+ dprintk(XENLOG_ERR VTDPREFIX, -+ "Cannot get valid iommu %u domid: %pd\n", -+ iommu->index, d); -+ - return -1; - } - -@@ -146,6 +148,17 @@ static int context_get_domain_id(struct context_entry *context, - return domid; - } - -+static void cleanup_domid_map(struct domain *domain, struct vtd_iommu *iommu) -+{ -+ int iommu_domid = domain_iommu_domid(domain, iommu); -+ -+ if ( iommu_domid >= 0 ) -+ { -+ clear_bit(iommu_domid, iommu->domid_bitmap); -+ iommu->domid_map[iommu_domid] = 0; -+ } -+} -+ - static int iommus_incoherent; - - static void sync_cache(const void *addr, unsigned int size) -@@ -1753,6 +1766,9 @@ static int domain_context_unmap(struct domain *domain, u8 devfn, - goto out; - } - -+ if ( ret ) -+ goto out; -+ - /* - * if no other devices under the same iommu owned by this domain, - * clear iommu in iommu_bitmap and clear domain_id in domid_bitmp -@@ -1772,19 +1788,8 @@ static int domain_context_unmap(struct domain *domain, u8 devfn, - - if ( found == 0 ) - { -- int iommu_domid; -- - clear_bit(iommu->index, &dom_iommu(domain)->arch.iommu_bitmap); -- -- iommu_domid = domain_iommu_domid(domain, iommu); -- if ( iommu_domid == -1 ) -- { -- ret = -EINVAL; -- goto out; -- } -- -- clear_bit(iommu_domid, iommu->domid_bitmap); -- iommu->domid_map[iommu_domid] = 0; -+ cleanup_domid_map(domain, iommu); - } - - out: -@@ -1795,6 +1800,7 @@ static void iommu_domain_teardown(struct domain *d) - { - struct domain_iommu *hd = dom_iommu(d); - struct mapped_rmrr *mrmrr, *tmp; -+ const struct acpi_drhd_unit *drhd; - - if ( list_empty(&acpi_drhd_units) ) - return; -@@ -1814,6 +1820,9 @@ static void iommu_domain_teardown(struct domain *d) - iommu_free_pagetable(hd->arch.pgd_maddr, agaw_to_level(hd->arch.agaw)); - hd->arch.pgd_maddr = 0; - spin_unlock(&hd->arch.mapping_lock); -+ -+ for_each_drhd_unit ( drhd ) -+ cleanup_domid_map(d, drhd->iommu); - } - - static int __must_check intel_iommu_map_page(struct domain *d, dfn_t dfn, --- -2.30.2 - diff --git a/xen.spec b/xen.spec index 8f7137a..ffb650d 100644 --- a/xen.spec +++ b/xen.spec @@ -57,8 +57,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.14.2 -Release: 4%{?dist} +Version: 4.14.3 +Release: 1%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -118,31 +118,6 @@ Patch47: xen.git-d6627cf1b63ce57a6a7e2c1800dbc50eed742c32.patch Patch48: xen.git-d8099d94dfaa3573bd86ebfc457cbc8f70a3ecda.patch Patch49: xen.git-8169f82049efb5b2044b33aa482ba3a136b7804d.patch Patch50: xsa363.patch -Patch51: xsa372-4.14-0001-xen-arm-Create-dom0less-domUs-earlier.patch -Patch52: xsa372-4.14-0002-xen-arm-Boot-modules-should-always-be-scrubbed-if-bo.patch -Patch53: xsa373-4.14-1.patch -Patch54: xsa373-4.14-2.patch -Patch55: xsa373-4.14-3.patch -Patch56: xsa373-4.14-4.patch -Patch57: xsa373-4.14-5.patch -Patch58: xsa375.patch -Patch59: xsa377.patch -Patch60: xen.git-e06d0c113e0067b86186db94aabae9c91aa09f35.patch -Patch61: xsa378-4.14-1.patch -Patch62: xsa378-4.14-2.patch -Patch63: xsa378-4.14-3.patch -Patch64: xsa378-4.14-4.patch -Patch65: xsa378-4.14-5.patch -Patch66: xsa378-4.14-6.patch -Patch67: xsa378-4.14-7.patch -Patch68: xsa378-4.14-8.patch -Patch69: xsa379-4.14.patch -Patch70: xsa380-4.14-1.patch -Patch71: xsa380-4.14-2.patch -Patch72: xsa382.patch -Patch73: xsa383.patch -Patch74: xsa380-3.patch -Patch75: xsa384-4.14.patch %if %build_qemutrad @@ -357,31 +332,6 @@ manage Xen virtual machines. %patch48 -p1 %patch49 -p1 %patch50 -p1 -%patch51 -p1 -%patch52 -p1 -%patch53 -p1 -%patch54 -p1 -%patch55 -p1 -%patch56 -p1 -%patch57 -p1 -%patch58 -p1 -%patch59 -p1 -%patch60 -p1 -%patch61 -p1 -%patch62 -p1 -%patch63 -p1 -%patch64 -p1 -%patch65 -p1 -%patch66 -p1 -%patch67 -p1 -%patch68 -p1 -%patch69 -p1 -%patch70 -p1 -%patch71 -p1 -%patch72 -p1 -%patch73 -p1 -%patch74 -p1 -%patch75 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -988,6 +938,10 @@ fi %endif %changelog +* Mon Sep 13 2021 Michael Young - 4.14.3-1 +- update to xen-4.14.3 + remove or adjust patches now included or superceded upstream + * Wed Sep 08 2021 Michael Young - 4.14.2-4 - Another race in XENMAPSPACE_grant_table handling [XSA-384, CVE-2021-28701] (#2002786) diff --git a/xsa372-4.14-0001-xen-arm-Create-dom0less-domUs-earlier.patch b/xsa372-4.14-0001-xen-arm-Create-dom0less-domUs-earlier.patch deleted file mode 100644 index a5289a8..0000000 --- a/xsa372-4.14-0001-xen-arm-Create-dom0less-domUs-earlier.patch +++ /dev/null @@ -1,83 +0,0 @@ -From f98c20aaaf909be04ada5cb6cb88c14b9bc75e15 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 17 May 2021 17:47:13 +0100 -Subject: [PATCH 1/2] xen/arm: Create dom0less domUs earlier - -In a follow-up patch we will need to unallocate the boot modules -before heap_init_late() is called. - -The modules will contain the domUs kernel and initramfs. Therefore Xen -will need to create extra domUs (used by dom0less) before heap_init_late(). - -This has two consequences on dom0less: - 1) Domains will not be unpaused as soon as they are created but - once all have been created. However, Xen doesn't guarantee an order - to unpause, so this is not something one could rely on. - - 2) The memory allocated for a domU will not be scrubbed anymore when an - admin select bootscrub=on. This is not something we advertised, but if - this is a concern we can introduce either force scrub for all domUs or - a per-domain flag in the DT. The behavior for bootscrub=off and - bootscrub=idle (default) has not changed. - -This is part of XSA-372 / CVE-2021-28693. - -Signed-off-by: Julien Grall -Reviewed-by: Jan Beulich -Reviewed-by: Stefano Stabellini -Tested-by: Stefano Stabellini ---- - xen/arch/arm/domain_build.c | 2 -- - xen/arch/arm/setup.c | 9 +++++---- - 2 files changed, 5 insertions(+), 6 deletions(-) - -diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c -index e824ba34b012..b07461f5d376 100644 ---- a/xen/arch/arm/domain_build.c -+++ b/xen/arch/arm/domain_build.c -@@ -2515,8 +2515,6 @@ void __init create_domUs(void) - - if ( construct_domU(d, node) != 0 ) - panic("Could not set up domain %s\n", dt_node_name(node)); -- -- domain_unpause_by_systemcontroller(d); - } - } - -diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c -index 7968cee47d05..1f26080b30bf 100644 ---- a/xen/arch/arm/setup.c -+++ b/xen/arch/arm/setup.c -@@ -779,7 +779,7 @@ void __init start_xen(unsigned long boot_phys_offset, - int cpus, i; - const char *cmdline; - struct bootmodule *xen_bootmodule; -- struct domain *dom0; -+ struct domain *dom0, *d; - struct xen_domctl_createdomain dom0_cfg = { - .flags = XEN_DOMCTL_CDF_hvm | XEN_DOMCTL_CDF_hap, - .max_evtchn_port = -1, -@@ -962,6 +962,8 @@ void __init start_xen(unsigned long boot_phys_offset, - if ( construct_dom0(dom0) != 0) - panic("Could not set up DOM0 guest OS\n"); - -+ create_domUs(); -+ - heap_init_late(); - - init_trace_bufs(); -@@ -975,9 +977,8 @@ void __init start_xen(unsigned long boot_phys_offset, - - system_state = SYS_STATE_active; - -- create_domUs(); -- -- domain_unpause_by_systemcontroller(dom0); -+ for_each_domain( d ) -+ domain_unpause_by_systemcontroller(d); - - /* Switch on to the dynamically allocated stack for the idle vcpu - * since the static one we're running on is about to be freed. */ --- -2.17.1 - diff --git a/xsa372-4.14-0002-xen-arm-Boot-modules-should-always-be-scrubbed-if-bo.patch b/xsa372-4.14-0002-xen-arm-Boot-modules-should-always-be-scrubbed-if-bo.patch deleted file mode 100644 index 3ed62f3..0000000 --- a/xsa372-4.14-0002-xen-arm-Boot-modules-should-always-be-scrubbed-if-bo.patch +++ /dev/null @@ -1,58 +0,0 @@ -From e7e475c1a3dc6b149252413589eebaa4ae138824 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Sat, 17 Apr 2021 17:38:28 +0100 -Subject: [PATCH 2/2] xen/arm: Boot modules should always be scrubbed if - bootscrub={on, idle} - -The function to initialize the pages (see init_heap_pages()) will request -scrub when the admin request idle bootscrub (default) and state == -SYS_STATE_active. When bootscrub=on, Xen will scrub any free pages in -heap_init_late(). - -Currently, the boot modules (e.g. kernels, initramfs) will be discarded/ -freed after heap_init_late() is called and system_state switched to -SYS_STATE_active. This means the pages associated with the boot modules -will not get scrubbed before getting re-purposed. - -If the memory is assigned to an untrusted domU, it may be able to -retrieve secrets from the modules. - -This is part of XSA-372 / CVE-2021-28693. - -Fixes: 1774e9b1df27 ("xen/arm: introduce create_domUs") -Signed-off-by: Julien Grall -Reviewed-by: Jan Beulich -Reviewed-by: Stefano Stabellini -Tested-by: Stefano Stabellini ---- - xen/arch/arm/setup.c | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - -diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c -index 1f26080b30bf..34b1c1a11ef6 100644 ---- a/xen/arch/arm/setup.c -+++ b/xen/arch/arm/setup.c -@@ -75,7 +75,6 @@ static __used void init_done(void) - /* Must be done past setting system_state. */ - unregister_init_virtual_region(); - -- discard_initial_modules(); - free_init_memory(); - startup_cpu_idle_loop(); - } -@@ -964,6 +963,12 @@ void __init start_xen(unsigned long boot_phys_offset, - - create_domUs(); - -+ /* -+ * This needs to be called **before** heap_init_late() so modules -+ * will be scrubbed (unless suppressed). -+ */ -+ discard_initial_modules(); -+ - heap_init_late(); - - init_trace_bufs(); --- -2.17.1 - diff --git a/xsa373-4.14-1.patch b/xsa373-4.14-1.patch deleted file mode 100644 index ee5229a..0000000 --- a/xsa373-4.14-1.patch +++ /dev/null @@ -1,120 +0,0 @@ -From: Jan Beulich -Subject: VT-d: size qinval queue dynamically - -With the present synchronous model, we need two slots for every -operation (the operation itself and a wait descriptor). There can be -one such pair of requests pending per CPU. To ensure that under all -normal circumstances a slot is always available when one is requested, -size the queue ring according to the number of present CPUs. - -This is part of XSA-373 / CVE-2021-28692. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/vtd/iommu.h -+++ b/xen/drivers/passthrough/vtd/iommu.h -@@ -450,17 +450,9 @@ struct qinval_entry { - }q; - }; - --/* Order of queue invalidation pages(max is 8) */ --#define QINVAL_PAGE_ORDER 2 -- --#define QINVAL_ARCH_PAGE_ORDER (QINVAL_PAGE_ORDER + PAGE_SHIFT_4K - PAGE_SHIFT) --#define QINVAL_ARCH_PAGE_NR ( QINVAL_ARCH_PAGE_ORDER < 0 ? \ -- 1 : \ -- 1 << QINVAL_ARCH_PAGE_ORDER ) -- - /* Each entry is 16 bytes, so 2^8 entries per page */ - #define QINVAL_ENTRY_ORDER ( PAGE_SHIFT - 4 ) --#define QINVAL_ENTRY_NR (1 << (QINVAL_PAGE_ORDER + 8)) -+#define QINVAL_MAX_ENTRY_NR (1u << (7 + QINVAL_ENTRY_ORDER)) - - /* Status data flag */ - #define QINVAL_STAT_INIT 0 ---- a/xen/drivers/passthrough/vtd/qinval.c -+++ b/xen/drivers/passthrough/vtd/qinval.c -@@ -31,6 +31,9 @@ - - #define VTD_QI_TIMEOUT 1 - -+static unsigned int __read_mostly qi_pg_order; -+static unsigned int __read_mostly qi_entry_nr; -+ - static int __must_check invalidate_sync(struct vtd_iommu *iommu); - - static void print_qi_regs(struct vtd_iommu *iommu) -@@ -55,7 +58,7 @@ static unsigned int qinval_next_index(st - tail >>= QINVAL_INDEX_SHIFT; - - /* (tail+1 == head) indicates a full queue, wait for HW */ -- while ( ( tail + 1 ) % QINVAL_ENTRY_NR == -+ while ( ((tail + 1) & (qi_entry_nr - 1)) == - ( dmar_readq(iommu->reg, DMAR_IQH_REG) >> QINVAL_INDEX_SHIFT ) ) - cpu_relax(); - -@@ -68,7 +71,7 @@ static void qinval_update_qtail(struct v - - /* Need hold register lock when update tail */ - ASSERT( spin_is_locked(&iommu->register_lock) ); -- val = (index + 1) % QINVAL_ENTRY_NR; -+ val = (index + 1) & (qi_entry_nr - 1); - dmar_writeq(iommu->reg, DMAR_IQT_REG, (val << QINVAL_INDEX_SHIFT)); - } - -@@ -403,8 +406,28 @@ int enable_qinval(struct vtd_iommu *iomm - - if ( iommu->qinval_maddr == 0 ) - { -- iommu->qinval_maddr = alloc_pgtable_maddr(QINVAL_ARCH_PAGE_NR, -- iommu->node); -+ if ( !qi_entry_nr ) -+ { -+ /* -+ * With the present synchronous model, we need two slots for every -+ * operation (the operation itself and a wait descriptor). There -+ * can be one such pair of requests pending per CPU. One extra -+ * entry is needed as the ring is considered full when there's -+ * only one entry left. -+ */ -+ BUILD_BUG_ON(CONFIG_NR_CPUS * 2 >= QINVAL_MAX_ENTRY_NR); -+ qi_pg_order = get_order_from_bytes((num_present_cpus() * 2 + 1) << -+ (PAGE_SHIFT - -+ QINVAL_ENTRY_ORDER)); -+ qi_entry_nr = 1u << (qi_pg_order + QINVAL_ENTRY_ORDER); -+ -+ dprintk(XENLOG_INFO VTDPREFIX, -+ "QI: using %u-entry ring(s)\n", qi_entry_nr); -+ } -+ -+ iommu->qinval_maddr = -+ alloc_pgtable_maddr(qi_entry_nr >> QINVAL_ENTRY_ORDER, -+ iommu->node); - if ( iommu->qinval_maddr == 0 ) - { - dprintk(XENLOG_WARNING VTDPREFIX, -@@ -418,15 +441,16 @@ int enable_qinval(struct vtd_iommu *iomm - - spin_lock_irqsave(&iommu->register_lock, flags); - -- /* Setup Invalidation Queue Address(IQA) register with the -- * address of the page we just allocated. QS field at -- * bits[2:0] to indicate size of queue is one 4KB page. -- * That's 256 entries. Queued Head (IQH) and Queue Tail (IQT) -- * registers are automatically reset to 0 with write -- * to IQA register. -+ /* -+ * Setup Invalidation Queue Address (IQA) register with the address of the -+ * pages we just allocated. The QS field at bits[2:0] indicates the size -+ * (page order) of the queue. -+ * -+ * Queued Head (IQH) and Queue Tail (IQT) registers are automatically -+ * reset to 0 with write to IQA register. - */ - dmar_writeq(iommu->reg, DMAR_IQA_REG, -- iommu->qinval_maddr | QINVAL_PAGE_ORDER); -+ iommu->qinval_maddr | qi_pg_order); - - dmar_writeq(iommu->reg, DMAR_IQT_REG, 0); - diff --git a/xsa373-4.14-2.patch b/xsa373-4.14-2.patch deleted file mode 100644 index 773cbfd..0000000 --- a/xsa373-4.14-2.patch +++ /dev/null @@ -1,102 +0,0 @@ -From: Jan Beulich -Subject: AMD/IOMMU: size command buffer dynamically - -With the present synchronous model, we need two slots for every -operation (the operation itself and a wait command). There can be one -such pair of commands pending per CPU. To ensure that under all normal -circumstances a slot is always available when one is requested, size the -command ring according to the number of present CPUs. - -This is part of XSA-373 / CVE-2021-28692. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/amd/iommu-defs.h -+++ b/xen/drivers/passthrough/amd/iommu-defs.h -@@ -20,9 +20,6 @@ - #ifndef AMD_IOMMU_DEFS_H - #define AMD_IOMMU_DEFS_H - --/* IOMMU Command Buffer entries: in power of 2 increments, minimum of 256 */ --#define IOMMU_CMD_BUFFER_DEFAULT_ENTRIES 512 -- - /* IOMMU Event Log entries: in power of 2 increments, minimum of 256 */ - #define IOMMU_EVENT_LOG_DEFAULT_ENTRIES 512 - -@@ -164,8 +161,8 @@ struct amd_iommu_dte { - #define IOMMU_CMD_BUFFER_LENGTH_MASK 0x0F000000 - #define IOMMU_CMD_BUFFER_LENGTH_SHIFT 24 - --#define IOMMU_CMD_BUFFER_ENTRY_SIZE 16 --#define IOMMU_CMD_BUFFER_POWER_OF2_ENTRIES_PER_PAGE 8 -+#define IOMMU_CMD_BUFFER_ENTRY_ORDER 4 -+#define IOMMU_CMD_BUFFER_MAX_ENTRIES (1u << 15) - - #define IOMMU_CMD_OPCODE_MASK 0xF0000000 - #define IOMMU_CMD_OPCODE_SHIFT 28 ---- a/xen/drivers/passthrough/amd/iommu_cmd.c -+++ b/xen/drivers/passthrough/amd/iommu_cmd.c -@@ -24,7 +24,7 @@ static int queue_iommu_command(struct am - { - uint32_t tail, head; - -- tail = iommu->cmd_buffer.tail + IOMMU_CMD_BUFFER_ENTRY_SIZE; -+ tail = iommu->cmd_buffer.tail + sizeof(cmd_entry_t); - if ( tail == iommu->cmd_buffer.size ) - tail = 0; - -@@ -33,7 +33,7 @@ static int queue_iommu_command(struct am - if ( head != tail ) - { - memcpy(iommu->cmd_buffer.buffer + iommu->cmd_buffer.tail, -- cmd, IOMMU_CMD_BUFFER_ENTRY_SIZE); -+ cmd, sizeof(cmd_entry_t)); - - iommu->cmd_buffer.tail = tail; - return 1; ---- a/xen/drivers/passthrough/amd/iommu_init.c -+++ b/xen/drivers/passthrough/amd/iommu_init.c -@@ -118,7 +118,7 @@ static void register_iommu_cmd_buffer_in - writel(entry, iommu->mmio_base + IOMMU_CMD_BUFFER_BASE_LOW_OFFSET); - - power_of2_entries = get_order_from_bytes(iommu->cmd_buffer.size) + -- IOMMU_CMD_BUFFER_POWER_OF2_ENTRIES_PER_PAGE; -+ PAGE_SHIFT - IOMMU_CMD_BUFFER_ENTRY_ORDER; - - entry = 0; - iommu_set_addr_hi_to_reg(&entry, addr_hi); -@@ -1022,9 +1022,31 @@ static void *__init allocate_ring_buffer - static void * __init allocate_cmd_buffer(struct amd_iommu *iommu) - { - /* allocate 'command buffer' in power of 2 increments of 4K */ -+ static unsigned int __read_mostly nr_ents; -+ -+ if ( !nr_ents ) -+ { -+ unsigned int order; -+ -+ /* -+ * With the present synchronous model, we need two slots for every -+ * operation (the operation itself and a wait command). There can be -+ * one such pair of requests pending per CPU. One extra entry is -+ * needed as the ring is considered full when there's only one entry -+ * left. -+ */ -+ BUILD_BUG_ON(CONFIG_NR_CPUS * 2 >= IOMMU_CMD_BUFFER_MAX_ENTRIES); -+ order = get_order_from_bytes((num_present_cpus() * 2 + 1) << -+ IOMMU_CMD_BUFFER_ENTRY_ORDER); -+ nr_ents = 1u << (order + PAGE_SHIFT - IOMMU_CMD_BUFFER_ENTRY_ORDER); -+ -+ AMD_IOMMU_DEBUG("using %u-entry cmd ring(s)\n", nr_ents); -+ } -+ -+ BUILD_BUG_ON(sizeof(cmd_entry_t) != (1u << IOMMU_CMD_BUFFER_ENTRY_ORDER)); -+ - return allocate_ring_buffer(&iommu->cmd_buffer, sizeof(cmd_entry_t), -- IOMMU_CMD_BUFFER_DEFAULT_ENTRIES, -- "Command Buffer", false); -+ nr_ents, "Command Buffer", false); - } - - static void * __init allocate_event_log(struct amd_iommu *iommu) diff --git a/xsa373-4.14-3.patch b/xsa373-4.14-3.patch deleted file mode 100644 index fe34546..0000000 --- a/xsa373-4.14-3.patch +++ /dev/null @@ -1,163 +0,0 @@ -From: Jan Beulich -Subject: VT-d: eliminate flush related timeouts - -Leaving an in-progress operation pending when it appears to take too -long is problematic: If e.g. a QI command completed later, the write to -the "poll slot" may instead be understood to signal a subsequently -started command's completion. Also our accounting of the timeout period -was actually wrong: We included the time it took for the command to -actually make it to the front of the queue, which could be heavily -affected by guests other than the one for which the flush is being -performed. - -Do away with all timeout detection on all flush related code paths. -Log excessively long processing times (with a progressive threshold) to -have some indication of problems in this area. - -Additionally log (once) if qinval_next_index() didn't immediately find -an available slot. Together with the earlier change sizing the queue(s) -dynamically, we should now have a guarantee that with our fully -synchronous model any demand for slots can actually be satisfied. - -This is part of XSA-373 / CVE-2021-28692. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/vtd/dmar.h -+++ b/xen/drivers/passthrough/vtd/dmar.h -@@ -127,6 +127,34 @@ do { - } \ - } while (0) - -+#define IOMMU_FLUSH_WAIT(what, iommu, offset, op, cond, sts) \ -+do { \ -+ static unsigned int __read_mostly threshold = 1; \ -+ s_time_t start = NOW(); \ -+ s_time_t timeout = start + DMAR_OPERATION_TIMEOUT * threshold; \ -+ \ -+ for ( ; ; ) \ -+ { \ -+ sts = op(iommu->reg, offset); \ -+ if ( cond ) \ -+ break; \ -+ if ( timeout && NOW() > timeout ) \ -+ { \ -+ threshold |= threshold << 1; \ -+ printk(XENLOG_WARNING VTDPREFIX \ -+ " IOMMU#%u: %s flush taking too long\n", \ -+ iommu->index, what); \ -+ timeout = 0; \ -+ } \ -+ cpu_relax(); \ -+ } \ -+ \ -+ if ( !timeout ) \ -+ printk(XENLOG_WARNING VTDPREFIX \ -+ " IOMMU#%u: %s flush took %lums\n", \ -+ iommu->index, what, (NOW() - start) / 10000000); \ -+} while ( false ) -+ - int vtd_hw_check(void); - void disable_pmr(struct vtd_iommu *iommu); - int is_igd_drhd(struct acpi_drhd_unit *drhd); ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -326,8 +326,8 @@ static void iommu_flush_write_buffer(str - dmar_writel(iommu->reg, DMAR_GCMD_REG, val | DMA_GCMD_WBF); - - /* Make sure hardware complete it */ -- IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, dmar_readl, -- !(val & DMA_GSTS_WBFS), val); -+ IOMMU_FLUSH_WAIT("write buffer", iommu, DMAR_GSTS_REG, dmar_readl, -+ !(val & DMA_GSTS_WBFS), val); - - spin_unlock_irqrestore(&iommu->register_lock, flags); - } -@@ -376,8 +376,8 @@ int vtd_flush_context_reg(struct vtd_iom - dmar_writeq(iommu->reg, DMAR_CCMD_REG, val); - - /* Make sure hardware complete it */ -- IOMMU_WAIT_OP(iommu, DMAR_CCMD_REG, dmar_readq, -- !(val & DMA_CCMD_ICC), val); -+ IOMMU_FLUSH_WAIT("context", iommu, DMAR_CCMD_REG, dmar_readq, -+ !(val & DMA_CCMD_ICC), val); - - spin_unlock_irqrestore(&iommu->register_lock, flags); - /* flush context entry will implicitly flush write buffer */ -@@ -454,8 +454,8 @@ int vtd_flush_iotlb_reg(struct vtd_iommu - dmar_writeq(iommu->reg, tlb_offset + 8, val); - - /* Make sure hardware complete it */ -- IOMMU_WAIT_OP(iommu, (tlb_offset + 8), dmar_readq, -- !(val & DMA_TLB_IVT), val); -+ IOMMU_FLUSH_WAIT("iotlb", iommu, (tlb_offset + 8), dmar_readq, -+ !(val & DMA_TLB_IVT), val); - spin_unlock_irqrestore(&iommu->register_lock, flags); - - /* check IOTLB invalidation granularity */ ---- a/xen/drivers/passthrough/vtd/qinval.c -+++ b/xen/drivers/passthrough/vtd/qinval.c -@@ -29,8 +29,6 @@ - #include "extern.h" - #include "../ats.h" - --#define VTD_QI_TIMEOUT 1 -- - static unsigned int __read_mostly qi_pg_order; - static unsigned int __read_mostly qi_entry_nr; - -@@ -60,7 +58,11 @@ static unsigned int qinval_next_index(st - /* (tail+1 == head) indicates a full queue, wait for HW */ - while ( ((tail + 1) & (qi_entry_nr - 1)) == - ( dmar_readq(iommu->reg, DMAR_IQH_REG) >> QINVAL_INDEX_SHIFT ) ) -+ { -+ printk_once(XENLOG_ERR VTDPREFIX " IOMMU#%u: no QI slot available\n", -+ iommu->index); - cpu_relax(); -+ } - - return tail; - } -@@ -180,23 +182,32 @@ static int __must_check queue_invalidate - /* Now we don't support interrupt method */ - if ( sw ) - { -- s_time_t timeout; -- -- /* In case all wait descriptor writes to same addr with same data */ -- timeout = NOW() + MILLISECS(flush_dev_iotlb ? -- iommu_dev_iotlb_timeout : VTD_QI_TIMEOUT); -+ static unsigned int __read_mostly threshold = 1; -+ s_time_t start = NOW(); -+ s_time_t timeout = start + (flush_dev_iotlb -+ ? iommu_dev_iotlb_timeout -+ : 100) * MILLISECS(threshold); - - while ( ACCESS_ONCE(*this_poll_slot) != QINVAL_STAT_DONE ) - { -- if ( NOW() > timeout ) -+ if ( timeout && NOW() > timeout ) - { -- print_qi_regs(iommu); -+ threshold |= threshold << 1; - printk(XENLOG_WARNING VTDPREFIX -- " Queue invalidate wait descriptor timed out\n"); -- return -ETIMEDOUT; -+ " IOMMU#%u: QI%s wait descriptor taking too long\n", -+ iommu->index, flush_dev_iotlb ? " dev" : ""); -+ print_qi_regs(iommu); -+ timeout = 0; - } - cpu_relax(); - } -+ -+ if ( !timeout ) -+ printk(XENLOG_WARNING VTDPREFIX -+ " IOMMU#%u: QI%s wait descriptor took %lums\n", -+ iommu->index, flush_dev_iotlb ? " dev" : "", -+ (NOW() - start) / 10000000); -+ - return 0; - } - diff --git a/xsa373-4.14-4.patch b/xsa373-4.14-4.patch deleted file mode 100644 index a1f186b..0000000 --- a/xsa373-4.14-4.patch +++ /dev/null @@ -1,81 +0,0 @@ -From: Jan Beulich -Subject: AMD/IOMMU: wait for command slot to be available - -No caller cared about send_iommu_command() indicating unavailability of -a slot. Hence if a sufficient number prior commands timed out, we did -blindly assume that the requested command was submitted to the IOMMU -when really it wasn't. This could mean both a hanging system (waiting -for a command to complete that was never seen by the IOMMU) or blindly -propagating success back to callers, making them believe they're fine -to e.g. free previously unmapped pages. - -Fold the three involved functions into one, add spin waiting for an -available slot along the lines of VT-d's qinval_next_index(), and as a -consequence drop all error indicator return types/values. - -This is part of XSA-373 / CVE-2021-28692. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/amd/iommu_cmd.c -+++ b/xen/drivers/passthrough/amd/iommu_cmd.c -@@ -20,43 +20,32 @@ - #include "iommu.h" - #include "../ats.h" - --static int queue_iommu_command(struct amd_iommu *iommu, u32 cmd[]) -+static void send_iommu_command(struct amd_iommu *iommu, -+ const uint32_t cmd[4]) - { -- uint32_t tail, head; -+ uint32_t tail; - - tail = iommu->cmd_buffer.tail + sizeof(cmd_entry_t); - if ( tail == iommu->cmd_buffer.size ) - tail = 0; - -- head = readl(iommu->mmio_base + -- IOMMU_CMD_BUFFER_HEAD_OFFSET) & IOMMU_RING_BUFFER_PTR_MASK; -- if ( head != tail ) -+ while ( tail == (readl(iommu->mmio_base + -+ IOMMU_CMD_BUFFER_HEAD_OFFSET) & -+ IOMMU_RING_BUFFER_PTR_MASK) ) - { -- memcpy(iommu->cmd_buffer.buffer + iommu->cmd_buffer.tail, -- cmd, sizeof(cmd_entry_t)); -- -- iommu->cmd_buffer.tail = tail; -- return 1; -+ printk_once(XENLOG_ERR -+ "AMD IOMMU %04x:%02x:%02x.%u: no cmd slot available\n", -+ iommu->seg, PCI_BUS(iommu->bdf), -+ PCI_SLOT(iommu->bdf), PCI_FUNC(iommu->bdf)); -+ cpu_relax(); - } - -- return 0; --} -- --static void commit_iommu_command_buffer(struct amd_iommu *iommu) --{ -- writel(iommu->cmd_buffer.tail, -- iommu->mmio_base + IOMMU_CMD_BUFFER_TAIL_OFFSET); --} -+ memcpy(iommu->cmd_buffer.buffer + iommu->cmd_buffer.tail, -+ cmd, sizeof(cmd_entry_t)); - --static int send_iommu_command(struct amd_iommu *iommu, u32 cmd[]) --{ -- if ( queue_iommu_command(iommu, cmd) ) -- { -- commit_iommu_command_buffer(iommu); -- return 1; -- } -+ iommu->cmd_buffer.tail = tail; - -- return 0; -+ writel(tail, iommu->mmio_base + IOMMU_CMD_BUFFER_TAIL_OFFSET); - } - - static void flush_command_buffer(struct amd_iommu *iommu) diff --git a/xsa373-4.14-5.patch b/xsa373-4.14-5.patch deleted file mode 100644 index 01556a8..0000000 --- a/xsa373-4.14-5.patch +++ /dev/null @@ -1,143 +0,0 @@ -From: Jan Beulich -Subject: AMD/IOMMU: drop command completion timeout - -First and foremost - such timeouts were not signaled to callers, making -them believe they're fine to e.g. free previously unmapped pages. - -Mirror VT-d's behavior: A fixed number of loop iterations is not a -suitable way to detect timeouts in an environment (CPU and bus speeds) -independent manner anyway. Furthermore, leaving an in-progress operation -pending when it appears to take too long is problematic: If a command -completed later, the signaling of its completion may instead be -understood to signal a subsequently started command's completion. - -Log excessively long processing times (with a progressive threshold) to -have some indication of problems in this area. Allow callers to specify -a non-default timeout bias for this logging, using the same values as -VT-d does, which in particular means a (by default) much larger value -for device IO TLB invalidation. - -This is part of XSA-373 / CVE-2021-28692. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/amd/iommu_cmd.c -+++ b/xen/drivers/passthrough/amd/iommu_cmd.c -@@ -48,10 +48,12 @@ static void send_iommu_command(struct am - writel(tail, iommu->mmio_base + IOMMU_CMD_BUFFER_TAIL_OFFSET); - } - --static void flush_command_buffer(struct amd_iommu *iommu) -+static void flush_command_buffer(struct amd_iommu *iommu, -+ unsigned int timeout_base) - { -- unsigned int cmd[4], status, loop_count; -- bool comp_wait; -+ uint32_t cmd[4]; -+ s_time_t start, timeout; -+ static unsigned int __read_mostly threshold = 1; - - /* RW1C 'ComWaitInt' in status register */ - writel(IOMMU_STATUS_COMP_WAIT_INT, -@@ -67,22 +69,31 @@ static void flush_command_buffer(struct - IOMMU_COMP_WAIT_I_FLAG_SHIFT, &cmd[0]); - send_iommu_command(iommu, cmd); - -- /* Make loop_count long enough for polling completion wait bit */ -- loop_count = 1000; -- do { -- status = readl(iommu->mmio_base + IOMMU_STATUS_MMIO_OFFSET); -- comp_wait = status & IOMMU_STATUS_COMP_WAIT_INT; -- --loop_count; -- } while ( !comp_wait && loop_count ); -- -- if ( comp_wait ) -+ start = NOW(); -+ timeout = start + (timeout_base ?: 100) * MILLISECS(threshold); -+ while ( !(readl(iommu->mmio_base + IOMMU_STATUS_MMIO_OFFSET) & -+ IOMMU_STATUS_COMP_WAIT_INT) ) - { -- /* RW1C 'ComWaitInt' in status register */ -- writel(IOMMU_STATUS_COMP_WAIT_INT, -- iommu->mmio_base + IOMMU_STATUS_MMIO_OFFSET); -- return; -+ if ( timeout && NOW() > timeout ) -+ { -+ threshold |= threshold << 1; -+ printk(XENLOG_WARNING -+ "AMD IOMMU %04x:%02x:%02x.%u: %scompletion wait taking too long\n", -+ iommu->seg, PCI_BUS(iommu->bdf), -+ PCI_SLOT(iommu->bdf), PCI_FUNC(iommu->bdf), -+ timeout_base ? "iotlb " : ""); -+ timeout = 0; -+ } -+ cpu_relax(); - } -- AMD_IOMMU_DEBUG("Warning: ComWaitInt bit did not assert!\n"); -+ -+ if ( !timeout ) -+ printk(XENLOG_WARNING -+ "AMD IOMMU %04x:%02x:%02x.%u: %scompletion wait took %lums\n", -+ iommu->seg, PCI_BUS(iommu->bdf), -+ PCI_SLOT(iommu->bdf), PCI_FUNC(iommu->bdf), -+ timeout_base ? "iotlb " : "", -+ (NOW() - start) / 10000000); - } - - /* Build low level iommu command messages */ -@@ -294,7 +305,7 @@ void amd_iommu_flush_iotlb(u8 devfn, con - /* send INVALIDATE_IOTLB_PAGES command */ - spin_lock_irqsave(&iommu->lock, flags); - invalidate_iotlb_pages(iommu, maxpend, 0, queueid, daddr, req_id, order); -- flush_command_buffer(iommu); -+ flush_command_buffer(iommu, iommu_dev_iotlb_timeout); - spin_unlock_irqrestore(&iommu->lock, flags); - } - -@@ -331,7 +342,7 @@ static void _amd_iommu_flush_pages(struc - { - spin_lock_irqsave(&iommu->lock, flags); - invalidate_iommu_pages(iommu, daddr, dom_id, order); -- flush_command_buffer(iommu); -+ flush_command_buffer(iommu, 0); - spin_unlock_irqrestore(&iommu->lock, flags); - } - -@@ -355,7 +366,7 @@ void amd_iommu_flush_device(struct amd_i - ASSERT( spin_is_locked(&iommu->lock) ); - - invalidate_dev_table_entry(iommu, bdf); -- flush_command_buffer(iommu); -+ flush_command_buffer(iommu, 0); - } - - void amd_iommu_flush_intremap(struct amd_iommu *iommu, uint16_t bdf) -@@ -363,7 +374,7 @@ void amd_iommu_flush_intremap(struct amd - ASSERT( spin_is_locked(&iommu->lock) ); - - invalidate_interrupt_table(iommu, bdf); -- flush_command_buffer(iommu); -+ flush_command_buffer(iommu, 0); - } - - void amd_iommu_flush_all_caches(struct amd_iommu *iommu) -@@ -371,7 +382,7 @@ void amd_iommu_flush_all_caches(struct a - ASSERT( spin_is_locked(&iommu->lock) ); - - invalidate_iommu_all(iommu); -- flush_command_buffer(iommu); -+ flush_command_buffer(iommu, 0); - } - - void amd_iommu_send_guest_cmd(struct amd_iommu *iommu, u32 cmd[]) -@@ -381,7 +392,8 @@ void amd_iommu_send_guest_cmd(struct amd - spin_lock_irqsave(&iommu->lock, flags); - - send_iommu_command(iommu, cmd); -- flush_command_buffer(iommu); -+ /* TBD: Timeout selection may require peeking into cmd[]. */ -+ flush_command_buffer(iommu, 0); - - spin_unlock_irqrestore(&iommu->lock, flags); - } diff --git a/xsa375.patch b/xsa375.patch deleted file mode 100644 index aa2e5ad..0000000 --- a/xsa375.patch +++ /dev/null @@ -1,50 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Protect against Speculative Code Store Bypass - -Modern x86 processors have far-better-than-architecturally-guaranteed self -modifying code detection. Typically, when a write hits an instruction in -flight, a Machine Clear occurs to flush stale content in the frontend and -backend. - -For self modifying code, before a write which hits an instruction in flight -retires, the frontend can speculatively decode and execute the old instruction -stream. Speculation of this form can suffer from type confusion in registers, -and potentially leak data. - -Furthermore, updates are typically byte-wise, rather than atomic. Depending -on timing, speculation can race ahead multiple times between individual -writes, and execute the transiently-malformed instruction stream. - -Xen has stubs which are used in certain cases for emulation purposes. Inhibit -speculation between updating the stub and executing it. - -This is XSA-375 / CVE-2021-0089. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c -index 8889509d2a..11467a1e3a 100644 ---- a/xen/arch/x86/pv/emul-priv-op.c -+++ b/xen/arch/x86/pv/emul-priv-op.c -@@ -138,6 +138,8 @@ static io_emul_stub_t *io_emul_stub_setup(struct priv_op_ctxt *ctxt, u8 opcode, - /* Runtime confirmation that we haven't clobbered an adjacent stub. */ - BUG_ON(STUB_BUF_SIZE / 2 < (p - ctxt->io_emul_stub)); - -+ block_speculation(); /* SCSB */ -+ - /* Handy function-typed pointer to the stub. */ - return (void *)stub_va; - -diff --git a/xen/arch/x86/x86_emulate/x86_emulate.c b/xen/arch/x86/x86_emulate/x86_emulate.c -index c25d88d0d8..f42ff2a837 100644 ---- a/xen/arch/x86/x86_emulate/x86_emulate.c -+++ b/xen/arch/x86/x86_emulate/x86_emulate.c -@@ -1257,6 +1257,7 @@ static inline int mkec(uint8_t e, int32_t ec, ...) - # define invoke_stub(pre, post, constraints...) do { \ - stub_exn.info = (union stub_exception_token) { .raw = ~0 }; \ - stub_exn.line = __LINE__; /* Utility outweighs livepatching cost */ \ -+ block_speculation(); /* SCSB */ \ - asm volatile ( pre "\n\tINDIRECT_CALL %[stub]\n\t" post "\n" \ - ".Lret%=:\n\t" \ - ".pushsection .fixup,\"ax\"\n" \ diff --git a/xsa377.patch b/xsa377.patch deleted file mode 100644 index 1a1887b..0000000 --- a/xsa377.patch +++ /dev/null @@ -1,27 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Mitigate TAA after S3 resume - -The user chosen setting for MSR_TSX_CTRL needs restoring after S3. - -All APs get the correct setting via start_secondary(), but the BSP was missed -out. - -This is XSA-377 / CVE-2021-28690. - -Fixes: 8c4330818f6 ("x86/spec-ctrl: Mitigate the TSX Asynchronous Abort sidechannel") -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/acpi/power.c b/xen/arch/x86/acpi/power.c -index 91a8c4d0bd..31a56f02d0 100644 ---- a/xen/arch/x86/acpi/power.c -+++ b/xen/arch/x86/acpi/power.c -@@ -288,6 +288,8 @@ static int enter_state(u32 state) - - microcode_update_one(); - -+ tsx_init(); /* Needs microcode. May change HLE/RTM feature bits. */ -+ - if ( !recheck_cpu_features(0) ) - panic("Missing previously available feature(s)\n"); - diff --git a/xsa378-4.14-1.patch b/xsa378-4.14-1.patch deleted file mode 100644 index 73956ea..0000000 --- a/xsa378-4.14-1.patch +++ /dev/null @@ -1,142 +0,0 @@ -From: Jan Beulich -Subject: AMD/IOMMU: correct global exclusion range extending - -Besides unity mapping regions, the AMD IOMMU spec also provides for -exclusion ranges (areas of memory not to be subject to DMA translation) -to be specified by firmware in the ACPI tables. The spec does not put -any constraints on the number of such regions. - -Blindly assuming all addresses between any two such ranges should also -be excluded can't be right. Since hardware has room for just a single -such range (comprised of the Exclusion Base Register and the Exclusion -Range Limit Register), combine only adjacent or overlapping regions (for -now; this may require further adjustment in case table entries aren't -sorted by address) with matching exclusion_allow_all settings. This -requires bubbling up error indicators, such that IOMMU init can be -failed when concatenation wasn't possible. - -Furthermore, since the exclusion range specified in IOMMU registers -implies R/W access, reject requests asking for less permissions (this -will be brought closer to the spec by a subsequent change). - -This is part of XSA-378 / CVE-2021-28695. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/amd/iommu_acpi.c -+++ b/xen/drivers/passthrough/amd/iommu_acpi.c -@@ -117,12 +117,21 @@ static struct amd_iommu * __init find_io - return NULL; - } - --static void __init reserve_iommu_exclusion_range( -- struct amd_iommu *iommu, uint64_t base, uint64_t limit) -+static int __init reserve_iommu_exclusion_range( -+ struct amd_iommu *iommu, uint64_t base, uint64_t limit, -+ bool all, bool iw, bool ir) - { -+ if ( !ir || !iw ) -+ return -EPERM; -+ - /* need to extend exclusion range? */ - if ( iommu->exclusion_enable ) - { -+ if ( iommu->exclusion_limit + PAGE_SIZE < base || -+ limit + PAGE_SIZE < iommu->exclusion_base || -+ iommu->exclusion_allow_all != all ) -+ return -EBUSY; -+ - if ( iommu->exclusion_base < base ) - base = iommu->exclusion_base; - if ( iommu->exclusion_limit > limit ) -@@ -130,16 +139,11 @@ static void __init reserve_iommu_exclusi - } - - iommu->exclusion_enable = IOMMU_CONTROL_ENABLED; -+ iommu->exclusion_allow_all = all; - iommu->exclusion_base = base; - iommu->exclusion_limit = limit; --} - --static void __init reserve_iommu_exclusion_range_all( -- struct amd_iommu *iommu, -- unsigned long base, unsigned long limit) --{ -- reserve_iommu_exclusion_range(iommu, base, limit); -- iommu->exclusion_allow_all = IOMMU_CONTROL_ENABLED; -+ return 0; - } - - static void __init reserve_unity_map_for_device( -@@ -177,6 +181,7 @@ static int __init register_exclusion_ran - unsigned long range_top, iommu_top, length; - struct amd_iommu *iommu; - unsigned int bdf; -+ int rc = 0; - - /* is part of exclusion range inside of IOMMU virtual address space? */ - /* note: 'limit' parameter is assumed to be page-aligned */ -@@ -198,10 +203,15 @@ static int __init register_exclusion_ran - if ( limit >= iommu_top ) - { - for_each_amd_iommu( iommu ) -- reserve_iommu_exclusion_range_all(iommu, base, limit); -+ { -+ rc = reserve_iommu_exclusion_range(iommu, base, limit, -+ true /* all */, iw, ir); -+ if ( rc ) -+ break; -+ } - } - -- return 0; -+ return rc; - } - - static int __init register_exclusion_range_for_device( -@@ -212,6 +222,7 @@ static int __init register_exclusion_ran - unsigned long range_top, iommu_top, length; - struct amd_iommu *iommu; - u16 req; -+ int rc = 0; - - iommu = find_iommu_for_device(seg, bdf); - if ( !iommu ) -@@ -241,12 +252,13 @@ static int __init register_exclusion_ran - /* register IOMMU exclusion range settings for device */ - if ( limit >= iommu_top ) - { -- reserve_iommu_exclusion_range(iommu, base, limit); -+ rc = reserve_iommu_exclusion_range(iommu, base, limit, -+ false /* all */, iw, ir); - ivrs_mappings[bdf].dte_allow_exclusion = true; - ivrs_mappings[req].dte_allow_exclusion = true; - } - -- return 0; -+ return rc; - } - - static int __init register_exclusion_range_for_iommu_devices( -@@ -256,6 +268,7 @@ static int __init register_exclusion_ran - unsigned long range_top, iommu_top, length; - unsigned int bdf; - u16 req; -+ int rc = 0; - - /* is part of exclusion range inside of IOMMU virtual address space? */ - /* note: 'limit' parameter is assumed to be page-aligned */ -@@ -286,8 +299,10 @@ static int __init register_exclusion_ran - - /* register IOMMU exclusion range settings */ - if ( limit >= iommu_top ) -- reserve_iommu_exclusion_range_all(iommu, base, limit); -- return 0; -+ rc = reserve_iommu_exclusion_range(iommu, base, limit, -+ true /* all */, iw, ir); -+ -+ return rc; - } - - static int __init parse_ivmd_device_select( diff --git a/xsa378-4.14-2.patch b/xsa378-4.14-2.patch deleted file mode 100644 index 5d44561..0000000 --- a/xsa378-4.14-2.patch +++ /dev/null @@ -1,218 +0,0 @@ -From: Jan Beulich -Subject: AMD/IOMMU: correct device unity map handling - -Blindly assuming all addresses between any two such ranges, specified by -firmware in the ACPI tables, should also be unity-mapped can't be right. -Nor can it be correct to merge ranges with differing permissions. Track -ranges individually; don't merge at all, but check for overlaps instead. -This requires bubbling up error indicators, such that IOMMU init can be -failed when allocation of a new tracking struct wasn't possible, or an -overlap was detected. - -At this occasion also stop ignoring -amd_iommu_reserve_domain_unity_map()'s return value. - -This is part of XSA-378 / CVE-2021-28695. - -Signed-off-by: Jan Beulich -Reviewed-by: George Dunlap -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/amd/iommu.h -+++ b/xen/drivers/passthrough/amd/iommu.h -@@ -107,20 +107,24 @@ struct amd_iommu { - struct list_head ats_devices; - }; - -+struct ivrs_unity_map { -+ bool read:1; -+ bool write:1; -+ paddr_t addr; -+ unsigned long length; -+ struct ivrs_unity_map *next; -+}; -+ - struct ivrs_mappings { - uint16_t dte_requestor_id; - bool valid:1; - bool dte_allow_exclusion:1; -- bool unity_map_enable:1; -- bool write_permission:1; -- bool read_permission:1; - - /* ivhd device data settings */ - uint8_t device_flags; - -- unsigned long addr_range_start; -- unsigned long addr_range_length; - struct amd_iommu *iommu; -+ struct ivrs_unity_map *unity_map; - - /* per device interrupt remapping table */ - void *intremap_table; ---- a/xen/drivers/passthrough/amd/iommu_acpi.c -+++ b/xen/drivers/passthrough/amd/iommu_acpi.c -@@ -146,32 +146,48 @@ static int __init reserve_iommu_exclusio - return 0; - } - --static void __init reserve_unity_map_for_device( -- u16 seg, u16 bdf, unsigned long base, -- unsigned long length, u8 iw, u8 ir) -+static int __init reserve_unity_map_for_device( -+ uint16_t seg, uint16_t bdf, unsigned long base, -+ unsigned long length, bool iw, bool ir) - { - struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(seg); -- unsigned long old_top, new_top; -+ struct ivrs_unity_map *unity_map = ivrs_mappings[bdf].unity_map; - -- /* need to extend unity-mapped range? */ -- if ( ivrs_mappings[bdf].unity_map_enable ) -+ /* Check for overlaps. */ -+ for ( ; unity_map; unity_map = unity_map->next ) - { -- old_top = ivrs_mappings[bdf].addr_range_start + -- ivrs_mappings[bdf].addr_range_length; -- new_top = base + length; -- if ( old_top > new_top ) -- new_top = old_top; -- if ( ivrs_mappings[bdf].addr_range_start < base ) -- base = ivrs_mappings[bdf].addr_range_start; -- length = new_top - base; -- } -- -- /* extend r/w permissioms and keep aggregate */ -- ivrs_mappings[bdf].write_permission = iw; -- ivrs_mappings[bdf].read_permission = ir; -- ivrs_mappings[bdf].unity_map_enable = true; -- ivrs_mappings[bdf].addr_range_start = base; -- ivrs_mappings[bdf].addr_range_length = length; -+ /* -+ * Exact matches are okay. This can in particular happen when -+ * register_exclusion_range_for_device() calls here twice for the -+ * same (s,b,d,f). -+ */ -+ if ( base == unity_map->addr && length == unity_map->length && -+ ir == unity_map->read && iw == unity_map->write ) -+ return 0; -+ -+ if ( unity_map->addr + unity_map->length > base && -+ base + length > unity_map->addr ) -+ { -+ AMD_IOMMU_DEBUG("IVMD Error: overlap [%lx,%lx) vs [%lx,%lx)\n", -+ base, base + length, unity_map->addr, -+ unity_map->addr + unity_map->length); -+ return -EPERM; -+ } -+ } -+ -+ /* Populate and insert a new unity map. */ -+ unity_map = xmalloc(struct ivrs_unity_map); -+ if ( !unity_map ) -+ return -ENOMEM; -+ -+ unity_map->read = ir; -+ unity_map->write = iw; -+ unity_map->addr = base; -+ unity_map->length = length; -+ unity_map->next = ivrs_mappings[bdf].unity_map; -+ ivrs_mappings[bdf].unity_map = unity_map; -+ -+ return 0; - } - - static int __init register_exclusion_range_for_all_devices( -@@ -194,13 +210,13 @@ static int __init register_exclusion_ran - length = range_top - base; - /* reserve r/w unity-mapped page entries for devices */ - /* note: these entries are part of the exclusion range */ -- for ( bdf = 0; bdf < ivrs_bdf_entries; bdf++ ) -- reserve_unity_map_for_device(seg, bdf, base, length, iw, ir); -+ for ( bdf = 0; !rc && bdf < ivrs_bdf_entries; bdf++ ) -+ rc = reserve_unity_map_for_device(seg, bdf, base, length, iw, ir); - /* push 'base' just outside of virtual address space */ - base = iommu_top; - } - /* register IOMMU exclusion range settings */ -- if ( limit >= iommu_top ) -+ if ( !rc && limit >= iommu_top ) - { - for_each_amd_iommu( iommu ) - { -@@ -242,15 +258,15 @@ static int __init register_exclusion_ran - length = range_top - base; - /* reserve unity-mapped page entries for device */ - /* note: these entries are part of the exclusion range */ -- reserve_unity_map_for_device(seg, bdf, base, length, iw, ir); -- reserve_unity_map_for_device(seg, req, base, length, iw, ir); -+ rc = reserve_unity_map_for_device(seg, bdf, base, length, iw, ir) ?: -+ reserve_unity_map_for_device(seg, req, base, length, iw, ir); - - /* push 'base' just outside of virtual address space */ - base = iommu_top; - } - - /* register IOMMU exclusion range settings for device */ -- if ( limit >= iommu_top ) -+ if ( !rc && limit >= iommu_top ) - { - rc = reserve_iommu_exclusion_range(iommu, base, limit, - false /* all */, iw, ir); -@@ -281,15 +297,15 @@ static int __init register_exclusion_ran - length = range_top - base; - /* reserve r/w unity-mapped page entries for devices */ - /* note: these entries are part of the exclusion range */ -- for ( bdf = 0; bdf < ivrs_bdf_entries; bdf++ ) -+ for ( bdf = 0; !rc && bdf < ivrs_bdf_entries; bdf++ ) - { - if ( iommu == find_iommu_for_device(iommu->seg, bdf) ) - { -- reserve_unity_map_for_device(iommu->seg, bdf, base, length, -- iw, ir); - req = get_ivrs_mappings(iommu->seg)[bdf].dte_requestor_id; -- reserve_unity_map_for_device(iommu->seg, req, base, length, -- iw, ir); -+ rc = reserve_unity_map_for_device(iommu->seg, bdf, base, length, -+ iw, ir) ?: -+ reserve_unity_map_for_device(iommu->seg, req, base, length, -+ iw, ir); - } - } - -@@ -298,7 +314,7 @@ static int __init register_exclusion_ran - } - - /* register IOMMU exclusion range settings */ -- if ( limit >= iommu_top ) -+ if ( !rc && limit >= iommu_top ) - rc = reserve_iommu_exclusion_range(iommu, base, limit, - true /* all */, iw, ir); - ---- a/xen/drivers/passthrough/amd/pci_amd_iommu.c -+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c -@@ -366,15 +366,17 @@ static int amd_iommu_assign_device(struc - struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(pdev->seg); - int bdf = PCI_BDF2(pdev->bus, devfn); - int req_id = get_dma_requestor_id(pdev->seg, bdf); -+ const struct ivrs_unity_map *unity_map; - -- if ( ivrs_mappings[req_id].unity_map_enable ) -+ for ( unity_map = ivrs_mappings[req_id].unity_map; unity_map; -+ unity_map = unity_map->next ) - { -- amd_iommu_reserve_domain_unity_map( -- d, -- ivrs_mappings[req_id].addr_range_start, -- ivrs_mappings[req_id].addr_range_length, -- ivrs_mappings[req_id].write_permission, -- ivrs_mappings[req_id].read_permission); -+ int rc = amd_iommu_reserve_domain_unity_map( -+ d, unity_map->addr, unity_map->length, -+ unity_map->write, unity_map->read); -+ -+ if ( rc ) -+ return rc; - } - - return reassign_device(pdev->domain, d, devfn, pdev); diff --git a/xsa378-4.14-3.patch b/xsa378-4.14-3.patch deleted file mode 100644 index 90474ad..0000000 --- a/xsa378-4.14-3.patch +++ /dev/null @@ -1,102 +0,0 @@ -From: Jan Beulich -Subject: IOMMU: also pass p2m_access_t to p2m_get_iommu_flags() - -A subsequent change will want to customize the IOMMU permissions based -on this. - -This is part of XSA-378. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/arch/x86/mm/p2m-ept.c -+++ b/xen/arch/x86/mm/p2m-ept.c -@@ -680,7 +680,7 @@ ept_set_entry(struct p2m_domain *p2m, gf - uint8_t ipat = 0; - bool_t need_modify_vtd_table = 1; - bool_t vtd_pte_present = 0; -- unsigned int iommu_flags = p2m_get_iommu_flags(p2mt, mfn); -+ unsigned int iommu_flags = p2m_get_iommu_flags(p2mt, p2ma, mfn); - bool_t needs_sync = 1; - ept_entry_t old_entry = { .epte = 0 }; - ept_entry_t new_entry = { .epte = 0 }; -@@ -808,8 +808,8 @@ ept_set_entry(struct p2m_domain *p2m, gf - - /* Safe to read-then-write because we hold the p2m lock */ - if ( ept_entry->mfn == new_entry.mfn && -- p2m_get_iommu_flags(ept_entry->sa_p2mt, _mfn(ept_entry->mfn)) == -- iommu_flags ) -+ p2m_get_iommu_flags(ept_entry->sa_p2mt, ept_entry->access, -+ _mfn(ept_entry->mfn)) == iommu_flags ) - need_modify_vtd_table = 0; - - ept_p2m_type_to_flags(p2m, &new_entry); ---- a/xen/arch/x86/mm/p2m-pt.c -+++ b/xen/arch/x86/mm/p2m-pt.c -@@ -480,6 +480,16 @@ int p2m_pt_handle_deferred_changes(uint6 - return rc; - } - -+/* Reconstruct a fake p2m_access_t from stored PTE flags. */ -+static p2m_access_t p2m_flags_to_access(unsigned int flags) -+{ -+ if ( flags & _PAGE_PRESENT ) -+ return p2m_access_n; -+ -+ /* No need to look at _PAGE_NX for now. */ -+ return flags & _PAGE_RW ? p2m_access_rw : p2m_access_r; -+} -+ - /* Checks only applicable to entries with order > PAGE_ORDER_4K */ - static void check_entry(mfn_t mfn, p2m_type_t new, p2m_type_t old, - unsigned int order) -@@ -514,7 +524,7 @@ p2m_pt_set_entry(struct p2m_domain *p2m, - l2_pgentry_t l2e_content; - l3_pgentry_t l3e_content; - int rc; -- unsigned int iommu_pte_flags = p2m_get_iommu_flags(p2mt, mfn); -+ unsigned int iommu_pte_flags = p2m_get_iommu_flags(p2mt, p2ma, mfn); - /* - * old_mfn and iommu_old_flags control possible flush/update needs on the - * IOMMU: We need to flush when MFN or flags (i.e. permissions) change. -@@ -577,6 +587,7 @@ p2m_pt_set_entry(struct p2m_domain *p2m, - old_mfn = l1e_get_pfn(*p2m_entry); - iommu_old_flags = - p2m_get_iommu_flags(p2m_flags_to_type(flags), -+ p2m_flags_to_access(flags), - _mfn(old_mfn)); - } - else -@@ -619,9 +630,10 @@ p2m_pt_set_entry(struct p2m_domain *p2m, - 0, L1_PAGETABLE_ENTRIES); - ASSERT(p2m_entry); - old_mfn = l1e_get_pfn(*p2m_entry); -+ flags = l1e_get_flags(*p2m_entry); - iommu_old_flags = -- p2m_get_iommu_flags(p2m_flags_to_type(l1e_get_flags(*p2m_entry)), -- _mfn(old_mfn)); -+ p2m_get_iommu_flags(p2m_flags_to_type(flags), -+ p2m_flags_to_access(flags), _mfn(old_mfn)); - - if ( mfn_valid(mfn) || p2m_allows_invalid_mfn(p2mt) ) - entry_content = p2m_l1e_from_pfn(mfn_x(mfn), -@@ -649,6 +661,7 @@ p2m_pt_set_entry(struct p2m_domain *p2m, - old_mfn = l1e_get_pfn(*p2m_entry); - iommu_old_flags = - p2m_get_iommu_flags(p2m_flags_to_type(flags), -+ p2m_flags_to_access(flags), - _mfn(old_mfn)); - } - else ---- a/xen/include/asm-x86/p2m.h -+++ b/xen/include/asm-x86/p2m.h -@@ -903,7 +903,8 @@ static inline void p2m_altp2m_check(stru - /* - * p2m type to IOMMU flags - */ --static inline unsigned int p2m_get_iommu_flags(p2m_type_t p2mt, mfn_t mfn) -+static inline unsigned int p2m_get_iommu_flags(p2m_type_t p2mt, -+ p2m_access_t p2ma, mfn_t mfn) - { - unsigned int flags; - diff --git a/xsa378-4.14-4.patch b/xsa378-4.14-4.patch deleted file mode 100644 index 62e63fd..0000000 --- a/xsa378-4.14-4.patch +++ /dev/null @@ -1,385 +0,0 @@ -From: Jan Beulich -Subject: IOMMU: generalize VT-d's tracking of mapped RMRR regions - -In order to re-use it elsewhere, move the logic to vendor independent -code and strip it of RMRR specifics. - -Note that the prior "map" parameter gets folded into the new "p2ma" one -(which AMD IOMMU code will want to make use of), assigning alternative -meaning ("unmap") to p2m_access_x. Prepare set_identity_p2m_entry() and -p2m_get_iommu_flags() for getting passed access types other than -p2m_access_rw (in the latter case just for p2m_mmio_direct requests). - -Note also that, to be on the safe side, an overlap check gets added to -the main loop of iommu_identity_mapping(). - -This is part of XSA-378. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/arch/x86/mm/p2m.c -+++ b/xen/arch/x86/mm/p2m.c -@@ -1353,7 +1353,7 @@ int set_identity_p2m_entry(struct domain - if ( !is_iommu_enabled(d) ) - return 0; - return iommu_legacy_map(d, _dfn(gfn_l), _mfn(gfn_l), PAGE_ORDER_4K, -- IOMMUF_readable | IOMMUF_writable); -+ p2m_access_to_iommu_flags(p2ma)); - } - - gfn_lock(p2m, gfn, 0); ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -42,12 +42,6 @@ - #include "vtd.h" - #include "../ats.h" - --struct mapped_rmrr { -- struct list_head list; -- u64 base, end; -- unsigned int count; --}; -- - /* Possible unfiltered LAPIC/MSI messages from untrusted sources? */ - bool __read_mostly untrusted_msi; - -@@ -1800,17 +1794,12 @@ out: - static void iommu_domain_teardown(struct domain *d) - { - struct domain_iommu *hd = dom_iommu(d); -- struct mapped_rmrr *mrmrr, *tmp; - const struct acpi_drhd_unit *drhd; - - if ( list_empty(&acpi_drhd_units) ) - return; - -- list_for_each_entry_safe ( mrmrr, tmp, &hd->arch.mapped_rmrrs, list ) -- { -- list_del(&mrmrr->list); -- xfree(mrmrr); -- } -+ iommu_identity_map_teardown(d); - - ASSERT(is_iommu_enabled(d)); - -@@ -1966,74 +1955,6 @@ static void iommu_set_pgd(struct domain - pagetable_get_paddr(pagetable_from_mfn(pgd_mfn)); - } - --static int rmrr_identity_mapping(struct domain *d, bool_t map, -- const struct acpi_rmrr_unit *rmrr, -- u32 flag) --{ -- unsigned long base_pfn = rmrr->base_address >> PAGE_SHIFT_4K; -- unsigned long end_pfn = PAGE_ALIGN_4K(rmrr->end_address) >> PAGE_SHIFT_4K; -- struct mapped_rmrr *mrmrr; -- struct domain_iommu *hd = dom_iommu(d); -- -- ASSERT(pcidevs_locked()); -- ASSERT(rmrr->base_address < rmrr->end_address); -- -- /* -- * No need to acquire hd->arch.mapping_lock: Both insertion and removal -- * get done while holding pcidevs_lock. -- */ -- list_for_each_entry( mrmrr, &hd->arch.mapped_rmrrs, list ) -- { -- if ( mrmrr->base == rmrr->base_address && -- mrmrr->end == rmrr->end_address ) -- { -- int ret = 0; -- -- if ( map ) -- { -- ++mrmrr->count; -- return 0; -- } -- -- if ( --mrmrr->count ) -- return 0; -- -- while ( base_pfn < end_pfn ) -- { -- if ( clear_identity_p2m_entry(d, base_pfn) ) -- ret = -ENXIO; -- base_pfn++; -- } -- -- list_del(&mrmrr->list); -- xfree(mrmrr); -- return ret; -- } -- } -- -- if ( !map ) -- return -ENOENT; -- -- while ( base_pfn < end_pfn ) -- { -- int err = set_identity_p2m_entry(d, base_pfn, p2m_access_rw, flag); -- -- if ( err ) -- return err; -- base_pfn++; -- } -- -- mrmrr = xmalloc(struct mapped_rmrr); -- if ( !mrmrr ) -- return -ENOMEM; -- mrmrr->base = rmrr->base_address; -- mrmrr->end = rmrr->end_address; -- mrmrr->count = 1; -- list_add_tail(&mrmrr->list, &hd->arch.mapped_rmrrs); -- -- return 0; --} -- - static int intel_iommu_add_device(u8 devfn, struct pci_dev *pdev) - { - struct acpi_rmrr_unit *rmrr; -@@ -2065,7 +1986,9 @@ static int intel_iommu_add_device(u8 dev - * Since RMRRs are always reserved in the e820 map for the hardware - * domain, there shouldn't be a conflict. - */ -- ret = rmrr_identity_mapping(pdev->domain, 1, rmrr, 0); -+ ret = iommu_identity_mapping(pdev->domain, p2m_access_rw, -+ rmrr->base_address, rmrr->end_address, -+ 0); - if ( ret ) - dprintk(XENLOG_ERR VTDPREFIX, "d%d: RMRR mapping failed\n", - pdev->domain->domain_id); -@@ -2110,7 +2033,8 @@ static int intel_iommu_remove_device(u8 - * Any flag is nothing to clear these mappings but here - * its always safe and strict to set 0. - */ -- rmrr_identity_mapping(pdev->domain, 0, rmrr, 0); -+ iommu_identity_mapping(pdev->domain, p2m_access_x, rmrr->base_address, -+ rmrr->end_address, 0); - } - - return domain_context_unmap(pdev->domain, devfn, pdev); -@@ -2309,7 +2233,8 @@ static void __hwdom_init setup_hwdom_rmr - * domain, there shouldn't be a conflict. So its always safe and - * strict to set 0. - */ -- ret = rmrr_identity_mapping(d, 1, rmrr, 0); -+ ret = iommu_identity_mapping(d, p2m_access_rw, rmrr->base_address, -+ rmrr->end_address, 0); - if ( ret ) - dprintk(XENLOG_ERR VTDPREFIX, - "IOMMU: mapping reserved region failed\n"); -@@ -2480,7 +2405,9 @@ static int reassign_device_ownership( - * Any RMRR flag is always ignored when remove a device, - * but its always safe and strict to set 0. - */ -- ret = rmrr_identity_mapping(source, 0, rmrr, 0); -+ ret = iommu_identity_mapping(source, p2m_access_x, -+ rmrr->base_address, -+ rmrr->end_address, 0); - if ( ret != -ENOENT ) - return ret; - } -@@ -2577,7 +2504,8 @@ static int intel_iommu_assign_device( - PCI_BUS(bdf) == bus && - PCI_DEVFN2(bdf) == devfn ) - { -- ret = rmrr_identity_mapping(d, 1, rmrr, flag); -+ ret = iommu_identity_mapping(d, p2m_access_rw, rmrr->base_address, -+ rmrr->end_address, flag); - if ( ret ) - { - int rc; ---- a/xen/drivers/passthrough/x86/iommu.c -+++ b/xen/drivers/passthrough/x86/iommu.c -@@ -139,7 +139,7 @@ int arch_iommu_domain_init(struct domain - struct domain_iommu *hd = dom_iommu(d); - - spin_lock_init(&hd->arch.mapping_lock); -- INIT_LIST_HEAD(&hd->arch.mapped_rmrrs); -+ INIT_LIST_HEAD(&hd->arch.identity_maps); - - return 0; - } -@@ -148,6 +148,99 @@ void arch_iommu_domain_destroy(struct do - { - } - -+struct identity_map { -+ struct list_head list; -+ paddr_t base, end; -+ p2m_access_t access; -+ unsigned int count; -+}; -+ -+int iommu_identity_mapping(struct domain *d, p2m_access_t p2ma, -+ paddr_t base, paddr_t end, -+ unsigned int flag) -+{ -+ unsigned long base_pfn = base >> PAGE_SHIFT_4K; -+ unsigned long end_pfn = PAGE_ALIGN_4K(end) >> PAGE_SHIFT_4K; -+ struct identity_map *map; -+ struct domain_iommu *hd = dom_iommu(d); -+ -+ ASSERT(pcidevs_locked()); -+ ASSERT(base < end); -+ -+ /* -+ * No need to acquire hd->arch.mapping_lock: Both insertion and removal -+ * get done while holding pcidevs_lock. -+ */ -+ list_for_each_entry( map, &hd->arch.identity_maps, list ) -+ { -+ if ( map->base == base && map->end == end ) -+ { -+ int ret = 0; -+ -+ if ( p2ma != p2m_access_x ) -+ { -+ if ( map->access != p2ma ) -+ return -EADDRINUSE; -+ ++map->count; -+ return 0; -+ } -+ -+ if ( --map->count ) -+ return 0; -+ -+ while ( base_pfn < end_pfn ) -+ { -+ if ( clear_identity_p2m_entry(d, base_pfn) ) -+ ret = -ENXIO; -+ base_pfn++; -+ } -+ -+ list_del(&map->list); -+ xfree(map); -+ -+ return ret; -+ } -+ -+ if ( end >= map->base && map->end >= base ) -+ return -EADDRINUSE; -+ } -+ -+ if ( p2ma == p2m_access_x ) -+ return -ENOENT; -+ -+ while ( base_pfn < end_pfn ) -+ { -+ int err = set_identity_p2m_entry(d, base_pfn, p2ma, flag); -+ -+ if ( err ) -+ return err; -+ base_pfn++; -+ } -+ -+ map = xmalloc(struct identity_map); -+ if ( !map ) -+ return -ENOMEM; -+ map->base = base; -+ map->end = end; -+ map->access = p2ma; -+ map->count = 1; -+ list_add_tail(&map->list, &hd->arch.identity_maps); -+ -+ return 0; -+} -+ -+void iommu_identity_map_teardown(struct domain *d) -+{ -+ struct domain_iommu *hd = dom_iommu(d); -+ struct identity_map *map, *tmp; -+ -+ list_for_each_entry_safe ( map, tmp, &hd->arch.identity_maps, list ) -+ { -+ list_del(&map->list); -+ xfree(map); -+ } -+} -+ - static bool __hwdom_init hwdom_iommu_map(const struct domain *d, - unsigned long pfn, - unsigned long max_pfn) ---- a/xen/include/asm-x86/iommu.h -+++ b/xen/include/asm-x86/iommu.h -@@ -16,6 +16,7 @@ - - #include - #include -+#include - #include - #include - #include -@@ -49,7 +50,7 @@ struct arch_iommu - spinlock_t mapping_lock; /* io page table lock */ - int agaw; /* adjusted guest address width, 0 is level 2 30-bit */ - u64 iommu_bitmap; /* bitmap of iommu(s) that the domain uses */ -- struct list_head mapped_rmrrs; -+ struct list_head identity_maps; - - /* amd iommu support */ - int paging_mode; -@@ -112,6 +113,11 @@ static inline void iommu_disable_x2apic( - iommu_ops.disable_x2apic(); - } - -+int iommu_identity_mapping(struct domain *d, p2m_access_t p2ma, -+ paddr_t base, paddr_t end, -+ unsigned int flag); -+void iommu_identity_map_teardown(struct domain *d); -+ - extern bool untrusted_msi; - - int pi_update_irte(const struct pi_desc *pi_desc, const struct pirq *pirq, ---- a/xen/include/asm-x86/p2m.h -+++ b/xen/include/asm-x86/p2m.h -@@ -900,6 +900,34 @@ struct p2m_domain *p2m_get_altp2m(struct - static inline void p2m_altp2m_check(struct vcpu *v, uint16_t idx) {} - #endif - -+/* p2m access to IOMMU flags */ -+static inline unsigned int p2m_access_to_iommu_flags(p2m_access_t p2ma) -+{ -+ switch ( p2ma ) -+ { -+ case p2m_access_rw: -+ case p2m_access_rwx: -+ return IOMMUF_readable | IOMMUF_writable; -+ -+ case p2m_access_r: -+ case p2m_access_rx: -+ case p2m_access_rx2rw: -+ return IOMMUF_readable; -+ -+ case p2m_access_w: -+ case p2m_access_wx: -+ return IOMMUF_writable; -+ -+ case p2m_access_n: -+ case p2m_access_x: -+ case p2m_access_n2rwx: -+ return 0; -+ } -+ -+ ASSERT_UNREACHABLE(); -+ return 0; -+} -+ - /* - * p2m type to IOMMU flags - */ -@@ -921,9 +949,10 @@ static inline unsigned int p2m_get_iommu - flags = IOMMUF_readable; - break; - case p2m_mmio_direct: -- flags = IOMMUF_readable; -- if ( !rangeset_contains_singleton(mmio_ro_ranges, mfn_x(mfn)) ) -- flags |= IOMMUF_writable; -+ flags = p2m_access_to_iommu_flags(p2ma); -+ if ( (flags & IOMMUF_writable) && -+ rangeset_contains_singleton(mmio_ro_ranges, mfn_x(mfn)) ) -+ flags &= ~IOMMUF_writable; - break; - default: - flags = 0; diff --git a/xsa378-4.14-5.patch b/xsa378-4.14-5.patch deleted file mode 100644 index 020f87c..0000000 --- a/xsa378-4.14-5.patch +++ /dev/null @@ -1,208 +0,0 @@ -From: Jan Beulich -Subject: AMD/IOMMU: re-arrange/complete re-assignment handling - -Prior to the assignment step having completed successfully, devices -should not get associated with their new owner. Hand the device to DomIO -(perhaps temporarily), until after the de-assignment step has completed. - -De-assignment of a device (from other than Dom0) as well as failure of -reassign_device() during assignment should result in unity mappings -getting torn down. This in turn requires switching to a refcounted -mapping approach, as was already used by VT-d for its RMRRs, to prevent -unmapping a region used by multiple devices. - -This is CVE-2021-28696 / part of XSA-378. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/amd/iommu.h -+++ b/xen/drivers/passthrough/amd/iommu.h -@@ -232,8 +232,10 @@ int __must_check amd_iommu_unmap_page(st - unsigned int *flush_flags); - int __must_check amd_iommu_alloc_root(struct domain_iommu *hd); - int amd_iommu_reserve_domain_unity_map(struct domain *domain, -- paddr_t phys_addr, unsigned long size, -- int iw, int ir); -+ const struct ivrs_unity_map *map, -+ unsigned int flag); -+int amd_iommu_reserve_domain_unity_unmap(struct domain *d, -+ const struct ivrs_unity_map *map); - int __must_check amd_iommu_flush_iotlb_pages(struct domain *d, dfn_t dfn, - unsigned int page_count, - unsigned int flush_flags); ---- a/xen/drivers/passthrough/amd/iommu_map.c -+++ b/xen/drivers/passthrough/amd/iommu_map.c -@@ -420,38 +420,49 @@ int amd_iommu_flush_iotlb_all(struct dom - return 0; - } - --int amd_iommu_reserve_domain_unity_map(struct domain *domain, -- paddr_t phys_addr, -- unsigned long size, int iw, int ir) -+int amd_iommu_reserve_domain_unity_map(struct domain *d, -+ const struct ivrs_unity_map *map, -+ unsigned int flag) - { -- unsigned long npages, i; -- unsigned long gfn; -- unsigned int flags = !!ir; -- unsigned int flush_flags = 0; -- int rt = 0; -- -- if ( iw ) -- flags |= IOMMUF_writable; -- -- npages = region_to_pages(phys_addr, size); -- gfn = phys_addr >> PAGE_SHIFT; -- for ( i = 0; i < npages; i++ ) -+ int rc; -+ -+ if ( d == dom_io ) -+ return 0; -+ -+ for ( rc = 0; !rc && map; map = map->next ) - { -- unsigned long frame = gfn + i; -+ p2m_access_t p2ma = p2m_access_n; -+ -+ if ( map->read ) -+ p2ma |= p2m_access_r; -+ if ( map->write ) -+ p2ma |= p2m_access_w; - -- rt = amd_iommu_map_page(domain, _dfn(frame), _mfn(frame), flags, -- &flush_flags); -- if ( rt != 0 ) -- break; -+ rc = iommu_identity_mapping(d, p2ma, map->addr, -+ map->addr + map->length - 1, flag); - } - -- /* Use while-break to avoid compiler warning */ -- while ( flush_flags && -- amd_iommu_flush_iotlb_pages(domain, _dfn(gfn), -- npages, flush_flags) ) -- break; -+ return rc; -+} -+ -+int amd_iommu_reserve_domain_unity_unmap(struct domain *d, -+ const struct ivrs_unity_map *map) -+{ -+ int rc; -+ -+ if ( d == dom_io ) -+ return 0; -+ -+ for ( rc = 0; map; map = map->next ) -+ { -+ int ret = iommu_identity_mapping(d, p2m_access_x, map->addr, -+ map->addr + map->length - 1, 0); -+ -+ if ( ret && ret != -ENOENT && !rc ) -+ rc = ret; -+ } - -- return rt; -+ return rc; - } - - int __init amd_iommu_quarantine_init(struct domain *d) ---- a/xen/drivers/passthrough/amd/pci_amd_iommu.c -+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c -@@ -327,6 +327,7 @@ static int reassign_device(struct domain - struct amd_iommu *iommu; - int bdf, rc; - struct domain_iommu *t = dom_iommu(target); -+ const struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(pdev->seg); - - bdf = PCI_BDF2(pdev->bus, pdev->devfn); - iommu = find_iommu_for_device(pdev->seg, bdf); -@@ -341,10 +342,24 @@ static int reassign_device(struct domain - - amd_iommu_disable_domain_device(source, iommu, devfn, pdev); - -- if ( devfn == pdev->devfn ) -+ /* -+ * If the device belongs to the hardware domain, and it has a unity mapping, -+ * don't remove it from the hardware domain, because BIOS may reference that -+ * mapping. -+ */ -+ if ( !is_hardware_domain(source) ) -+ { -+ rc = amd_iommu_reserve_domain_unity_unmap( -+ source, -+ ivrs_mappings[get_dma_requestor_id(pdev->seg, bdf)].unity_map); -+ if ( rc ) -+ return rc; -+ } -+ -+ if ( devfn == pdev->devfn && pdev->domain != dom_io ) - { -- list_move(&pdev->domain_list, &target->pdev_list); -- pdev->domain = target; -+ list_move(&pdev->domain_list, &dom_io->pdev_list); -+ pdev->domain = dom_io; - } - - rc = allocate_domain_resources(t); -@@ -356,6 +371,12 @@ static int reassign_device(struct domain - pdev->seg, pdev->bus, PCI_SLOT(devfn), PCI_FUNC(devfn), - source->domain_id, target->domain_id); - -+ if ( devfn == pdev->devfn && pdev->domain != target ) -+ { -+ list_move(&pdev->domain_list, &target->pdev_list); -+ pdev->domain = target; -+ } -+ - return 0; - } - -@@ -366,20 +387,28 @@ static int amd_iommu_assign_device(struc - struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(pdev->seg); - int bdf = PCI_BDF2(pdev->bus, devfn); - int req_id = get_dma_requestor_id(pdev->seg, bdf); -- const struct ivrs_unity_map *unity_map; -+ int rc = amd_iommu_reserve_domain_unity_map( -+ d, ivrs_mappings[req_id].unity_map, flag); -+ -+ if ( !rc ) -+ rc = reassign_device(pdev->domain, d, devfn, pdev); - -- for ( unity_map = ivrs_mappings[req_id].unity_map; unity_map; -- unity_map = unity_map->next ) -+ if ( rc && !is_hardware_domain(d) ) - { -- int rc = amd_iommu_reserve_domain_unity_map( -- d, unity_map->addr, unity_map->length, -- unity_map->write, unity_map->read); -+ int ret = amd_iommu_reserve_domain_unity_unmap( -+ d, ivrs_mappings[req_id].unity_map); - -- if ( rc ) -- return rc; -+ if ( ret ) -+ { -+ printk(XENLOG_ERR "AMD-Vi: " -+ "unity-unmap for %pd/%04x:%02x:%02x.%u failed (%d)\n", -+ d, pdev->seg, pdev->bus, -+ PCI_SLOT(devfn), PCI_FUNC(devfn), ret); -+ domain_crash(d); -+ } - } - -- return reassign_device(pdev->domain, d, devfn, pdev); -+ return rc; - } - - static void deallocate_next_page_table(struct page_info *pg, int level) -@@ -438,6 +467,7 @@ static void deallocate_iommu_page_tables - - static void amd_iommu_domain_destroy(struct domain *d) - { -+ iommu_identity_map_teardown(d); - deallocate_iommu_page_tables(d); - amd_iommu_flush_all_pages(d); - } diff --git a/xsa378-4.14-6.patch b/xsa378-4.14-6.patch deleted file mode 100644 index ccecb97..0000000 --- a/xsa378-4.14-6.patch +++ /dev/null @@ -1,411 +0,0 @@ -From: Jan Beulich -Subject: AMD/IOMMU: re-arrange exclusion range and unity map recording - -The spec makes no provisions for OS behavior here to depend on the -amount of RAM found on the system. While the spec may not sufficiently -clearly distinguish both kinds of regions, they are surely meant to be -separate things: Only regions with ACPI_IVMD_EXCLUSION_RANGE set should -be candidates for putting in the exclusion range registers. (As there's -only a single such pair of registers per IOMMU, secondary non-adjacent -regions with the flag set already get converted to unity mapped -regions.) - -First of all, drop the dependency on max_page. With commit b4f042236ae0 -("AMD/IOMMU: Cease using a dynamic height for the IOMMU pagetables") the -use of it here was stale anyway; it was bogus already before, as it -didn't account for max_page getting increased later on. Simply try an -exclusion range registration first, and if it fails (for being -unsuitable or non-mergeable), register a unity mapping range. - -With this various local variables become unnecessary and hence get -dropped at the same time. - -With the max_page boundary dropped for using unity maps, the minimum -page table tree height now needs both recording and enforcing in -amd_iommu_domain_init(). Since we can't predict which devices may get -assigned to a domain, our only option is to uniformly force at least -that height for all domains, now that the height isn't dynamic anymore. - -Further don't make use of the exclusion range unless ACPI data says so. - -Note that exclusion range registration in -register_range_for_all_devices() is on a best effort basis. Hence unity -map entries also registered are redundant when the former succeeded, but -they also do no harm. Improvements in this area can be done later imo. - -Also adjust types where suitable without touching extra lines. - -This is part of XSA-378. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/amd/iommu.h -+++ b/xen/drivers/passthrough/amd/iommu.h -@@ -308,6 +308,8 @@ extern struct hpet_sbdf { - } init; - } hpet_sbdf; - -+extern int amd_iommu_min_paging_mode; -+ - extern void *shared_intremap_table; - extern unsigned long *shared_intremap_inuse; - ---- a/xen/drivers/passthrough/amd/iommu_acpi.c -+++ b/xen/drivers/passthrough/amd/iommu_acpi.c -@@ -118,12 +118,8 @@ static struct amd_iommu * __init find_io - } - - static int __init reserve_iommu_exclusion_range( -- struct amd_iommu *iommu, uint64_t base, uint64_t limit, -- bool all, bool iw, bool ir) -+ struct amd_iommu *iommu, paddr_t base, paddr_t limit, bool all) - { -- if ( !ir || !iw ) -- return -EPERM; -- - /* need to extend exclusion range? */ - if ( iommu->exclusion_enable ) - { -@@ -152,14 +148,18 @@ static int __init reserve_unity_map_for_ - { - struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(seg); - struct ivrs_unity_map *unity_map = ivrs_mappings[bdf].unity_map; -+ int paging_mode = amd_iommu_get_paging_mode(PFN_UP(base + length)); -+ -+ if ( paging_mode < 0 ) -+ return paging_mode; - - /* Check for overlaps. */ - for ( ; unity_map; unity_map = unity_map->next ) - { - /* - * Exact matches are okay. This can in particular happen when -- * register_exclusion_range_for_device() calls here twice for the -- * same (s,b,d,f). -+ * register_range_for_device() calls here twice for the same -+ * (s,b,d,f). - */ - if ( base == unity_map->addr && length == unity_map->length && - ir == unity_map->read && iw == unity_map->write ) -@@ -187,55 +187,52 @@ static int __init reserve_unity_map_for_ - unity_map->next = ivrs_mappings[bdf].unity_map; - ivrs_mappings[bdf].unity_map = unity_map; - -+ if ( paging_mode > amd_iommu_min_paging_mode ) -+ amd_iommu_min_paging_mode = paging_mode; -+ - return 0; - } - --static int __init register_exclusion_range_for_all_devices( -- unsigned long base, unsigned long limit, u8 iw, u8 ir) -+static int __init register_range_for_all_devices( -+ paddr_t base, paddr_t limit, bool iw, bool ir, bool exclusion) - { - int seg = 0; /* XXX */ -- unsigned long range_top, iommu_top, length; - struct amd_iommu *iommu; -- unsigned int bdf; - int rc = 0; - - /* is part of exclusion range inside of IOMMU virtual address space? */ - /* note: 'limit' parameter is assumed to be page-aligned */ -- range_top = limit + PAGE_SIZE; -- iommu_top = max_page * PAGE_SIZE; -- if ( base < iommu_top ) -- { -- if ( range_top > iommu_top ) -- range_top = iommu_top; -- length = range_top - base; -- /* reserve r/w unity-mapped page entries for devices */ -- /* note: these entries are part of the exclusion range */ -- for ( bdf = 0; !rc && bdf < ivrs_bdf_entries; bdf++ ) -- rc = reserve_unity_map_for_device(seg, bdf, base, length, iw, ir); -- /* push 'base' just outside of virtual address space */ -- base = iommu_top; -- } -- /* register IOMMU exclusion range settings */ -- if ( !rc && limit >= iommu_top ) -+ if ( exclusion ) - { - for_each_amd_iommu( iommu ) - { -- rc = reserve_iommu_exclusion_range(iommu, base, limit, -- true /* all */, iw, ir); -- if ( rc ) -- break; -+ int ret = reserve_iommu_exclusion_range(iommu, base, limit, -+ true /* all */); -+ -+ if ( ret && !rc ) -+ rc = ret; - } - } - -+ if ( !exclusion || rc ) -+ { -+ paddr_t length = limit + PAGE_SIZE - base; -+ unsigned int bdf; -+ -+ /* reserve r/w unity-mapped page entries for devices */ -+ for ( bdf = rc = 0; !rc && bdf < ivrs_bdf_entries; bdf++ ) -+ rc = reserve_unity_map_for_device(seg, bdf, base, length, iw, ir); -+ } -+ - return rc; - } - --static int __init register_exclusion_range_for_device( -- u16 bdf, unsigned long base, unsigned long limit, u8 iw, u8 ir) -+static int __init register_range_for_device( -+ unsigned int bdf, paddr_t base, paddr_t limit, -+ bool iw, bool ir, bool exclusion) - { - int seg = 0; /* XXX */ - struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(seg); -- unsigned long range_top, iommu_top, length; - struct amd_iommu *iommu; - u16 req; - int rc = 0; -@@ -249,27 +246,19 @@ static int __init register_exclusion_ran - req = ivrs_mappings[bdf].dte_requestor_id; - - /* note: 'limit' parameter is assumed to be page-aligned */ -- range_top = limit + PAGE_SIZE; -- iommu_top = max_page * PAGE_SIZE; -- if ( base < iommu_top ) -- { -- if ( range_top > iommu_top ) -- range_top = iommu_top; -- length = range_top - base; -+ if ( exclusion ) -+ rc = reserve_iommu_exclusion_range(iommu, base, limit, -+ false /* all */); -+ if ( !exclusion || rc ) -+ { -+ paddr_t length = limit + PAGE_SIZE - base; -+ - /* reserve unity-mapped page entries for device */ -- /* note: these entries are part of the exclusion range */ - rc = reserve_unity_map_for_device(seg, bdf, base, length, iw, ir) ?: - reserve_unity_map_for_device(seg, req, base, length, iw, ir); -- -- /* push 'base' just outside of virtual address space */ -- base = iommu_top; - } -- -- /* register IOMMU exclusion range settings for device */ -- if ( !rc && limit >= iommu_top ) -+ else - { -- rc = reserve_iommu_exclusion_range(iommu, base, limit, -- false /* all */, iw, ir); - ivrs_mappings[bdf].dte_allow_exclusion = true; - ivrs_mappings[req].dte_allow_exclusion = true; - } -@@ -277,53 +266,42 @@ static int __init register_exclusion_ran - return rc; - } - --static int __init register_exclusion_range_for_iommu_devices( -- struct amd_iommu *iommu, -- unsigned long base, unsigned long limit, u8 iw, u8 ir) -+static int __init register_range_for_iommu_devices( -+ struct amd_iommu *iommu, paddr_t base, paddr_t limit, -+ bool iw, bool ir, bool exclusion) - { -- unsigned long range_top, iommu_top, length; -+ /* note: 'limit' parameter is assumed to be page-aligned */ -+ paddr_t length = limit + PAGE_SIZE - base; - unsigned int bdf; - u16 req; -- int rc = 0; -+ int rc; - -- /* is part of exclusion range inside of IOMMU virtual address space? */ -- /* note: 'limit' parameter is assumed to be page-aligned */ -- range_top = limit + PAGE_SIZE; -- iommu_top = max_page * PAGE_SIZE; -- if ( base < iommu_top ) -- { -- if ( range_top > iommu_top ) -- range_top = iommu_top; -- length = range_top - base; -- /* reserve r/w unity-mapped page entries for devices */ -- /* note: these entries are part of the exclusion range */ -- for ( bdf = 0; !rc && bdf < ivrs_bdf_entries; bdf++ ) -- { -- if ( iommu == find_iommu_for_device(iommu->seg, bdf) ) -- { -- req = get_ivrs_mappings(iommu->seg)[bdf].dte_requestor_id; -- rc = reserve_unity_map_for_device(iommu->seg, bdf, base, length, -- iw, ir) ?: -- reserve_unity_map_for_device(iommu->seg, req, base, length, -- iw, ir); -- } -- } -- -- /* push 'base' just outside of virtual address space */ -- base = iommu_top; -+ if ( exclusion ) -+ { -+ rc = reserve_iommu_exclusion_range(iommu, base, limit, true /* all */); -+ if ( !rc ) -+ return 0; - } - -- /* register IOMMU exclusion range settings */ -- if ( !rc && limit >= iommu_top ) -- rc = reserve_iommu_exclusion_range(iommu, base, limit, -- true /* all */, iw, ir); -+ /* reserve unity-mapped page entries for devices */ -+ for ( bdf = rc = 0; !rc && bdf < ivrs_bdf_entries; bdf++ ) -+ { -+ if ( iommu != find_iommu_for_device(iommu->seg, bdf) ) -+ continue; -+ -+ req = get_ivrs_mappings(iommu->seg)[bdf].dte_requestor_id; -+ rc = reserve_unity_map_for_device(iommu->seg, bdf, base, length, -+ iw, ir) ?: -+ reserve_unity_map_for_device(iommu->seg, req, base, length, -+ iw, ir); -+ } - - return rc; - } - - static int __init parse_ivmd_device_select( - const struct acpi_ivrs_memory *ivmd_block, -- unsigned long base, unsigned long limit, u8 iw, u8 ir) -+ paddr_t base, paddr_t limit, bool iw, bool ir, bool exclusion) - { - u16 bdf; - -@@ -334,12 +312,12 @@ static int __init parse_ivmd_device_sele - return -ENODEV; - } - -- return register_exclusion_range_for_device(bdf, base, limit, iw, ir); -+ return register_range_for_device(bdf, base, limit, iw, ir, exclusion); - } - - static int __init parse_ivmd_device_range( - const struct acpi_ivrs_memory *ivmd_block, -- unsigned long base, unsigned long limit, u8 iw, u8 ir) -+ paddr_t base, paddr_t limit, bool iw, bool ir, bool exclusion) - { - unsigned int first_bdf, last_bdf, bdf; - int error; -@@ -361,15 +339,15 @@ static int __init parse_ivmd_device_rang - } - - for ( bdf = first_bdf, error = 0; (bdf <= last_bdf) && !error; bdf++ ) -- error = register_exclusion_range_for_device( -- bdf, base, limit, iw, ir); -+ error = register_range_for_device( -+ bdf, base, limit, iw, ir, exclusion); - - return error; - } - - static int __init parse_ivmd_device_iommu( - const struct acpi_ivrs_memory *ivmd_block, -- unsigned long base, unsigned long limit, u8 iw, u8 ir) -+ paddr_t base, paddr_t limit, bool iw, bool ir, bool exclusion) - { - int seg = 0; /* XXX */ - struct amd_iommu *iommu; -@@ -384,14 +362,14 @@ static int __init parse_ivmd_device_iomm - return -ENODEV; - } - -- return register_exclusion_range_for_iommu_devices( -- iommu, base, limit, iw, ir); -+ return register_range_for_iommu_devices( -+ iommu, base, limit, iw, ir, exclusion); - } - - static int __init parse_ivmd_block(const struct acpi_ivrs_memory *ivmd_block) - { - unsigned long start_addr, mem_length, base, limit; -- u8 iw, ir; -+ bool iw = true, ir = true, exclusion = false; - - if ( ivmd_block->header.length < sizeof(*ivmd_block) ) - { -@@ -408,13 +386,11 @@ static int __init parse_ivmd_block(const - ivmd_block->header.type, start_addr, mem_length); - - if ( ivmd_block->header.flags & ACPI_IVMD_EXCLUSION_RANGE ) -- iw = ir = IOMMU_CONTROL_ENABLED; -+ exclusion = true; - else if ( ivmd_block->header.flags & ACPI_IVMD_UNITY ) - { -- iw = ivmd_block->header.flags & ACPI_IVMD_READ ? -- IOMMU_CONTROL_ENABLED : IOMMU_CONTROL_DISABLED; -- ir = ivmd_block->header.flags & ACPI_IVMD_WRITE ? -- IOMMU_CONTROL_ENABLED : IOMMU_CONTROL_DISABLED; -+ iw = ivmd_block->header.flags & ACPI_IVMD_READ; -+ ir = ivmd_block->header.flags & ACPI_IVMD_WRITE; - } - else - { -@@ -425,20 +401,20 @@ static int __init parse_ivmd_block(const - switch( ivmd_block->header.type ) - { - case ACPI_IVRS_TYPE_MEMORY_ALL: -- return register_exclusion_range_for_all_devices( -- base, limit, iw, ir); -+ return register_range_for_all_devices( -+ base, limit, iw, ir, exclusion); - - case ACPI_IVRS_TYPE_MEMORY_ONE: -- return parse_ivmd_device_select(ivmd_block, -- base, limit, iw, ir); -+ return parse_ivmd_device_select(ivmd_block, base, limit, -+ iw, ir, exclusion); - - case ACPI_IVRS_TYPE_MEMORY_RANGE: -- return parse_ivmd_device_range(ivmd_block, -- base, limit, iw, ir); -+ return parse_ivmd_device_range(ivmd_block, base, limit, -+ iw, ir, exclusion); - - case ACPI_IVRS_TYPE_MEMORY_IOMMU: -- return parse_ivmd_device_iommu(ivmd_block, -- base, limit, iw, ir); -+ return parse_ivmd_device_iommu(ivmd_block, base, limit, -+ iw, ir, exclusion); - - default: - AMD_IOMMU_DEBUG("IVMD Error: Invalid Block Type!\n"); ---- a/xen/drivers/passthrough/amd/pci_amd_iommu.c -+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c -@@ -231,6 +231,8 @@ static int __must_check allocate_domain_ - return rc; - } - -+int __read_mostly amd_iommu_min_paging_mode = 1; -+ - static int amd_iommu_domain_init(struct domain *d) - { - struct domain_iommu *hd = dom_iommu(d); -@@ -242,11 +244,13 @@ static int amd_iommu_domain_init(struct - * - HVM could in principle use 3 or 4 depending on how much guest - * physical address space we give it, but this isn't known yet so use 4 - * unilaterally. -+ * - Unity maps may require an even higher number. - */ -- hd->arch.paging_mode = amd_iommu_get_paging_mode( -- is_hvm_domain(d) -- ? 1ul << (DEFAULT_DOMAIN_ADDRESS_WIDTH - PAGE_SHIFT) -- : get_upper_mfn_bound() + 1); -+ hd->arch.paging_mode = max(amd_iommu_get_paging_mode( -+ is_hvm_domain(d) -+ ? 1ul << (DEFAULT_DOMAIN_ADDRESS_WIDTH - PAGE_SHIFT) -+ : get_upper_mfn_bound() + 1), -+ amd_iommu_min_paging_mode); - - return 0; - } diff --git a/xsa378-4.14-7.patch b/xsa378-4.14-7.patch deleted file mode 100644 index 6548b7b..0000000 --- a/xsa378-4.14-7.patch +++ /dev/null @@ -1,88 +0,0 @@ -From: Jan Beulich -Subject: x86/p2m: introduce p2m_is_special() - -Seeing the similarity of grant, foreign, and (subsequently) direct-MMIO -handling, introduce a new P2M type group named "special" (as in "needing -special accessors to create/destroy"). - -Also use -EPERM instead of other error codes on the two domain_crash() -paths touched. - -This is part of XSA-378. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/arch/x86/mm/p2m.c -+++ b/xen/arch/x86/mm/p2m.c -@@ -807,7 +807,7 @@ p2m_remove_page(struct p2m_domain *p2m, - for ( i = 0; i < (1UL << page_order); i++ ) - { - p2m->get_entry(p2m, gfn_add(gfn, i), &t, &a, 0, NULL, NULL); -- if ( !p2m_is_grant(t) && !p2m_is_shared(t) && !p2m_is_foreign(t) ) -+ if ( !p2m_is_special(t) && !p2m_is_shared(t) ) - set_gpfn_from_mfn(mfn_x(mfn) + i, INVALID_M2P_ENTRY); - } - } -@@ -935,13 +935,13 @@ guest_physmap_add_entry(struct domain *d - &ot, &a, 0, NULL, NULL); - ASSERT(!p2m_is_shared(ot)); - } -- if ( p2m_is_grant(ot) || p2m_is_foreign(ot) ) -+ if ( p2m_is_special(ot) ) - { -- /* Really shouldn't be unmapping grant/foreign maps this way */ -+ /* Don't permit unmapping grant/foreign this way. */ - domain_crash(d); - p2m_unlock(p2m); - -- return -EINVAL; -+ return -EPERM; - } - else if ( p2m_is_ram(ot) && !p2m_is_paged(ot) ) - { -@@ -1035,8 +1035,7 @@ int p2m_change_type_one(struct domain *d - struct p2m_domain *p2m = p2m_get_hostp2m(d); - int rc; - -- BUG_ON(p2m_is_grant(ot) || p2m_is_grant(nt)); -- BUG_ON(p2m_is_foreign(ot) || p2m_is_foreign(nt)); -+ BUG_ON(p2m_is_special(ot) || p2m_is_special(nt)); - - gfn_lock(p2m, gfn, 0); - -@@ -1283,11 +1282,11 @@ static int set_typed_p2m_entry(struct do - gfn_unlock(p2m, gfn, order); - return cur_order + 1; - } -- if ( p2m_is_grant(ot) || p2m_is_foreign(ot) ) -+ if ( p2m_is_special(ot) ) - { - gfn_unlock(p2m, gfn, order); - domain_crash(d); -- return -ENOENT; -+ return -EPERM; - } - else if ( p2m_is_ram(ot) ) - { ---- a/xen/include/asm-x86/p2m.h -+++ b/xen/include/asm-x86/p2m.h -@@ -141,6 +141,10 @@ typedef unsigned int p2m_query_t; - | p2m_to_mask(p2m_ram_logdirty) ) - #define P2M_SHARED_TYPES (p2m_to_mask(p2m_ram_shared)) - -+/* Types established/cleaned up via special accessors. */ -+#define P2M_SPECIAL_TYPES (P2M_GRANT_TYPES | \ -+ p2m_to_mask(p2m_map_foreign)) -+ - /* Valid types not necessarily associated with a (valid) MFN. */ - #define P2M_INVALID_MFN_TYPES (P2M_POD_TYPES \ - | p2m_to_mask(p2m_mmio_direct) \ -@@ -169,6 +173,7 @@ typedef unsigned int p2m_query_t; - #define p2m_is_paged(_t) (p2m_to_mask(_t) & P2M_PAGED_TYPES) - #define p2m_is_sharable(_t) (p2m_to_mask(_t) & P2M_SHARABLE_TYPES) - #define p2m_is_shared(_t) (p2m_to_mask(_t) & P2M_SHARED_TYPES) -+#define p2m_is_special(_t) (p2m_to_mask(_t) & P2M_SPECIAL_TYPES) - #define p2m_is_broken(_t) (p2m_to_mask(_t) & P2M_BROKEN_TYPES) - #define p2m_is_foreign(_t) (p2m_to_mask(_t) & p2m_to_mask(p2m_map_foreign)) - diff --git a/xsa378-4.14-8.patch b/xsa378-4.14-8.patch deleted file mode 100644 index cb12394..0000000 --- a/xsa378-4.14-8.patch +++ /dev/null @@ -1,157 +0,0 @@ -From: Jan Beulich -Subject: x86/p2m: guard (in particular) identity mapping entries - -Such entries, created by set_identity_p2m_entry(), should only be -destroyed by clear_identity_p2m_entry(). However, similarly, entries -created by set_mmio_p2m_entry() should only be torn down by -clear_mmio_p2m_entry(), so the logic gets based upon p2m_mmio_direct as -the entry type (separation between "ordinary" and 1:1 mappings would -require a further indicator to tell apart the two). - -As to the guest_remove_page() change, commit 48dfb297a20a ("x86/PVH: -allow guest_remove_page to remove p2m_mmio_direct pages"), which -introduced the call to clear_mmio_p2m_entry(), claimed this was done for -hwdom only without this actually having been the case. However, this -code shouldn't be there in the first place, as MMIO entries shouldn't be -dropped this way. Avoid triggering the warning again that 48dfb297a20a -silenced by an adjustment to xenmem_add_to_physmap_one() instead. - -Note that guest_physmap_mark_populate_on_demand() gets tightened beyond -the immediate purpose of this change. - -Note also that I didn't inspect code which isn't security supported, -e.g. sharing, paging, or altp2m. - -This is CVE-2021-28694 / part of XSA-378. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -4652,7 +4652,9 @@ int xenmem_add_to_physmap_one( - - /* Remove previously mapped page if it was present. */ - prev_mfn = get_gfn(d, gfn_x(gpfn), &p2mt); -- if ( mfn_valid(prev_mfn) ) -+ if ( p2mt == p2m_mmio_direct ) -+ rc = -EPERM; -+ else if ( mfn_valid(prev_mfn) ) - { - if ( is_special_page(mfn_to_page(prev_mfn)) ) - /* Special pages are simply unhooked from this phys slot. */ ---- a/xen/arch/x86/mm/p2m.c -+++ b/xen/arch/x86/mm/p2m.c -@@ -795,7 +795,8 @@ p2m_remove_page(struct p2m_domain *p2m, - &cur_order, NULL); - - if ( p2m_is_valid(t) && -- (!mfn_valid(mfn) || !mfn_eq(mfn_add(mfn, i), mfn_return)) ) -+ (!mfn_valid(mfn) || t == p2m_mmio_direct || -+ !mfn_eq(mfn_add(mfn, i), mfn_return)) ) - return -EILSEQ; - - i += (1UL << cur_order) - -@@ -893,7 +894,7 @@ guest_physmap_add_entry(struct domain *d - if ( p2m_is_foreign(t) ) - return -EINVAL; - -- if ( !mfn_valid(mfn) ) -+ if ( !mfn_valid(mfn) || t == p2m_mmio_direct ) - { - ASSERT_UNREACHABLE(); - return -EINVAL; -@@ -937,7 +938,7 @@ guest_physmap_add_entry(struct domain *d - } - if ( p2m_is_special(ot) ) - { -- /* Don't permit unmapping grant/foreign this way. */ -+ /* Don't permit unmapping grant/foreign/direct-MMIO this way. */ - domain_crash(d); - p2m_unlock(p2m); - -@@ -1387,8 +1388,8 @@ int set_identity_p2m_entry(struct domain - * order+1 for caller to retry with order (guaranteed smaller than - * the order value passed in) - */ --int clear_mmio_p2m_entry(struct domain *d, unsigned long gfn_l, mfn_t mfn, -- unsigned int order) -+static int clear_mmio_p2m_entry(struct domain *d, unsigned long gfn_l, -+ mfn_t mfn, unsigned int order) - { - int rc = -EINVAL; - gfn_t gfn = _gfn(gfn_l); ---- a/xen/arch/x86/mm/p2m-pod.c -+++ b/xen/arch/x86/mm/p2m-pod.c -@@ -1292,17 +1292,17 @@ guest_physmap_mark_populate_on_demand(st - - p2m->get_entry(p2m, gfn_add(gfn, i), &ot, &a, 0, &cur_order, NULL); - n = 1UL << min(order, cur_order); -- if ( p2m_is_ram(ot) ) -+ if ( ot == p2m_populate_on_demand ) -+ { -+ /* Count how many PoD entries we'll be replacing if successful */ -+ pod_count += n; -+ } -+ else if ( ot != p2m_invalid && ot != p2m_mmio_dm ) - { - P2M_DEBUG("gfn_to_mfn returned type %d!\n", ot); - rc = -EBUSY; - goto out; - } -- else if ( ot == p2m_populate_on_demand ) -- { -- /* Count how man PoD entries we'll be replacing if successful */ -- pod_count += n; -- } - } - - /* Now, actually do the two-way mapping */ ---- a/xen/common/memory.c -+++ b/xen/common/memory.c -@@ -329,7 +329,7 @@ int guest_remove_page(struct domain *d, - } - if ( p2mt == p2m_mmio_direct ) - { -- rc = clear_mmio_p2m_entry(d, gmfn, mfn, PAGE_ORDER_4K); -+ rc = -EPERM; - goto out_put_gfn; - } - #else -@@ -1721,6 +1721,15 @@ int check_get_page_from_gfn(struct domai - return -EAGAIN; - } - #endif -+#ifdef CONFIG_X86 -+ if ( p2mt == p2m_mmio_direct ) -+ { -+ if ( page ) -+ put_page(page); -+ -+ return -EPERM; -+ } -+#endif - - if ( !page ) - return -EINVAL; ---- a/xen/include/asm-x86/p2m.h -+++ b/xen/include/asm-x86/p2m.h -@@ -143,7 +143,8 @@ typedef unsigned int p2m_query_t; - - /* Types established/cleaned up via special accessors. */ - #define P2M_SPECIAL_TYPES (P2M_GRANT_TYPES | \ -- p2m_to_mask(p2m_map_foreign)) -+ p2m_to_mask(p2m_map_foreign) | \ -+ p2m_to_mask(p2m_mmio_direct)) - - /* Valid types not necessarily associated with a (valid) MFN. */ - #define P2M_INVALID_MFN_TYPES (P2M_POD_TYPES \ -@@ -645,8 +646,6 @@ int set_foreign_p2m_entry(struct domain - /* Set mmio addresses in the p2m table (for pass-through) */ - int set_mmio_p2m_entry(struct domain *d, gfn_t gfn, mfn_t mfn, - unsigned int order); --int clear_mmio_p2m_entry(struct domain *d, unsigned long gfn, mfn_t mfn, -- unsigned int order); - - /* Set identity addresses in the p2m table (for pass-through) */ - int set_identity_p2m_entry(struct domain *d, unsigned long gfn, diff --git a/xsa379-4.14.patch b/xsa379-4.14.patch deleted file mode 100644 index 25894b3..0000000 --- a/xsa379-4.14.patch +++ /dev/null @@ -1,77 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: widen locked region in xenmem_add_to_physmap_one() - -For pages which can be made part of the P2M by the guest, but which can -also later be de-allocated (grant table v2 status pages being the -present example), it is imperative that they be mapped at no more than a -single GFN. We therefore need to make sure that of two parallel -XENMAPSPACE_grant_table requests for the same status page one completes -before the second checks at which other GFN the underlying MFN is -presently mapped. - -Push down the respective put_gfn(). This leverages that gfn_lock() -really aliases p2m_lock(), but the function makes this assumption -already anyway: In the XENMAPSPACE_gmfn case lock nesting constraints -for both involved GFNs would otherwise need to be enforced to avoid ABBA -deadlocks. - -This is CVE-2021-28697 / XSA-379. - -Signed-off-by: Jan Beulich -Reviewed-by: Julien Grall - ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -4650,8 +4650,20 @@ int xenmem_add_to_physmap_one( - goto put_both; - } - -- /* Remove previously mapped page if it was present. */ -+ /* -+ * Note that we're (ab)using GFN locking (to really be locking of the -+ * entire P2M) here in (at least) two ways: Finer grained locking would -+ * expose lock order violations in the XENMAPSPACE_gmfn case (due to the -+ * earlier get_gfn_unshare() above). Plus at the very least for the grant -+ * table v2 status page case we need to guarantee that the same page can -+ * only appear at a single GFN. While this is a property we want in -+ * general, for pages which can subsequently be freed this imperative: -+ * Upon freeing we wouldn't be able to find other mappings in the P2M -+ * (unless we did a brute force search). -+ */ - prev_mfn = get_gfn(d, gfn_x(gpfn), &p2mt); -+ -+ /* Remove previously mapped page if it was present. */ - if ( p2mt == p2m_mmio_direct ) - rc = -EPERM; - else if ( mfn_valid(prev_mfn) ) -@@ -4663,27 +4675,21 @@ int xenmem_add_to_physmap_one( - /* Normal domain memory is freed, to avoid leaking memory. */ - rc = guest_remove_page(d, gfn_x(gpfn)); - } -- /* In the XENMAPSPACE_gmfn case we still hold a ref on the old page. */ -- put_gfn(d, gfn_x(gpfn)); -- -- if ( rc ) -- goto put_both; - - /* Unmap from old location, if any. */ - old_gpfn = get_gpfn_from_mfn(mfn_x(mfn)); - ASSERT(!SHARED_M2P(old_gpfn)); - if ( space == XENMAPSPACE_gmfn && old_gpfn != gfn ) -- { - rc = -EXDEV; -- goto put_both; -- } -- if ( old_gpfn != INVALID_M2P_ENTRY ) -+ else if ( !rc && old_gpfn != INVALID_M2P_ENTRY ) - rc = guest_physmap_remove_page(d, _gfn(old_gpfn), mfn, PAGE_ORDER_4K); - - /* Map at new location. */ - if ( !rc ) - rc = guest_physmap_add_page(d, gpfn, mfn, PAGE_ORDER_4K); - -+ put_gfn(d, gfn_x(gpfn)); -+ - put_both: - /* - * In the XENMAPSPACE_gmfn case, we took a ref of the gfn at the top. diff --git a/xsa380-3.patch b/xsa380-3.patch deleted file mode 100644 index 1517249..0000000 --- a/xsa380-3.patch +++ /dev/null @@ -1,74 +0,0 @@ -From: Jan Beulich -Subject: gnttab: avoid triggering assertion in radix_tree_ulong_to_ptr() - -Relevant quotes from the C11 standard: - -"Except where explicitly stated otherwise, for the purposes of this - subclause unnamed members of objects of structure and union type do not - participate in initialization. Unnamed members of structure objects - have indeterminate value even after initialization." - -"If there are fewer initializers in a brace-enclosed list than there are - elements or members of an aggregate, [...], the remainder of the - aggregate shall be initialized implicitly the same as objects that have - static storage duration." - -"If an object that has static or thread storage duration is not - initialized explicitly, then: - [...] - — if it is an aggregate, every member is initialized (recursively) - according to these rules, and any padding is initialized to zero - bits; - [...]" - -"A bit-field declaration with no declarator, but only a colon and a - width, indicates an unnamed bit-field." Footnote: "An unnamed bit-field - structure member is useful for padding to conform to externally imposed - layouts." - -"There may be unnamed padding within a structure object, but not at its - beginning." - -Which makes me conclude: -- Whether an unnamed bit-field member is an unnamed member or padding is - unclear, and hence also whether the last quote above would render the - big endian case of the structure declaration invalid. -- Whether the number of members of an aggregate includes unnamed ones is - also not really clear. -- The initializer in map_grant_ref() initializes all fields of the "cnt" - sub-structure of the union, so assuming the second quote above applies - here (indirectly), the compiler isn't required to implicitly - initialize the rest (i.e. in particular any padding) like would happen - for static storage duration objects. - -Gcc 7.4.1 can be observed (apparently in debug builds only) to translate -aforementioned initializer to a read-modify-write operation of a stack -variable, leaving unchanged the top two bits of whatever was previously -in that stack slot. Clearly if either of the two bits were set, -radix_tree_ulong_to_ptr()'s assertion would trigger. - -Therefore, to be on the safe side, add an explicit padding field for the -non-big-endian-bitfields case and give a dummy name to both padding -fields. - -Fixes: 9781b51efde2 ("gnttab: replace mapkind()") -Signed-off-by: Jan Beulich -Acked-by: Andrew Cooper - ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -952,10 +952,13 @@ union maptrack_node { - struct { - /* Radix tree slot pointers use two of the bits. */ - #ifdef __BIG_ENDIAN_BITFIELD -- unsigned long : 2; -+ unsigned long _0 : 2; - #endif - unsigned long rd : BITS_PER_LONG / 2 - 1; - unsigned long wr : BITS_PER_LONG / 2 - 1; -+#ifndef __BIG_ENDIAN_BITFIELD -+ unsigned long _0 : 2; -+#endif - } cnt; - unsigned long raw; - }; diff --git a/xsa380-4.14-1.patch b/xsa380-4.14-1.patch deleted file mode 100644 index 7221248..0000000 --- a/xsa380-4.14-1.patch +++ /dev/null @@ -1,148 +0,0 @@ -From: Jan Beulich -Subject: gnttab: add preemption check to gnttab_release_mappings() - -A guest may die with many grant mappings still in place, or simply with -a large maptrack table. Iterating through this may take more time than -is reasonable without intermediate preemption (to run softirqs and -perhaps the scheduler). - -Move the invocation of the function to the section where other -restartable functions get invoked, and have the function itself check -for preemption every once in a while. Have it iterate the table -backwards, such that decreasing the maptrack limit is all it takes to -convey restart information. - -In domain_teardown() introduce PROG_none such that inserting at the -front will be easier going forward. - -This is part of CVE-2021-28698 / XSA-380. - -Reported-by: Andrew Cooper -Signed-off-by: Jan Beulich -Reviewed-by: Julien Grall - ---- a/xen/common/domain.c -+++ b/xen/common/domain.c -@@ -721,11 +721,13 @@ int domain_kill(struct domain *d) - return domain_kill(d); - d->is_dying = DOMDYING_dying; - argo_destroy(d); -- gnttab_release_mappings(d); - vnuma_destroy(d->vnuma); - domain_set_outstanding_pages(d, 0); - /* fallthrough */ - case DOMDYING_dying: -+ rc = gnttab_release_mappings(d); -+ if ( rc ) -+ break; - rc = evtchn_destroy(d); - if ( rc ) - break; ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -64,7 +64,13 @@ struct grant_table { - unsigned int nr_grant_frames; - /* Number of grant status frames shared with guest (for version 2) */ - unsigned int nr_status_frames; -- /* Number of available maptrack entries. */ -+ /* -+ * Number of available maptrack entries. For cleanup purposes it is -+ * important to realize that this field and @maptrack further down will -+ * only ever be accessed by the local domain. Thus it is okay to clean -+ * up early, and to shrink the limit for the purpose of tracking cleanup -+ * progress. -+ */ - unsigned int maptrack_limit; - /* Shared grant table (see include/public/grant_table.h). */ - union { -@@ -3708,9 +3714,7 @@ do_grant_table_op( - #include "compat/grant_table.c" - #endif - --void --gnttab_release_mappings( -- struct domain *d) -+int gnttab_release_mappings(struct domain *d) - { - struct grant_table *gt = d->grant_table, *rgt; - struct grant_mapping *map; -@@ -3724,10 +3728,34 @@ gnttab_release_mappings( - - BUG_ON(!d->is_dying); - -- for ( handle = 0; handle < gt->maptrack_limit; handle++ ) -+ if ( !gt || !gt->maptrack ) -+ return 0; -+ -+ for ( handle = gt->maptrack_limit; handle; ) - { - unsigned int clear_flags = 0; - -+ /* -+ * Deal with full pages such that their freeing (in the body of the -+ * if()) remains simple. -+ */ -+ if ( handle < gt->maptrack_limit && !(handle % MAPTRACK_PER_PAGE) ) -+ { -+ /* -+ * Changing maptrack_limit alters nr_maptrack_frames()'es return -+ * value. Free the then excess trailing page right here, rather -+ * than leaving it to grant_table_destroy() (and in turn requiring -+ * to leave gt->maptrack_limit unaltered). -+ */ -+ gt->maptrack_limit = handle; -+ FREE_XENHEAP_PAGE(gt->maptrack[nr_maptrack_frames(gt)]); -+ -+ if ( hypercall_preempt_check() ) -+ return -ERESTART; -+ } -+ -+ --handle; -+ - map = &maptrack_entry(gt, handle); - if ( !(map->flags & (GNTMAP_device_map|GNTMAP_host_map)) ) - continue; -@@ -3818,6 +3846,11 @@ gnttab_release_mappings( - - map->flags = 0; - } -+ -+ gt->maptrack_limit = 0; -+ FREE_XENHEAP_PAGE(gt->maptrack[0]); -+ -+ return 0; - } - - void grant_table_warn_active_grants(struct domain *d) -@@ -3881,8 +3914,7 @@ grant_table_destroy( - free_xenheap_page(t->shared_raw[i]); - xfree(t->shared_raw); - -- for ( i = 0; i < nr_maptrack_frames(t); i++ ) -- free_xenheap_page(t->maptrack[i]); -+ ASSERT(!t->maptrack_limit); - vfree(t->maptrack); - - for ( i = 0; i < nr_active_grant_frames(t); i++ ) ---- a/xen/include/xen/grant_table.h -+++ b/xen/include/xen/grant_table.h -@@ -47,9 +47,7 @@ void grant_table_init_vcpu(struct vcpu * - void grant_table_warn_active_grants(struct domain *d); - - /* Domain death release of granted mappings of other domains' memory. */ --void --gnttab_release_mappings( -- struct domain *d); -+int gnttab_release_mappings(struct domain *d); - - int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref, - gfn_t *gfn, uint16_t *status); -@@ -78,7 +76,7 @@ static inline void grant_table_init_vcpu - - static inline void grant_table_warn_active_grants(struct domain *d) {} - --static inline void gnttab_release_mappings(struct domain *d) {} -+static inline int gnttab_release_mappings(struct domain *d) { return 0; } - - static inline int mem_sharing_gref_to_gfn(struct grant_table *gt, - grant_ref_t ref, diff --git a/xsa380-4.14-2.patch b/xsa380-4.14-2.patch deleted file mode 100644 index d8b38b9..0000000 --- a/xsa380-4.14-2.patch +++ /dev/null @@ -1,383 +0,0 @@ -From: Jan Beulich -Subject: gnttab: replace mapkind() - -mapkind() doesn't scale very well with larger maptrack entry counts, -using a brute force linear search through all entries, with the only -option of an early loop exit if a matching writable entry was found. -Introduce a radix tree alongside the main maptrack table, thus -allowing much faster MFN-based lookup. To avoid the need to actually -allocate space for the individual nodes, encode the two counters in the -node pointers themselves, thus limiting the number of permitted -simultaneous r/o and r/w mappings of the same MFN to 2³¹-1 (64-bit) / -2¹⁵-1 (32-bit) each. - -To avoid enforcing an unnecessarily low bound on the number of -simultaneous mappings of a single MFN, introduce -radix_tree_{ulong_to_ptr,ptr_to_ulong} paralleling -radix_tree_{int_to_ptr,ptr_to_int}. - -As a consequence locking changes are also applicable: With there no -longer being any inspection of the remote domain's active entries, -there's also no need anymore to hold the remote domain's grant table -lock. And since we're no longer iterating over the local domain's map -track table, the lock in map_grant_ref() can also be dropped before the -new maptrack entry actually gets populated. - -As a nice side effect this also reduces the number of IOMMU operations -in unmap_common(): Previously we would have "established" a readable -mapping whenever we didn't find a writable entry anymore (yet, of -course, at least one readable one). But we only need to do this if we -actually dropped the last writable entry, not if there were none already -before. - -This is part of CVE-2021-28698 / XSA-380. - -Signed-off-by: Jan Beulich -Reviewed-by: Julien Grall - ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -37,6 +37,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -82,8 +83,13 @@ struct grant_table { - grant_status_t **status; - /* Active grant table. */ - struct active_grant_entry **active; -- /* Mapping tracking table per vcpu. */ -+ /* Handle-indexed tracking table of mappings. */ - struct grant_mapping **maptrack; -+ /* -+ * MFN-indexed tracking tree of mappings, if needed. Note that this is -+ * protected by @lock, not @maptrack_lock. -+ */ -+ struct radix_tree_root maptrack_tree; - - /* Domain to which this struct grant_table belongs. */ - const struct domain *domain; -@@ -501,34 +507,6 @@ static int get_paged_frame(unsigned long - return GNTST_okay; - } - --static inline void --double_gt_lock(struct grant_table *lgt, struct grant_table *rgt) --{ -- /* -- * See mapkind() for why the write lock is also required for the -- * remote domain. -- */ -- if ( lgt < rgt ) -- { -- grant_write_lock(lgt); -- grant_write_lock(rgt); -- } -- else -- { -- if ( lgt != rgt ) -- grant_write_lock(rgt); -- grant_write_lock(lgt); -- } --} -- --static inline void --double_gt_unlock(struct grant_table *lgt, struct grant_table *rgt) --{ -- grant_write_unlock(lgt); -- if ( lgt != rgt ) -- grant_write_unlock(rgt); --} -- - #define INVALID_MAPTRACK_HANDLE UINT_MAX - - static inline grant_handle_t -@@ -948,41 +926,17 @@ static struct active_grant_entry *grant_ - return ERR_PTR(-EINVAL); - } - --#define MAPKIND_READ 1 --#define MAPKIND_WRITE 2 --static unsigned int mapkind( -- struct grant_table *lgt, const struct domain *rd, mfn_t mfn) --{ -- struct grant_mapping *map; -- grant_handle_t handle, limit = lgt->maptrack_limit; -- unsigned int kind = 0; -- -- /* -- * Must have the local domain's grant table write lock when -- * iterating over its maptrack entries. -- */ -- ASSERT(percpu_rw_is_write_locked(&lgt->lock)); -- /* -- * Must have the remote domain's grant table write lock while -- * counting its active entries. -- */ -- ASSERT(percpu_rw_is_write_locked(&rd->grant_table->lock)); -- -- smp_rmb(); -- -- for ( handle = 0; !(kind & MAPKIND_WRITE) && handle < limit; handle++ ) -- { -- map = &maptrack_entry(lgt, handle); -- if ( !(map->flags & (GNTMAP_device_map|GNTMAP_host_map)) || -- map->domid != rd->domain_id ) -- continue; -- if ( mfn_eq(_active_entry(rd->grant_table, map->ref).mfn, mfn) ) -- kind |= map->flags & GNTMAP_readonly ? -- MAPKIND_READ : MAPKIND_WRITE; -- } -- -- return kind; --} -+union maptrack_node { -+ struct { -+ /* Radix tree slot pointers use two of the bits. */ -+#ifdef __BIG_ENDIAN_BITFIELD -+ unsigned long : 2; -+#endif -+ unsigned long rd : BITS_PER_LONG / 2 - 1; -+ unsigned long wr : BITS_PER_LONG / 2 - 1; -+ } cnt; -+ unsigned long raw; -+}; - - static void - map_grant_ref( -@@ -1001,7 +955,6 @@ map_grant_ref( - struct grant_mapping *mt; - grant_entry_header_t *shah; - uint16_t *status; -- bool_t need_iommu; - - ld = current->domain; - -@@ -1220,31 +1173,75 @@ map_grant_ref( - * as mem-sharing and IOMMU use are incompatible). The dom_io case would - * need checking separately if we compared against owner here. - */ -- need_iommu = ld != rd && gnttab_need_iommu_mapping(ld); -- if ( need_iommu ) -+ if ( ld != rd && gnttab_need_iommu_mapping(ld) ) - { -+ union maptrack_node node = { -+ .cnt.rd = !!(op->flags & GNTMAP_readonly), -+ .cnt.wr = !(op->flags & GNTMAP_readonly), -+ }; -+ int err; -+ void **slot = NULL; - unsigned int kind; - -- double_gt_lock(lgt, rgt); -+ grant_write_lock(lgt); -+ -+ err = radix_tree_insert(&lgt->maptrack_tree, mfn_x(mfn), -+ radix_tree_ulong_to_ptr(node.raw)); -+ if ( err == -EEXIST ) -+ { -+ slot = radix_tree_lookup_slot(&lgt->maptrack_tree, mfn_x(mfn)); -+ if ( likely(slot) ) -+ { -+ node.raw = radix_tree_ptr_to_ulong(*slot); -+ err = -EBUSY; -+ -+ /* Update node only when refcount doesn't overflow. */ -+ if ( op->flags & GNTMAP_readonly ? ++node.cnt.rd -+ : ++node.cnt.wr ) -+ { -+ radix_tree_replace_slot(slot, -+ radix_tree_ulong_to_ptr(node.raw)); -+ err = 0; -+ } -+ } -+ else -+ ASSERT_UNREACHABLE(); -+ } - - /* - * We're not translated, so we know that dfns and mfns are - * the same things, so the IOMMU entry is always 1-to-1. - */ -- kind = mapkind(lgt, rd, mfn); -- if ( !(op->flags & GNTMAP_readonly) && -- !(kind & MAPKIND_WRITE) ) -+ if ( !(op->flags & GNTMAP_readonly) && node.cnt.wr == 1 ) - kind = IOMMUF_readable | IOMMUF_writable; -- else if ( !kind ) -+ else if ( (op->flags & GNTMAP_readonly) && -+ node.cnt.rd == 1 && !node.cnt.wr ) - kind = IOMMUF_readable; - else - kind = 0; -- if ( kind && iommu_legacy_map(ld, _dfn(mfn_x(mfn)), mfn, 0, kind) ) -+ if ( err || -+ (kind && iommu_legacy_map(ld, _dfn(mfn_x(mfn)), mfn, 0, kind)) ) - { -- double_gt_unlock(lgt, rgt); -+ if ( !err ) -+ { -+ if ( slot ) -+ { -+ op->flags & GNTMAP_readonly ? node.cnt.rd-- -+ : node.cnt.wr--; -+ radix_tree_replace_slot(slot, -+ radix_tree_ulong_to_ptr(node.raw)); -+ } -+ else -+ radix_tree_delete(&lgt->maptrack_tree, mfn_x(mfn)); -+ } -+ - rc = GNTST_general_error; -- goto undo_out; - } -+ -+ grant_write_unlock(lgt); -+ -+ if ( rc != GNTST_okay ) -+ goto undo_out; - } - - TRACE_1D(TRC_MEM_PAGE_GRANT_MAP, op->dom); -@@ -1252,10 +1249,6 @@ map_grant_ref( - /* - * All maptrack entry users check mt->flags first before using the - * other fields so just ensure the flags field is stored last. -- * -- * However, if gnttab_need_iommu_mapping() then this would race -- * with a concurrent mapkind() call (on an unmap, for example) -- * and a lock is required. - */ - mt = &maptrack_entry(lgt, handle); - mt->domid = op->dom; -@@ -1263,9 +1256,6 @@ map_grant_ref( - smp_wmb(); - write_atomic(&mt->flags, op->flags); - -- if ( need_iommu ) -- double_gt_unlock(lgt, rgt); -- - op->dev_bus_addr = mfn_to_maddr(mfn); - op->handle = handle; - op->status = GNTST_okay; -@@ -1487,19 +1477,34 @@ unmap_common( - /* See the respective comment in map_grant_ref(). */ - if ( rc == GNTST_okay && ld != rd && gnttab_need_iommu_mapping(ld) ) - { -- unsigned int kind; -+ void **slot; -+ union maptrack_node node; - int err = 0; - -- double_gt_lock(lgt, rgt); -+ grant_write_lock(lgt); -+ slot = radix_tree_lookup_slot(&lgt->maptrack_tree, mfn_x(op->mfn)); -+ node.raw = likely(slot) ? radix_tree_ptr_to_ulong(*slot) : 0; -+ -+ /* Refcount must not underflow. */ -+ if ( !(flags & GNTMAP_readonly ? node.cnt.rd-- -+ : node.cnt.wr--) ) -+ BUG(); - -- kind = mapkind(lgt, rd, op->mfn); -- if ( !kind ) -+ if ( !node.raw ) - err = iommu_legacy_unmap(ld, _dfn(mfn_x(op->mfn)), 0); -- else if ( !(kind & MAPKIND_WRITE) ) -+ else if ( !(flags & GNTMAP_readonly) && !node.cnt.wr ) - err = iommu_legacy_map(ld, _dfn(mfn_x(op->mfn)), op->mfn, 0, - IOMMUF_readable); - -- double_gt_unlock(lgt, rgt); -+ if ( err ) -+ ; -+ else if ( !node.raw ) -+ radix_tree_delete(&lgt->maptrack_tree, mfn_x(op->mfn)); -+ else -+ radix_tree_replace_slot(slot, -+ radix_tree_ulong_to_ptr(node.raw)); -+ -+ grant_write_unlock(lgt); - - if ( err ) - rc = GNTST_general_error; -@@ -1951,6 +1956,8 @@ int grant_table_init(struct domain *d, i - gt->maptrack = vzalloc(gt->max_maptrack_frames * sizeof(*gt->maptrack)); - if ( gt->maptrack == NULL ) - goto out; -+ -+ radix_tree_init(>->maptrack_tree); - } - - /* Shared grant table. */ -@@ -3734,6 +3741,7 @@ int gnttab_release_mappings(struct domai - for ( handle = gt->maptrack_limit; handle; ) - { - unsigned int clear_flags = 0; -+ mfn_t mfn; - - /* - * Deal with full pages such that their freeing (in the body of the -@@ -3839,17 +3847,31 @@ int gnttab_release_mappings(struct domai - if ( clear_flags ) - gnttab_clear_flags(rd, clear_flags, status); - -+ mfn = act->mfn; -+ - active_entry_release(act); - grant_read_unlock(rgt); - - rcu_unlock_domain(rd); - - map->flags = 0; -+ -+ /* -+ * This is excessive in that a single such call would suffice per -+ * mapped MFN (or none at all, if no entry was ever inserted). But it -+ * should be the common case for an MFN to be mapped just once, and -+ * this way we don't need to further maintain the counters. We also -+ * don't want to leave cleaning up of the tree as a whole to the end -+ * of the function, as this could take quite some time. -+ */ -+ radix_tree_delete(>->maptrack_tree, mfn_x(mfn)); - } - - gt->maptrack_limit = 0; - FREE_XENHEAP_PAGE(gt->maptrack[0]); - -+ radix_tree_destroy(>->maptrack_tree, NULL); -+ - return 0; - } - ---- a/xen/include/xen/radix-tree.h -+++ b/xen/include/xen/radix-tree.h -@@ -190,6 +190,25 @@ static inline int radix_tree_ptr_to_int( - return (int)((long)ptr >> 2); - } - -+/** -+ * radix_tree_{ulong_to_ptr,ptr_to_ulong}: -+ * -+ * Same for unsigned long values. Beware though that only BITS_PER_LONG-2 -+ * bits are actually usable for the value. -+ */ -+static inline void *radix_tree_ulong_to_ptr(unsigned long val) -+{ -+ unsigned long ptr = (val << 2) | 0x2; -+ ASSERT((ptr >> 2) == val); -+ return (void *)ptr; -+} -+ -+static inline unsigned long radix_tree_ptr_to_ulong(void *ptr) -+{ -+ ASSERT(((unsigned long)ptr & 0x3) == 0x2); -+ return (unsigned long)ptr >> 2; -+} -+ - int radix_tree_insert(struct radix_tree_root *, unsigned long, void *); - void *radix_tree_lookup(struct radix_tree_root *, unsigned long); - void **radix_tree_lookup_slot(struct radix_tree_root *, unsigned long); diff --git a/xsa382.patch b/xsa382.patch deleted file mode 100644 index 936c2de..0000000 --- a/xsa382.patch +++ /dev/null @@ -1,34 +0,0 @@ -From: Jan Beulich -Subject: gnttab: fix array capacity check in gnttab_get_status_frames() - -The number of grant frames is of no interest here; converting the passed -in op.nr_frames this way means we allow for 8 times as many GFNs to be -written as actually fit in the array. We would corrupt xlat areas of -higher vCPU-s (after having faulted many times while trying to write to -the guard pages between any two areas) for 32-bit PV guests. For HVM -guests we'd simply crash as soon as we hit the first guard page, as -accesses to the xlat area are simply memcpy() there. - -This is CVE-2021-28699 / XSA-382. - -Fixes: 18b1be5e324b ("gnttab: make resource limits per domain") -Signed-off-by: Jan Beulich - ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -3243,12 +3243,11 @@ gnttab_get_status_frames(XEN_GUEST_HANDL - goto unlock; - } - -- if ( unlikely(limit_max < grant_to_status_frames(op.nr_frames)) ) -+ if ( unlikely(limit_max < op.nr_frames) ) - { - gdprintk(XENLOG_WARNING, -- "grant_to_status_frames(%u) for d%d is too large (%u,%u)\n", -- op.nr_frames, d->domain_id, -- grant_to_status_frames(op.nr_frames), limit_max); -+ "nr_status_frames for %pd is too large (%u,%u)\n", -+ d, op.nr_frames, limit_max); - op.status = GNTST_general_error; - goto unlock; - } diff --git a/xsa383.patch b/xsa383.patch deleted file mode 100644 index 9ab5eb3..0000000 --- a/xsa383.patch +++ /dev/null @@ -1,55 +0,0 @@ -From: Julien Grall -Date: Sat, 3 Jul 2021 14:03:36 +0100 -Subject: [PATCH] xen/arm: Restrict the amount of memory that dom0less domU and - dom0 can allocate - -Currently, both dom0less domUs and dom0 can allocate an "unlimited" -amount of memory because d->max_pages is set to ~0U. - -In particular, the former are meant to be unprivileged. Therefore the -memory they could allocate should be bounded. As the domain are not yet -officially aware of Xen (we don't expose advertise it in the DT, yet -the hypercalls are accessible), they should not need to allocate more -than the initial amount. So cap set d->max_pages directly the amount of -memory we are meant to allocate. - -Take the opportunity to also restrict the memory for dom0 as the -domain is direct mapped (e.g. MFN == GFN) and therefore cannot -allocate outside of the pre-allocated region. - -This is CVE-2021-28700 / XSA-383. - -Reported-by: Julien Grall -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini -Tested-by: Stefano Stabellini ---- - xen/arch/arm/domain_build.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c -index 6c86d527810f..206038d1c022 100644 ---- a/xen/arch/arm/domain_build.c -+++ b/xen/arch/arm/domain_build.c -@@ -2440,7 +2440,8 @@ static int __init construct_domU(struct domain *d, - - if ( vcpu_create(d, 0) == NULL ) - return -ENOMEM; -- d->max_pages = ~0U; -+ -+ d->max_pages = ((paddr_t)mem * SZ_1K) >> PAGE_SHIFT; - - kinfo.d = d; - -@@ -2546,7 +2547,7 @@ static int __init construct_dom0(struct domain *d) - - iommu_hwdom_init(d); - -- d->max_pages = ~0U; -+ d->max_pages = dom0_mem >> PAGE_SHIFT; - - kinfo.unassigned_mem = dom0_mem; - kinfo.d = d; --- -2.17.1 - diff --git a/xsa384-4.14.patch b/xsa384-4.14.patch deleted file mode 100644 index 9efe1b0..0000000 --- a/xsa384-4.14.patch +++ /dev/null @@ -1,79 +0,0 @@ -From: Jan Beulich -Subject: gnttab: deal with status frame mapping race - -Once gnttab_map_frame() drops the grant table lock, the MFN it reports -back to its caller is free to other manipulation. In particular -gnttab_unpopulate_status_frames() might free it, by a racing request on -another CPU, thus resulting in a reference to a deallocated page getting -added to a domain's P2M. - -Obtain a page reference in gnttab_map_frame() to prevent freeing of the -page until xenmem_add_to_physmap_one() has actually completed its acting -on the page. Do so uniformly, even if only strictly required for v2 -status pages, to avoid extra conditionals (which then would all need to -be kept in sync going forward). - -This is CVE-2021-28701 / XSA-384. - -Reported-by: Julien Grall -Signed-off-by: Jan Beulich -Reviewed-by: Julien Grall - ---- a/xen/arch/arm/mm.c -+++ b/xen/arch/arm/mm.c -@@ -1423,6 +1423,8 @@ int xenmem_add_to_physmap_one( - if ( rc ) - return rc; - -+ /* Need to take care of the reference obtained in gnttab_map_frame(). */ -+ page = mfn_to_page(mfn); - t = p2m_ram_rw; - - break; -@@ -1490,9 +1492,12 @@ int xenmem_add_to_physmap_one( - /* Map at new location. */ - rc = guest_physmap_add_entry(d, gfn, mfn, 0, t); - -- /* If we fail to add the mapping, we need to drop the reference we -- * took earlier on foreign pages */ -- if ( rc && space == XENMAPSPACE_gmfn_foreign ) -+ /* -+ * For XENMAPSPACE_gmfn_foreign if we failed to add the mapping, we need -+ * to drop the reference we took earlier. In all other cases we need to -+ * drop any reference we took earlier (perhaps indirectly). -+ */ -+ if ( space == XENMAPSPACE_gmfn_foreign ? rc : page != NULL ) - { - ASSERT(page != NULL); - put_page(page); ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -4620,6 +4620,8 @@ int xenmem_add_to_physmap_one( - rc = gnttab_map_frame(d, idx, gpfn, &mfn); - if ( rc ) - return rc; -+ /* Need to take care of the ref obtained in gnttab_map_frame(). */ -+ page = mfn_to_page(mfn); - break; - case XENMAPSPACE_gmfn: - { ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -4102,7 +4102,16 @@ int gnttab_map_frame(struct domain *d, u - } - - if ( !rc ) -- gnttab_set_frame_gfn(gt, status, idx, gfn); -+ { -+ /* -+ * Make sure gnttab_unpopulate_status_frames() won't (successfully) -+ * free the page until our caller has completed its operation. -+ */ -+ if ( get_page(mfn_to_page(*mfn), d) ) -+ gnttab_set_frame_gfn(gt, status, idx, gfn); -+ else -+ rc = -EBUSY; -+ } - - grant_write_unlock(gt); - From 41796d118d4a85f1d66871d36ffbf798120e6e57 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 5 Oct 2021 22:11:15 +0100 Subject: [PATCH 023/137] PCI devices with RMRRs not deassigned correctly [XSA-386, CVE-2021-28702] --- xen.spec | 7 ++++++- xsa386.patch | 29 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 xsa386.patch diff --git a/xen.spec b/xen.spec index 9b0c237..6458986 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.15.1 -Release: 1%{?dist} +Release: 2%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -113,6 +113,7 @@ Patch41: xen.gcc9.fixes.patch Patch42: xen.gcc10.fixes.patch Patch43: xen.gcc11.fixes.patch Patch44: xen.ocaml.4.12.fixes.patch +Patch45: xsa386.patch %if %build_qemutrad @@ -322,6 +323,7 @@ manage Xen virtual machines. %patch42 -p1 %patch43 -p1 %patch44 -p1 +%patch45 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -917,6 +919,9 @@ fi %endif %changelog +* Sun Sep 12 2021 Michael Young - 4.15.1-2 +- PCI devices with RMRRs not deassigned correctly [XSA-386, CVE-2021-28702] + * Sun Sep 12 2021 Michael Young - 4.15.1-1 - update to xen-4.15.1 remove or adjust patches now included or superceded upstream diff --git a/xsa386.patch b/xsa386.patch new file mode 100644 index 0000000..83f24d3 --- /dev/null +++ b/xsa386.patch @@ -0,0 +1,29 @@ +From: Jan Beulich +Subject: VT-d: fix deassign of device with RMRR +Date: Fri, 1 Oct 2021 15:05:42 +0200 + +Ignoring a specific error code here was not meant to short circuit +deassign to _just_ the unmapping of RMRRs. This bug was previously +hidden by the bogus (potentially indefinite) looping in +pci_release_devices(), until f591755823a7 ("IOMMU/PCI: don't let domain +cleanup continue when device de-assignment failed") fixed that loop. + +This is CVE-2021-28702 / XSA-386. + +Fixes: 8b99f4400b69 ("VT-d: fix RMRR related error handling") +Reported-by: Ivan Kardykov +Signed-off-by: Jan Beulich +Tested-by: Ivan Kardykov + +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -2409,7 +2409,7 @@ static int reassign_device_ownership( + ret = iommu_identity_mapping(source, p2m_access_x, + rmrr->base_address, + rmrr->end_address, 0); +- if ( ret != -ENOENT ) ++ if ( ret && ret != -ENOENT ) + return ret; + } + } + From 8d859c10eead83bfa921685060e1a1c8043ccc03 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 6 Oct 2021 20:37:12 +0100 Subject: [PATCH 024/137] PCI devices with RMRRs not deassigned correctly [XSA-386, CVE-2021-28702] --- xen.spec | 8 +++++++- xsa386.patch | 29 +++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 xsa386.patch diff --git a/xen.spec b/xen.spec index ffb650d..3f5c8ed 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.14.3 -Release: 1%{?dist} +Release: 2%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -118,6 +118,7 @@ Patch47: xen.git-d6627cf1b63ce57a6a7e2c1800dbc50eed742c32.patch Patch48: xen.git-d8099d94dfaa3573bd86ebfc457cbc8f70a3ecda.patch Patch49: xen.git-8169f82049efb5b2044b33aa482ba3a136b7804d.patch Patch50: xsa363.patch +Patch51: xsa386.patch %if %build_qemutrad @@ -332,6 +333,7 @@ manage Xen virtual machines. %patch48 -p1 %patch49 -p1 %patch50 -p1 +%patch51 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -938,6 +940,10 @@ fi %endif %changelog +* Wed Oct 06 2021 Michael Young - 4.14.3-2 +- PCI devices with RMRRs not deassigned correctly [XSA-386, CVE-2021-28702] + (#2011248) + * Mon Sep 13 2021 Michael Young - 4.14.3-1 - update to xen-4.14.3 remove or adjust patches now included or superceded upstream diff --git a/xsa386.patch b/xsa386.patch new file mode 100644 index 0000000..83f24d3 --- /dev/null +++ b/xsa386.patch @@ -0,0 +1,29 @@ +From: Jan Beulich +Subject: VT-d: fix deassign of device with RMRR +Date: Fri, 1 Oct 2021 15:05:42 +0200 + +Ignoring a specific error code here was not meant to short circuit +deassign to _just_ the unmapping of RMRRs. This bug was previously +hidden by the bogus (potentially indefinite) looping in +pci_release_devices(), until f591755823a7 ("IOMMU/PCI: don't let domain +cleanup continue when device de-assignment failed") fixed that loop. + +This is CVE-2021-28702 / XSA-386. + +Fixes: 8b99f4400b69 ("VT-d: fix RMRR related error handling") +Reported-by: Ivan Kardykov +Signed-off-by: Jan Beulich +Tested-by: Ivan Kardykov + +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -2409,7 +2409,7 @@ static int reassign_device_ownership( + ret = iommu_identity_mapping(source, p2m_access_x, + rmrr->base_address, + rmrr->end_address, 0); +- if ( ret != -ENOENT ) ++ if ( ret && ret != -ENOENT ) + return ret; + } + } + From 6e2824bf21b55d24ea81d221eacd44942138f70f Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 6 Oct 2021 21:29:26 +0100 Subject: [PATCH 025/137] rebuild (f36 only) for OCaml 4.13.1 also fix date in changelog and add bug reference --- xen.spec | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/xen.spec b/xen.spec index 6458986..0a72c76 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.15.1 -Release: 2%{?dist} +Release: 3%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -919,8 +919,12 @@ fi %endif %changelog -* Sun Sep 12 2021 Michael Young - 4.15.1-2 +* Wed Oct 06 2021 Michael Young - 4.15.1-3 +- rebuild (f36 only) for OCaml 4.13.1 + +* Tue Oct 05 2021 Michael Young - 4.15.1-2 - PCI devices with RMRRs not deassigned correctly [XSA-386, CVE-2021-28702] + (#2011248) * Sun Sep 12 2021 Michael Young - 4.15.1-1 - update to xen-4.15.1 From 25ef88628b624c78ab0755a2dbf22c7b43451e4e Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 23 Nov 2021 18:54:02 +0000 Subject: [PATCH 026/137] 4 security fixes guests may exceed their designated memory limit [XSA-385, CVE-2021-28706] PoD operations on misaligned GFNs [XSA-388, CVE-2021-28704, CVE-2021-28707 CVE-2021-28708] issues with partially successful P2M updates on x86 [XSA-389, CVE-2021-28705, CVE-2021-28709] certain VT-d IOMMUs may not work in shared page table mode [XSA-390, CVE-2021-28710] --- xen.spec | 21 ++++- xsa385-4.15.patch | 96 +++++++++++++++++++++++ xsa388-4.15-1.patch | 174 ++++++++++++++++++++++++++++++++++++++++++ xsa388-4.15-2.patch | 36 +++++++++ xsa389-4.15.patch | 182 ++++++++++++++++++++++++++++++++++++++++++++ xsa390.patch | 46 +++++++++++ 6 files changed, 554 insertions(+), 1 deletion(-) create mode 100644 xsa385-4.15.patch create mode 100644 xsa388-4.15-1.patch create mode 100644 xsa388-4.15-2.patch create mode 100644 xsa389-4.15.patch create mode 100644 xsa390.patch diff --git a/xen.spec b/xen.spec index 0a72c76..e6b00db 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.15.1 -Release: 3%{?dist} +Release: 4%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -114,6 +114,11 @@ Patch42: xen.gcc10.fixes.patch Patch43: xen.gcc11.fixes.patch Patch44: xen.ocaml.4.12.fixes.patch Patch45: xsa386.patch +Patch46: xsa385-4.15.patch +Patch47: xsa388-4.15-1.patch +Patch48: xsa388-4.15-2.patch +Patch49: xsa389-4.15.patch +Patch50: xsa390.patch %if %build_qemutrad @@ -324,6 +329,11 @@ manage Xen virtual machines. %patch43 -p1 %patch44 -p1 %patch45 -p1 +%patch46 -p1 +%patch47 -p1 +%patch48 -p1 +%patch49 -p1 +%patch50 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -919,6 +929,15 @@ fi %endif %changelog +* Tue Nov 23 2021 Michael Young - 4.15.1-4 +- guests may exceed their designated memory limit [XSA-385, CVE-2021-28706] +- PoD operations on misaligned GFNs [XSA-388, CVE-2021-28704, CVE-2021-28707 + CVE-2021-28708] +- issues with partially successful P2M updates on x86 [XSA-389, + CVE-2021-28705, CVE-2021-28709] +- certain VT-d IOMMUs may not work in shared page table mode [XSA-390, + CVE-2021-28710] + * Wed Oct 06 2021 Michael Young - 4.15.1-3 - rebuild (f36 only) for OCaml 4.13.1 diff --git a/xsa385-4.15.patch b/xsa385-4.15.patch new file mode 100644 index 0000000..69b9704 --- /dev/null +++ b/xsa385-4.15.patch @@ -0,0 +1,96 @@ +From: Julien Grall +Subject: xen/page_alloc: Harden assign_pages() + +domain_tot_pages() and d->max_pages are 32-bit values. While the order +should always be quite small, it would still be possible to overflow +if domain_tot_pages() is near to (2^32 - 1). + +As this code may be called by a guest via XENMEM_increase_reservation +and XENMEM_populate_physmap, we want to make sure the guest is not going +to be able to allocate more than it is allowed. + +Rework the allocation check to avoid any possible overflow. While the +check domain_tot_pages() < d->max_pages should technically not be +necessary, it is probably best to have it to catch any possible +inconsistencies in the future. + +This is CVE-2021-28706 / XSA-385. + +Signed-off-by: Julien Grall +Signed-off-by: Jan Beulich + +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -2336,7 +2336,8 @@ gnttab_transfer( + * pages when it is dying. + */ + if ( unlikely(e->is_dying) || +- unlikely(domain_tot_pages(e) >= e->max_pages) ) ++ unlikely(domain_tot_pages(e) >= e->max_pages) || ++ unlikely(!(e->tot_pages + 1)) ) + { + spin_unlock(&e->page_alloc_lock); + +@@ -2345,8 +2346,8 @@ gnttab_transfer( + e->domain_id); + else + gdprintk(XENLOG_INFO, +- "Transferee d%d has no headroom (tot %u, max %u)\n", +- e->domain_id, domain_tot_pages(e), e->max_pages); ++ "Transferee %pd has no headroom (tot %u, max %u, ex %u)\n", ++ e, domain_tot_pages(e), e->max_pages, e->extra_pages); + + gop.status = GNTST_general_error; + goto unlock_and_copyback; +--- a/xen/common/page_alloc.c ++++ b/xen/common/page_alloc.c +@@ -2298,20 +2298,43 @@ int assign_pages( + } + else if ( !(memflags & MEMF_no_refcount) ) + { +- unsigned int tot_pages = domain_tot_pages(d) + (1 << order); ++ unsigned int tot_pages = domain_tot_pages(d), nr = 1u << order; + + if ( unlikely(tot_pages > d->max_pages) ) + { +- gprintk(XENLOG_INFO, "Over-allocation for domain %u: " +- "%u > %u\n", d->domain_id, tot_pages, d->max_pages); ++ gprintk(XENLOG_INFO, "Inconsistent allocation for %pd: %u > %u\n", ++ d, tot_pages, d->max_pages); ++ rc = -EPERM; ++ goto out; ++ } ++ ++ if ( unlikely(nr > d->max_pages - tot_pages) ) ++ { ++ gprintk(XENLOG_INFO, "Over-allocation for %pd: %Lu > %u\n", ++ d, tot_pages + 0ull + nr, d->max_pages); + rc = -E2BIG; + goto out; + } + } + +- if ( !(memflags & MEMF_no_refcount) && +- unlikely(domain_adjust_tot_pages(d, 1 << order) == (1 << order)) ) +- get_knownalive_domain(d); ++ if ( !(memflags & MEMF_no_refcount) ) ++ { ++ unsigned int nr = 1u << order; ++ ++ if ( unlikely(d->tot_pages + nr < nr) ) ++ { ++ gprintk(XENLOG_INFO, ++ "Excess allocation for %pd: %Lu (%u extra)\n", ++ d, d->tot_pages + 0ull + nr, d->extra_pages); ++ if ( pg[0].count_info & PGC_extra ) ++ d->extra_pages -= nr; ++ rc = -E2BIG; ++ goto out; ++ } ++ ++ if ( unlikely(domain_adjust_tot_pages(d, nr) == nr) ) ++ get_knownalive_domain(d); ++ } + + for ( i = 0; i < (1 << order); i++ ) + { diff --git a/xsa388-4.15-1.patch b/xsa388-4.15-1.patch new file mode 100644 index 0000000..b4d9003 --- /dev/null +++ b/xsa388-4.15-1.patch @@ -0,0 +1,174 @@ +From: Jan Beulich +Subject: x86/PoD: deal with misaligned GFNs + +Users of XENMEM_decrease_reservation and XENMEM_populate_physmap aren't +required to pass in order-aligned GFN values. (While I consider this +bogus, I don't think we can fix this there, as that might break existing +code, e.g Linux'es swiotlb, which - while affecting PV only - until +recently had been enforcing only page alignment on the original +allocation.) Only non-PoD code paths (guest_physmap_{add,remove}_page(), +p2m_set_entry()) look to be dealing with this properly (in part by being +implemented inefficiently, handling every 4k page separately). + +Introduce wrappers taking care of splitting the incoming request into +aligned chunks, without putting much effort in trying to determine the +largest possible chunk at every iteration. + +Also "handle" p2m_set_entry() failure for non-order-0 requests by +crashing the domain in one more place. Alongside putting a log message +there, also add one to the other similar path. + +Note regarding locking: This is left in the actual worker functions on +the assumption that callers aren't guaranteed atomicity wrt acting on +multiple pages at a time. For mis-aligned GFNs gfn_lock() wouldn't have +locked the correct GFN range anyway, if it didn't simply resolve to +p2m_lock(), and for well-behaved callers there continues to be only a +single iteration, i.e. behavior is unchanged for them. (FTAOD pulling +out just pod_lock() into p2m_pod_decrease_reservation() would result in +a lock order violation.) + +This is CVE-2021-28704 and CVE-2021-28707 / part of XSA-388. + +Fixes: 3c352011c0d3 ("x86/PoD: shorten certain operations on higher order ranges") +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné + +--- a/xen/arch/x86/mm/p2m-pod.c ++++ b/xen/arch/x86/mm/p2m-pod.c +@@ -496,7 +496,7 @@ p2m_pod_zero_check_superpage(struct p2m_ + + + /* +- * This function is needed for two reasons: ++ * This pair of functions is needed for two reasons: + * + To properly handle clearing of PoD entries + * + To "steal back" memory being freed for the PoD cache, rather than + * releasing it. +@@ -504,8 +504,8 @@ p2m_pod_zero_check_superpage(struct p2m_ + * Once both of these functions have been completed, we can return and + * allow decrease_reservation() to handle everything else. + */ +-unsigned long +-p2m_pod_decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order) ++static unsigned long ++decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order) + { + unsigned long ret = 0, i, n; + struct p2m_domain *p2m = p2m_get_hostp2m(d); +@@ -552,8 +552,10 @@ p2m_pod_decrease_reservation(struct doma + * All PoD: Mark the whole region invalid and tell caller + * we're done. + */ +- if ( p2m_set_entry(p2m, gfn, INVALID_MFN, order, p2m_invalid, +- p2m->default_access) ) ++ int rc = p2m_set_entry(p2m, gfn, INVALID_MFN, order, p2m_invalid, ++ p2m->default_access); ++ ++ if ( rc ) + { + /* + * If this fails, we can't tell how much of the range was changed. +@@ -561,7 +563,12 @@ p2m_pod_decrease_reservation(struct doma + * impossible. + */ + if ( order != 0 ) ++ { ++ printk(XENLOG_G_ERR ++ "%pd: marking GFN %#lx (order %u) as non-PoD failed: %d\n", ++ d, gfn_x(gfn), order, rc); + domain_crash(d); ++ } + goto out_unlock; + } + ret = 1UL << order; +@@ -670,6 +677,22 @@ out_unlock: + return ret; + } + ++unsigned long ++p2m_pod_decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order) ++{ ++ unsigned long left = 1UL << order, ret = 0; ++ unsigned int chunk_order = find_first_set_bit(gfn_x(gfn) | left); ++ ++ do { ++ ret += decrease_reservation(d, gfn, chunk_order); ++ ++ left -= 1UL << chunk_order; ++ gfn = gfn_add(gfn, 1UL << chunk_order); ++ } while ( left ); ++ ++ return ret; ++} ++ + void p2m_pod_dump_data(struct domain *d) + { + struct p2m_domain *p2m = p2m_get_hostp2m(d); +@@ -1273,19 +1296,15 @@ remap_and_retry: + return true; + } + +- +-int +-guest_physmap_mark_populate_on_demand(struct domain *d, unsigned long gfn_l, +- unsigned int order) ++static int ++mark_populate_on_demand(struct domain *d, unsigned long gfn_l, ++ unsigned int order) + { + struct p2m_domain *p2m = p2m_get_hostp2m(d); + gfn_t gfn = _gfn(gfn_l); + unsigned long i, n, pod_count = 0; + int rc = 0; + +- if ( !paging_mode_translate(d) ) +- return -EINVAL; +- + gfn_lock(p2m, gfn, order); + + P2M_DEBUG("mark pod gfn=%#lx\n", gfn_l); +@@ -1325,12 +1344,44 @@ guest_physmap_mark_populate_on_demand(st + + ioreq_request_mapcache_invalidate(d); + } ++ else if ( order ) ++ { ++ /* ++ * If this failed, we can't tell how much of the range was changed. ++ * Best to crash the domain. ++ */ ++ printk(XENLOG_G_ERR ++ "%pd: marking GFN %#lx (order %u) as PoD failed: %d\n", ++ d, gfn_l, order, rc); ++ domain_crash(d); ++ } + + out: + gfn_unlock(p2m, gfn, order); + + return rc; + } ++ ++int ++guest_physmap_mark_populate_on_demand(struct domain *d, unsigned long gfn, ++ unsigned int order) ++{ ++ unsigned long left = 1UL << order; ++ unsigned int chunk_order = find_first_set_bit(gfn | left); ++ int rc; ++ ++ if ( !paging_mode_translate(d) ) ++ return -EINVAL; ++ ++ do { ++ rc = mark_populate_on_demand(d, gfn, chunk_order); ++ ++ left -= 1UL << chunk_order; ++ gfn += 1UL << chunk_order; ++ } while ( !rc && left ); ++ ++ return rc; ++} + + void p2m_pod_init(struct p2m_domain *p2m) + { diff --git a/xsa388-4.15-2.patch b/xsa388-4.15-2.patch new file mode 100644 index 0000000..ccccb20 --- /dev/null +++ b/xsa388-4.15-2.patch @@ -0,0 +1,36 @@ +From: Jan Beulich +Subject: x86/PoD: handle intermediate page orders in p2m_pod_cache_add() + +p2m_pod_decrease_reservation() may pass pages to the function which +aren't 4k, 2M, or 1G. Handle all intermediate orders as well, to avoid +hitting the BUG() at the switch() statement's "default" case. + +This is CVE-2021-28708 / part of XSA-388. + +Fixes: 3c352011c0d3 ("x86/PoD: shorten certain operations on higher order ranges") +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné + +--- a/xen/arch/x86/mm/p2m-pod.c ++++ b/xen/arch/x86/mm/p2m-pod.c +@@ -112,15 +112,13 @@ p2m_pod_cache_add(struct p2m_domain *p2m + /* Then add to the appropriate populate-on-demand list. */ + switch ( order ) + { +- case PAGE_ORDER_1G: +- for ( i = 0; i < (1UL << PAGE_ORDER_1G); i += 1UL << PAGE_ORDER_2M ) ++ case PAGE_ORDER_2M ... PAGE_ORDER_1G: ++ for ( i = 0; i < (1UL << order); i += 1UL << PAGE_ORDER_2M ) + page_list_add_tail(page + i, &p2m->pod.super); + break; +- case PAGE_ORDER_2M: +- page_list_add_tail(page, &p2m->pod.super); +- break; +- case PAGE_ORDER_4K: +- page_list_add_tail(page, &p2m->pod.single); ++ case PAGE_ORDER_4K ... PAGE_ORDER_2M - 1: ++ for ( i = 0; i < (1UL << order); i += 1UL << PAGE_ORDER_4K ) ++ page_list_add_tail(page + i, &p2m->pod.single); + break; + default: + BUG(); diff --git a/xsa389-4.15.patch b/xsa389-4.15.patch new file mode 100644 index 0000000..402a38e --- /dev/null +++ b/xsa389-4.15.patch @@ -0,0 +1,182 @@ +From: Jan Beulich +Subject: x86/P2M: deal with partial success of p2m_set_entry() + +M2P and PoD stats need to remain in sync with P2M; if an update succeeds +only partially, respective adjustments need to be made. If updates get +made before the call, they may also need undoing upon complete failure +(i.e. including the single-page case). + +Log-dirty state would better also be kept in sync. + +Note that the change to set_typed_p2m_entry() may not be strictly +necessary (due to the order restriction enforced near the top of the +function), but is being kept here to be on the safe side. + +This is CVE-2021-28705 and CVE-2021-28709 / XSA-389. + +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné + +--- a/xen/arch/x86/mm/p2m.c ++++ b/xen/arch/x86/mm/p2m.c +@@ -784,6 +784,7 @@ p2m_remove_page(struct p2m_domain *p2m, + unsigned long i; + p2m_type_t t; + p2m_access_t a; ++ int rc; + + /* IOMMU for PV guests is handled in get_page_type() and put_page(). */ + if ( !paging_mode_translate(p2m->domain) ) +@@ -819,8 +820,27 @@ p2m_remove_page(struct p2m_domain *p2m, + + ioreq_request_mapcache_invalidate(p2m->domain); + +- return p2m_set_entry(p2m, gfn, INVALID_MFN, page_order, p2m_invalid, +- p2m->default_access); ++ rc = p2m_set_entry(p2m, gfn, INVALID_MFN, page_order, p2m_invalid, ++ p2m->default_access); ++ if ( likely(!rc) || !mfn_valid(mfn) ) ++ return rc; ++ ++ /* ++ * The operation may have partially succeeded. For the failed part we need ++ * to undo the M2P update and, out of precaution, mark the pages dirty ++ * again. ++ */ ++ for ( i = 0; i < (1UL << page_order); ++i ) ++ { ++ p2m->get_entry(p2m, gfn_add(gfn, i), &t, &a, 0, NULL, NULL); ++ if ( !p2m_is_hole(t) && !p2m_is_special(t) && !p2m_is_shared(t) ) ++ { ++ set_gpfn_from_mfn(mfn_x(mfn) + i, gfn_x(gfn) + i); ++ paging_mark_pfn_dirty(p2m->domain, _pfn(gfn_x(gfn) + i)); ++ } ++ } ++ ++ return rc; + } + + int +@@ -1009,13 +1029,8 @@ guest_physmap_add_entry(struct domain *d + + /* Now, actually do the two-way mapping */ + rc = p2m_set_entry(p2m, gfn, mfn, page_order, t, p2m->default_access); +- if ( rc == 0 ) ++ if ( likely(!rc) ) + { +- pod_lock(p2m); +- p2m->pod.entry_count -= pod_count; +- BUG_ON(p2m->pod.entry_count < 0); +- pod_unlock(p2m); +- + if ( !p2m_is_grant(t) ) + { + for ( i = 0; i < (1UL << page_order); i++ ) +@@ -1023,6 +1038,42 @@ guest_physmap_add_entry(struct domain *d + gfn_x(gfn_add(gfn, i))); + } + } ++ else ++ { ++ /* ++ * The operation may have partially succeeded. For the successful part ++ * we need to update M2P and dirty state, while for the failed part we ++ * may need to adjust PoD stats as well as undo the earlier M2P update. ++ */ ++ for ( i = 0; i < (1UL << page_order); ++i ) ++ { ++ omfn = p2m->get_entry(p2m, gfn_add(gfn, i), &ot, &a, 0, NULL, NULL); ++ if ( p2m_is_pod(ot) ) ++ { ++ BUG_ON(!pod_count); ++ --pod_count; ++ } ++ else if ( mfn_eq(omfn, mfn_add(mfn, i)) && ot == t && ++ a == p2m->default_access && !p2m_is_grant(t) ) ++ { ++ set_gpfn_from_mfn(mfn_x(omfn), gfn_x(gfn) + i); ++ paging_mark_pfn_dirty(d, _pfn(gfn_x(gfn) + i)); ++ } ++ else if ( p2m_is_ram(ot) && !p2m_is_paged(ot) ) ++ { ++ ASSERT(mfn_valid(omfn)); ++ set_gpfn_from_mfn(mfn_x(omfn), gfn_x(gfn) + i); ++ } ++ } ++ } ++ ++ if ( pod_count ) ++ { ++ pod_lock(p2m); ++ p2m->pod.entry_count -= pod_count; ++ BUG_ON(p2m->pod.entry_count < 0); ++ pod_unlock(p2m); ++ } + + out: + p2m_unlock(p2m); +@@ -1314,6 +1365,51 @@ static int set_typed_p2m_entry(struct do + return 0; + } + } ++ ++ P2M_DEBUG("set %d %lx %lx\n", gfn_p2mt, gfn_l, mfn_x(mfn)); ++ rc = p2m_set_entry(p2m, gfn, mfn, order, gfn_p2mt, access); ++ if ( unlikely(rc) ) ++ { ++ gdprintk(XENLOG_ERR, "p2m_set_entry: %#lx:%u -> %d (0x%"PRI_mfn")\n", ++ gfn_l, order, rc, mfn_x(mfn)); ++ ++ /* ++ * The operation may have partially succeeded. For the successful part ++ * we need to update PoD stats, M2P, and dirty state. ++ */ ++ if ( order != PAGE_ORDER_4K ) ++ { ++ unsigned long i; ++ ++ for ( i = 0; i < (1UL << order); ++i ) ++ { ++ p2m_type_t t; ++ mfn_t cmfn = p2m->get_entry(p2m, gfn_add(gfn, i), &t, &a, 0, ++ NULL, NULL); ++ ++ if ( !mfn_eq(cmfn, mfn_add(mfn, i)) || t != gfn_p2mt || ++ a != access ) ++ continue; ++ ++ if ( p2m_is_ram(ot) ) ++ { ++ ASSERT(mfn_valid(mfn_add(omfn, i))); ++ set_gpfn_from_mfn(mfn_x(omfn) + i, INVALID_M2P_ENTRY); ++ ++ ioreq_request_mapcache_invalidate(d); ++ } ++#ifdef CONFIG_HVM ++ else if ( p2m_is_pod(ot) ) ++ { ++ pod_lock(p2m); ++ BUG_ON(!p2m->pod.entry_count); ++ --p2m->pod.entry_count; ++ pod_unlock(p2m); ++ } ++#endif ++ } ++ } ++ } + else if ( p2m_is_ram(ot) ) + { + unsigned long i; +@@ -1326,12 +1422,6 @@ static int set_typed_p2m_entry(struct do + + ioreq_request_mapcache_invalidate(d); + } +- +- P2M_DEBUG("set %d %lx %lx\n", gfn_p2mt, gfn_l, mfn_x(mfn)); +- rc = p2m_set_entry(p2m, gfn, mfn, order, gfn_p2mt, access); +- if ( rc ) +- gdprintk(XENLOG_ERR, "p2m_set_entry: %#lx:%u -> %d (0x%"PRI_mfn")\n", +- gfn_l, order, rc, mfn_x(mfn)); + #ifdef CONFIG_HVM + else if ( p2m_is_pod(ot) ) + { diff --git a/xsa390.patch b/xsa390.patch new file mode 100644 index 0000000..3c008a9 --- /dev/null +++ b/xsa390.patch @@ -0,0 +1,46 @@ +From: Jan Beulich +Subject: VT-d: fix reduced page table levels support when sharing tables + +domain_pgd_maddr() contains logic to adjust the root address to be put +in the context entry in case 4-level page tables aren't supported by an +IOMMU. This logic may not be bypassed when sharing page tables. + +This is CVE-2021-28710 / XSA-390. + +Fixes: 25ccd093425c ("iommu: remove the share_p2m operation") +Signed-off-by: Jan Beulich +Reviewed-by: Kevin Tian + +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -340,19 +340,21 @@ static uint64_t domain_pgd_maddr(struct + { + pagetable_t pgt = p2m_get_pagetable(p2m_get_hostp2m(d)); + +- return pagetable_get_paddr(pgt); ++ pgd_maddr = pagetable_get_paddr(pgt); + } +- +- if ( !hd->arch.vtd.pgd_maddr ) ++ else + { +- /* Ensure we have pagetables allocated down to leaf PTE. */ +- addr_to_dma_page_maddr(d, 0, 1); +- + if ( !hd->arch.vtd.pgd_maddr ) +- return 0; +- } ++ { ++ /* Ensure we have pagetables allocated down to leaf PTE. */ ++ addr_to_dma_page_maddr(d, 0, 1); + +- pgd_maddr = hd->arch.vtd.pgd_maddr; ++ if ( !hd->arch.vtd.pgd_maddr ) ++ return 0; ++ } ++ ++ pgd_maddr = hd->arch.vtd.pgd_maddr; ++ } + + /* Skip top levels of page tables for 2- and 3-level DRHDs. */ + for ( agaw = level_to_agaw(4); From 1126cbfcae95427a32ef89bcdad3ab5af6b38c28 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 23 Nov 2021 20:17:55 +0000 Subject: [PATCH 027/137] 3 security fixes guests may exceed their designated memory limit [XSA-385, CVE-2021-28706] PoD operations on misaligned GFNs [XSA-388, CVE-2021-28704, CVE-2021-28707 CVE-2021-28708] issues with partially successful P2M updates on x86 [XSA-389, CVE-2021-28705, CVE-2021-28709] --- xen.spec | 17 ++++- xsa385-4.15.patch | 96 +++++++++++++++++++++++ xsa388-4.14-1.patch | 174 ++++++++++++++++++++++++++++++++++++++++++ xsa388-4.14-2.patch | 36 +++++++++ xsa389-4.14.patch | 180 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 502 insertions(+), 1 deletion(-) create mode 100644 xsa385-4.15.patch create mode 100644 xsa388-4.14-1.patch create mode 100644 xsa388-4.14-2.patch create mode 100644 xsa389-4.14.patch diff --git a/xen.spec b/xen.spec index 3f5c8ed..324986b 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.14.3 -Release: 2%{?dist} +Release: 3%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -119,6 +119,10 @@ Patch48: xen.git-d8099d94dfaa3573bd86ebfc457cbc8f70a3ecda.patch Patch49: xen.git-8169f82049efb5b2044b33aa482ba3a136b7804d.patch Patch50: xsa363.patch Patch51: xsa386.patch +Patch52: xsa385-4.15.patch +Patch53: xsa388-4.14-1.patch +Patch54: xsa388-4.14-2.patch +Patch55: xsa389-4.14.patch %if %build_qemutrad @@ -334,6 +338,10 @@ manage Xen virtual machines. %patch49 -p1 %patch50 -p1 %patch51 -p1 +%patch52 -p1 +%patch53 -p1 +%patch54 -p1 +%patch55 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -940,6 +948,13 @@ fi %endif %changelog +* Tue Nov 23 2021 Michael Young - 4.14.3-3 +- guests may exceed their designated memory limit [XSA-385, CVE-2021-28706] +- PoD operations on misaligned GFNs [XSA-388, CVE-2021-28704, CVE-2021-28707 + CVE-2021-28708] +- issues with partially successful P2M updates on x86 [XSA-389, + CVE-2021-28705, CVE-2021-28709] + * Wed Oct 06 2021 Michael Young - 4.14.3-2 - PCI devices with RMRRs not deassigned correctly [XSA-386, CVE-2021-28702] (#2011248) diff --git a/xsa385-4.15.patch b/xsa385-4.15.patch new file mode 100644 index 0000000..69b9704 --- /dev/null +++ b/xsa385-4.15.patch @@ -0,0 +1,96 @@ +From: Julien Grall +Subject: xen/page_alloc: Harden assign_pages() + +domain_tot_pages() and d->max_pages are 32-bit values. While the order +should always be quite small, it would still be possible to overflow +if domain_tot_pages() is near to (2^32 - 1). + +As this code may be called by a guest via XENMEM_increase_reservation +and XENMEM_populate_physmap, we want to make sure the guest is not going +to be able to allocate more than it is allowed. + +Rework the allocation check to avoid any possible overflow. While the +check domain_tot_pages() < d->max_pages should technically not be +necessary, it is probably best to have it to catch any possible +inconsistencies in the future. + +This is CVE-2021-28706 / XSA-385. + +Signed-off-by: Julien Grall +Signed-off-by: Jan Beulich + +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -2336,7 +2336,8 @@ gnttab_transfer( + * pages when it is dying. + */ + if ( unlikely(e->is_dying) || +- unlikely(domain_tot_pages(e) >= e->max_pages) ) ++ unlikely(domain_tot_pages(e) >= e->max_pages) || ++ unlikely(!(e->tot_pages + 1)) ) + { + spin_unlock(&e->page_alloc_lock); + +@@ -2345,8 +2346,8 @@ gnttab_transfer( + e->domain_id); + else + gdprintk(XENLOG_INFO, +- "Transferee d%d has no headroom (tot %u, max %u)\n", +- e->domain_id, domain_tot_pages(e), e->max_pages); ++ "Transferee %pd has no headroom (tot %u, max %u, ex %u)\n", ++ e, domain_tot_pages(e), e->max_pages, e->extra_pages); + + gop.status = GNTST_general_error; + goto unlock_and_copyback; +--- a/xen/common/page_alloc.c ++++ b/xen/common/page_alloc.c +@@ -2298,20 +2298,43 @@ int assign_pages( + } + else if ( !(memflags & MEMF_no_refcount) ) + { +- unsigned int tot_pages = domain_tot_pages(d) + (1 << order); ++ unsigned int tot_pages = domain_tot_pages(d), nr = 1u << order; + + if ( unlikely(tot_pages > d->max_pages) ) + { +- gprintk(XENLOG_INFO, "Over-allocation for domain %u: " +- "%u > %u\n", d->domain_id, tot_pages, d->max_pages); ++ gprintk(XENLOG_INFO, "Inconsistent allocation for %pd: %u > %u\n", ++ d, tot_pages, d->max_pages); ++ rc = -EPERM; ++ goto out; ++ } ++ ++ if ( unlikely(nr > d->max_pages - tot_pages) ) ++ { ++ gprintk(XENLOG_INFO, "Over-allocation for %pd: %Lu > %u\n", ++ d, tot_pages + 0ull + nr, d->max_pages); + rc = -E2BIG; + goto out; + } + } + +- if ( !(memflags & MEMF_no_refcount) && +- unlikely(domain_adjust_tot_pages(d, 1 << order) == (1 << order)) ) +- get_knownalive_domain(d); ++ if ( !(memflags & MEMF_no_refcount) ) ++ { ++ unsigned int nr = 1u << order; ++ ++ if ( unlikely(d->tot_pages + nr < nr) ) ++ { ++ gprintk(XENLOG_INFO, ++ "Excess allocation for %pd: %Lu (%u extra)\n", ++ d, d->tot_pages + 0ull + nr, d->extra_pages); ++ if ( pg[0].count_info & PGC_extra ) ++ d->extra_pages -= nr; ++ rc = -E2BIG; ++ goto out; ++ } ++ ++ if ( unlikely(domain_adjust_tot_pages(d, nr) == nr) ) ++ get_knownalive_domain(d); ++ } + + for ( i = 0; i < (1 << order); i++ ) + { diff --git a/xsa388-4.14-1.patch b/xsa388-4.14-1.patch new file mode 100644 index 0000000..f76f2d5 --- /dev/null +++ b/xsa388-4.14-1.patch @@ -0,0 +1,174 @@ +From: Jan Beulich +Subject: x86/PoD: deal with misaligned GFNs + +Users of XENMEM_decrease_reservation and XENMEM_populate_physmap aren't +required to pass in order-aligned GFN values. (While I consider this +bogus, I don't think we can fix this there, as that might break existing +code, e.g Linux'es swiotlb, which - while affecting PV only - until +recently had been enforcing only page alignment on the original +allocation.) Only non-PoD code paths (guest_physmap_{add,remove}_page(), +p2m_set_entry()) look to be dealing with this properly (in part by being +implemented inefficiently, handling every 4k page separately). + +Introduce wrappers taking care of splitting the incoming request into +aligned chunks, without putting much effort in trying to determine the +largest possible chunk at every iteration. + +Also "handle" p2m_set_entry() failure for non-order-0 requests by +crashing the domain in one more place. Alongside putting a log message +there, also add one to the other similar path. + +Note regarding locking: This is left in the actual worker functions on +the assumption that callers aren't guaranteed atomicity wrt acting on +multiple pages at a time. For mis-aligned GFNs gfn_lock() wouldn't have +locked the correct GFN range anyway, if it didn't simply resolve to +p2m_lock(), and for well-behaved callers there continues to be only a +single iteration, i.e. behavior is unchanged for them. (FTAOD pulling +out just pod_lock() into p2m_pod_decrease_reservation() would result in +a lock order violation.) + +This is CVE-2021-28704 and CVE-2021-28707 / part of XSA-388. + +Fixes: 3c352011c0d3 ("x86/PoD: shorten certain operations on higher order ranges") +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné + +--- a/xen/arch/x86/mm/p2m-pod.c ++++ b/xen/arch/x86/mm/p2m-pod.c +@@ -495,7 +495,7 @@ p2m_pod_zero_check_superpage(struct p2m_ + + + /* +- * This function is needed for two reasons: ++ * This pair of functions is needed for two reasons: + * + To properly handle clearing of PoD entries + * + To "steal back" memory being freed for the PoD cache, rather than + * releasing it. +@@ -503,8 +503,8 @@ p2m_pod_zero_check_superpage(struct p2m_ + * Once both of these functions have been completed, we can return and + * allow decrease_reservation() to handle everything else. + */ +-unsigned long +-p2m_pod_decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order) ++static unsigned long ++decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order) + { + unsigned long ret = 0, i, n; + struct p2m_domain *p2m = p2m_get_hostp2m(d); +@@ -551,8 +551,10 @@ p2m_pod_decrease_reservation(struct doma + * All PoD: Mark the whole region invalid and tell caller + * we're done. + */ +- if ( p2m_set_entry(p2m, gfn, INVALID_MFN, order, p2m_invalid, +- p2m->default_access) ) ++ int rc = p2m_set_entry(p2m, gfn, INVALID_MFN, order, p2m_invalid, ++ p2m->default_access); ++ ++ if ( rc ) + { + /* + * If this fails, we can't tell how much of the range was changed. +@@ -560,7 +562,12 @@ p2m_pod_decrease_reservation(struct doma + * impossible. + */ + if ( order != 0 ) ++ { ++ printk(XENLOG_G_ERR ++ "%pd: marking GFN %#lx (order %u) as non-PoD failed: %d\n", ++ d, gfn_x(gfn), order, rc); + domain_crash(d); ++ } + goto out_unlock; + } + ret = 1UL << order; +@@ -667,6 +674,22 @@ out_unlock: + return ret; + } + ++unsigned long ++p2m_pod_decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order) ++{ ++ unsigned long left = 1UL << order, ret = 0; ++ unsigned int chunk_order = find_first_set_bit(gfn_x(gfn) | left); ++ ++ do { ++ ret += decrease_reservation(d, gfn, chunk_order); ++ ++ left -= 1UL << chunk_order; ++ gfn = gfn_add(gfn, 1UL << chunk_order); ++ } while ( left ); ++ ++ return ret; ++} ++ + void p2m_pod_dump_data(struct domain *d) + { + struct p2m_domain *p2m = p2m_get_hostp2m(d); +@@ -1266,19 +1289,15 @@ remap_and_retry: + return true; + } + +- +-int +-guest_physmap_mark_populate_on_demand(struct domain *d, unsigned long gfn_l, +- unsigned int order) ++static int ++mark_populate_on_demand(struct domain *d, unsigned long gfn_l, ++ unsigned int order) + { + struct p2m_domain *p2m = p2m_get_hostp2m(d); + gfn_t gfn = _gfn(gfn_l); + unsigned long i, n, pod_count = 0; + int rc = 0; + +- if ( !paging_mode_translate(d) ) +- return -EINVAL; +- + gfn_lock(p2m, gfn, order); + + P2M_DEBUG("mark pod gfn=%#lx\n", gfn_l); +@@ -1316,12 +1335,44 @@ guest_physmap_mark_populate_on_demand(st + BUG_ON(p2m->pod.entry_count < 0); + pod_unlock(p2m); + } ++ else if ( order ) ++ { ++ /* ++ * If this failed, we can't tell how much of the range was changed. ++ * Best to crash the domain. ++ */ ++ printk(XENLOG_G_ERR ++ "%pd: marking GFN %#lx (order %u) as PoD failed: %d\n", ++ d, gfn_l, order, rc); ++ domain_crash(d); ++ } + + out: + gfn_unlock(p2m, gfn, order); + + return rc; + } ++ ++int ++guest_physmap_mark_populate_on_demand(struct domain *d, unsigned long gfn, ++ unsigned int order) ++{ ++ unsigned long left = 1UL << order; ++ unsigned int chunk_order = find_first_set_bit(gfn | left); ++ int rc; ++ ++ if ( !paging_mode_translate(d) ) ++ return -EINVAL; ++ ++ do { ++ rc = mark_populate_on_demand(d, gfn, chunk_order); ++ ++ left -= 1UL << chunk_order; ++ gfn += 1UL << chunk_order; ++ } while ( !rc && left ); ++ ++ return rc; ++} + + void p2m_pod_init(struct p2m_domain *p2m) + { diff --git a/xsa388-4.14-2.patch b/xsa388-4.14-2.patch new file mode 100644 index 0000000..2f8cc88 --- /dev/null +++ b/xsa388-4.14-2.patch @@ -0,0 +1,36 @@ +From: Jan Beulich +Subject: x86/PoD: handle intermediate page orders in p2m_pod_cache_add() + +p2m_pod_decrease_reservation() may pass pages to the function which +aren't 4k, 2M, or 1G. Handle all intermediate orders as well, to avoid +hitting the BUG() at the switch() statement's "default" case. + +This is CVE-2021-28708 / part of XSA-388. + +Fixes: 3c352011c0d3 ("x86/PoD: shorten certain operations on higher order ranges") +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné + +--- a/xen/arch/x86/mm/p2m-pod.c ++++ b/xen/arch/x86/mm/p2m-pod.c +@@ -111,15 +111,13 @@ p2m_pod_cache_add(struct p2m_domain *p2m + /* Then add to the appropriate populate-on-demand list. */ + switch ( order ) + { +- case PAGE_ORDER_1G: +- for ( i = 0; i < (1UL << PAGE_ORDER_1G); i += 1UL << PAGE_ORDER_2M ) ++ case PAGE_ORDER_2M ... PAGE_ORDER_1G: ++ for ( i = 0; i < (1UL << order); i += 1UL << PAGE_ORDER_2M ) + page_list_add_tail(page + i, &p2m->pod.super); + break; +- case PAGE_ORDER_2M: +- page_list_add_tail(page, &p2m->pod.super); +- break; +- case PAGE_ORDER_4K: +- page_list_add_tail(page, &p2m->pod.single); ++ case PAGE_ORDER_4K ... PAGE_ORDER_2M - 1: ++ for ( i = 0; i < (1UL << order); i += 1UL << PAGE_ORDER_4K ) ++ page_list_add_tail(page + i, &p2m->pod.single); + break; + default: + BUG(); diff --git a/xsa389-4.14.patch b/xsa389-4.14.patch new file mode 100644 index 0000000..1d893f1 --- /dev/null +++ b/xsa389-4.14.patch @@ -0,0 +1,180 @@ +From: Jan Beulich +Subject: x86/P2M: deal with partial success of p2m_set_entry() + +M2P and PoD stats need to remain in sync with P2M; if an update succeeds +only partially, respective adjustments need to be made. If updates get +made before the call, they may also need undoing upon complete failure +(i.e. including the single-page case). + +Log-dirty state would better also be kept in sync. + +Note that the change to set_typed_p2m_entry() may not be strictly +necessary (due to the order restriction enforced near the top of the +function), but is being kept here to be on the safe side. + +This is CVE-2021-28705 and CVE-2021-28709 / XSA-389. + +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné + +--- a/xen/arch/x86/mm/p2m.c ++++ b/xen/arch/x86/mm/p2m.c +@@ -780,6 +780,7 @@ p2m_remove_page(struct p2m_domain *p2m, + unsigned long i; + p2m_type_t t; + p2m_access_t a; ++ int rc; + + /* IOMMU for PV guests is handled in get_page_type() and put_page(). */ + if ( !paging_mode_translate(p2m->domain) ) +@@ -813,8 +814,27 @@ p2m_remove_page(struct p2m_domain *p2m, + } + } + +- return p2m_set_entry(p2m, gfn, INVALID_MFN, page_order, p2m_invalid, +- p2m->default_access); ++ rc = p2m_set_entry(p2m, gfn, INVALID_MFN, page_order, p2m_invalid, ++ p2m->default_access); ++ if ( likely(!rc) || !mfn_valid(mfn) ) ++ return rc; ++ ++ /* ++ * The operation may have partially succeeded. For the failed part we need ++ * to undo the M2P update and, out of precaution, mark the pages dirty ++ * again. ++ */ ++ for ( i = 0; i < (1UL << page_order); ++i ) ++ { ++ p2m->get_entry(p2m, gfn_add(gfn, i), &t, &a, 0, NULL, NULL); ++ if ( !p2m_is_hole(t) && !p2m_is_special(t) && !p2m_is_shared(t) ) ++ { ++ set_gpfn_from_mfn(mfn_x(mfn) + i, gfn_x(gfn) + i); ++ paging_mark_pfn_dirty(p2m->domain, _pfn(gfn_x(gfn) + i)); ++ } ++ } ++ ++ return rc; + } + + int +@@ -1003,13 +1023,8 @@ guest_physmap_add_entry(struct domain *d + + /* Now, actually do the two-way mapping */ + rc = p2m_set_entry(p2m, gfn, mfn, page_order, t, p2m->default_access); +- if ( rc == 0 ) ++ if ( likely(!rc) ) + { +- pod_lock(p2m); +- p2m->pod.entry_count -= pod_count; +- BUG_ON(p2m->pod.entry_count < 0); +- pod_unlock(p2m); +- + if ( !p2m_is_grant(t) ) + { + for ( i = 0; i < (1UL << page_order); i++ ) +@@ -1017,6 +1032,42 @@ guest_physmap_add_entry(struct domain *d + gfn_x(gfn_add(gfn, i))); + } + } ++ else ++ { ++ /* ++ * The operation may have partially succeeded. For the successful part ++ * we need to update M2P and dirty state, while for the failed part we ++ * may need to adjust PoD stats as well as undo the earlier M2P update. ++ */ ++ for ( i = 0; i < (1UL << page_order); ++i ) ++ { ++ omfn = p2m->get_entry(p2m, gfn_add(gfn, i), &ot, &a, 0, NULL, NULL); ++ if ( p2m_is_pod(ot) ) ++ { ++ BUG_ON(!pod_count); ++ --pod_count; ++ } ++ else if ( mfn_eq(omfn, mfn_add(mfn, i)) && ot == t && ++ a == p2m->default_access && !p2m_is_grant(t) ) ++ { ++ set_gpfn_from_mfn(mfn_x(omfn), gfn_x(gfn) + i); ++ paging_mark_pfn_dirty(d, _pfn(gfn_x(gfn) + i)); ++ } ++ else if ( p2m_is_ram(ot) && !p2m_is_paged(ot) ) ++ { ++ ASSERT(mfn_valid(omfn)); ++ set_gpfn_from_mfn(mfn_x(omfn), gfn_x(gfn) + i); ++ } ++ } ++ } ++ ++ if ( pod_count ) ++ { ++ pod_lock(p2m); ++ p2m->pod.entry_count -= pod_count; ++ BUG_ON(p2m->pod.entry_count < 0); ++ pod_unlock(p2m); ++ } + + out: + p2m_unlock(p2m); +@@ -1308,6 +1359,49 @@ static int set_typed_p2m_entry(struct do + return 0; + } + } ++ ++ P2M_DEBUG("set %d %lx %lx\n", gfn_p2mt, gfn_l, mfn_x(mfn)); ++ rc = p2m_set_entry(p2m, gfn, mfn, order, gfn_p2mt, access); ++ if ( unlikely(rc) ) ++ { ++ gdprintk(XENLOG_ERR, "p2m_set_entry: %#lx:%u -> %d (0x%"PRI_mfn")\n", ++ gfn_l, order, rc, mfn_x(mfn)); ++ ++ /* ++ * The operation may have partially succeeded. For the successful part ++ * we need to update PoD stats, M2P, and dirty state. ++ */ ++ if ( order != PAGE_ORDER_4K ) ++ { ++ unsigned long i; ++ ++ for ( i = 0; i < (1UL << order); ++i ) ++ { ++ p2m_type_t t; ++ mfn_t cmfn = p2m->get_entry(p2m, gfn_add(gfn, i), &t, &a, 0, ++ NULL, NULL); ++ ++ if ( !mfn_eq(cmfn, mfn_add(mfn, i)) || t != gfn_p2mt || ++ a != access ) ++ continue; ++ ++ if ( p2m_is_ram(ot) ) ++ { ++ ASSERT(mfn_valid(mfn_add(omfn, i))); ++ set_gpfn_from_mfn(mfn_x(omfn) + i, INVALID_M2P_ENTRY); ++ } ++#ifdef CONFIG_HVM ++ else if ( p2m_is_pod(ot) ) ++ { ++ pod_lock(p2m); ++ BUG_ON(!p2m->pod.entry_count); ++ --p2m->pod.entry_count; ++ pod_unlock(p2m); ++ } ++#endif ++ } ++ } ++ } + else if ( p2m_is_ram(ot) ) + { + unsigned long i; +@@ -1318,12 +1412,6 @@ static int set_typed_p2m_entry(struct do + set_gpfn_from_mfn(mfn_x(omfn) + i, INVALID_M2P_ENTRY); + } + } +- +- P2M_DEBUG("set %d %lx %lx\n", gfn_p2mt, gfn_l, mfn_x(mfn)); +- rc = p2m_set_entry(p2m, gfn, mfn, order, gfn_p2mt, access); +- if ( rc ) +- gdprintk(XENLOG_ERR, "p2m_set_entry: %#lx:%u -> %d (0x%"PRI_mfn")\n", +- gfn_l, order, rc, mfn_x(mfn)); + #ifdef CONFIG_HVM + else if ( p2m_is_pod(ot) ) + { From 8c923532c155a944ee4e9aea6196d281d847c6c4 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Mon, 10 Jan 2022 19:46:29 +0000 Subject: [PATCH 028/137] update to xen-4.16.0 don't build qemu-traditional or pv-grub by default (following upstream) change grub module package dependencies from Suggests to Recommends and move to hypervisor package rework seabios configure logic (bios.bin is no longer useful) frontends vulnerable to backends [XSA-376] (document change only) --- .gitignore | 2 +- sources | 2 +- xen.canonicalize.patch | 2 +- xen.fedora.efi.build.patch | 10 -- xen.gcc11.fixes.patch | 2 +- xen.ocaml.4.12.fixes.patch | 42 --------- xen.spec | 91 +++++++++---------- xsa376.patch | 145 +++++++++++++++++++++++++++++ xsa385-4.15.patch | 96 ------------------- xsa386.patch | 29 ------ xsa388-4.15-1.patch | 174 ----------------------------------- xsa388-4.15-2.patch | 36 -------- xsa389-4.15.patch | 182 ------------------------------------- xsa390.patch | 46 ---------- 14 files changed, 194 insertions(+), 665 deletions(-) delete mode 100644 xen.fedora.efi.build.patch delete mode 100644 xen.ocaml.4.12.fixes.patch create mode 100644 xsa376.patch delete mode 100644 xsa385-4.15.patch delete mode 100644 xsa386.patch delete mode 100644 xsa388-4.15-1.patch delete mode 100644 xsa388-4.15-2.patch delete mode 100644 xsa389-4.15.patch delete mode 100644 xsa390.patch diff --git a/.gitignore b/.gitignore index 10627f5..dcc3690 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.15.1.tar.gz +/xen-4.16.0.tar.gz diff --git a/sources b/sources index 977c8aa..10bfdea 100644 --- a/sources +++ b/sources @@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.15.1.tar.gz) = 8d3cbdf708f46477e32ee7cbd16a490c82efa855cecd84ee712b8680df4d69c987ba9ab00ff3851f627b98a8ebbc5dab71f92f142ed958ee2bc538bc792cd4b9 +SHA512 (xen-4.16.0.tar.gz) = 2869ed90d1779c9754d7f2397f5fc67a655304d9c32953ac20655ef96cb154521d8fce9f23915ac0c91f984dc54f72c67e5e619e2da318b5997748f44cf21b87 diff --git a/xen.canonicalize.patch b/xen.canonicalize.patch index c8caca8..e339530 100644 --- a/xen.canonicalize.patch +++ b/xen.canonicalize.patch @@ -106,4 +106,4 @@ +char *xenstore_canonicalize(struct connection *conn, const void *ctx, const char *node); /* Get access permissions. */ - enum xs_perm_type perm_for_conn(struct connection *conn, + unsigned int perm_for_conn(struct connection *conn, diff --git a/xen.fedora.efi.build.patch b/xen.fedora.efi.build.patch deleted file mode 100644 index 36f9608..0000000 --- a/xen.fedora.efi.build.patch +++ /dev/null @@ -1,10 +0,0 @@ ---- xen-4.8.0/xen/Makefile.orig 2016-12-05 12:03:27.000000000 +0000 -+++ xen-4.8.0/xen/Makefile 2017-02-28 00:02:54.080529810 +0000 -@@ -20,6 +20,7 @@ - MAKEFLAGS += -rR - - EFI_MOUNTPOINT ?= $(BOOT_DIR)/efi -+EFI_VENDOR=fedora - - ARCH=$(XEN_TARGET_ARCH) - SRCARCH=$(shell echo $(ARCH) | sed -e 's/x86.*/x86/' -e s'/arm\(32\|64\)/arm/g') diff --git a/xen.gcc11.fixes.patch b/xen.gcc11.fixes.patch index 31db0cb..6971080 100644 --- a/xen.gcc11.fixes.patch +++ b/xen.gcc11.fixes.patch @@ -19,6 +19,6 @@ index 320e06f..618ae92 100644 { - tboot_shared_t *tboot_shared; + tboot_shared_t * volatile tboot_shared; + static const uuid_t __initconst tboot_shared_uuid = TBOOT_SHARED_UUID; /* Look for valid page-aligned address for shared page. */ - if ( !opt_tboot_pa || (opt_tboot_pa & ~PAGE_MASK) ) diff --git a/xen.ocaml.4.12.fixes.patch b/xen.ocaml.4.12.fixes.patch deleted file mode 100644 index 640d6cc..0000000 --- a/xen.ocaml.4.12.fixes.patch +++ /dev/null @@ -1,42 +0,0 @@ ---- xen-4.15.0/tools/ocaml/libs/xc/xenctrl_stubs.c.orig 2021-04-06 18:14:18.000000000 +0100 -+++ xen-4.15.0/tools/ocaml/libs/xc/xenctrl_stubs.c 2021-04-11 21:38:22.684624663 +0100 -@@ -43,7 +43,9 @@ - #define _H(__h) ((xc_interface *)(__h)) - #define _D(__d) ((uint32_t)Int_val(__d)) - -+#ifndef Val_none - #define Val_none (Val_int(0)) -+#endif - - #define string_of_option_array(array, index) \ - ((Field(array, index) == Val_none) ? NULL : String_val(Field(Field(array, index), 0))) ---- xen-4.15.0/tools/ocaml/libs/xl/xenlight_stubs.c.orig 2021-04-06 18:14:18.000000000 +0100 -+++ xen-4.15.0/tools/ocaml/libs/xl/xenlight_stubs.c 2021-04-11 21:40:41.651115504 +0100 -@@ -227,8 +227,12 @@ - } - - /* Option type support as per http://www.linux-nantes.org/~fmonnier/ocaml/ocaml-wrapping-c.php */ -+#ifndef Val_none - #define Val_none Val_int(0) -+#endif -+#ifndef Some_val - #define Some_val(v) Field(v,0) -+#endif - - static value Val_some(value v) - { ---- xen-4.15.0/tools/ocaml/libs/xentoollog/xentoollog_stubs.c.orig 2021-04-06 18:14:18.000000000 +0100 -+++ xen-4.15.0/tools/ocaml/libs/xentoollog/xentoollog_stubs.c 2021-04-11 21:44:26.659529408 +0100 -@@ -53,8 +53,12 @@ - #include "_xtl_levels.inc" - - /* Option type support as per http://www.linux-nantes.org/~fmonnier/ocaml/ocaml-wrapping-c.php */ -+#ifndef Val_none - #define Val_none Val_int(0) -+#endif -+#ifndef Some_val - #define Some_val(v) Field(v,0) -+#endif - - static value Val_some(value v) - { diff --git a/xen.spec b/xen.spec index e6b00db..c7be39a 100644 --- a/xen.spec +++ b/xen.spec @@ -6,8 +6,8 @@ %define build_docs %{?_without_docs: 0} %{?!_without_docs: 1} # Build with stubdom unless rpmbuild was run with --without stubdom %define build_stubdom %{?_without_stubdom: 0} %{?!_without_stubdom: 1} -# Build with qemu-traditional unless rpmbuild was run with --without qemutrad -%define build_qemutrad %{?_without_qemutrad: 0} %{?!_without_qemutrad: 1} +# Only build with qemu-traditional if rpmbuild was run with --with qemutrad +%define build_qemutrad %{?_with_qemutrad: 1} %{?!_with_qemutrad: 0} # build with ovmf from edk2-ovmf unless rpmbuild was run with --without ovmf %define build_ovmf %{?_without_ovmf: 0} %{?!_without_ovmf: 1} # set to 0 for archs that don't use qemu or ovmf (reduces build dependencies) @@ -15,9 +15,6 @@ %define build_qemutrad 0 %define build_ovmf 0 %endif -%if ! %build_qemutrad -%define build_stubdom 0 -%endif # Build with xen hypervisor unless rpmbuild was run with --without hyp %define build_hyp %{?_without_hyp: 0} %{?!_without_hyp: 1} # build xsm support unless rpmbuild was run with --without xsm @@ -53,12 +50,12 @@ %endif # Hypervisor ABI -%define hv_abi 4.15 +%define hv_abi 4.16 Summary: Xen is a virtual machine monitor Name: xen -Version: 4.15.1 -Release: 4%{?dist} +Version: 4.16.0 +Release: 1%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -73,7 +70,6 @@ Source15: polarssl-1.1.4-gpl.tgz # .config file for xen hypervisor Source21: xen.hypervisor.config -Patch3: xen.fedora.efi.build.patch Patch4: CVE-2014-0150.patch Patch5: xen.fedora.systemd.patch Patch6: xen.ocaml.selinux.fix.patch @@ -112,25 +108,19 @@ Patch40: xen.drop.brctl.patch Patch41: xen.gcc9.fixes.patch Patch42: xen.gcc10.fixes.patch Patch43: xen.gcc11.fixes.patch -Patch44: xen.ocaml.4.12.fixes.patch -Patch45: xsa386.patch -Patch46: xsa385-4.15.patch -Patch47: xsa388-4.15-1.patch -Patch48: xsa388-4.15-2.patch -Patch49: xsa389-4.15.patch -Patch50: xsa390.patch +Patch44: xsa376.patch %if %build_qemutrad BuildRequires: libidn-devel zlib-devel SDL-devel curl-devel BuildRequires: libX11-devel gtk2-devel libaio-devel +%endif # build using Fedora seabios and ipxe packages for roms BuildRequires: seabios-bin ipxe-roms-qemu %ifarch %{ix86} x86_64 # for the VMX "bios" BuildRequires: dev86 %endif -%endif BuildRequires: python3-devel ncurses-devel BuildRequires: perl-interpreter perl-generators %ifarch %{ix86} x86_64 @@ -190,12 +180,6 @@ BuildRequires: edk2-ovmf %endif %if %build_hyp BuildRequires: bison flex -%ifarch %{ix86} -Suggests: grub2-pc-modules -%endif -%ifarch x86_64 -Suggests: grub2-pc-modules grub2-efi-x64-modules -%endif %endif %description @@ -238,6 +222,14 @@ form the core Xen userspace environment. Summary: Libraries for Xen tools Provides: xen-hypervisor-abi = %{hv_abi} Requires: xen-licenses +%if %build_hyp +%ifarch %{ix86} +Recommends: grub2-pc-modules +%endif +%ifarch x86_64 +Recommends: grub2-pc-modules grub2-efi-x64-modules +%endif +%endif %description hypervisor This package contains the Xen hypervisor @@ -322,18 +314,11 @@ manage Xen virtual machines. %patch33 -p1 %patch34 -p1 %patch37 -p1 -%patch3 -p1 %patch40 -p1 %patch41 -p1 %patch42 -p1 %patch43 -p1 %patch44 -p1 -%patch45 -p1 -%patch46 -p1 -%patch47 -p1 -%patch48 -p1 -%patch49 -p1 -%patch50 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -373,12 +358,6 @@ mkdir -p dist/install/boot/efi/efi/fedora %if %build_ocaml mkdir -p dist/install%{_libdir}/ocaml/stublibs %endif -%if %(test -f /usr/share/seabios/bios-256k.bin && echo 1|| echo 0) -%define seabiosloc /usr/share/seabios/bios-256k.bin -%else -%define seabiosloc /usr/share/seabios/bios.bin -%endif -#export XEN_VENDORVERSION="-%{release}" export EXTRA_CFLAGS_XEN_TOOLS="$RPM_OPT_FLAGS -Wno-error=declaration-after-statement" export EXTRA_CFLAGS_QEMU_TRADITIONAL="$RPM_OPT_FLAGS" export EXTRA_CFLAGS_QEMU_XEN="$RPM_OPT_FLAGS" @@ -398,8 +377,8 @@ XEN_TARGET_ARCH=x86_64 %make_build prefix=/usr xen CC="/usr/bin/x86_64-linux-gnu %endif %endif %endif -%if ! %build_qemutrad -CONFIG_EXTRA="--disable-qemu-traditional" +%if %build_qemutrad +CONFIG_EXTRA="--enable-qemu-traditional" %else CONFIG_EXTRA="" %endif @@ -409,7 +388,12 @@ CONFIG_EXTRA="$CONFIG_EXTRA --with-system-ovmf=%{_libexecdir}/%{name}/boot/ovmf. %ifnarch armv7hl aarch64 CONFIG_EXTRA="$CONFIG_EXTRA --with-system-ipxe=/usr/share/ipxe/10ec8139.rom" %endif -./configure --prefix=%{_prefix} --libdir=%{_libdir} --libexecdir=%{_libexecdir} --with-system-seabios=%{seabiosloc} --with-system-qemu=/usr/bin/qemu-system-i386 --with-linux-backend-modules="xen-evtchn xen-gntdev xen-gntalloc xen-blkback xen-netback xen-pciback xen-scsiback xen-acpi-processor" $CONFIG_EXTRA +%if %(test -f /usr/share/seabios/bios-256k.bin && echo 1|| echo 0) +CONFIG_EXTRA="$CONFIG_EXTRA --with-system-seabios=/usr/share/seabios/bios-256k.bin" +%else +CONFIG_EXTRA="$CONFIG_EXTRA --disable-seabios" +%endif +./configure --prefix=%{_prefix} --libdir=%{_libdir} --libexecdir=%{_libexecdir} --with-system-qemu=/usr/bin/qemu-system-i386 --with-linux-backend-modules="xen-evtchn xen-gntdev xen-gntalloc xen-blkback xen-netback xen-pciback xen-scsiback xen-acpi-processor" $CONFIG_EXTRA %make_build %{?ocaml_flags} prefix=/usr tools %if %build_docs make prefix=/usr docs @@ -425,7 +409,7 @@ make -C stubdom build %endif %ifarch x86_64 export EXTRA_CFLAGS_XEN_TOOLS="$RPM_OPT_FLAGS_RED" -XEN_TARGET_ARCH=x86_32 make -C stubdom pv-grub +XEN_TARGET_ARCH=x86_32 make -C stubdom pv-grub-if-enabled %endif %endif @@ -700,8 +684,8 @@ fi %{_libdir}/libxenguest.so.4.* %{_libdir}/libxenlight.so.4.* %{_libdir}/libxenstat.so.4.* -%{_libdir}/libxenstore.so.3.0 -%{_libdir}/libxenstore.so.3.0.3 +%{_libdir}/libxenstore.so.4 +%{_libdir}/libxenstore.so.4.0 %{_libdir}/libxentoolcore.so.1 %{_libdir}/libxentoolcore.so.1.0 %{_libdir}/libxentoollog.so.1 @@ -791,9 +775,10 @@ fi %{_libexecdir}/xen/boot/ovmf.bin %endif %if %build_stubdom +%if %build_qemutrad %{_libexecdir}/xen/boot/ioemu-stubdom.gz +%endif %{_libexecdir}/xen/boot/xenstore-stubdom.gz -%{_libexecdir}/xen/boot/pv-grub*.gz %{_libexecdir}/xen/boot/xenstorepvh-stubdom.gz %endif %endif @@ -863,6 +848,8 @@ fi %ifnarch armv7hl aarch64 %{_sbindir}/xen-ucode %{_sbindir}/xen-memshare +%{_sbindir}/xen-mceinj +%{_sbindir}/xen-vmtrace %endif %{_bindir}/vchan-socket-proxy %{_sbindir}/xenhypfs @@ -886,9 +873,6 @@ fi %dir %attr(0755,root,root) /boot/flask /boot/flask/xenpolicy* %endif -%if %build_efi -/boot/efi/EFI/fedora/*.efi -%endif /usr/lib/debug/xen* %endif @@ -929,6 +913,21 @@ fi %endif %changelog +* Mon Jan 10 2022 Michael Young - 4.16.0-1 +- update to xen-4.16.0 + rebase xen.canonicalize.patch and xen.gcc11.fixes.patch + drop xen.fedora.efi.build.patch which is no longer useful + remove or adjust patches now included or superceded upstream + update libxenstore libary versions + unpackage /boot/efi/EFI/fedora/xen*.efi + package xen-mceinj and xen-vmtrace +- don't build qemu-traditional or pv-grub by default (following upstream) +- fix some incorrect dependencies on building qemu-traditional +- change grub module package dependencies from Suggests to Recommends + and move to hypervisor package +- rework seabios configure logic (bios.bin is no longer useful) +- frontends vulnerable to backends [XSA-376] (document change only) + * Tue Nov 23 2021 Michael Young - 4.15.1-4 - guests may exceed their designated memory limit [XSA-385, CVE-2021-28706] - PoD operations on misaligned GFNs [XSA-388, CVE-2021-28704, CVE-2021-28707 diff --git a/xsa376.patch b/xsa376.patch new file mode 100644 index 0000000..08e0bff --- /dev/null +++ b/xsa376.patch @@ -0,0 +1,145 @@ +From 02d3a57d6466363b316b60ffbba414a4a2cb90c5 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Thu, 25 Nov 2021 13:38:29 +0100 +Subject: [PATCH] SUPPORT.md: limit support statement for Linux and Windows + frontends + +Change the support state of Linux and Windows pv frontends from +"supported" to "supported with caveats" in order to reflect that the +frontends can probably be harmed by their respective backends. + +Some of the Linux frontends have been hardened already. + +This is XSA-376 + +Signed-off-by: Juergen Gross +--- + SUPPORT.md | 57 +++++++++++++++++++++++++++++++++++++++++++++--------- + 1 file changed, 48 insertions(+), 9 deletions(-) + +diff --git a/SUPPORT.md b/SUPPORT.md +index 3a34933c89..6e3e305b01 100644 +--- a/SUPPORT.md ++++ b/SUPPORT.md +@@ -411,7 +411,11 @@ Guest-side driver capable of speaking the Xen PV block protocol + Status, FreeBSD: Supported, Security support external + Status, NetBSD: Supported, Security support external + Status, OpenBSD: Supported, Security support external +- Status, Windows: Supported ++ Status, Windows: Supported, with caveats ++ ++Windows frontend currently trusts the backend; ++bugs in the frontend which allow backend to cause mischief will not be ++considered security vulnerabilities. + + ### Netfront + +@@ -421,20 +425,32 @@ Guest-side driver capable of speaking the Xen PV networking protocol + Status, FreeBSD: Supported, Security support external + Status, NetBSD: Supported, Security support external + Status, OpenBSD: Supported, Security support external +- Status, Windows: Supported ++ Status, Windows: Supported, with caveats ++ ++Windows frontend currently trusts the backend; ++bugs in the frontend which allow backend to cause mischief will not be ++considered security vulnerabilities. + + ### PV Framebuffer (frontend) + + Guest-side driver capable of speaking the Xen PV Framebuffer protocol + +- Status, Linux (xen-fbfront): Supported ++ Status, Linux (xen-fbfront): Supported, with caveats ++ ++Linux frontend currently trusts the backend; ++bugs in the frontend which allow backend to cause mischief will not be ++considered security vulnerabilities. + + ### PV display (frontend) + + Guest-side driver capable of speaking the Xen PV display protocol + +- Status, Linux: Supported (outside of "backend allocation" mode) +- Status, Linux: Experimental (in "backend allocation" mode) ++ Status, Linux, outside of "backend allocation" mode: Supported, with caveats ++ Status, Linux, "backend allocation" mode: Experimental ++ ++Linux frontend currently trusts the backend; ++bugs in the frontend which allow backend to cause mischief will not be ++considered security vulnerabilities. + + ### PV Console (frontend) + +@@ -443,7 +459,11 @@ Guest-side driver capable of speaking the Xen PV console protocol + Status, Linux (hvc_xen): Supported + Status, FreeBSD: Supported, Security support external + Status, NetBSD: Supported, Security support external +- Status, Windows: Supported ++ Status, Windows: Supported, with caveats ++ ++Windows frontend currently trusts the backend; ++bugs in the frontend which allow backend to cause mischief will not be ++considered security vulnerabilities. + + ### PV keyboard (frontend) + +@@ -451,11 +471,19 @@ Guest-side driver capable of speaking the Xen PV keyboard protocol. + Note that the "keyboard protocol" includes mouse / pointer / + multi-touch support as well. + +- Status, Linux (xen-kbdfront): Supported ++ Status, Linux (xen-kbdfront): Supported, with caveats ++ ++Linux frontend currently trusts the backend; ++bugs in the frontend which allow backend to cause mischief will not be ++considered security vulnerabilities. + + ### PV USB (frontend) + +- Status, Linux: Supported ++ Status, Linux: Supported, with caveats ++ ++Linux frontend currently trusts the backend; ++bugs in the frontend which allow backend to cause mischief will not be ++considered security vulnerabilities. + + ### PV SCSI protocol (frontend) + +@@ -464,6 +492,10 @@ multi-touch support as well. + NB that while the PV SCSI frontend is in Linux and tested regularly, + there is currently no xl support. + ++Linux frontend currently trusts the backend; ++bugs in the frontend which allow backend to cause mischief will not be ++considered security vulnerabilities. ++ + ### PV TPM (frontend) + + Guest-side driver capable of speaking the Xen PV TPM protocol +@@ -486,7 +518,11 @@ Guest-side driver capable of making pv system calls + + Guest-side driver capable of speaking the Xen PV sound protocol + +- Status, Linux: Supported ++ Status, Linux: Supported, with caveats ++ ++Linux frontend currently trusts the backend; ++bugs in the frontend which allow backend to cause mischief will not be ++considered security vulnerabilities. + + ## Virtual device support, host side + +@@ -987,6 +1023,9 @@ are given the following labels: + + This feature is security supported + by a different organization (not the XenProject). ++ The extent of support is defined by that organization. ++ It might be limited, e.g. like described in **Supported, with caveats** ++ below. + See **External security support** below. + + * **Supported, with caveats** +-- +2.26.2 + diff --git a/xsa385-4.15.patch b/xsa385-4.15.patch deleted file mode 100644 index 69b9704..0000000 --- a/xsa385-4.15.patch +++ /dev/null @@ -1,96 +0,0 @@ -From: Julien Grall -Subject: xen/page_alloc: Harden assign_pages() - -domain_tot_pages() and d->max_pages are 32-bit values. While the order -should always be quite small, it would still be possible to overflow -if domain_tot_pages() is near to (2^32 - 1). - -As this code may be called by a guest via XENMEM_increase_reservation -and XENMEM_populate_physmap, we want to make sure the guest is not going -to be able to allocate more than it is allowed. - -Rework the allocation check to avoid any possible overflow. While the -check domain_tot_pages() < d->max_pages should technically not be -necessary, it is probably best to have it to catch any possible -inconsistencies in the future. - -This is CVE-2021-28706 / XSA-385. - -Signed-off-by: Julien Grall -Signed-off-by: Jan Beulich - ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -2336,7 +2336,8 @@ gnttab_transfer( - * pages when it is dying. - */ - if ( unlikely(e->is_dying) || -- unlikely(domain_tot_pages(e) >= e->max_pages) ) -+ unlikely(domain_tot_pages(e) >= e->max_pages) || -+ unlikely(!(e->tot_pages + 1)) ) - { - spin_unlock(&e->page_alloc_lock); - -@@ -2345,8 +2346,8 @@ gnttab_transfer( - e->domain_id); - else - gdprintk(XENLOG_INFO, -- "Transferee d%d has no headroom (tot %u, max %u)\n", -- e->domain_id, domain_tot_pages(e), e->max_pages); -+ "Transferee %pd has no headroom (tot %u, max %u, ex %u)\n", -+ e, domain_tot_pages(e), e->max_pages, e->extra_pages); - - gop.status = GNTST_general_error; - goto unlock_and_copyback; ---- a/xen/common/page_alloc.c -+++ b/xen/common/page_alloc.c -@@ -2298,20 +2298,43 @@ int assign_pages( - } - else if ( !(memflags & MEMF_no_refcount) ) - { -- unsigned int tot_pages = domain_tot_pages(d) + (1 << order); -+ unsigned int tot_pages = domain_tot_pages(d), nr = 1u << order; - - if ( unlikely(tot_pages > d->max_pages) ) - { -- gprintk(XENLOG_INFO, "Over-allocation for domain %u: " -- "%u > %u\n", d->domain_id, tot_pages, d->max_pages); -+ gprintk(XENLOG_INFO, "Inconsistent allocation for %pd: %u > %u\n", -+ d, tot_pages, d->max_pages); -+ rc = -EPERM; -+ goto out; -+ } -+ -+ if ( unlikely(nr > d->max_pages - tot_pages) ) -+ { -+ gprintk(XENLOG_INFO, "Over-allocation for %pd: %Lu > %u\n", -+ d, tot_pages + 0ull + nr, d->max_pages); - rc = -E2BIG; - goto out; - } - } - -- if ( !(memflags & MEMF_no_refcount) && -- unlikely(domain_adjust_tot_pages(d, 1 << order) == (1 << order)) ) -- get_knownalive_domain(d); -+ if ( !(memflags & MEMF_no_refcount) ) -+ { -+ unsigned int nr = 1u << order; -+ -+ if ( unlikely(d->tot_pages + nr < nr) ) -+ { -+ gprintk(XENLOG_INFO, -+ "Excess allocation for %pd: %Lu (%u extra)\n", -+ d, d->tot_pages + 0ull + nr, d->extra_pages); -+ if ( pg[0].count_info & PGC_extra ) -+ d->extra_pages -= nr; -+ rc = -E2BIG; -+ goto out; -+ } -+ -+ if ( unlikely(domain_adjust_tot_pages(d, nr) == nr) ) -+ get_knownalive_domain(d); -+ } - - for ( i = 0; i < (1 << order); i++ ) - { diff --git a/xsa386.patch b/xsa386.patch deleted file mode 100644 index 83f24d3..0000000 --- a/xsa386.patch +++ /dev/null @@ -1,29 +0,0 @@ -From: Jan Beulich -Subject: VT-d: fix deassign of device with RMRR -Date: Fri, 1 Oct 2021 15:05:42 +0200 - -Ignoring a specific error code here was not meant to short circuit -deassign to _just_ the unmapping of RMRRs. This bug was previously -hidden by the bogus (potentially indefinite) looping in -pci_release_devices(), until f591755823a7 ("IOMMU/PCI: don't let domain -cleanup continue when device de-assignment failed") fixed that loop. - -This is CVE-2021-28702 / XSA-386. - -Fixes: 8b99f4400b69 ("VT-d: fix RMRR related error handling") -Reported-by: Ivan Kardykov -Signed-off-by: Jan Beulich -Tested-by: Ivan Kardykov - ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -2409,7 +2409,7 @@ static int reassign_device_ownership( - ret = iommu_identity_mapping(source, p2m_access_x, - rmrr->base_address, - rmrr->end_address, 0); -- if ( ret != -ENOENT ) -+ if ( ret && ret != -ENOENT ) - return ret; - } - } - diff --git a/xsa388-4.15-1.patch b/xsa388-4.15-1.patch deleted file mode 100644 index b4d9003..0000000 --- a/xsa388-4.15-1.patch +++ /dev/null @@ -1,174 +0,0 @@ -From: Jan Beulich -Subject: x86/PoD: deal with misaligned GFNs - -Users of XENMEM_decrease_reservation and XENMEM_populate_physmap aren't -required to pass in order-aligned GFN values. (While I consider this -bogus, I don't think we can fix this there, as that might break existing -code, e.g Linux'es swiotlb, which - while affecting PV only - until -recently had been enforcing only page alignment on the original -allocation.) Only non-PoD code paths (guest_physmap_{add,remove}_page(), -p2m_set_entry()) look to be dealing with this properly (in part by being -implemented inefficiently, handling every 4k page separately). - -Introduce wrappers taking care of splitting the incoming request into -aligned chunks, without putting much effort in trying to determine the -largest possible chunk at every iteration. - -Also "handle" p2m_set_entry() failure for non-order-0 requests by -crashing the domain in one more place. Alongside putting a log message -there, also add one to the other similar path. - -Note regarding locking: This is left in the actual worker functions on -the assumption that callers aren't guaranteed atomicity wrt acting on -multiple pages at a time. For mis-aligned GFNs gfn_lock() wouldn't have -locked the correct GFN range anyway, if it didn't simply resolve to -p2m_lock(), and for well-behaved callers there continues to be only a -single iteration, i.e. behavior is unchanged for them. (FTAOD pulling -out just pod_lock() into p2m_pod_decrease_reservation() would result in -a lock order violation.) - -This is CVE-2021-28704 and CVE-2021-28707 / part of XSA-388. - -Fixes: 3c352011c0d3 ("x86/PoD: shorten certain operations on higher order ranges") -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné - ---- a/xen/arch/x86/mm/p2m-pod.c -+++ b/xen/arch/x86/mm/p2m-pod.c -@@ -496,7 +496,7 @@ p2m_pod_zero_check_superpage(struct p2m_ - - - /* -- * This function is needed for two reasons: -+ * This pair of functions is needed for two reasons: - * + To properly handle clearing of PoD entries - * + To "steal back" memory being freed for the PoD cache, rather than - * releasing it. -@@ -504,8 +504,8 @@ p2m_pod_zero_check_superpage(struct p2m_ - * Once both of these functions have been completed, we can return and - * allow decrease_reservation() to handle everything else. - */ --unsigned long --p2m_pod_decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order) -+static unsigned long -+decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order) - { - unsigned long ret = 0, i, n; - struct p2m_domain *p2m = p2m_get_hostp2m(d); -@@ -552,8 +552,10 @@ p2m_pod_decrease_reservation(struct doma - * All PoD: Mark the whole region invalid and tell caller - * we're done. - */ -- if ( p2m_set_entry(p2m, gfn, INVALID_MFN, order, p2m_invalid, -- p2m->default_access) ) -+ int rc = p2m_set_entry(p2m, gfn, INVALID_MFN, order, p2m_invalid, -+ p2m->default_access); -+ -+ if ( rc ) - { - /* - * If this fails, we can't tell how much of the range was changed. -@@ -561,7 +563,12 @@ p2m_pod_decrease_reservation(struct doma - * impossible. - */ - if ( order != 0 ) -+ { -+ printk(XENLOG_G_ERR -+ "%pd: marking GFN %#lx (order %u) as non-PoD failed: %d\n", -+ d, gfn_x(gfn), order, rc); - domain_crash(d); -+ } - goto out_unlock; - } - ret = 1UL << order; -@@ -670,6 +677,22 @@ out_unlock: - return ret; - } - -+unsigned long -+p2m_pod_decrease_reservation(struct domain *d, gfn_t gfn, unsigned int order) -+{ -+ unsigned long left = 1UL << order, ret = 0; -+ unsigned int chunk_order = find_first_set_bit(gfn_x(gfn) | left); -+ -+ do { -+ ret += decrease_reservation(d, gfn, chunk_order); -+ -+ left -= 1UL << chunk_order; -+ gfn = gfn_add(gfn, 1UL << chunk_order); -+ } while ( left ); -+ -+ return ret; -+} -+ - void p2m_pod_dump_data(struct domain *d) - { - struct p2m_domain *p2m = p2m_get_hostp2m(d); -@@ -1273,19 +1296,15 @@ remap_and_retry: - return true; - } - -- --int --guest_physmap_mark_populate_on_demand(struct domain *d, unsigned long gfn_l, -- unsigned int order) -+static int -+mark_populate_on_demand(struct domain *d, unsigned long gfn_l, -+ unsigned int order) - { - struct p2m_domain *p2m = p2m_get_hostp2m(d); - gfn_t gfn = _gfn(gfn_l); - unsigned long i, n, pod_count = 0; - int rc = 0; - -- if ( !paging_mode_translate(d) ) -- return -EINVAL; -- - gfn_lock(p2m, gfn, order); - - P2M_DEBUG("mark pod gfn=%#lx\n", gfn_l); -@@ -1325,12 +1344,44 @@ guest_physmap_mark_populate_on_demand(st - - ioreq_request_mapcache_invalidate(d); - } -+ else if ( order ) -+ { -+ /* -+ * If this failed, we can't tell how much of the range was changed. -+ * Best to crash the domain. -+ */ -+ printk(XENLOG_G_ERR -+ "%pd: marking GFN %#lx (order %u) as PoD failed: %d\n", -+ d, gfn_l, order, rc); -+ domain_crash(d); -+ } - - out: - gfn_unlock(p2m, gfn, order); - - return rc; - } -+ -+int -+guest_physmap_mark_populate_on_demand(struct domain *d, unsigned long gfn, -+ unsigned int order) -+{ -+ unsigned long left = 1UL << order; -+ unsigned int chunk_order = find_first_set_bit(gfn | left); -+ int rc; -+ -+ if ( !paging_mode_translate(d) ) -+ return -EINVAL; -+ -+ do { -+ rc = mark_populate_on_demand(d, gfn, chunk_order); -+ -+ left -= 1UL << chunk_order; -+ gfn += 1UL << chunk_order; -+ } while ( !rc && left ); -+ -+ return rc; -+} - - void p2m_pod_init(struct p2m_domain *p2m) - { diff --git a/xsa388-4.15-2.patch b/xsa388-4.15-2.patch deleted file mode 100644 index ccccb20..0000000 --- a/xsa388-4.15-2.patch +++ /dev/null @@ -1,36 +0,0 @@ -From: Jan Beulich -Subject: x86/PoD: handle intermediate page orders in p2m_pod_cache_add() - -p2m_pod_decrease_reservation() may pass pages to the function which -aren't 4k, 2M, or 1G. Handle all intermediate orders as well, to avoid -hitting the BUG() at the switch() statement's "default" case. - -This is CVE-2021-28708 / part of XSA-388. - -Fixes: 3c352011c0d3 ("x86/PoD: shorten certain operations on higher order ranges") -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné - ---- a/xen/arch/x86/mm/p2m-pod.c -+++ b/xen/arch/x86/mm/p2m-pod.c -@@ -112,15 +112,13 @@ p2m_pod_cache_add(struct p2m_domain *p2m - /* Then add to the appropriate populate-on-demand list. */ - switch ( order ) - { -- case PAGE_ORDER_1G: -- for ( i = 0; i < (1UL << PAGE_ORDER_1G); i += 1UL << PAGE_ORDER_2M ) -+ case PAGE_ORDER_2M ... PAGE_ORDER_1G: -+ for ( i = 0; i < (1UL << order); i += 1UL << PAGE_ORDER_2M ) - page_list_add_tail(page + i, &p2m->pod.super); - break; -- case PAGE_ORDER_2M: -- page_list_add_tail(page, &p2m->pod.super); -- break; -- case PAGE_ORDER_4K: -- page_list_add_tail(page, &p2m->pod.single); -+ case PAGE_ORDER_4K ... PAGE_ORDER_2M - 1: -+ for ( i = 0; i < (1UL << order); i += 1UL << PAGE_ORDER_4K ) -+ page_list_add_tail(page + i, &p2m->pod.single); - break; - default: - BUG(); diff --git a/xsa389-4.15.patch b/xsa389-4.15.patch deleted file mode 100644 index 402a38e..0000000 --- a/xsa389-4.15.patch +++ /dev/null @@ -1,182 +0,0 @@ -From: Jan Beulich -Subject: x86/P2M: deal with partial success of p2m_set_entry() - -M2P and PoD stats need to remain in sync with P2M; if an update succeeds -only partially, respective adjustments need to be made. If updates get -made before the call, they may also need undoing upon complete failure -(i.e. including the single-page case). - -Log-dirty state would better also be kept in sync. - -Note that the change to set_typed_p2m_entry() may not be strictly -necessary (due to the order restriction enforced near the top of the -function), but is being kept here to be on the safe side. - -This is CVE-2021-28705 and CVE-2021-28709 / XSA-389. - -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné - ---- a/xen/arch/x86/mm/p2m.c -+++ b/xen/arch/x86/mm/p2m.c -@@ -784,6 +784,7 @@ p2m_remove_page(struct p2m_domain *p2m, - unsigned long i; - p2m_type_t t; - p2m_access_t a; -+ int rc; - - /* IOMMU for PV guests is handled in get_page_type() and put_page(). */ - if ( !paging_mode_translate(p2m->domain) ) -@@ -819,8 +820,27 @@ p2m_remove_page(struct p2m_domain *p2m, - - ioreq_request_mapcache_invalidate(p2m->domain); - -- return p2m_set_entry(p2m, gfn, INVALID_MFN, page_order, p2m_invalid, -- p2m->default_access); -+ rc = p2m_set_entry(p2m, gfn, INVALID_MFN, page_order, p2m_invalid, -+ p2m->default_access); -+ if ( likely(!rc) || !mfn_valid(mfn) ) -+ return rc; -+ -+ /* -+ * The operation may have partially succeeded. For the failed part we need -+ * to undo the M2P update and, out of precaution, mark the pages dirty -+ * again. -+ */ -+ for ( i = 0; i < (1UL << page_order); ++i ) -+ { -+ p2m->get_entry(p2m, gfn_add(gfn, i), &t, &a, 0, NULL, NULL); -+ if ( !p2m_is_hole(t) && !p2m_is_special(t) && !p2m_is_shared(t) ) -+ { -+ set_gpfn_from_mfn(mfn_x(mfn) + i, gfn_x(gfn) + i); -+ paging_mark_pfn_dirty(p2m->domain, _pfn(gfn_x(gfn) + i)); -+ } -+ } -+ -+ return rc; - } - - int -@@ -1009,13 +1029,8 @@ guest_physmap_add_entry(struct domain *d - - /* Now, actually do the two-way mapping */ - rc = p2m_set_entry(p2m, gfn, mfn, page_order, t, p2m->default_access); -- if ( rc == 0 ) -+ if ( likely(!rc) ) - { -- pod_lock(p2m); -- p2m->pod.entry_count -= pod_count; -- BUG_ON(p2m->pod.entry_count < 0); -- pod_unlock(p2m); -- - if ( !p2m_is_grant(t) ) - { - for ( i = 0; i < (1UL << page_order); i++ ) -@@ -1023,6 +1038,42 @@ guest_physmap_add_entry(struct domain *d - gfn_x(gfn_add(gfn, i))); - } - } -+ else -+ { -+ /* -+ * The operation may have partially succeeded. For the successful part -+ * we need to update M2P and dirty state, while for the failed part we -+ * may need to adjust PoD stats as well as undo the earlier M2P update. -+ */ -+ for ( i = 0; i < (1UL << page_order); ++i ) -+ { -+ omfn = p2m->get_entry(p2m, gfn_add(gfn, i), &ot, &a, 0, NULL, NULL); -+ if ( p2m_is_pod(ot) ) -+ { -+ BUG_ON(!pod_count); -+ --pod_count; -+ } -+ else if ( mfn_eq(omfn, mfn_add(mfn, i)) && ot == t && -+ a == p2m->default_access && !p2m_is_grant(t) ) -+ { -+ set_gpfn_from_mfn(mfn_x(omfn), gfn_x(gfn) + i); -+ paging_mark_pfn_dirty(d, _pfn(gfn_x(gfn) + i)); -+ } -+ else if ( p2m_is_ram(ot) && !p2m_is_paged(ot) ) -+ { -+ ASSERT(mfn_valid(omfn)); -+ set_gpfn_from_mfn(mfn_x(omfn), gfn_x(gfn) + i); -+ } -+ } -+ } -+ -+ if ( pod_count ) -+ { -+ pod_lock(p2m); -+ p2m->pod.entry_count -= pod_count; -+ BUG_ON(p2m->pod.entry_count < 0); -+ pod_unlock(p2m); -+ } - - out: - p2m_unlock(p2m); -@@ -1314,6 +1365,51 @@ static int set_typed_p2m_entry(struct do - return 0; - } - } -+ -+ P2M_DEBUG("set %d %lx %lx\n", gfn_p2mt, gfn_l, mfn_x(mfn)); -+ rc = p2m_set_entry(p2m, gfn, mfn, order, gfn_p2mt, access); -+ if ( unlikely(rc) ) -+ { -+ gdprintk(XENLOG_ERR, "p2m_set_entry: %#lx:%u -> %d (0x%"PRI_mfn")\n", -+ gfn_l, order, rc, mfn_x(mfn)); -+ -+ /* -+ * The operation may have partially succeeded. For the successful part -+ * we need to update PoD stats, M2P, and dirty state. -+ */ -+ if ( order != PAGE_ORDER_4K ) -+ { -+ unsigned long i; -+ -+ for ( i = 0; i < (1UL << order); ++i ) -+ { -+ p2m_type_t t; -+ mfn_t cmfn = p2m->get_entry(p2m, gfn_add(gfn, i), &t, &a, 0, -+ NULL, NULL); -+ -+ if ( !mfn_eq(cmfn, mfn_add(mfn, i)) || t != gfn_p2mt || -+ a != access ) -+ continue; -+ -+ if ( p2m_is_ram(ot) ) -+ { -+ ASSERT(mfn_valid(mfn_add(omfn, i))); -+ set_gpfn_from_mfn(mfn_x(omfn) + i, INVALID_M2P_ENTRY); -+ -+ ioreq_request_mapcache_invalidate(d); -+ } -+#ifdef CONFIG_HVM -+ else if ( p2m_is_pod(ot) ) -+ { -+ pod_lock(p2m); -+ BUG_ON(!p2m->pod.entry_count); -+ --p2m->pod.entry_count; -+ pod_unlock(p2m); -+ } -+#endif -+ } -+ } -+ } - else if ( p2m_is_ram(ot) ) - { - unsigned long i; -@@ -1326,12 +1422,6 @@ static int set_typed_p2m_entry(struct do - - ioreq_request_mapcache_invalidate(d); - } -- -- P2M_DEBUG("set %d %lx %lx\n", gfn_p2mt, gfn_l, mfn_x(mfn)); -- rc = p2m_set_entry(p2m, gfn, mfn, order, gfn_p2mt, access); -- if ( rc ) -- gdprintk(XENLOG_ERR, "p2m_set_entry: %#lx:%u -> %d (0x%"PRI_mfn")\n", -- gfn_l, order, rc, mfn_x(mfn)); - #ifdef CONFIG_HVM - else if ( p2m_is_pod(ot) ) - { diff --git a/xsa390.patch b/xsa390.patch deleted file mode 100644 index 3c008a9..0000000 --- a/xsa390.patch +++ /dev/null @@ -1,46 +0,0 @@ -From: Jan Beulich -Subject: VT-d: fix reduced page table levels support when sharing tables - -domain_pgd_maddr() contains logic to adjust the root address to be put -in the context entry in case 4-level page tables aren't supported by an -IOMMU. This logic may not be bypassed when sharing page tables. - -This is CVE-2021-28710 / XSA-390. - -Fixes: 25ccd093425c ("iommu: remove the share_p2m operation") -Signed-off-by: Jan Beulich -Reviewed-by: Kevin Tian - ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -340,19 +340,21 @@ static uint64_t domain_pgd_maddr(struct - { - pagetable_t pgt = p2m_get_pagetable(p2m_get_hostp2m(d)); - -- return pagetable_get_paddr(pgt); -+ pgd_maddr = pagetable_get_paddr(pgt); - } -- -- if ( !hd->arch.vtd.pgd_maddr ) -+ else - { -- /* Ensure we have pagetables allocated down to leaf PTE. */ -- addr_to_dma_page_maddr(d, 0, 1); -- - if ( !hd->arch.vtd.pgd_maddr ) -- return 0; -- } -+ { -+ /* Ensure we have pagetables allocated down to leaf PTE. */ -+ addr_to_dma_page_maddr(d, 0, 1); - -- pgd_maddr = hd->arch.vtd.pgd_maddr; -+ if ( !hd->arch.vtd.pgd_maddr ) -+ return 0; -+ } -+ -+ pgd_maddr = hd->arch.vtd.pgd_maddr; -+ } - - /* Skip top levels of page tables for 2- and 3-level DRHDs. */ - for ( agaw = level_to_agaw(4); From f752e6d4e7250efca01bbde306dfbb15f10196f6 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Sat, 22 Jan 2022 04:58:02 +0000 Subject: [PATCH 029/137] - Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index c7be39a..558e76b 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.16.0 -Release: 1%{?dist} +Release: 2%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -913,6 +913,9 @@ fi %endif %changelog +* Sat Jan 22 2022 Fedora Release Engineering - 4.16.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild + * Mon Jan 10 2022 Michael Young - 4.16.0-1 - update to xen-4.16.0 rebase xen.canonicalize.patch and xen.gcc11.fixes.patch From 6ccd000828e54e976b938f505ba4325a069c10f4 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Sat, 29 Jan 2022 11:59:54 +0000 Subject: [PATCH 030/137] 3 security updates arm: guest_physmap_remove_page not removing the p2m mappings [XSA-393, CVE-2022-23033] (#2045044) A PV guest could DoS Xen while unmapping a grant [XSA-394, CVE-2022-23034] (#2045042) Insufficient cleanup of passed-through device IRQs [XSA-395, CVE-2022-23035] (#2045040) --- xen.spec | 14 ++++++++++++ xsa393.patch | 49 ++++++++++++++++++++++++++++++++++++++++ xsa394.patch | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++ xsa395.patch | 42 +++++++++++++++++++++++++++++++++++ 4 files changed, 168 insertions(+) create mode 100644 xsa393.patch create mode 100644 xsa394.patch create mode 100644 xsa395.patch diff --git a/xen.spec b/xen.spec index 558e76b..ca4b349 100644 --- a/xen.spec +++ b/xen.spec @@ -109,6 +109,9 @@ Patch41: xen.gcc9.fixes.patch Patch42: xen.gcc10.fixes.patch Patch43: xen.gcc11.fixes.patch Patch44: xsa376.patch +Patch45: xsa393.patch +Patch46: xsa394.patch +Patch47: xsa395.patch %if %build_qemutrad @@ -319,6 +322,9 @@ manage Xen virtual machines. %patch42 -p1 %patch43 -p1 %patch44 -p1 +%patch45 -p1 +%patch46 -p1 +%patch47 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -913,6 +919,14 @@ fi %endif %changelog +* Sat Jan 29 2022 Michael Young +- arm: guest_physmap_remove_page not removing the p2m mappings [XSA-393, + CVE-2022-23033] (#2045044) +- A PV guest could DoS Xen while unmapping a grant [XSA-394, CVE-2022-23034] + (#2045042) +- Insufficient cleanup of passed-through device IRQs [XSA-395, + CVE-2022-23035] (#2045040) + * Sat Jan 22 2022 Fedora Release Engineering - 4.16.0-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild diff --git a/xsa393.patch b/xsa393.patch new file mode 100644 index 0000000..57af36b --- /dev/null +++ b/xsa393.patch @@ -0,0 +1,49 @@ +From 7ff58ab770157a03c92604155a0c745bcab834c2 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Tue, 14 Dec 2021 09:53:44 +0000 +Subject: [PATCH] xen/arm: p2m: Always clear the P2M entry when the mapping is + removed + +Commit 2148a125b73b ("xen/arm: Track page accessed between batch of +Set/Way operations") allowed an entry to be invalid from the CPU PoV +(lpae_is_valid()) but valid for Xen (p2m_is_valid()). This is useful +to track which page is accessed and only perform an action on them +(e.g. clean & invalidate the cache after a set/way instruction). + +Unfortunately, __p2m_set_entry() is only zeroing the P2M entry when +lpae_is_valid() returns true. This means the entry will not be zeroed +if the entry was valid from Xen PoV but invalid from the CPU PoV for +tracking purpose. + +As a consequence, this will allow a domain to continue to access the +page after it was removed. + +Resolve the issue by always zeroing the entry if it the LPAE bit is +set or the entry is about to be removed. + +This is CVE-2022-23033 / XSA-393. + +Reported-by: Dmytro Firsov +Fixes: 2148a125b73b ("xen/arm: Track page accessed between batch of Set/Way operations") +Reviewed-by: Stefano Stabellini +Signed-off-by: Julien Grall +--- + xen/arch/arm/p2m.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c +index 8b20b430777e..fb71fa4c1c90 100644 +--- a/xen/arch/arm/p2m.c ++++ b/xen/arch/arm/p2m.c +@@ -1016,7 +1016,7 @@ static int __p2m_set_entry(struct p2m_domain *p2m, + * sequence when updating the translation table (D4.7.1 in ARM DDI + * 0487A.j). + */ +- if ( lpae_is_valid(orig_pte) ) ++ if ( lpae_is_valid(orig_pte) || removing_mapping ) + p2m_remove_pte(entry, p2m->clean_pte); + + if ( removing_mapping ) +-- +2.32.0 + diff --git a/xsa394.patch b/xsa394.patch new file mode 100644 index 0000000..1704c5b --- /dev/null +++ b/xsa394.patch @@ -0,0 +1,63 @@ +From a8bdee7a30d0cd13341d2ca1753569b171daf5b8 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Fri, 19 Nov 2021 11:27:47 +0000 +Subject: [PATCH] xen/grant-table: Only decrement the refcounter when grant is + fully unmapped + +The grant unmapping hypercall (GNTTABOP_unmap_grant_ref) is not a +simple revert of the changes done by the grant mapping hypercall +(GNTTABOP_map_grant_ref). + +Instead, it is possible to partially (or even not) clear some flags. +This will leave the grant is mapped until a future call where all +the flags would be cleared. + +XSA-380 introduced a refcounting that is meant to only be dropped +when the grant is fully unmapped. Unfortunately, unmap_common() will +decrement the refcount for every successful call. + +A consequence is a domain would be able to underflow the refcount +and trigger a BUG(). + +Looking at the code, it is not clear to me why a domain would +want to partially clear some flags in the grant-table. But as +this is part of the ABI, it is better to not change the behavior +for now. + +Fix it by checking if the maptrack handle has been released before +decrementing the refcounting. + +This is CVE-2022-23034 / XSA-394. + +Fixes: 9781b51efde2 ("gnttab: replace mapkind()") +Signed-off-by: Julien Grall +Reviewed-by: Jan Beulich +--- + xen/common/grant_table.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c +index 0262f2c48af8..ed1e2fabcea6 100644 +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -1488,8 +1488,15 @@ unmap_common( + if ( put_handle ) + put_maptrack_handle(lgt, op->handle); + +- /* See the respective comment in map_grant_ref(). */ +- if ( rc == GNTST_okay && ld != rd && gnttab_need_iommu_mapping(ld) ) ++ /* ++ * map_grant_ref() will only increment the refcount (and update the ++ * IOMMU) once per mapping. So we only want to decrement it once the ++ * maptrack handle has been put, alongside the further IOMMU update. ++ * ++ * For the second and third check, see the respective comment in ++ * map_grant_ref(). ++ */ ++ if ( put_handle && ld != rd && gnttab_need_iommu_mapping(ld) ) + { + void **slot; + union maptrack_node node; +-- +2.32.0 + diff --git a/xsa395.patch b/xsa395.patch new file mode 100644 index 0000000..13b7311 --- /dev/null +++ b/xsa395.patch @@ -0,0 +1,42 @@ +From 4cc924c3e3a0d53306d08b04720c427d1c298ba8 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Wed, 5 Jan 2022 18:09:20 +0000 +Subject: [PATCH] passthrough/x86: stop pirq iteration immediately in case of + error + +pt_pirq_iterate() will iterate in batch over all the PIRQs. The outer +loop will bail out if 'rc' is non-zero but the inner loop will continue. + +This means 'rc' will get clobbered and we may miss any errors (such as +-ERESTART in the case of the callback pci_clean_dpci_irq()). + +This is CVE-2022-23035 / XSA-395. + +Fixes: c24536b636f2 ("replace d->nr_pirqs sized arrays with radix tree") +Fixes: f6dd295381f4 ("dpci: replace tasklet with softirq") +Signed-off-by: Julien Grall +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné +--- + xen/drivers/passthrough/x86/hvm.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/xen/drivers/passthrough/x86/hvm.c b/xen/drivers/passthrough/x86/hvm.c +index 351daafdc9bf..0b37cd145b60 100644 +--- a/xen/drivers/passthrough/x86/hvm.c ++++ b/xen/drivers/passthrough/x86/hvm.c +@@ -732,7 +732,11 @@ int pt_pirq_iterate(struct domain *d, + + pirq = pirqs[i]->pirq; + if ( (pirq_dpci->flags & HVM_IRQ_DPCI_MAPPED) ) ++ { + rc = cb(d, pirq_dpci, arg); ++ if ( rc ) ++ break; ++ } + } + } while ( !rc && ++pirq < d->nr_pirqs && n == ARRAY_SIZE(pirqs) ); + +-- +2.32.0 + From a1bf0486e5e3e7176bc846da7d57508387b1e593 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Sat, 29 Jan 2022 17:23:43 +0000 Subject: [PATCH 031/137] adjust build script and patches for gcc12 and package note support --- xen.gcc12.fixes.patch | 10 ++++++++++ xen.spec | 44 ++++++++++++++++++++++++++----------------- 2 files changed, 37 insertions(+), 17 deletions(-) create mode 100644 xen.gcc12.fixes.patch diff --git a/xen.gcc12.fixes.patch b/xen.gcc12.fixes.patch new file mode 100644 index 0000000..66d13b1 --- /dev/null +++ b/xen.gcc12.fixes.patch @@ -0,0 +1,10 @@ +--- xen-4.16.0/Config.mk.orig 2021-11-30 11:42:42.000000000 +0000 ++++ xen-4.16.0/Config.mk 2022-01-24 20:25:16.687125822 +0000 +@@ -186,6 +186,7 @@ + $(call cc-option-add,CFLAGS,CC,-Wdeclaration-after-statement) + $(call cc-option-add,CFLAGS,CC,-Wno-unused-but-set-variable) + $(call cc-option-add,CFLAGS,CC,-Wno-unused-local-typedefs) ++$(call cc-option-add,CFLAGS,CC,-Wno-error=array-bounds) + + LDFLAGS += $(foreach i, $(EXTRA_LIB), -L$(i)) + CFLAGS += $(foreach i, $(EXTRA_INCLUDES), -I$(i)) diff --git a/xen.spec b/xen.spec index ca4b349..087c3e9 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.16.0 -Release: 2%{?dist} +Release: 3%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -112,6 +112,7 @@ Patch44: xsa376.patch Patch45: xsa393.patch Patch46: xsa394.patch Patch47: xsa395.patch +Patch48: xen.gcc12.fixes.patch %if %build_qemutrad @@ -325,6 +326,7 @@ manage Xen virtual machines. %patch45 -p1 %patch46 -p1 %patch47 -p1 +%patch48 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -364,25 +366,12 @@ mkdir -p dist/install/boot/efi/efi/fedora %if %build_ocaml mkdir -p dist/install%{_libdir}/ocaml/stublibs %endif -export EXTRA_CFLAGS_XEN_TOOLS="$RPM_OPT_FLAGS -Wno-error=declaration-after-statement" +export EXTRA_CFLAGS_XEN_TOOLS="$RPM_OPT_FLAGS -Wno-error=use-after-free $LDFLAGS" export EXTRA_CFLAGS_QEMU_TRADITIONAL="$RPM_OPT_FLAGS" export EXTRA_CFLAGS_QEMU_XEN="$RPM_OPT_FLAGS" export PYTHON="/usr/bin/python3" -%if %build_hyp -%if %build_crosshyp -XEN_TARGET_ARCH=x86_64 %make_build prefix=/usr xen CC="/usr/bin/x86_64-linux-gnu-gcc `echo $RPM_OPT_FLAGS | sed -e 's/-m32//g' -e 's/-march=i686//g' -e 's/-mtune=atom//g' -e 's/-specs=\/usr\/lib\/rpm\/redhat\/redhat-annobin-cc1//g' -e 's/-fstack-clash-protection//g' -e 's/-mcet//g' -e 's/-fcf-protection//g'`" -%else -%ifarch armv7hl -%make_build prefix=/usr xen CC="gcc `echo $RPM_OPT_FLAGS | sed -e 's/-mfloat-abi=hard//g' -e 's/-march=armv7-a//g'`" -%else -%ifarch aarch64 -%make_build prefix=/usr xen CC="gcc $RPM_OPT_FLAGS -mno-outline-atomics" -%else -%make_build prefix=/usr xen CC="gcc `echo $RPM_OPT_FLAGS | sed -e 's/-specs=\/usr\/lib\/rpm\/redhat\/redhat-annobin-cc1//g' -e 's/-fcf-protection//g'`" -%endif -%endif -%endif -%endif +export LDFLAGS_SAVE=`echo $LDFLAGS | sed -e 's/-Wl,//g' -e 's/,/ /g' -e 's? -specs=[-a-z/0-9]*??g'` +export CFLAGS_SAVE="$CFLAGS" %if %build_qemutrad CONFIG_EXTRA="--enable-qemu-traditional" %else @@ -400,6 +389,24 @@ CONFIG_EXTRA="$CONFIG_EXTRA --with-system-seabios=/usr/share/seabios/bios-256k.b CONFIG_EXTRA="$CONFIG_EXTRA --disable-seabios" %endif ./configure --prefix=%{_prefix} --libdir=%{_libdir} --libexecdir=%{_libexecdir} --with-system-qemu=/usr/bin/qemu-system-i386 --with-linux-backend-modules="xen-evtchn xen-gntdev xen-gntalloc xen-blkback xen-netback xen-pciback xen-scsiback xen-acpi-processor" $CONFIG_EXTRA +unset CFLAGS CXXFLAGS FFLAGS LDFLAGS +export LDFLAGS="$LDFLAGS_SAVE" +export CFLAGS="$CFLAGS_SAVE -Wno-error=address" + +%if %build_hyp +%if %build_crosshyp +export CFLAGS=`echo $CFLAGS | sed -e 's/-m32//g' -e 's/-march=i686//g' 's/-specs=\/usr\/lib\/rpm\/redhat\/redhat-annobin-cc1//g'` +XEN_TARGET_ARCH=x86_64 %make_build prefix=/usr xen CC="/usr/bin/x86_64-linux-gnu-gcc" +%else +%ifarch armv7hl +export CFLAGS=`echo $CFLAGS | sed -e 's/-mfloat-abi=hard//g' -e 's/-march=armv7-a//g'` +%endif +# armv7hl aarch64 or x86_64 +%make_build prefix=/usr xen +%endif +%endif +unset CFLAGS CXXFLAGS FFLAGS LDFLAGS + %make_build %{?ocaml_flags} prefix=/usr tools %if %build_docs make prefix=/usr docs @@ -919,6 +926,9 @@ fi %endif %changelog +* Sat Jan 29 2022 Michael Young - 4.16.0-3 +- adjust build script and patches for gcc12 and package note support + * Sat Jan 29 2022 Michael Young - arm: guest_physmap_remove_page not removing the p2m mappings [XSA-393, CVE-2022-23033] (#2045044) From d3847f2127d943d58703e1689a0972efbf868d22 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 15 Mar 2022 22:26:35 +0000 Subject: [PATCH 032/137] Multiple speculative security issues [XSA-398] additional patches so above applies cleanly --- ...03c855c071e9b1aaaa96403f2a90433336a7.patch | 94 +++++ ...0feed10571543061abeaedd21ce8adc60114.patch | 125 +++++++ ...26a2c5ad64c05281dc8ed2f1f57c0ee5988c.patch | 32 ++ ...77b4f367269dc1b768a335cfa16f48f7f02f.patch | 209 +++++++++++ ...32726add103ee8f63293e326ad43b1643239.patch | 148 ++++++++ ...02da23861f686c349a6808b2f4c9adc15f9f.patch | 45 +++ ...b6a895d10744bab32fc843246c45da444d8b.patch | 62 ++++ xen.spec | 32 +- ...xen-arm-Introduce-new-Arm-processors.patch | 63 ++++ ...n-arm-move-errata-CSV2-check-earlier.patch | 53 +++ ...arm-Add-ECBHB-and-CLEARBHB-ID-fields.patch | 94 +++++ ...6-4-xen-arm-Add-Spectre-BHB-handling.patch | 351 ++++++++++++++++++ ...-discover-and-use-SMCCC_ARCH_WORKARO.patch | 91 +++++ ...ctrl-Cease-using-thunk-lfence-on-AMD.patch | 118 ++++++ 14 files changed, 1516 insertions(+), 1 deletion(-) create mode 100644 xen.git-08fc03c855c071e9b1aaaa96403f2a90433336a7.patch create mode 100644 xen.git-21d70feed10571543061abeaedd21ce8adc60114.patch create mode 100644 xen.git-243026a2c5ad64c05281dc8ed2f1f57c0ee5988c.patch create mode 100644 xen.git-41e477b4f367269dc1b768a335cfa16f48f7f02f.patch create mode 100644 xen.git-6ef732726add103ee8f63293e326ad43b1643239.patch create mode 100644 xen.git-72ef02da23861f686c349a6808b2f4c9adc15f9f.patch create mode 100644 xen.git-7f34b6a895d10744bab32fc843246c45da444d8b.patch create mode 100644 xsa398-4.16-1-xen-arm-Introduce-new-Arm-processors.patch create mode 100644 xsa398-4.16-2-xen-arm-move-errata-CSV2-check-earlier.patch create mode 100644 xsa398-4.16-3-xen-arm-Add-ECBHB-and-CLEARBHB-ID-fields.patch create mode 100644 xsa398-4.16-4-xen-arm-Add-Spectre-BHB-handling.patch create mode 100644 xsa398-4.16-5-xen-arm-Allow-to-discover-and-use-SMCCC_ARCH_WORKARO.patch create mode 100644 xsa398-4.16-6-x86-spec-ctrl-Cease-using-thunk-lfence-on-AMD.patch diff --git a/xen.git-08fc03c855c071e9b1aaaa96403f2a90433336a7.patch b/xen.git-08fc03c855c071e9b1aaaa96403f2a90433336a7.patch new file mode 100644 index 0000000..84514e0 --- /dev/null +++ b/xen.git-08fc03c855c071e9b1aaaa96403f2a90433336a7.patch @@ -0,0 +1,94 @@ +From: Andrew Cooper +Date: Tue, 25 Jan 2022 17:14:48 +0000 (+0000) +Subject: x86/spec-ctrl: Introduce new has_spec_ctrl boolean +X-Git-Url: http://xenbits.xenproject.org/gitweb/?p=xen.git;a=commitdiff_plain;h=08fc03c855c071e9b1aaaa96403f2a90433336a7 + +x86/spec-ctrl: Introduce new has_spec_ctrl boolean + +Most MSR_SPEC_CTRL setup will be common between Intel and AMD. Instead of +opencoding an OR of two features everywhere, introduce has_spec_ctrl instead. + +Reword the comment above the Intel specific alternatives block to highlight +that it is Intel specific, and pull the setting of default_xen_spec_ctrl.IBRS +out because it will want to be common. + +No functional change. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 5d9eff3a312763d889cfbf3c8468b6dfb3ab490c) +--- + +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 8a550d0a09..2072daf662 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -927,7 +927,7 @@ static __init void mds_calculations(uint64_t caps) + void __init init_speculation_mitigations(void) + { + enum ind_thunk thunk = THUNK_DEFAULT; +- bool ibrs = false, hw_smt_enabled; ++ bool has_spec_ctrl, ibrs = false, hw_smt_enabled; + bool cpu_has_bug_taa; + uint64_t caps = 0; + +@@ -936,6 +936,8 @@ void __init init_speculation_mitigations(void) + + hw_smt_enabled = check_smt_enabled(); + ++ has_spec_ctrl = boot_cpu_has(X86_FEATURE_IBRSB); ++ + /* + * First, disable the use of retpolines if Xen is using shadow stacks, as + * they are incompatible. +@@ -973,11 +975,11 @@ void __init init_speculation_mitigations(void) + */ + else if ( retpoline_safe(caps) ) + thunk = THUNK_RETPOLINE; +- else if ( boot_cpu_has(X86_FEATURE_IBRSB) ) ++ else if ( has_spec_ctrl ) + ibrs = true; + } + /* Without compiler thunk support, use IBRS if available. */ +- else if ( boot_cpu_has(X86_FEATURE_IBRSB) ) ++ else if ( has_spec_ctrl ) + ibrs = true; + } + +@@ -1008,10 +1010,7 @@ void __init init_speculation_mitigations(void) + else if ( thunk == THUNK_JMP ) + setup_force_cpu_cap(X86_FEATURE_IND_THUNK_JMP); + +- /* +- * If we are on hardware supporting MSR_SPEC_CTRL, see about setting up +- * the alternatives blocks so we can virtualise support for guests. +- */ ++ /* Intel hardware: MSR_SPEC_CTRL alternatives setup. */ + if ( boot_cpu_has(X86_FEATURE_IBRSB) ) + { + if ( opt_msr_sc_pv ) +@@ -1030,11 +1029,12 @@ void __init init_speculation_mitigations(void) + default_spec_ctrl_flags |= SCF_ist_wrmsr; + setup_force_cpu_cap(X86_FEATURE_SC_MSR_HVM); + } +- +- if ( ibrs ) +- default_xen_spec_ctrl |= SPEC_CTRL_IBRS; + } + ++ /* If we have IBRS available, see whether we should use it. */ ++ if ( has_spec_ctrl && ibrs ) ++ default_xen_spec_ctrl |= SPEC_CTRL_IBRS; ++ + /* If we have SSBD available, see whether we should use it. */ + if ( boot_cpu_has(X86_FEATURE_SSBD) && opt_ssbd ) + default_xen_spec_ctrl |= SPEC_CTRL_SSBD; +@@ -1268,7 +1268,7 @@ void __init init_speculation_mitigations(void) + * boot won't have any other code running in a position to mount an + * attack. + */ +- if ( boot_cpu_has(X86_FEATURE_IBRSB) ) ++ if ( has_spec_ctrl ) + { + bsp_delay_spec_ctrl = !cpu_has_hypervisor && default_xen_spec_ctrl; + diff --git a/xen.git-21d70feed10571543061abeaedd21ce8adc60114.patch b/xen.git-21d70feed10571543061abeaedd21ce8adc60114.patch new file mode 100644 index 0000000..2164b76 --- /dev/null +++ b/xen.git-21d70feed10571543061abeaedd21ce8adc60114.patch @@ -0,0 +1,125 @@ +From: Andrew Cooper +Date: Tue, 25 Jan 2022 12:39:31 +0000 (+0100) +Subject: x86/spec-ctrl: Drop SPEC_CTRL_{ENTRY_FROM,EXIT_TO}_HVM +X-Git-Url: http://xenbits.xenproject.org/gitweb/?p=xen.git;a=commitdiff_plain;h=21d70feed10571543061abeaedd21ce8adc60114 + +x86/spec-ctrl: Drop SPEC_CTRL_{ENTRY_FROM,EXIT_TO}_HVM + +These were written before Spectre/Meltdown went public, and there was large +uncertainty in how the protections would evolve. As it turns out, they're +very specific to Intel hardware, and not very suitable for AMD. + +Drop the macros, opencoding the relevant subset of functionality, and leaving +grep-fodder to locate the logic. No change at all for VT-x. + +For AMD, the only relevant piece of functionality is DO_OVERWRITE_RSB, +although we will soon be adding (different) logic to handle MSR_SPEC_CTRL. + +This has a marginal improvement of removing an unconditional pile of long-nops +from the vmentry/exit path. + +Signed-off-by: Andrew Cooper +Reviewed-by: Roger Pau Monné +master commit: 95b13fa43e0753b7514bef13abe28253e8614f62 +master date: 2022-01-20 16:32:11 +0000 +--- + +diff --git a/xen/arch/x86/hvm/svm/entry.S b/xen/arch/x86/hvm/svm/entry.S +index e208a4b32a..276215d36a 100644 +--- a/xen/arch/x86/hvm/svm/entry.S ++++ b/xen/arch/x86/hvm/svm/entry.S +@@ -59,7 +59,7 @@ __UNLIKELY_END(nsvm_hap) + mov VCPUMSR_spec_ctrl_raw(%rax), %eax + + /* WARNING! `ret`, `call *`, `jmp *` not safe beyond this point. */ +- SPEC_CTRL_EXIT_TO_HVM /* Req: a=spec_ctrl %rsp=regs/cpuinfo, Clob: cd */ ++ /* SPEC_CTRL_EXIT_TO_SVM (nothing currently) */ + + pop %r15 + pop %r14 +@@ -86,7 +86,8 @@ __UNLIKELY_END(nsvm_hap) + + GET_CURRENT(bx) + +- SPEC_CTRL_ENTRY_FROM_HVM /* Req: b=curr %rsp=regs/cpuinfo, Clob: acd */ ++ /* SPEC_CTRL_ENTRY_FROM_SVM Req: b=curr %rsp=regs/cpuinfo, Clob: ac */ ++ ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_HVM + /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ + + stgi +diff --git a/xen/arch/x86/hvm/vmx/entry.S b/xen/arch/x86/hvm/vmx/entry.S +index 27c8c5ca49..30139ae58e 100644 +--- a/xen/arch/x86/hvm/vmx/entry.S ++++ b/xen/arch/x86/hvm/vmx/entry.S +@@ -33,7 +33,9 @@ ENTRY(vmx_asm_vmexit_handler) + movb $1,VCPU_vmx_launched(%rbx) + mov %rax,VCPU_hvm_guest_cr2(%rbx) + +- SPEC_CTRL_ENTRY_FROM_HVM /* Req: b=curr %rsp=regs/cpuinfo, Clob: acd */ ++ /* SPEC_CTRL_ENTRY_FROM_VMX Req: b=curr %rsp=regs/cpuinfo, Clob: acd */ ++ ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_HVM ++ ALTERNATIVE "", DO_SPEC_CTRL_ENTRY_FROM_HVM, X86_FEATURE_SC_MSR_HVM + /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ + + /* Hardware clears MSR_DEBUGCTL on VMExit. Reinstate it if debugging Xen. */ +@@ -80,7 +82,9 @@ UNLIKELY_END(realmode) + mov VCPUMSR_spec_ctrl_raw(%rax), %eax + + /* WARNING! `ret`, `call *`, `jmp *` not safe beyond this point. */ +- SPEC_CTRL_EXIT_TO_HVM /* Req: a=spec_ctrl %rsp=regs/cpuinfo, Clob: cd */ ++ /* SPEC_CTRL_EXIT_TO_VMX Req: a=spec_ctrl %rsp=regs/cpuinfo, Clob: cd */ ++ ALTERNATIVE "", DO_SPEC_CTRL_EXIT_TO_GUEST, X86_FEATURE_SC_MSR_HVM ++ ALTERNATIVE "", __stringify(verw CPUINFO_verw_sel(%rsp)), X86_FEATURE_SC_VERW_HVM + + mov VCPU_hvm_guest_cr2(%rbx),%rax + +diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h +index cb34299a86..2b3f123cb5 100644 +--- a/xen/include/asm-x86/spec_ctrl_asm.h ++++ b/xen/include/asm-x86/spec_ctrl_asm.h +@@ -68,14 +68,16 @@ + * + * The following ASM fragments implement this algorithm. See their local + * comments for further details. +- * - SPEC_CTRL_ENTRY_FROM_HVM + * - SPEC_CTRL_ENTRY_FROM_PV + * - SPEC_CTRL_ENTRY_FROM_INTR + * - SPEC_CTRL_ENTRY_FROM_INTR_IST + * - SPEC_CTRL_EXIT_TO_XEN_IST + * - SPEC_CTRL_EXIT_TO_XEN + * - SPEC_CTRL_EXIT_TO_PV +- * - SPEC_CTRL_EXIT_TO_HVM ++ * ++ * Additionally, the following grep-fodder exists to find the HVM logic. ++ * - SPEC_CTRL_ENTRY_FROM_{SVM,VMX} ++ * - SPEC_CTRL_EXIT_TO_{SVM,VMX} + */ + + .macro DO_OVERWRITE_RSB tmp=rax +@@ -225,12 +227,6 @@ + wrmsr + .endm + +-/* Use after a VMEXIT from an HVM guest. */ +-#define SPEC_CTRL_ENTRY_FROM_HVM \ +- ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_HVM; \ +- ALTERNATIVE "", DO_SPEC_CTRL_ENTRY_FROM_HVM, \ +- X86_FEATURE_SC_MSR_HVM +- + /* Use after an entry from PV context (syscall/sysenter/int80/int82/etc). */ + #define SPEC_CTRL_ENTRY_FROM_PV \ + ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_PV; \ +@@ -255,13 +251,6 @@ + ALTERNATIVE "", __stringify(verw CPUINFO_verw_sel(%rsp)), \ + X86_FEATURE_SC_VERW_PV + +-/* Use when exiting to HVM guest context. */ +-#define SPEC_CTRL_EXIT_TO_HVM \ +- ALTERNATIVE "", \ +- DO_SPEC_CTRL_EXIT_TO_GUEST, X86_FEATURE_SC_MSR_HVM; \ +- ALTERNATIVE "", __stringify(verw CPUINFO_verw_sel(%rsp)), \ +- X86_FEATURE_SC_VERW_HVM +- + /* + * Use in IST interrupt/exception context. May interrupt Xen or PV context. + * Fine grain control of SCF_ist_wrmsr is needed for safety in the S3 resume diff --git a/xen.git-243026a2c5ad64c05281dc8ed2f1f57c0ee5988c.patch b/xen.git-243026a2c5ad64c05281dc8ed2f1f57c0ee5988c.patch new file mode 100644 index 0000000..5693970 --- /dev/null +++ b/xen.git-243026a2c5ad64c05281dc8ed2f1f57c0ee5988c.patch @@ -0,0 +1,32 @@ +From: Andrew Cooper +Date: Thu, 6 Jan 2022 13:15:14 +0000 (+0100) +Subject: x86/spec-ctrl: Fix default calculation of opt_srb_lock +X-Git-Url: http://xenbits.xenproject.org/gitweb/?p=xen.git;a=commitdiff_plain;h=243026a2c5ad64c05281dc8ed2f1f57c0ee5988c + +x86/spec-ctrl: Fix default calculation of opt_srb_lock + +Since this logic was introduced, opt_tsx has become more complicated and +shouldn't be compared to 0 directly. While there are no buggy logic paths, +the correct expression is !(opt_tsx & 1) but the rtm_disabled boolean is +easier and clearer to use. + +Fixes: 8fe24090d940 ("x86/cpuid: Rework HLE and RTM handling") +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +master commit: 31f3bc97f4508687215e459a5e35676eecf1772b +master date: 2022-01-05 09:44:26 +0000 +--- + +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index a5569c7f2b..c18cc8aa49 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -1245,7 +1245,7 @@ void __init init_speculation_mitigations(void) + */ + if ( opt_srb_lock == -1 && + (caps & (ARCH_CAPS_MDS_NO|ARCH_CAPS_TAA_NO)) == ARCH_CAPS_MDS_NO && +- (!cpu_has_hle || ((caps & ARCH_CAPS_TSX_CTRL) && opt_tsx == 0)) ) ++ (!cpu_has_hle || ((caps & ARCH_CAPS_TSX_CTRL) && rtm_disabled)) ) + opt_srb_lock = 0; + + val &= ~MCU_OPT_CTRL_RNGDS_MITG_DIS; diff --git a/xen.git-41e477b4f367269dc1b768a335cfa16f48f7f02f.patch b/xen.git-41e477b4f367269dc1b768a335cfa16f48f7f02f.patch new file mode 100644 index 0000000..4bd651a --- /dev/null +++ b/xen.git-41e477b4f367269dc1b768a335cfa16f48f7f02f.patch @@ -0,0 +1,209 @@ +From: Andrew Cooper +Date: Wed, 19 May 2021 18:40:28 +0000 (+0100) +Subject: x86/spec-ctrl: Clean up MSR_MCU_OPT_CTRL handling +X-Git-Url: http://xenbits.xenproject.org/gitweb/?p=xen.git;a=commitdiff_plain;h=41e477b4f367269dc1b768a335cfa16f48f7f02f + +x86/spec-ctrl: Clean up MSR_MCU_OPT_CTRL handling + +Introduce cpu_has_srbds_ctrl as more users are going to appear shortly. + +MSR_MCU_OPT_CTRL is gaining extra functionality, meaning that the current +default_xen_mcu_opt_ctrl is no longer a good fit. + +Introduce two new helpers, update_mcu_opt_ctrl() which does a full RMW cycle +on the MSR, and set_in_mcu_opt_ctrl() which lets callers configure specific +bits at a time without clobbering each others settings. + +No functional change. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 39a40f3835efcc25c1b05a25c321a01d7e11cbd7) +--- + +diff --git a/xen/arch/x86/acpi/power.c b/xen/arch/x86/acpi/power.c +index d4bdc3e7df..5eaa77f66a 100644 +--- a/xen/arch/x86/acpi/power.c ++++ b/xen/arch/x86/acpi/power.c +@@ -301,8 +301,7 @@ static int enter_state(u32 state) + ci->last_spec_ctrl = default_xen_spec_ctrl; + } + +- if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) +- wrmsrl(MSR_MCU_OPT_CTRL, default_xen_mcu_opt_ctrl); ++ update_mcu_opt_ctrl(); + + /* (re)initialise SYSCALL/SYSENTER state, amongst other things. */ + percpu_traps_init(); +diff --git a/xen/arch/x86/cpu/intel.c b/xen/arch/x86/cpu/intel.c +index 9b011c3446..e7d4dd652f 100644 +--- a/xen/arch/x86/cpu/intel.c ++++ b/xen/arch/x86/cpu/intel.c +@@ -14,6 +14,38 @@ + + #include "cpu.h" + ++/* ++ * MSR_MCU_OPT_CTRL is a collection of unrelated functionality, with separate ++ * enablement requirements, but which want to be consistent across the system. ++ */ ++static uint32_t __read_mostly mcu_opt_ctrl_mask; ++static uint32_t __read_mostly mcu_opt_ctrl_val; ++ ++void update_mcu_opt_ctrl(void) ++{ ++ uint32_t mask = mcu_opt_ctrl_mask, lo, hi; ++ ++ if ( !mask ) ++ return; ++ ++ rdmsr(MSR_MCU_OPT_CTRL, lo, hi); ++ ++ lo &= ~mask; ++ lo |= mcu_opt_ctrl_val; ++ ++ wrmsr(MSR_MCU_OPT_CTRL, lo, hi); ++} ++ ++void __init set_in_mcu_opt_ctrl(uint32_t mask, uint32_t val) ++{ ++ mcu_opt_ctrl_mask |= mask; ++ ++ mcu_opt_ctrl_val &= ~mask; ++ mcu_opt_ctrl_val |= (val & mask); ++ ++ update_mcu_opt_ctrl(); ++} ++ + /* + * Processors which have self-snooping capability can handle conflicting + * memory type across CPUs by snooping its own cache. However, there exists +diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c +index 54237c6c6d..2596e4374b 100644 +--- a/xen/arch/x86/smpboot.c ++++ b/xen/arch/x86/smpboot.c +@@ -384,8 +384,7 @@ void start_secondary(void *unused) + wrmsrl(MSR_SPEC_CTRL, default_xen_spec_ctrl); + info->last_spec_ctrl = default_xen_spec_ctrl; + } +- if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) +- wrmsrl(MSR_MCU_OPT_CTRL, default_xen_mcu_opt_ctrl); ++ update_mcu_opt_ctrl(); + + tsx_init(); /* Needs microcode. May change HLE/RTM feature bits. */ + +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index ee862089b7..3628b4b415 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -67,7 +67,6 @@ static bool __initdata cpu_has_bug_msbds_only; /* => minimal HT impact. */ + static bool __initdata cpu_has_bug_mds; /* Any other M{LP,SB,FB}DS combination. */ + + static int8_t __initdata opt_srb_lock = -1; +-uint64_t __read_mostly default_xen_mcu_opt_ctrl; + + static int __init parse_spec_ctrl(const char *s) + { +@@ -376,7 +375,7 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + (default_xen_spec_ctrl & SPEC_CTRL_SSBD) ? " SSBD+" : " SSBD-", + !(caps & ARCH_CAPS_TSX_CTRL) ? "" : + (opt_tsx & 1) ? " TSX+" : " TSX-", +- !boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ? "" : ++ !cpu_has_srbds_ctrl ? "" : + opt_srb_lock ? " SRB_LOCK+" : " SRB_LOCK-", + opt_ibpb ? " IBPB" : "", + opt_l1d_flush ? " L1D_FLUSH" : "", +@@ -1251,32 +1250,24 @@ void __init init_speculation_mitigations(void) + tsx_init(); + } + +- /* Calculate suitable defaults for MSR_MCU_OPT_CTRL */ +- if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) ++ /* ++ * On some SRBDS-affected hardware, it may be safe to relax srb-lock by ++ * default. ++ * ++ * On parts which enumerate MDS_NO and not TAA_NO, TSX is the only known ++ * way to access the Fill Buffer. If TSX isn't available (inc. SKU ++ * reasons on some models), or TSX is explicitly disabled, then there is ++ * no need for the extra overhead to protect RDRAND/RDSEED. ++ */ ++ if ( cpu_has_srbds_ctrl ) + { +- uint64_t val; +- +- rdmsrl(MSR_MCU_OPT_CTRL, val); +- +- /* +- * On some SRBDS-affected hardware, it may be safe to relax srb-lock +- * by default. +- * +- * On parts which enumerate MDS_NO and not TAA_NO, TSX is the only way +- * to access the Fill Buffer. If TSX isn't available (inc. SKU +- * reasons on some models), or TSX is explicitly disabled, then there +- * is no need for the extra overhead to protect RDRAND/RDSEED. +- */ + if ( opt_srb_lock == -1 && + (caps & (ARCH_CAPS_MDS_NO|ARCH_CAPS_TAA_NO)) == ARCH_CAPS_MDS_NO && + (!cpu_has_hle || ((caps & ARCH_CAPS_TSX_CTRL) && rtm_disabled)) ) + opt_srb_lock = 0; + +- val &= ~MCU_OPT_CTRL_RNGDS_MITG_DIS; +- if ( !opt_srb_lock ) +- val |= MCU_OPT_CTRL_RNGDS_MITG_DIS; +- +- default_xen_mcu_opt_ctrl = val; ++ set_in_mcu_opt_ctrl(MCU_OPT_CTRL_RNGDS_MITG_DIS, ++ opt_srb_lock ? 0 : MCU_OPT_CTRL_RNGDS_MITG_DIS); + } + + print_details(thunk, caps); +@@ -1314,9 +1305,6 @@ void __init init_speculation_mitigations(void) + wrmsrl(MSR_SPEC_CTRL, val); + info->last_spec_ctrl = val; + } +- +- if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) +- wrmsrl(MSR_MCU_OPT_CTRL, default_xen_mcu_opt_ctrl); + } + + static void __init __maybe_unused build_assertions(void) +diff --git a/xen/include/asm-x86/cpufeature.h b/xen/include/asm-x86/cpufeature.h +index ba0fe7c0aa..0ff6d899f9 100644 +--- a/xen/include/asm-x86/cpufeature.h ++++ b/xen/include/asm-x86/cpufeature.h +@@ -133,6 +133,7 @@ + #define cpu_has_avx512_4vnniw boot_cpu_has(X86_FEATURE_AVX512_4VNNIW) + #define cpu_has_avx512_4fmaps boot_cpu_has(X86_FEATURE_AVX512_4FMAPS) + #define cpu_has_avx512_vp2intersect boot_cpu_has(X86_FEATURE_AVX512_VP2INTERSECT) ++#define cpu_has_srbds_ctrl boot_cpu_has(X86_FEATURE_SRBDS_CTRL) + #define cpu_has_rtm_always_abort boot_cpu_has(X86_FEATURE_RTM_ALWAYS_ABORT) + #define cpu_has_tsx_force_abort boot_cpu_has(X86_FEATURE_TSX_FORCE_ABORT) + #define cpu_has_serialize boot_cpu_has(X86_FEATURE_SERIALIZE) +diff --git a/xen/include/asm-x86/processor.h b/xen/include/asm-x86/processor.h +index bc4dc69253..3d8aacd3aa 100644 +--- a/xen/include/asm-x86/processor.h ++++ b/xen/include/asm-x86/processor.h +@@ -630,6 +630,9 @@ extern int8_t opt_tsx, cpu_has_tsx_ctrl; + extern bool rtm_disabled; + void tsx_init(void); + ++void update_mcu_opt_ctrl(void); ++void set_in_mcu_opt_ctrl(uint32_t mask, uint32_t val); ++ + enum ap_boot_method { + AP_BOOT_NORMAL, + AP_BOOT_SKINIT, +diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h +index a803d16f90..f760295236 100644 +--- a/xen/include/asm-x86/spec_ctrl.h ++++ b/xen/include/asm-x86/spec_ctrl.h +@@ -54,8 +54,6 @@ extern int8_t opt_pv_l1tf_hwdom, opt_pv_l1tf_domu; + */ + extern paddr_t l1tf_addr_mask, l1tf_safe_maddr; + +-extern uint64_t default_xen_mcu_opt_ctrl; +- + static inline void init_shadow_spec_ctrl_state(void) + { + struct cpu_info *info = get_cpu_info(); diff --git a/xen.git-6ef732726add103ee8f63293e326ad43b1643239.patch b/xen.git-6ef732726add103ee8f63293e326ad43b1643239.patch new file mode 100644 index 0000000..c5983a1 --- /dev/null +++ b/xen.git-6ef732726add103ee8f63293e326ad43b1643239.patch @@ -0,0 +1,148 @@ +From: Andrew Cooper +Date: Fri, 28 Jan 2022 11:57:19 +0000 (+0000) +Subject: x86/spec-ctrl: Record the last write to MSR_SPEC_CTRL +X-Git-Url: http://xenbits.xenproject.org/gitweb/?p=xen.git;a=commitdiff_plain;h=6ef732726add103ee8f63293e326ad43b1643239 + +x86/spec-ctrl: Record the last write to MSR_SPEC_CTRL + +In some cases, writes to MSR_SPEC_CTRL do not have interesting side effects, +and we should implement lazy context switching like we do with other MSRs. + +In the short term, this will be used by the SVM infrastructure, but I expect +to extend it to other contexts in due course. + +Introduce cpu_info.last_spec_ctrl for the purpose, and cache writes made from +the boot/resume paths. The value can't live in regular per-cpu data when it +is eventually used for PV guests when XPTI might be active. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 00f2992b6c7a9d4090443c1a85bf83224a87eeb9) +--- + +diff --git a/xen/arch/x86/acpi/power.c b/xen/arch/x86/acpi/power.c +index 0837a3ead4..bac9c16389 100644 +--- a/xen/arch/x86/acpi/power.c ++++ b/xen/arch/x86/acpi/power.c +@@ -296,7 +296,10 @@ static int enter_state(u32 state) + ci->spec_ctrl_flags |= (default_spec_ctrl_flags & SCF_ist_wrmsr); + + if ( boot_cpu_has(X86_FEATURE_IBRSB) ) ++ { + wrmsrl(MSR_SPEC_CTRL, default_xen_spec_ctrl); ++ ci->last_spec_ctrl = default_xen_spec_ctrl; ++ } + + if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) + wrmsrl(MSR_MCU_OPT_CTRL, default_xen_mcu_opt_ctrl); +diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c +index da47cdea14..369691dd13 100644 +--- a/xen/arch/x86/setup.c ++++ b/xen/arch/x86/setup.c +@@ -1944,9 +1944,12 @@ void __init noreturn __start_xen(unsigned long mbi_p) + + if ( bsp_delay_spec_ctrl ) + { +- get_cpu_info()->spec_ctrl_flags &= ~SCF_use_shadow; ++ struct cpu_info *info = get_cpu_info(); ++ ++ info->spec_ctrl_flags &= ~SCF_use_shadow; + barrier(); + wrmsrl(MSR_SPEC_CTRL, default_xen_spec_ctrl); ++ info->last_spec_ctrl = default_xen_spec_ctrl; + } + + /* Jump to the 1:1 virtual mappings of cpu0_stack. */ +diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c +index 329cfdb6c9..ee3e86cc78 100644 +--- a/xen/arch/x86/smpboot.c ++++ b/xen/arch/x86/smpboot.c +@@ -322,6 +322,8 @@ static void set_cpu_sibling_map(unsigned int cpu) + + void start_secondary(void *unused) + { ++ struct cpu_info *info = get_cpu_info(); ++ + /* + * Dont put anything before smp_callin(), SMP booting is so fragile that we + * want to limit the things done here to the most necessary things. +@@ -378,7 +380,10 @@ void start_secondary(void *unused) + * microcode. + */ + if ( boot_cpu_has(X86_FEATURE_IBRSB) ) ++ { + wrmsrl(MSR_SPEC_CTRL, default_xen_spec_ctrl); ++ info->last_spec_ctrl = default_xen_spec_ctrl; ++ } + if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) + wrmsrl(MSR_MCU_OPT_CTRL, default_xen_mcu_opt_ctrl); + +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 2072daf662..b2fd86ebe5 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -1270,6 +1270,9 @@ void __init init_speculation_mitigations(void) + */ + if ( has_spec_ctrl ) + { ++ struct cpu_info *info = get_cpu_info(); ++ unsigned int val; ++ + bsp_delay_spec_ctrl = !cpu_has_hypervisor && default_xen_spec_ctrl; + + /* +@@ -1278,15 +1281,16 @@ void __init init_speculation_mitigations(void) + */ + if ( bsp_delay_spec_ctrl ) + { +- struct cpu_info *info = get_cpu_info(); +- + info->shadow_spec_ctrl = 0; + barrier(); + info->spec_ctrl_flags |= SCF_use_shadow; + barrier(); + } + +- wrmsrl(MSR_SPEC_CTRL, bsp_delay_spec_ctrl ? 0 : default_xen_spec_ctrl); ++ val = bsp_delay_spec_ctrl ? 0 : default_xen_spec_ctrl; ++ ++ wrmsrl(MSR_SPEC_CTRL, val); ++ info->last_spec_ctrl = val; + } + + if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) +diff --git a/xen/include/asm-x86/current.h b/xen/include/asm-x86/current.h +index a74ad4bc4c..8ea4aecc5e 100644 +--- a/xen/include/asm-x86/current.h ++++ b/xen/include/asm-x86/current.h +@@ -56,6 +56,7 @@ struct cpu_info { + /* See asm-x86/spec_ctrl_asm.h for usage. */ + unsigned int shadow_spec_ctrl; + uint8_t xen_spec_ctrl; ++ uint8_t last_spec_ctrl; + uint8_t spec_ctrl_flags; + + /* +@@ -73,7 +74,6 @@ struct cpu_info { + */ + bool use_pv_cr3; + +- unsigned long __pad; + /* get_stack_bottom() must be 16-byte aligned */ + }; + +diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h +index bf82528a12..9c0c7622c4 100644 +--- a/xen/include/asm-x86/spec_ctrl_asm.h ++++ b/xen/include/asm-x86/spec_ctrl_asm.h +@@ -67,6 +67,10 @@ + * steps 2 and 6 will restore the shadow value rather than leaving Xen's value + * loaded and corrupting the value used in guest context. + * ++ * Additionally, in some cases it is safe to skip writes to MSR_SPEC_CTRL when ++ * we don't require any of the side effects of an identical write. Maintain a ++ * per-cpu last_spec_ctrl value for this purpose. ++ * + * The following ASM fragments implement this algorithm. See their local + * comments for further details. + * - SPEC_CTRL_ENTRY_FROM_PV diff --git a/xen.git-72ef02da23861f686c349a6808b2f4c9adc15f9f.patch b/xen.git-72ef02da23861f686c349a6808b2f4c9adc15f9f.patch new file mode 100644 index 0000000..3231b6a --- /dev/null +++ b/xen.git-72ef02da23861f686c349a6808b2f4c9adc15f9f.patch @@ -0,0 +1,45 @@ +From: Andrew Cooper +Date: Fri, 28 Jan 2022 12:03:42 +0000 (+0000) +Subject: x86/spec-ctrl: Don't use spec_ctrl_{enter,exit}_idle() for S3 +X-Git-Url: http://xenbits.xenproject.org/gitweb/?p=xen.git;a=commitdiff_plain;h=72ef02da23861f686c349a6808b2f4c9adc15f9f + +x86/spec-ctrl: Don't use spec_ctrl_{enter,exit}_idle() for S3 + +'idle' here refers to hlt/mwait. The S3 path isn't an idle path - it is a +platform reset. + +We need to load default_xen_spec_ctrl unilaterally on the way back up. +Currently it happens as a side effect of X86_FEATURE_SC_MSR_IDLE or the next +return-to-guest, but that's fragile behaviour. + +Conversely, there is no need to clear IBRS and flush the store buffers on the +way down; we're microseconds away from cutting power. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 71fac402e05ade7b0af2c34f77517449f6f7e2c1) +--- + +diff --git a/xen/arch/x86/acpi/power.c b/xen/arch/x86/acpi/power.c +index 31a56f02d0..0837a3ead4 100644 +--- a/xen/arch/x86/acpi/power.c ++++ b/xen/arch/x86/acpi/power.c +@@ -248,7 +248,6 @@ static int enter_state(u32 state) + error = 0; + + ci = get_cpu_info(); +- spec_ctrl_enter_idle(ci); + /* Avoid NMI/#MC using MSR_SPEC_CTRL until we've reloaded microcode. */ + ci->spec_ctrl_flags &= ~SCF_ist_wrmsr; + +@@ -295,7 +294,9 @@ static int enter_state(u32 state) + + /* Re-enabled default NMI/#MC use of MSR_SPEC_CTRL. */ + ci->spec_ctrl_flags |= (default_spec_ctrl_flags & SCF_ist_wrmsr); +- spec_ctrl_exit_idle(ci); ++ ++ if ( boot_cpu_has(X86_FEATURE_IBRSB) ) ++ wrmsrl(MSR_SPEC_CTRL, default_xen_spec_ctrl); + + if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) + wrmsrl(MSR_MCU_OPT_CTRL, default_xen_mcu_opt_ctrl); diff --git a/xen.git-7f34b6a895d10744bab32fc843246c45da444d8b.patch b/xen.git-7f34b6a895d10744bab32fc843246c45da444d8b.patch new file mode 100644 index 0000000..8ce520d --- /dev/null +++ b/xen.git-7f34b6a895d10744bab32fc843246c45da444d8b.patch @@ -0,0 +1,62 @@ +From: Andrew Cooper +Date: Tue, 25 Jan 2022 16:09:59 +0000 (+0000) +Subject: x86/spec-ctrl: Drop use_spec_ctrl boolean +X-Git-Url: http://xenbits.xenproject.org/gitweb/?p=xen.git;a=commitdiff_plain;h=7f34b6a895d10744bab32fc843246c45da444d8b + +x86/spec-ctrl: Drop use_spec_ctrl boolean + +Several bugfixes have reduced the utility of this variable from it's original +purpose, and now all it does is aid in the setup of SCF_ist_wrmsr. + +Simplify the logic by drop the variable, and doubling up the setting of +SCF_ist_wrmsr for the PV and HVM blocks, which will make the AMD SPEC_CTRL +support easier to follow. Leave a comment explaining why SCF_ist_wrmsr is +still necessary for the VMExit case. + +No functional change. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit ec083bf552c35e10347449e21809f4780f8155d2) +--- + +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index c18cc8aa49..8a550d0a09 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -927,7 +927,7 @@ static __init void mds_calculations(uint64_t caps) + void __init init_speculation_mitigations(void) + { + enum ind_thunk thunk = THUNK_DEFAULT; +- bool use_spec_ctrl = false, ibrs = false, hw_smt_enabled; ++ bool ibrs = false, hw_smt_enabled; + bool cpu_has_bug_taa; + uint64_t caps = 0; + +@@ -1016,19 +1016,21 @@ void __init init_speculation_mitigations(void) + { + if ( opt_msr_sc_pv ) + { +- use_spec_ctrl = true; ++ default_spec_ctrl_flags |= SCF_ist_wrmsr; + setup_force_cpu_cap(X86_FEATURE_SC_MSR_PV); + } + + if ( opt_msr_sc_hvm ) + { +- use_spec_ctrl = true; ++ /* ++ * While the guest MSR_SPEC_CTRL value is loaded/saved atomically, ++ * Xen's value is not restored atomically. An early NMI hitting ++ * the VMExit path needs to restore Xen's value for safety. ++ */ ++ default_spec_ctrl_flags |= SCF_ist_wrmsr; + setup_force_cpu_cap(X86_FEATURE_SC_MSR_HVM); + } + +- if ( use_spec_ctrl ) +- default_spec_ctrl_flags |= SCF_ist_wrmsr; +- + if ( ibrs ) + default_xen_spec_ctrl |= SPEC_CTRL_IBRS; + } diff --git a/xen.spec b/xen.spec index 087c3e9..ad0f6fe 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.16.0 -Release: 3%{?dist} +Release: 4%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -113,6 +113,19 @@ Patch45: xsa393.patch Patch46: xsa394.patch Patch47: xsa395.patch Patch48: xen.gcc12.fixes.patch +Patch49: xsa398-4.16-1-xen-arm-Introduce-new-Arm-processors.patch +Patch50: xsa398-4.16-2-xen-arm-move-errata-CSV2-check-earlier.patch +Patch51: xsa398-4.16-3-xen-arm-Add-ECBHB-and-CLEARBHB-ID-fields.patch +Patch52: xsa398-4.16-4-xen-arm-Add-Spectre-BHB-handling.patch +Patch53: xsa398-4.16-5-xen-arm-Allow-to-discover-and-use-SMCCC_ARCH_WORKARO.patch +Patch54: xen.git-7f34b6a895d10744bab32fc843246c45da444d8b.patch +Patch55: xen.git-08fc03c855c071e9b1aaaa96403f2a90433336a7.patch +Patch56: xen.git-21d70feed10571543061abeaedd21ce8adc60114.patch +Patch57: xen.git-72ef02da23861f686c349a6808b2f4c9adc15f9f.patch +Patch58: xen.git-6ef732726add103ee8f63293e326ad43b1643239.patch +Patch59: xen.git-243026a2c5ad64c05281dc8ed2f1f57c0ee5988c.patch +Patch60: xen.git-41e477b4f367269dc1b768a335cfa16f48f7f02f.patch +Patch61: xsa398-4.16-6-x86-spec-ctrl-Cease-using-thunk-lfence-on-AMD.patch %if %build_qemutrad @@ -327,6 +340,19 @@ manage Xen virtual machines. %patch46 -p1 %patch47 -p1 %patch48 -p1 +%patch49 -p1 +%patch50 -p1 +%patch51 -p1 +%patch52 -p1 +%patch53 -p1 +%patch54 -p1 +%patch55 -p1 +%patch56 -p1 +%patch57 -p1 +%patch58 -p1 +%patch59 -p1 +%patch60 -p1 +%patch61 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -926,6 +952,10 @@ fi %endif %changelog +* Tue Mar 15 2022 Michael Young - 4.16.0-4 +- Multiple speculative security issues [XSA-398] +- additional patches so above applies cleanly + * Sat Jan 29 2022 Michael Young - 4.16.0-3 - adjust build script and patches for gcc12 and package note support diff --git a/xsa398-4.16-1-xen-arm-Introduce-new-Arm-processors.patch b/xsa398-4.16-1-xen-arm-Introduce-new-Arm-processors.patch new file mode 100644 index 0000000..7f61307 --- /dev/null +++ b/xsa398-4.16-1-xen-arm-Introduce-new-Arm-processors.patch @@ -0,0 +1,63 @@ +From 8d18b03c95850239a1a9ebaeb565936c7c9ae070 Mon Sep 17 00:00:00 2001 +From: Bertrand Marquis +Date: Tue, 15 Feb 2022 10:37:51 +0000 +Subject: xen/arm: Introduce new Arm processors + +Add some new processor identifiers in processor.h and sync Xen +definitions with status of Linux 5.17 (declared in +arch/arm64/include/asm/cputype.h). + +This is part of XSA-398 / CVE-2022-23960. + +Signed-off-by: Bertrand Marquis +Acked-by: Julien Grall +(cherry picked from commit 35d1b85a6b43483f6bd007d48757434e54743e98) + +diff --git a/xen/include/asm-arm/processor.h b/xen/include/asm-arm/processor.h +index 8ab2940f688e..852b5f3c24b8 100644 +--- a/xen/include/asm-arm/processor.h ++++ b/xen/include/asm-arm/processor.h +@@ -65,6 +65,7 @@ + #define ARM_CPU_PART_CORTEX_A17 0xC0E + #define ARM_CPU_PART_CORTEX_A15 0xC0F + #define ARM_CPU_PART_CORTEX_A53 0xD03 ++#define ARM_CPU_PART_CORTEX_A35 0xD04 + #define ARM_CPU_PART_CORTEX_A55 0xD05 + #define ARM_CPU_PART_CORTEX_A57 0xD07 + #define ARM_CPU_PART_CORTEX_A72 0xD08 +@@ -72,11 +73,20 @@ + #define ARM_CPU_PART_CORTEX_A75 0xD0A + #define ARM_CPU_PART_CORTEX_A76 0xD0B + #define ARM_CPU_PART_NEOVERSE_N1 0xD0C ++#define ARM_CPU_PART_CORTEX_A77 0xD0D ++#define ARM_CPU_PART_NEOVERSE_V1 0xD40 ++#define ARM_CPU_PART_CORTEX_A78 0xD41 ++#define ARM_CPU_PART_CORTEX_X1 0xD44 ++#define ARM_CPU_PART_CORTEX_A710 0xD47 ++#define ARM_CPU_PART_CORTEX_X2 0xD48 ++#define ARM_CPU_PART_NEOVERSE_N2 0xD49 ++#define ARM_CPU_PART_CORTEX_A78C 0xD4B + + #define MIDR_CORTEX_A12 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A12) + #define MIDR_CORTEX_A17 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A17) + #define MIDR_CORTEX_A15 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A15) + #define MIDR_CORTEX_A53 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A53) ++#define MIDR_CORTEX_A35 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A35) + #define MIDR_CORTEX_A55 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A55) + #define MIDR_CORTEX_A57 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A57) + #define MIDR_CORTEX_A72 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A72) +@@ -84,6 +94,14 @@ + #define MIDR_CORTEX_A75 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A75) + #define MIDR_CORTEX_A76 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A76) + #define MIDR_NEOVERSE_N1 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_N1) ++#define MIDR_CORTEX_A77 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A77) ++#define MIDR_NEOVERSE_V1 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V1) ++#define MIDR_CORTEX_A78 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A78) ++#define MIDR_CORTEX_X1 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X1) ++#define MIDR_CORTEX_A710 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A710) ++#define MIDR_CORTEX_X2 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X2) ++#define MIDR_NEOVERSE_N2 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_N2) ++#define MIDR_CORTEX_A78C MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A78C) + + /* MPIDR Multiprocessor Affinity Register */ + #define _MPIDR_UP (30) diff --git a/xsa398-4.16-2-xen-arm-move-errata-CSV2-check-earlier.patch b/xsa398-4.16-2-xen-arm-move-errata-CSV2-check-earlier.patch new file mode 100644 index 0000000..4d84830 --- /dev/null +++ b/xsa398-4.16-2-xen-arm-move-errata-CSV2-check-earlier.patch @@ -0,0 +1,53 @@ +From 3d963874461b3001e33f3ff90e285670f04d16c4 Mon Sep 17 00:00:00 2001 +From: Bertrand Marquis +Date: Tue, 15 Feb 2022 10:39:47 +0000 +Subject: xen/arm: move errata CSV2 check earlier + +CSV2 availability check is done after printing to the user that +workaround 1 will be used. Move the check before to prevent saying to the +user that workaround 1 is used when it is not because it is not needed. +This will also allow to reuse install_bp_hardening_vec function for +other use cases. + +Code previously returning "true", now returns "0" to conform to +enable_smccc_arch_workaround_1 returning an int and surrounding code +doing a "return 0" if workaround is not needed. + +This is part of XSA-398 / CVE-2022-23960. + +Signed-off-by: Bertrand Marquis +Reviewed-by: Julien Grall +(cherry picked from commit 599616d70eb886b9ad0ef9d6b51693ce790504ba) + +diff --git a/xen/arch/arm/cpuerrata.c b/xen/arch/arm/cpuerrata.c +index b398d480f113..00f9ebe9cee0 100644 +--- a/xen/arch/arm/cpuerrata.c ++++ b/xen/arch/arm/cpuerrata.c +@@ -103,13 +103,6 @@ install_bp_hardening_vec(const struct arm_cpu_capabilities *entry, + printk(XENLOG_INFO "CPU%u will %s on exception entry\n", + smp_processor_id(), desc); + +- /* +- * No need to install hardened vector when the processor has +- * ID_AA64PRF0_EL1.CSV2 set. +- */ +- if ( cpu_data[smp_processor_id()].pfr64.csv2 ) +- return true; +- + spin_lock(&bp_lock); + + /* +@@ -167,6 +160,13 @@ static int enable_smccc_arch_workaround_1(void *data) + if ( !entry->matches(entry) ) + return 0; + ++ /* ++ * No need to install hardened vector when the processor has ++ * ID_AA64PRF0_EL1.CSV2 set. ++ */ ++ if ( cpu_data[smp_processor_id()].pfr64.csv2 ) ++ return 0; ++ + if ( smccc_ver < SMCCC_VERSION(1, 1) ) + goto warn; + diff --git a/xsa398-4.16-3-xen-arm-Add-ECBHB-and-CLEARBHB-ID-fields.patch b/xsa398-4.16-3-xen-arm-Add-ECBHB-and-CLEARBHB-ID-fields.patch new file mode 100644 index 0000000..d5f360c --- /dev/null +++ b/xsa398-4.16-3-xen-arm-Add-ECBHB-and-CLEARBHB-ID-fields.patch @@ -0,0 +1,94 @@ +From 8aa3833db97e8fe1143c5ece110b9321ce1494ea Mon Sep 17 00:00:00 2001 +From: Bertrand Marquis +Date: Wed, 23 Feb 2022 09:42:18 +0000 +Subject: xen/arm: Add ECBHB and CLEARBHB ID fields + +Introduce ID coprocessor register ID_AA64ISAR2_EL1. +Add definitions in cpufeature and sysregs of ECBHB field in mmfr1 and +CLEARBHB in isar2 ID coprocessor registers. + +This is part of XSA-398 / CVE-2022-23960. + +Signed-off-by: Bertrand Marquis +Acked-by: Julien Grall +(cherry picked from commit 4b68d12d98b8790d8002fcc2c25a9d713374a4d7) + +diff --git a/xen/arch/arm/cpufeature.c b/xen/arch/arm/cpufeature.c +index 6e51f530a80e..a58965f7b9bf 100644 +--- a/xen/arch/arm/cpufeature.c ++++ b/xen/arch/arm/cpufeature.c +@@ -122,6 +122,7 @@ void identify_cpu(struct cpuinfo_arm *c) + + c->isa64.bits[0] = READ_SYSREG(ID_AA64ISAR0_EL1); + c->isa64.bits[1] = READ_SYSREG(ID_AA64ISAR1_EL1); ++ c->isa64.bits[2] = READ_SYSREG(ID_AA64ISAR2_EL1); + + c->zfr64.bits[0] = READ_SYSREG(ID_AA64ZFR0_EL1); + +diff --git a/xen/include/asm-arm/arm64/sysregs.h b/xen/include/asm-arm/arm64/sysregs.h +index d7e4772f217f..eac08ed33f53 100644 +--- a/xen/include/asm-arm/arm64/sysregs.h ++++ b/xen/include/asm-arm/arm64/sysregs.h +@@ -84,6 +84,9 @@ + #ifndef ID_DFR1_EL1 + #define ID_DFR1_EL1 S3_0_C0_C3_5 + #endif ++#ifndef ID_AA64ISAR2_EL1 ++#define ID_AA64ISAR2_EL1 S3_0_C0_C6_2 ++#endif + + /* ID registers (imported from arm64/include/asm/sysreg.h in Linux) */ + +@@ -139,6 +142,9 @@ + #define ID_AA64ISAR1_GPI_NI 0x0 + #define ID_AA64ISAR1_GPI_IMP_DEF 0x1 + ++/* id_aa64isar2 */ ++#define ID_AA64ISAR2_CLEARBHB_SHIFT 28 ++ + /* id_aa64pfr0 */ + #define ID_AA64PFR0_CSV3_SHIFT 60 + #define ID_AA64PFR0_CSV2_SHIFT 56 +@@ -232,6 +238,7 @@ + #define ID_AA64MMFR0_PARANGE_52 0x6 + + /* id_aa64mmfr1 */ ++#define ID_AA64MMFR1_ECBHB_SHIFT 60 + #define ID_AA64MMFR1_ETS_SHIFT 36 + #define ID_AA64MMFR1_TWED_SHIFT 32 + #define ID_AA64MMFR1_XNX_SHIFT 28 +diff --git a/xen/include/asm-arm/cpufeature.h b/xen/include/asm-arm/cpufeature.h +index 8a5afbaf0baf..db126508f159 100644 +--- a/xen/include/asm-arm/cpufeature.h ++++ b/xen/include/asm-arm/cpufeature.h +@@ -243,14 +243,15 @@ struct cpuinfo_arm { + unsigned long lo:4; + unsigned long pan:4; + unsigned long __res1:8; +- unsigned long __res2:32; ++ unsigned long __res2:28; ++ unsigned long ecbhb:4; + + unsigned long __res3:64; + }; + } mm64; + + union { +- register_t bits[2]; ++ register_t bits[3]; + struct { + /* ISAR0 */ + unsigned long __res0:4; +@@ -286,6 +287,12 @@ struct cpuinfo_arm { + unsigned long dgh:4; + unsigned long i8mm:4; + unsigned long __res2:8; ++ ++ /* ISAR2 */ ++ unsigned long __res3:28; ++ unsigned long clearbhb:4; ++ ++ unsigned long __res4:32; + }; + } isa64; + diff --git a/xsa398-4.16-4-xen-arm-Add-Spectre-BHB-handling.patch b/xsa398-4.16-4-xen-arm-Add-Spectre-BHB-handling.patch new file mode 100644 index 0000000..6959081 --- /dev/null +++ b/xsa398-4.16-4-xen-arm-Add-Spectre-BHB-handling.patch @@ -0,0 +1,351 @@ +From 789523a2aac88e3668f9c4ad892fa47b5f6bf1a7 Mon Sep 17 00:00:00 2001 +From: Rahul Singh +Date: Mon, 14 Feb 2022 18:47:32 +0000 +Subject: xen/arm: Add Spectre BHB handling + +This commit is adding Spectre BHB handling to Xen on Arm. +The commit is introducing new alternative code to be executed during +exception entry: +- SMCC workaround 3 call +- loop workaround (with 8, 24 or 32 iterations) +- use of new clearbhb instruction + +Cpuerrata is modified by this patch to apply the required workaround for +CPU affected by Spectre BHB when CONFIG_ARM64_HARDEN_BRANCH_PREDICTOR is +enabled. + +To do this the system previously used to apply smcc workaround 1 is +reused and new alternative code to be copied in the exception handler is +introduced. + +To define the type of workaround required by a processor, 4 new cpu +capabilities are introduced (for each number of loop and for smcc +workaround 3). + +When a processor is affected, enable_spectre_bhb_workaround is called +and if the processor does not have CSV2 set to 3 or ECBHB feature (which +would mean that the processor is doing what is required in hardware), +the proper code is enabled at exception entry. + +In the case where workaround 3 is not supported by the firmware, we +enable workaround 1 when possible as it will also mitigate Spectre BHB +on systems without CSV2. + +This is part of XSA-398 / CVE-2022-23960. + +Signed-off-by: Bertrand Marquis +Signed-off-by: Rahul Singh +Acked-by: Julien Grall +(cherry picked from commit 62c91eb66a2904eefb1d1d9642e3697a1e3c3a3c) + +diff --git a/xen/arch/arm/arm64/bpi.S b/xen/arch/arm/arm64/bpi.S +index d8743d955c4a..4e6382522048 100644 +--- a/xen/arch/arm/arm64/bpi.S ++++ b/xen/arch/arm/arm64/bpi.S +@@ -58,16 +58,42 @@ ENTRY(__bp_harden_hyp_vecs_start) + .endr + ENTRY(__bp_harden_hyp_vecs_end) + +-ENTRY(__smccc_workaround_1_smc_start) ++.macro mitigate_spectre_bhb_loop count ++ENTRY(__mitigate_spectre_bhb_loop_start_\count) ++ stp x0, x1, [sp, #-16]! ++ mov x0, \count ++.Lspectre_bhb_loop\@: ++ b . + 4 ++ subs x0, x0, #1 ++ b.ne .Lspectre_bhb_loop\@ ++ sb ++ ldp x0, x1, [sp], #16 ++ENTRY(__mitigate_spectre_bhb_loop_end_\count) ++.endm ++ ++.macro smccc_workaround num smcc_id ++ENTRY(__smccc_workaround_smc_start_\num) + sub sp, sp, #(8 * 4) + stp x0, x1, [sp, #(8 * 2)] + stp x2, x3, [sp, #(8 * 0)] +- mov w0, #ARM_SMCCC_ARCH_WORKAROUND_1_FID ++ mov w0, \smcc_id + smc #0 + ldp x2, x3, [sp, #(8 * 0)] + ldp x0, x1, [sp, #(8 * 2)] + add sp, sp, #(8 * 4) +-ENTRY(__smccc_workaround_1_smc_end) ++ENTRY(__smccc_workaround_smc_end_\num) ++.endm ++ ++ENTRY(__mitigate_spectre_bhb_clear_insn_start) ++ clearbhb ++ isb ++ENTRY(__mitigate_spectre_bhb_clear_insn_end) ++ ++mitigate_spectre_bhb_loop 8 ++mitigate_spectre_bhb_loop 24 ++mitigate_spectre_bhb_loop 32 ++smccc_workaround 1, #ARM_SMCCC_ARCH_WORKAROUND_1_FID ++smccc_workaround 3, #ARM_SMCCC_ARCH_WORKAROUND_3_FID + + /* + * Local variables: +diff --git a/xen/arch/arm/cpuerrata.c b/xen/arch/arm/cpuerrata.c +index 00f9ebe9cee0..ae649d16ef02 100644 +--- a/xen/arch/arm/cpuerrata.c ++++ b/xen/arch/arm/cpuerrata.c +@@ -145,7 +145,16 @@ install_bp_hardening_vec(const struct arm_cpu_capabilities *entry, + return ret; + } + +-extern char __smccc_workaround_1_smc_start[], __smccc_workaround_1_smc_end[]; ++extern char __smccc_workaround_smc_start_1[], __smccc_workaround_smc_end_1[]; ++extern char __smccc_workaround_smc_start_3[], __smccc_workaround_smc_end_3[]; ++extern char __mitigate_spectre_bhb_clear_insn_start[], ++ __mitigate_spectre_bhb_clear_insn_end[]; ++extern char __mitigate_spectre_bhb_loop_start_8[], ++ __mitigate_spectre_bhb_loop_end_8[]; ++extern char __mitigate_spectre_bhb_loop_start_24[], ++ __mitigate_spectre_bhb_loop_end_24[]; ++extern char __mitigate_spectre_bhb_loop_start_32[], ++ __mitigate_spectre_bhb_loop_end_32[]; + + static int enable_smccc_arch_workaround_1(void *data) + { +@@ -176,8 +185,8 @@ static int enable_smccc_arch_workaround_1(void *data) + if ( (int)res.a0 < 0 ) + goto warn; + +- return !install_bp_hardening_vec(entry,__smccc_workaround_1_smc_start, +- __smccc_workaround_1_smc_end, ++ return !install_bp_hardening_vec(entry,__smccc_workaround_smc_start_1, ++ __smccc_workaround_smc_end_1, + "call ARM_SMCCC_ARCH_WORKAROUND_1"); + + warn: +@@ -187,6 +196,93 @@ static int enable_smccc_arch_workaround_1(void *data) + return 0; + } + ++/* ++ * Spectre BHB Mitigation ++ * ++ * CPU is either: ++ * - Having CVS2.3 so it is not affected. ++ * - Having ECBHB and is clearing the branch history buffer when an exception ++ * to a different exception level is happening so no mitigation is needed. ++ * - Mitigating using a loop on exception entry (number of loop depending on ++ * the CPU). ++ * - Mitigating using the firmware. ++ */ ++static int enable_spectre_bhb_workaround(void *data) ++{ ++ const struct arm_cpu_capabilities *entry = data; ++ ++ /* ++ * Enable callbacks are called on every CPU based on the capabilities, so ++ * double-check whether the CPU matches the entry. ++ */ ++ if ( !entry->matches(entry) ) ++ return 0; ++ ++ if ( cpu_data[smp_processor_id()].pfr64.csv2 == 3 ) ++ return 0; ++ ++ if ( cpu_data[smp_processor_id()].mm64.ecbhb ) ++ return 0; ++ ++ if ( cpu_data[smp_processor_id()].isa64.clearbhb ) ++ return !install_bp_hardening_vec(entry, ++ __mitigate_spectre_bhb_clear_insn_start, ++ __mitigate_spectre_bhb_clear_insn_end, ++ "use clearBHB instruction"); ++ ++ /* Apply solution depending on hwcaps set on arm_errata */ ++ if ( cpus_have_cap(ARM_WORKAROUND_BHB_LOOP_8) ) ++ return !install_bp_hardening_vec(entry, ++ __mitigate_spectre_bhb_loop_start_8, ++ __mitigate_spectre_bhb_loop_end_8, ++ "use 8 loops workaround"); ++ ++ if ( cpus_have_cap(ARM_WORKAROUND_BHB_LOOP_24) ) ++ return !install_bp_hardening_vec(entry, ++ __mitigate_spectre_bhb_loop_start_24, ++ __mitigate_spectre_bhb_loop_end_24, ++ "use 24 loops workaround"); ++ ++ if ( cpus_have_cap(ARM_WORKAROUND_BHB_LOOP_32) ) ++ return !install_bp_hardening_vec(entry, ++ __mitigate_spectre_bhb_loop_start_32, ++ __mitigate_spectre_bhb_loop_end_32, ++ "use 32 loops workaround"); ++ ++ if ( cpus_have_cap(ARM_WORKAROUND_BHB_SMCC_3) ) ++ { ++ struct arm_smccc_res res; ++ ++ if ( smccc_ver < SMCCC_VERSION(1, 1) ) ++ goto warn; ++ ++ arm_smccc_1_1_smc(ARM_SMCCC_ARCH_FEATURES_FID, ++ ARM_SMCCC_ARCH_WORKAROUND_3_FID, &res); ++ /* The return value is in the lower 32-bits. */ ++ if ( (int)res.a0 < 0 ) ++ { ++ /* ++ * On processor affected with CSV2=0, workaround 1 will mitigate ++ * both Spectre v2 and BHB so use it when available ++ */ ++ if ( enable_smccc_arch_workaround_1(data) ) ++ return 1; ++ ++ goto warn; ++ } ++ ++ return !install_bp_hardening_vec(entry,__smccc_workaround_smc_start_3, ++ __smccc_workaround_smc_end_3, ++ "call ARM_SMCCC_ARCH_WORKAROUND_3"); ++ } ++ ++warn: ++ printk_once("**** No support for any spectre BHB workaround. ****\n" ++ "**** Please update your firmware. ****\n"); ++ ++ return 0; ++} ++ + #endif /* CONFIG_ARM64_HARDEN_BRANCH_PREDICTOR */ + + /* Hardening Branch predictor code for Arm32 */ +@@ -446,19 +542,77 @@ static const struct arm_cpu_capabilities arm_errata[] = { + }, + { + .capability = ARM_HARDEN_BRANCH_PREDICTOR, +- MIDR_ALL_VERSIONS(MIDR_CORTEX_A72), ++ MIDR_RANGE(MIDR_CORTEX_A72, 0, 1 << MIDR_VARIANT_SHIFT), + .enable = enable_smccc_arch_workaround_1, + }, + { +- .capability = ARM_HARDEN_BRANCH_PREDICTOR, ++ .capability = ARM_WORKAROUND_BHB_SMCC_3, + MIDR_ALL_VERSIONS(MIDR_CORTEX_A73), +- .enable = enable_smccc_arch_workaround_1, ++ .enable = enable_spectre_bhb_workaround, + }, + { +- .capability = ARM_HARDEN_BRANCH_PREDICTOR, ++ .capability = ARM_WORKAROUND_BHB_SMCC_3, + MIDR_ALL_VERSIONS(MIDR_CORTEX_A75), +- .enable = enable_smccc_arch_workaround_1, ++ .enable = enable_spectre_bhb_workaround, ++ }, ++ /* spectre BHB */ ++ { ++ .capability = ARM_WORKAROUND_BHB_LOOP_8, ++ MIDR_RANGE(MIDR_CORTEX_A72, 1 << MIDR_VARIANT_SHIFT, ++ (MIDR_VARIANT_MASK | MIDR_REVISION_MASK)), ++ .enable = enable_spectre_bhb_workaround, ++ }, ++ { ++ .capability = ARM_WORKAROUND_BHB_LOOP_24, ++ MIDR_ALL_VERSIONS(MIDR_CORTEX_A76), ++ .enable = enable_spectre_bhb_workaround, ++ }, ++ { ++ .capability = ARM_WORKAROUND_BHB_LOOP_24, ++ MIDR_ALL_VERSIONS(MIDR_CORTEX_A77), ++ .enable = enable_spectre_bhb_workaround, ++ }, ++ { ++ .capability = ARM_WORKAROUND_BHB_LOOP_32, ++ MIDR_ALL_VERSIONS(MIDR_CORTEX_A78), ++ .enable = enable_spectre_bhb_workaround, ++ }, ++ { ++ .capability = ARM_WORKAROUND_BHB_LOOP_32, ++ MIDR_ALL_VERSIONS(MIDR_CORTEX_A78C), ++ .enable = enable_spectre_bhb_workaround, ++ }, ++ { ++ .capability = ARM_WORKAROUND_BHB_LOOP_32, ++ MIDR_ALL_VERSIONS(MIDR_CORTEX_X1), ++ .enable = enable_spectre_bhb_workaround, ++ }, ++ { ++ .capability = ARM_WORKAROUND_BHB_LOOP_32, ++ MIDR_ALL_VERSIONS(MIDR_CORTEX_X2), ++ .enable = enable_spectre_bhb_workaround, ++ }, ++ { ++ .capability = ARM_WORKAROUND_BHB_LOOP_32, ++ MIDR_ALL_VERSIONS(MIDR_CORTEX_A710), ++ .enable = enable_spectre_bhb_workaround, + }, ++ { ++ .capability = ARM_WORKAROUND_BHB_LOOP_24, ++ MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N1), ++ .enable = enable_spectre_bhb_workaround, ++ }, ++ { ++ .capability = ARM_WORKAROUND_BHB_LOOP_32, ++ MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N2), ++ .enable = enable_spectre_bhb_workaround, ++ }, ++ { ++ .capability = ARM_WORKAROUND_BHB_LOOP_32, ++ MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V1), ++ .enable = enable_spectre_bhb_workaround, ++ }, ++ + #endif + #ifdef CONFIG_ARM32_HARDEN_BRANCH_PREDICTOR + { +diff --git a/xen/include/asm-arm/arm64/macros.h b/xen/include/asm-arm/arm64/macros.h +index 5ad66efd6ba4..140e223b4c99 100644 +--- a/xen/include/asm-arm/arm64/macros.h ++++ b/xen/include/asm-arm/arm64/macros.h +@@ -27,6 +27,11 @@ + sb + .endm + ++ /* clearbhb instruction clearing the branch history */ ++ .macro clearbhb ++ hint #22 ++ .endm ++ + /* + * Register aliases. + */ +diff --git a/xen/include/asm-arm/cpufeature.h b/xen/include/asm-arm/cpufeature.h +index db126508f159..f7368766c07c 100644 +--- a/xen/include/asm-arm/cpufeature.h ++++ b/xen/include/asm-arm/cpufeature.h +@@ -63,8 +63,12 @@ + #define ARM64_WORKAROUND_AT_SPECULATE 9 + #define ARM_WORKAROUND_858921 10 + #define ARM64_WORKAROUND_REPEAT_TLBI 11 ++#define ARM_WORKAROUND_BHB_LOOP_8 12 ++#define ARM_WORKAROUND_BHB_LOOP_24 13 ++#define ARM_WORKAROUND_BHB_LOOP_32 14 ++#define ARM_WORKAROUND_BHB_SMCC_3 15 + +-#define ARM_NCAPS 12 ++#define ARM_NCAPS 16 + + #ifndef __ASSEMBLY__ + +diff --git a/xen/include/asm-arm/smccc.h b/xen/include/asm-arm/smccc.h +index 9d94beb3df2d..b3dbeecc90ad 100644 +--- a/xen/include/asm-arm/smccc.h ++++ b/xen/include/asm-arm/smccc.h +@@ -334,6 +334,12 @@ void __arm_smccc_1_0_smc(register_t a0, register_t a1, register_t a2, + ARM_SMCCC_OWNER_ARCH, \ + 0x7FFF) + ++#define ARM_SMCCC_ARCH_WORKAROUND_3_FID \ ++ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ ++ ARM_SMCCC_CONV_32, \ ++ ARM_SMCCC_OWNER_ARCH, \ ++ 0x3FFF) ++ + /* SMCCC error codes */ + #define ARM_SMCCC_NOT_REQUIRED (-2) + #define ARM_SMCCC_ERR_UNKNOWN_FUNCTION (-1) diff --git a/xsa398-4.16-5-xen-arm-Allow-to-discover-and-use-SMCCC_ARCH_WORKARO.patch b/xsa398-4.16-5-xen-arm-Allow-to-discover-and-use-SMCCC_ARCH_WORKARO.patch new file mode 100644 index 0000000..efe99ac --- /dev/null +++ b/xsa398-4.16-5-xen-arm-Allow-to-discover-and-use-SMCCC_ARCH_WORKARO.patch @@ -0,0 +1,91 @@ +From dab616cd3d4856a7a4d4f3a429a82dbdbf1aeeb9 Mon Sep 17 00:00:00 2001 +From: Bertrand Marquis +Date: Thu, 17 Feb 2022 14:52:54 +0000 +Subject: xen/arm: Allow to discover and use SMCCC_ARCH_WORKAROUND_3 + +Allow guest to discover whether or not SMCCC_ARCH_WORKAROUND_3 is +supported and create a fastpath in the code to handle guests request to +do the workaround. + +The function SMCCC_ARCH_WORKAROUND_3 will be called by the guest for +flushing the branch history. So we want the handling to be as fast as +possible. + +As the mitigation is applied on every guest exit, we can check for the +call before saving all context and return very early. + +This is part of XSA-398 / CVE-2022-23960. + +Signed-off-by: Bertrand Marquis +Reviewed-by: Julien Grall +(cherry picked from commit c0a56ea0fd92ecb471936b7355ddbecbaea3707c) + +diff --git a/xen/arch/arm/arm64/entry.S b/xen/arch/arm/arm64/entry.S +index fc3811ad0ad5..cf7b9d826f54 100644 +--- a/xen/arch/arm/arm64/entry.S ++++ b/xen/arch/arm/arm64/entry.S +@@ -336,16 +336,26 @@ guest_sync: + cbnz x1, guest_sync_slowpath /* should be 0 for HVC #0 */ + + /* +- * Fastest path possible for ARM_SMCCC_ARCH_WORKAROUND_1. +- * The workaround has already been applied on the exception ++ * Fastest path possible for ARM_SMCCC_ARCH_WORKAROUND_1 and ++ * ARM_SMCCC_ARCH_WORKAROUND_3. ++ * The workaround needed has already been applied on the exception + * entry from the guest, so let's quickly get back to the guest. + * + * Note that eor is used because the function identifier cannot + * be encoded as an immediate for cmp. + */ + eor w0, w0, #ARM_SMCCC_ARCH_WORKAROUND_1_FID +- cbnz w0, check_wa2 ++ cbz w0, fastpath_out_workaround + ++ /* ARM_SMCCC_ARCH_WORKAROUND_2 handling */ ++ eor w0, w0, #(ARM_SMCCC_ARCH_WORKAROUND_1_FID ^ ARM_SMCCC_ARCH_WORKAROUND_2_FID) ++ cbz w0, wa2_ssbd ++ ++ /* Fastpath out for ARM_SMCCC_ARCH_WORKAROUND_3 */ ++ eor w0, w0, #(ARM_SMCCC_ARCH_WORKAROUND_2_FID ^ ARM_SMCCC_ARCH_WORKAROUND_3_FID) ++ cbnz w0, guest_sync_slowpath ++ ++fastpath_out_workaround: + /* + * Clobber both x0 and x1 to prevent leakage. Note that thanks + * the eor, x0 = 0. +@@ -354,10 +364,7 @@ guest_sync: + eret + sb + +-check_wa2: +- /* ARM_SMCCC_ARCH_WORKAROUND_2 handling */ +- eor w0, w0, #(ARM_SMCCC_ARCH_WORKAROUND_1_FID ^ ARM_SMCCC_ARCH_WORKAROUND_2_FID) +- cbnz w0, guest_sync_slowpath ++wa2_ssbd: + #ifdef CONFIG_ARM_SSBD + alternative_cb arm_enable_wa2_handling + b wa2_end +diff --git a/xen/arch/arm/vsmc.c b/xen/arch/arm/vsmc.c +index a36db15fffc0..b633ff2fe897 100644 +--- a/xen/arch/arm/vsmc.c ++++ b/xen/arch/arm/vsmc.c +@@ -124,6 +124,10 @@ static bool handle_arch(struct cpu_user_regs *regs) + break; + } + break; ++ case ARM_SMCCC_ARCH_WORKAROUND_3_FID: ++ if ( cpus_have_cap(ARM_WORKAROUND_BHB_SMCC_3) ) ++ ret = 0; ++ break; + } + + set_user_reg(regs, 0, ret); +@@ -132,6 +136,7 @@ static bool handle_arch(struct cpu_user_regs *regs) + } + + case ARM_SMCCC_ARCH_WORKAROUND_1_FID: ++ case ARM_SMCCC_ARCH_WORKAROUND_3_FID: + /* No return value */ + return true; + diff --git a/xsa398-4.16-6-x86-spec-ctrl-Cease-using-thunk-lfence-on-AMD.patch b/xsa398-4.16-6-x86-spec-ctrl-Cease-using-thunk-lfence-on-AMD.patch new file mode 100644 index 0000000..7c28ac0 --- /dev/null +++ b/xsa398-4.16-6-x86-spec-ctrl-Cease-using-thunk-lfence-on-AMD.patch @@ -0,0 +1,118 @@ +From c374a8c5cc74535e16410b7a0d9e92bf5de54f79 Mon Sep 17 00:00:00 2001 +From: Andrew Cooper +Date: Mon, 7 Mar 2022 16:35:52 +0000 +Subject: x86/spec-ctrl: Cease using thunk=lfence on AMD + +AMD have updated their Spectre v2 guidance, and lfence/jmp is no longer +considered safe. AMD are recommending using retpoline everywhere. + +Retpoline is incompatible with CET. All CET-capable hardware has efficient +IBRS (specifically, not something retrofitted in microcode), so use IBRS (and +STIBP for consistency sake). + +This is a logical change on AMD, but not on Intel as the default calculations +would end up with these settings anyway. Leave behind a message if IBRS is +found to be missing. + +Also update the default heuristics to never select THUNK_LFENCE. This causes +AMD CPUs to change their default to retpoline. + +Also update the printed message to include the AMD MSR_SPEC_CTRL settings, and +STIBP now that we set it for consistency sake. + +This is part of XSA-398 / CVE-2021-26401. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 8d03080d2a339840d3a59e0932a94f804e45110d) + +diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc +index 995197f4b23e..f606dc0e14c1 100644 +--- a/docs/misc/xen-command-line.pandoc ++++ b/docs/misc/xen-command-line.pandoc +@@ -2269,9 +2269,9 @@ to use. + + If Xen was compiled with INDIRECT_THUNK support, `bti-thunk=` can be used to + select which of the thunks gets patched into the `__x86_indirect_thunk_%reg` +-locations. The default thunk is `retpoline` (generally preferred for Intel +-hardware), with the alternatives being `jmp` (a `jmp *%reg` gadget, minimal +-overhead), and `lfence` (an `lfence; jmp *%reg` gadget, preferred for AMD). ++locations. The default thunk is `retpoline` (generally preferred), with the ++alternatives being `jmp` (a `jmp *%reg` gadget, minimal overhead), and ++`lfence` (an `lfence; jmp *%reg` gadget). + + On hardware supporting IBRS (Indirect Branch Restricted Speculation), the + `ibrs=` option can be used to force or prevent Xen using the feature itself. +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index cbeeb199037e..ae076bec3ab0 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -367,14 +367,19 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + "\n"); + + /* Settings for Xen's protection, irrespective of guests. */ +- printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s, Other:%s%s%s%s%s\n", ++ printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s%s, Other:%s%s%s%s%s\n", + thunk == THUNK_NONE ? "N/A" : + thunk == THUNK_RETPOLINE ? "RETPOLINE" : + thunk == THUNK_LFENCE ? "LFENCE" : + thunk == THUNK_JMP ? "JMP" : "?", +- !boot_cpu_has(X86_FEATURE_IBRSB) ? "No" : ++ (!boot_cpu_has(X86_FEATURE_IBRSB) && ++ !boot_cpu_has(X86_FEATURE_IBRS)) ? "No" : + (default_xen_spec_ctrl & SPEC_CTRL_IBRS) ? "IBRS+" : "IBRS-", +- !boot_cpu_has(X86_FEATURE_SSBD) ? "" : ++ (!boot_cpu_has(X86_FEATURE_STIBP) && ++ !boot_cpu_has(X86_FEATURE_AMD_STIBP)) ? "" : ++ (default_xen_spec_ctrl & SPEC_CTRL_STIBP) ? " STIBP+" : " STIBP-", ++ (!boot_cpu_has(X86_FEATURE_SSBD) && ++ !boot_cpu_has(X86_FEATURE_AMD_SSBD)) ? "" : + (default_xen_spec_ctrl & SPEC_CTRL_SSBD) ? " SSBD+" : " SSBD-", + !(caps & ARCH_CAPS_TSX_CTRL) ? "" : + (opt_tsx & 1) ? " TSX+" : " TSX-", +@@ -945,10 +950,23 @@ void __init init_speculation_mitigations(void) + /* + * First, disable the use of retpolines if Xen is using shadow stacks, as + * they are incompatible. ++ * ++ * In the absence of retpolines, IBRS needs to be used for speculative ++ * safety. All CET-capable hardware has efficient IBRS. + */ +- if ( cpu_has_xen_shstk && +- (opt_thunk == THUNK_DEFAULT || opt_thunk == THUNK_RETPOLINE) ) +- thunk = THUNK_JMP; ++ if ( cpu_has_xen_shstk ) ++ { ++ if ( !has_spec_ctrl ) ++ printk(XENLOG_WARNING "?!? CET active, but no MSR_SPEC_CTRL?\n"); ++ else if ( opt_ibrs == -1 ) ++ { ++ opt_ibrs = ibrs = true; ++ default_xen_spec_ctrl |= SPEC_CTRL_IBRS | SPEC_CTRL_STIBP; ++ } ++ ++ if ( opt_thunk == THUNK_DEFAULT || opt_thunk == THUNK_RETPOLINE ) ++ thunk = THUNK_JMP; ++ } + + /* + * Has the user specified any custom BTI mitigations? If so, follow their +@@ -968,16 +986,10 @@ void __init init_speculation_mitigations(void) + if ( IS_ENABLED(CONFIG_INDIRECT_THUNK) ) + { + /* +- * AMD's recommended mitigation is to set lfence as being dispatch +- * serialising, and to use IND_THUNK_LFENCE. +- */ +- if ( cpu_has_lfence_dispatch ) +- thunk = THUNK_LFENCE; +- /* +- * On Intel hardware, we'd like to use retpoline in preference to ++ * On all hardware, we'd like to use retpoline in preference to + * IBRS, but only if it is safe on this hardware. + */ +- else if ( retpoline_safe(caps) ) ++ if ( retpoline_safe(caps) ) + thunk = THUNK_RETPOLINE; + else if ( has_spec_ctrl ) + ibrs = true; From f1540fc3633427c32bbf55b87e532e05315ac14e Mon Sep 17 00:00:00 2001 From: Michael Young Date: Mon, 21 Mar 2022 09:44:00 +0000 Subject: [PATCH 033/137] fix build of xen*.efi file and package it in /usr/lib*/efi --- xen.fedora.efi.patch | 12 ++++++++++++ xen.spec | 27 ++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 xen.fedora.efi.patch diff --git a/xen.fedora.efi.patch b/xen.fedora.efi.patch new file mode 100644 index 0000000..d0c160b --- /dev/null +++ b/xen.fedora.efi.patch @@ -0,0 +1,12 @@ +--- xen-4.16.0/xen/arch/x86/Makefile.orig 2021-11-30 11:42:42.000000000 +0000 ++++ xen-4.16.0/xen/arch/x86/Makefile 2022-03-17 22:43:21.077990559 +0000 +@@ -127,7 +127,8 @@ + CFLAGS-$(XEN_BUILD_EFI) += -DXEN_BUILD_EFI + + # Check if the linker supports PE. +-EFI_LDFLAGS = $(patsubst -m%,-mi386pep,$(XEN_LDFLAGS)) --subsystem=10 ++#EFI_LDFLAGS = $(patsubst -m%,-mi386pep,$(XEN_LDFLAGS)) --subsystem=10 ++EFI_LDFLAGS = --as-needed --build-id=sha1 -mi386pep --subsystem=10 + XEN_BUILD_PE := $(if $(XEN_BUILD_EFI),$(call ld-option,$(EFI_LDFLAGS) --image-base=0x100000000 -o efi/check.efi efi/check.o)) + # If the above failed, it may be merely because of the linker not dealing well + # with debug info. Try again with stripping it. diff --git a/xen.spec b/xen.spec index ad0f6fe..efc326e 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.16.0 -Release: 4%{?dist} +Release: 5%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -126,6 +126,7 @@ Patch58: xen.git-6ef732726add103ee8f63293e326ad43b1643239.patch Patch59: xen.git-243026a2c5ad64c05281dc8ed2f1f57c0ee5988c.patch Patch60: xen.git-41e477b4f367269dc1b768a335cfa16f48f7f02f.patch Patch61: xsa398-4.16-6-x86-spec-ctrl-Cease-using-thunk-lfence-on-AMD.patch +Patch62: xen.efi.build.patch %if %build_qemutrad @@ -353,6 +354,7 @@ manage Xen virtual machines. %patch59 -p1 %patch60 -p1 %patch61 -p1 +%patch62 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -483,8 +485,9 @@ find %{buildroot} -print | xargs ls -ld | sed -e 's|.*%{buildroot}||' > f1.list rm -rf %{buildroot}/usr/*-xen-elf # hypervisor symlinks -rm -rf %{buildroot}/boot/xen-4.0.gz +rm -rf %{buildroot}/boot/xen-%{hv_abi}.gz rm -rf %{buildroot}/boot/xen-4.gz +rm -rf %{buildroot}/boot/xen.gz %if !%build_hyp rm -rf %{buildroot}/boot %endif @@ -518,9 +521,14 @@ rm -rf %{buildroot}/%{_libdir}/*.a %if %build_efi # clean up extra efi files -rm -rf %{buildroot}/%{_libdir}/efi %ifarch %{ix86} -rm -rf %{buildroot}/usr/lib64/efi +rm -f %{buildroot}/usr/lib64/efi/xen-%{hv_abi}.efi +rm -f %{buildroot}/usr/lib64/efi/xen-4.efi +rm -f %{buildroot}/usr/lib64/efi/xen.efi +%else +rm -f %{buildroot}/%{_libdir}/efi/xen-%{hv_abi}.efi +rm -f %{buildroot}/%{_libdir}/efi/xen-4.efi +rm -f %{buildroot}/%{_libdir}/efi/xen.efi %endif %endif @@ -903,7 +911,6 @@ fi %if %build_hyp %ifnarch armv7hl aarch64 /boot/xen-*.gz -/boot/xen.gz /boot/xen*.config %else /boot/xen* @@ -914,6 +921,13 @@ fi %endif /usr/lib/debug/xen* %endif +%if %build_efi +%ifarch %{ix86} +/usr/lib64/efi/*.efi +%else +%{_libdir}/efi/*.efi +%endif +%endif %if %build_docs %files doc @@ -952,6 +966,9 @@ fi %endif %changelog +* Mon Mar 21 2022 Michael Young - 4.16.0-5 +- fix build of xen*.efi file and package it in /usr/lib*/efi + * Tue Mar 15 2022 Michael Young - 4.16.0-4 - Multiple speculative security issues [XSA-398] - additional patches so above applies cleanly From 288b363e21ba8c6b3309b7fdfdc01d4c5bfa40e8 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Mon, 21 Mar 2022 10:12:29 +0000 Subject: [PATCH 034/137] fix patch name --- xen.fedora.efi.patch => xen.efi.build.patch | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename xen.fedora.efi.patch => xen.efi.build.patch (100%) diff --git a/xen.fedora.efi.patch b/xen.efi.build.patch similarity index 100% rename from xen.fedora.efi.patch rename to xen.efi.build.patch From cac6545cdc25c6f177f10fd94bfba058d3652068 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 5 Apr 2022 22:06:41 +0100 Subject: [PATCH 035/137] 3 security update Racy interactions between dirty vram tracking and paging log dirty hypercalls [XSA-397, CVE-2022-26356] race in VT-d domain ID cleanup [XSA-399, CVE-2022-26357] IOMMU: RMRR (VT-d) and unity map (AMD-Vi) handling issues [XSA-400, CVE-2022-26358, CVE-2022-26359, CVE-2022-26360, CVE-2022-26361] additional patches so above applies cleanly --- ...7e8b53935de9a1123f677213f1b146843a0e.patch | 41 + ...518e952c3a8fe5b9ec6a2d007cda73fd05a4.patch | 36 + ...f6b5560e738955993fe061a04d64c6f71c14.patch | 142 +++ xen.spec | 44 +- xsa397-4.16.patch | 98 ++ xsa399-4.16.patch | 45 + xsa400-4.16-01.patch | 105 ++ xsa400-4.16-02.patch | 80 ++ xsa400-4.16-03.patch | 134 +++ xsa400-4.16-04.patch | 94 ++ xsa400-4.16-05.patch | 570 +++++++++++ xsa400-4.16-06.patch | 330 ++++++ xsa400-4.16-07.patch | 333 ++++++ xsa400-4.16-08.patch | 144 +++ xsa400-4.16-09.patch | 447 +++++++++ xsa400-4.16-10.patch | 38 + xsa400-4.16-11.patch | 29 + xsa400-4.16-12.patch | 945 ++++++++++++++++++ 18 files changed, 3654 insertions(+), 1 deletion(-) create mode 100644 xen.git-84977e8b53935de9a1123f677213f1b146843a0e.patch create mode 100644 xen.git-b2db518e952c3a8fe5b9ec6a2d007cda73fd05a4.patch create mode 100644 xen.git-fa45f6b5560e738955993fe061a04d64c6f71c14.patch create mode 100644 xsa397-4.16.patch create mode 100644 xsa399-4.16.patch create mode 100644 xsa400-4.16-01.patch create mode 100644 xsa400-4.16-02.patch create mode 100644 xsa400-4.16-03.patch create mode 100644 xsa400-4.16-04.patch create mode 100644 xsa400-4.16-05.patch create mode 100644 xsa400-4.16-06.patch create mode 100644 xsa400-4.16-07.patch create mode 100644 xsa400-4.16-08.patch create mode 100644 xsa400-4.16-09.patch create mode 100644 xsa400-4.16-10.patch create mode 100644 xsa400-4.16-11.patch create mode 100644 xsa400-4.16-12.patch diff --git a/xen.git-84977e8b53935de9a1123f677213f1b146843a0e.patch b/xen.git-84977e8b53935de9a1123f677213f1b146843a0e.patch new file mode 100644 index 0000000..1b73e73 --- /dev/null +++ b/xen.git-84977e8b53935de9a1123f677213f1b146843a0e.patch @@ -0,0 +1,41 @@ +From 84977e8b53935de9a1123f677213f1b146843a0e Mon Sep 17 00:00:00 2001 +From: Jan Beulich +Date: Thu, 6 Jan 2022 14:11:23 +0100 +Subject: [PATCH] VT-d: don't leak domid mapping on error path + +While domain_context_mapping() invokes domain_context_unmap() in a sub- +case of handling DEV_TYPE_PCI when encountering an error, thus avoiding +a leak, individual calls to domain_context_mapping_one() aren't +similarly covered. Such a leak might persist until domain destruction. +Leverage that these cases can be recognized by pdev being non-NULL. + +Fixes: dec403cc668f ("VT-d: fix iommu_domid for PCI/PCIx devices assignment") +Signed-off-by: Jan Beulich +Reviewed-by: Kevin Tian +master commit: e6252a51faf42c892eb5fc71f8a2617580832196 +master date: 2021-11-24 11:07:11 +0100 +--- + xen/drivers/passthrough/vtd/iommu.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c +index de11c258ca..3b37bad25e 100644 +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -1517,7 +1517,12 @@ int domain_context_mapping_one( + rc = me_wifi_quirk(domain, bus, devfn, MAP_ME_PHANTOM_FUNC); + + if ( rc ) +- domain_context_unmap_one(domain, iommu, bus, devfn); ++ { ++ ret = domain_context_unmap_one(domain, iommu, bus, devfn); ++ ++ if ( !ret && pdev && pdev->devfn == devfn ) ++ check_cleanup_domid_map(domain, pdev, iommu); ++ } + + return rc; + } +-- +2.30.2 + diff --git a/xen.git-b2db518e952c3a8fe5b9ec6a2d007cda73fd05a4.patch b/xen.git-b2db518e952c3a8fe5b9ec6a2d007cda73fd05a4.patch new file mode 100644 index 0000000..0b2d15a --- /dev/null +++ b/xen.git-b2db518e952c3a8fe5b9ec6a2d007cda73fd05a4.patch @@ -0,0 +1,36 @@ +From b2db518e952c3a8fe5b9ec6a2d007cda73fd05a4 Mon Sep 17 00:00:00 2001 +From: Jan Beulich +Date: Thu, 10 Mar 2022 09:43:50 +0100 +Subject: [PATCH] VT-d: drop undue address-of from check_cleanup_domid_map() +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf8 +Content-Transfer-Encoding: 8bit + +For an unknown reason I added back the operator while backporting, +despite 4.16 having c06e3d810314 ("VT-d: per-domain IOMMU bitmap needs +to have dynamic size"). I can only assume that I mistakenly took the +4.15 backport as basis and/or reference. + +Fixes: fa45f6b5560e ("VT-d: split domid map cleanup check into a function") +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné +--- + xen/drivers/passthrough/vtd/iommu.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c +index 3b37bad25e..ead12db6a4 100644 +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -197,7 +197,7 @@ static void check_cleanup_domid_map(struct domain *d, + + if ( !found ) + { +- clear_bit(iommu->index, &dom_iommu(d)->arch.vtd.iommu_bitmap); ++ clear_bit(iommu->index, dom_iommu(d)->arch.vtd.iommu_bitmap); + cleanup_domid_map(d, iommu); + } + } +-- +2.30.2 + diff --git a/xen.git-fa45f6b5560e738955993fe061a04d64c6f71c14.patch b/xen.git-fa45f6b5560e738955993fe061a04d64c6f71c14.patch new file mode 100644 index 0000000..a5fad75 --- /dev/null +++ b/xen.git-fa45f6b5560e738955993fe061a04d64c6f71c14.patch @@ -0,0 +1,142 @@ +From: Jan Beulich +Date: Thu, 6 Jan 2022 13:10:05 +0000 (+0100) +Subject: VT-d: split domid map cleanup check into a function +X-Git-Url: http://xenbits.xenproject.org/gitweb/?p=xen.git;a=commitdiff_plain;h=fa45f6b5560e738955993fe061a04d64c6f71c14 + +VT-d: split domid map cleanup check into a function + +This logic will want invoking from elsewhere. + +No functional change intended. + +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné +Reviewed-by: Kevin Tian +master commit: 9fdc10abe9457e4c9879a266f82372cb08e88ffb +master date: 2021-11-24 11:06:20 +0100 +--- + +diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c +index f9ce402f22..de11c258ca 100644 +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -157,6 +157,51 @@ static void cleanup_domid_map(struct domain *domain, struct vtd_iommu *iommu) + } + } + ++static bool any_pdev_behind_iommu(const struct domain *d, ++ const struct pci_dev *exclude, ++ const struct vtd_iommu *iommu) ++{ ++ const struct pci_dev *pdev; ++ ++ for_each_pdev ( d, pdev ) ++ { ++ const struct acpi_drhd_unit *drhd; ++ ++ if ( pdev == exclude ) ++ continue; ++ ++ drhd = acpi_find_matched_drhd_unit(pdev); ++ if ( drhd && drhd->iommu == iommu ) ++ return true; ++ } ++ ++ return false; ++} ++ ++/* ++ * If no other devices under the same iommu owned by this domain, ++ * clear iommu in iommu_bitmap and clear domain_id in domid_bitmap. ++ */ ++static void check_cleanup_domid_map(struct domain *d, ++ const struct pci_dev *exclude, ++ struct vtd_iommu *iommu) ++{ ++ bool found = any_pdev_behind_iommu(d, exclude, iommu); ++ ++ /* ++ * Hidden devices are associated with DomXEN but usable by the hardware ++ * domain. Hence they need considering here as well. ++ */ ++ if ( !found && is_hardware_domain(d) ) ++ found = any_pdev_behind_iommu(dom_xen, exclude, iommu); ++ ++ if ( !found ) ++ { ++ clear_bit(iommu->index, &dom_iommu(d)->arch.vtd.iommu_bitmap); ++ cleanup_domid_map(d, iommu); ++ } ++} ++ + static void sync_cache(const void *addr, unsigned int size) + { + static unsigned long clflush_size = 0; +@@ -1674,27 +1719,6 @@ int domain_context_unmap_one( + return rc; + } + +-static bool any_pdev_behind_iommu(const struct domain *d, +- const struct pci_dev *exclude, +- const struct vtd_iommu *iommu) +-{ +- const struct pci_dev *pdev; +- +- for_each_pdev ( d, pdev ) +- { +- const struct acpi_drhd_unit *drhd; +- +- if ( pdev == exclude ) +- continue; +- +- drhd = acpi_find_matched_drhd_unit(pdev); +- if ( drhd && drhd->iommu == iommu ) +- return true; +- } +- +- return false; +-} +- + static int domain_context_unmap(struct domain *domain, u8 devfn, + struct pci_dev *pdev) + { +@@ -1703,7 +1727,6 @@ static int domain_context_unmap(struct domain *domain, u8 devfn, + int ret; + uint16_t seg = pdev->seg; + uint8_t bus = pdev->bus, tmp_bus, tmp_devfn, secbus; +- bool found; + + switch ( pdev->type ) + { +@@ -1779,28 +1802,10 @@ static int domain_context_unmap(struct domain *domain, u8 devfn, + return -EINVAL; + } + +- if ( ret || QUARANTINE_SKIP(domain) || pdev->devfn != devfn ) +- return ret; ++ if ( !ret && !QUARANTINE_SKIP(domain) && pdev->devfn == devfn ) ++ check_cleanup_domid_map(domain, pdev, iommu); + +- /* +- * If no other devices under the same iommu owned by this domain, +- * clear iommu in iommu_bitmap and clear domain_id in domid_bitmap. +- */ +- found = any_pdev_behind_iommu(domain, pdev, iommu); +- /* +- * Hidden devices are associated with DomXEN but usable by the hardware +- * domain. Hence they need considering here as well. +- */ +- if ( !found && is_hardware_domain(domain) ) +- found = any_pdev_behind_iommu(dom_xen, pdev, iommu); +- +- if ( !found ) +- { +- clear_bit(iommu->index, dom_iommu(domain)->arch.vtd.iommu_bitmap); +- cleanup_domid_map(domain, iommu); +- } +- +- return 0; ++ return ret; + } + + static void iommu_clear_root_pgtable(struct domain *d) diff --git a/xen.spec b/xen.spec index efc326e..d14f502 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.16.0 -Release: 5%{?dist} +Release: 6%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -127,6 +127,23 @@ Patch59: xen.git-243026a2c5ad64c05281dc8ed2f1f57c0ee5988c.patch Patch60: xen.git-41e477b4f367269dc1b768a335cfa16f48f7f02f.patch Patch61: xsa398-4.16-6-x86-spec-ctrl-Cease-using-thunk-lfence-on-AMD.patch Patch62: xen.efi.build.patch +Patch63: xsa397-4.16.patch +Patch64: xsa399-4.16.patch +Patch65: xen.git-fa45f6b5560e738955993fe061a04d64c6f71c14.patch +Patch66: xen.git-84977e8b53935de9a1123f677213f1b146843a0e.patch +Patch67: xen.git-b2db518e952c3a8fe5b9ec6a2d007cda73fd05a4.patch +Patch68: xsa400-4.16-01.patch +Patch69: xsa400-4.16-02.patch +Patch70: xsa400-4.16-03.patch +Patch71: xsa400-4.16-04.patch +Patch72: xsa400-4.16-05.patch +Patch73: xsa400-4.16-06.patch +Patch74: xsa400-4.16-07.patch +Patch75: xsa400-4.16-08.patch +Patch76: xsa400-4.16-09.patch +Patch77: xsa400-4.16-10.patch +Patch78: xsa400-4.16-11.patch +Patch79: xsa400-4.16-12.patch %if %build_qemutrad @@ -355,6 +372,23 @@ manage Xen virtual machines. %patch60 -p1 %patch61 -p1 %patch62 -p1 +%patch63 -p1 +%patch64 -p1 +%patch65 -p1 +%patch66 -p1 +%patch67 -p1 +%patch68 -p1 +%patch69 -p1 +%patch70 -p1 +%patch71 -p1 +%patch72 -p1 +%patch73 -p1 +%patch74 -p1 +%patch75 -p1 +%patch76 -p1 +%patch77 -p1 +%patch78 -p1 +%patch79 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -966,6 +1000,14 @@ fi %endif %changelog +* Tue Apr 05 2022 Michael Young - 4.16.0-6 +- Racy interactions between dirty vram tracking and paging log dirty + hypercalls [XSA-397, CVE-2022-26356] +- race in VT-d domain ID cleanup [XSA-399, CVE-2022-26357] +- IOMMU: RMRR (VT-d) and unity map (AMD-Vi) handling issues [XSA-400, + CVE-2022-26358, CVE-2022-26359, CVE-2022-26360, CVE-2022-26361] +- additional patches so above applies cleanly + * Mon Mar 21 2022 Michael Young - 4.16.0-5 - fix build of xen*.efi file and package it in /usr/lib*/efi diff --git a/xsa397-4.16.patch b/xsa397-4.16.patch new file mode 100644 index 0000000..c04cd54 --- /dev/null +++ b/xsa397-4.16.patch @@ -0,0 +1,98 @@ +From: Roger Pau Monne +Subject: x86/hap: do not switch on log dirty for VRAM tracking + +XEN_DMOP_track_dirty_vram possibly calls into paging_log_dirty_enable +when using HAP mode, and it can interact badly with other ongoing +paging domctls, as XEN_DMOP_track_dirty_vram is not holding the domctl +lock. + +This was detected as a result of the following assert triggering when +doing repeated migrations of a HAP HVM domain with a stubdom: + +Assertion 'd->arch.paging.log_dirty.allocs == 0' failed at paging.c:198 +----[ Xen-4.17-unstable x86_64 debug=y Not tainted ]---- +CPU: 34 +RIP: e008:[] arch/x86/mm/paging.c#paging_free_log_dirty_bitmap+0x606/0x6 +RFLAGS: 0000000000010206 CONTEXT: hypervisor (d0v23) +[...] +Xen call trace: + [] R arch/x86/mm/paging.c#paging_free_log_dirty_bitmap+0x606/0x63a + [] S xsm/flask/hooks.c#domain_has_perm+0x5a/0x67 + [] F paging_domctl+0x251/0xd41 + [] F paging_domctl_continuation+0x19d/0x202 + [] F pv_hypercall+0x150/0x2a7 + [] F lstar_enter+0x12d/0x140 + +Such assert triggered because the stubdom used +XEN_DMOP_track_dirty_vram while dom0 was in the middle of executing +XEN_DOMCTL_SHADOW_OP_OFF, and so log dirty become enabled while +retiring the old structures, thus leading to new entries being +populated in already clear slots. + +Fix this by not enabling log dirty for VRAM tracking, similar to what +is done when using shadow instead of HAP. Call +p2m_enable_hardware_log_dirty when enabling VRAM tracking in order to +get some hardware assistance if available. As a side effect the memory +pressure on the p2m pool should go down if only VRAM tracking is +enabled, as the dirty bitmap is no longer allocated. + +Note that paging_log_dirty_range (used to get the dirty bitmap for +VRAM tracking) doesn't use the log dirty bitmap, and instead relies on +checking whether each gfn on the range has been switched from +p2m_ram_logdirty to p2m_ram_rw in order to account for dirty pages. + +This is CVE-2022-26356 / XSA-397. + +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich + +--- a/xen/include/asm-x86/paging.h ++++ b/xen/include/asm-x86/paging.h +@@ -162,9 +162,6 @@ void paging_log_dirty_range(struct domai + unsigned long nr, + uint8_t *dirty_bitmap); + +-/* enable log dirty */ +-int paging_log_dirty_enable(struct domain *d, bool log_global); +- + /* log dirty initialization */ + void paging_log_dirty_init(struct domain *d, const struct log_dirty_ops *ops); + +--- a/xen/arch/x86/mm/hap/hap.c ++++ b/xen/arch/x86/mm/hap/hap.c +@@ -69,13 +69,6 @@ int hap_track_dirty_vram(struct domain * + { + unsigned int size = DIV_ROUND_UP(nr_frames, BITS_PER_BYTE); + +- if ( !paging_mode_log_dirty(d) ) +- { +- rc = paging_log_dirty_enable(d, false); +- if ( rc ) +- goto out; +- } +- + rc = -ENOMEM; + dirty_bitmap = vzalloc(size); + if ( !dirty_bitmap ) +@@ -107,6 +100,10 @@ int hap_track_dirty_vram(struct domain * + + paging_unlock(d); + ++ domain_pause(d); ++ p2m_enable_hardware_log_dirty(d); ++ domain_unpause(d); ++ + if ( oend > ostart ) + p2m_change_type_range(d, ostart, oend, + p2m_ram_logdirty, p2m_ram_rw); +--- a/xen/arch/x86/mm/paging.c ++++ b/xen/arch/x86/mm/paging.c +@@ -211,7 +211,7 @@ static int paging_free_log_dirty_bitmap( + return rc; + } + +-int paging_log_dirty_enable(struct domain *d, bool log_global) ++static int paging_log_dirty_enable(struct domain *d, bool log_global) + { + int ret; + diff --git a/xsa399-4.16.patch b/xsa399-4.16.patch new file mode 100644 index 0000000..5f3850e --- /dev/null +++ b/xsa399-4.16.patch @@ -0,0 +1,45 @@ +From: Jan Beulich +Subject: VT-d: correct ordering of operations in cleanup_domid_map() + +The function may be called without any locks held (leaving aside the +domctl one, which we surely don't want to depend on here), so needs to +play safe wrt other accesses to domid_map[] and domid_bitmap[]. This is +to avoid context_set_domain_id()'s writing of domid_map[] to be reset to +zero right away in the case of it racing the freeing of a DID. + +For the interaction with context_set_domain_id() and ->domid_map[] reads +see the code comment. + +{check_,}cleanup_domid_map() are called with pcidevs_lock held or during +domain cleanup only (and pcidevs_lock is also held around +context_set_domain_id()), i.e. racing calls with the same (dom, iommu) +tuple cannot occur. + +domain_iommu_domid(), besides its use by cleanup_domid_map(), has its +result used only to control flushing, and hence a stale result would +only lead to a stray extra flush. + +This is CVE-2022-26357 / XSA-399. + +Fixes: b9c20c78789f ("VT-d: per-iommu domain-id") +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné + +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -152,8 +152,14 @@ static void cleanup_domid_map(struct dom + + if ( iommu_domid >= 0 ) + { ++ /* ++ * Update domid_map[] /before/ domid_bitmap[] to avoid a race with ++ * context_set_domain_id(), setting the slot to DOMID_INVALID for ++ * ->domid_map[] reads to produce a suitable value while the bit is ++ * still set. ++ */ ++ iommu->domid_map[iommu_domid] = DOMID_INVALID; + clear_bit(iommu_domid, iommu->domid_bitmap); +- iommu->domid_map[iommu_domid] = 0; + } + } + diff --git a/xsa400-4.16-01.patch b/xsa400-4.16-01.patch new file mode 100644 index 0000000..f44f08e --- /dev/null +++ b/xsa400-4.16-01.patch @@ -0,0 +1,105 @@ +From: Jan Beulich +Subject: VT-d: fix (de)assign ordering when RMRRs are in use + +In the event that the RMRR mappings are essential for device operation, +they should be established before updating the device's context entry, +while they should be torn down only after the device's context entry was +successfully updated. + +Also adjust a related log message. + +This is CVE-2022-26358 / part of XSA-400. + +Fixes: 8b99f4400b69 ("VT-d: fix RMRR related error handling") +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné +Reviewed-by: Paul Durrant +Reviewed-by: Kevin Tian + +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -2419,6 +2419,10 @@ static int reassign_device_ownership( + { + int ret; + ++ ret = domain_context_unmap(source, devfn, pdev); ++ if ( ret ) ++ return ret; ++ + /* + * Devices assigned to untrusted domains (here assumed to be any domU) + * can attempt to send arbitrary LAPIC/MSI messages. We are unprotected +@@ -2455,10 +2459,6 @@ static int reassign_device_ownership( + } + } + +- ret = domain_context_unmap(source, devfn, pdev); +- if ( ret ) +- return ret; +- + if ( devfn == pdev->devfn && pdev->domain != dom_io ) + { + list_move(&pdev->domain_list, &dom_io->pdev_list); +@@ -2534,9 +2534,8 @@ static int intel_iommu_assign_device( + } + } + +- ret = reassign_device_ownership(s, d, devfn, pdev); +- if ( ret || d == dom_io ) +- return ret; ++ if ( d == dom_io ) ++ return reassign_device_ownership(s, d, devfn, pdev); + + /* Setup rmrr identity mapping */ + for_each_rmrr_device( rmrr, bdf, i ) +@@ -2549,20 +2548,37 @@ static int intel_iommu_assign_device( + rmrr->end_address, flag); + if ( ret ) + { +- int rc; +- +- rc = reassign_device_ownership(d, s, devfn, pdev); + printk(XENLOG_G_ERR VTDPREFIX +- " cannot map reserved region (%"PRIx64",%"PRIx64"] for Dom%d (%d)\n", +- rmrr->base_address, rmrr->end_address, +- d->domain_id, ret); +- if ( rc ) +- { +- printk(XENLOG_ERR VTDPREFIX +- " failed to reclaim %pp from %pd (%d)\n", +- &PCI_SBDF3(seg, bus, devfn), d, rc); +- domain_crash(d); +- } ++ "%pd: cannot map reserved region [%"PRIx64",%"PRIx64"]: %d\n", ++ d, rmrr->base_address, rmrr->end_address, ret); ++ break; ++ } ++ } ++ } ++ ++ if ( !ret ) ++ ret = reassign_device_ownership(s, d, devfn, pdev); ++ ++ /* See reassign_device_ownership() for the hwdom aspect. */ ++ if ( !ret || is_hardware_domain(d) ) ++ return ret; ++ ++ for_each_rmrr_device( rmrr, bdf, i ) ++ { ++ if ( rmrr->segment == seg && ++ PCI_BUS(bdf) == bus && ++ PCI_DEVFN2(bdf) == devfn ) ++ { ++ int rc = iommu_identity_mapping(d, p2m_access_x, ++ rmrr->base_address, ++ rmrr->end_address, 0); ++ ++ if ( rc && rc != -ENOENT ) ++ { ++ printk(XENLOG_ERR VTDPREFIX ++ "%pd: cannot unmap reserved region [%"PRIx64",%"PRIx64"]: %d\n", ++ d, rmrr->base_address, rmrr->end_address, rc); ++ domain_crash(d); + break; + } + } diff --git a/xsa400-4.16-02.patch b/xsa400-4.16-02.patch new file mode 100644 index 0000000..e8de874 --- /dev/null +++ b/xsa400-4.16-02.patch @@ -0,0 +1,80 @@ +From: Jan Beulich +Subject: VT-d: fix add/remove ordering when RMRRs are in use + +In the event that the RMRR mappings are essential for device operation, +they should be established before updating the device's context entry, +while they should be torn down only after the device's context entry was +successfully cleared. + +Also switch to %pd in related log messages. + +Fixes: fa88cfadf918 ("vt-d: Map RMRR in intel_iommu_add_device() if the device has RMRR") +Fixes: 8b99f4400b69 ("VT-d: fix RMRR related error handling") +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné +Reviewed-by: Kevin Tian + +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -1997,14 +1997,6 @@ static int intel_iommu_add_device(u8 dev + if ( !pdev->domain ) + return -EINVAL; + +- ret = domain_context_mapping(pdev->domain, devfn, pdev); +- if ( ret ) +- { +- dprintk(XENLOG_ERR VTDPREFIX, "d%d: context mapping failed\n", +- pdev->domain->domain_id); +- return ret; +- } +- + for_each_rmrr_device ( rmrr, bdf, i ) + { + if ( rmrr->segment == pdev->seg && +@@ -2021,12 +2013,17 @@ static int intel_iommu_add_device(u8 dev + rmrr->base_address, rmrr->end_address, + 0); + if ( ret ) +- dprintk(XENLOG_ERR VTDPREFIX, "d%d: RMRR mapping failed\n", +- pdev->domain->domain_id); ++ dprintk(XENLOG_ERR VTDPREFIX, "%pd: RMRR mapping failed\n", ++ pdev->domain); + } + } + +- return 0; ++ ret = domain_context_mapping(pdev->domain, devfn, pdev); ++ if ( ret ) ++ dprintk(XENLOG_ERR VTDPREFIX, "%pd: context mapping failed\n", ++ pdev->domain); ++ ++ return ret; + } + + static int intel_iommu_enable_device(struct pci_dev *pdev) +@@ -2048,11 +2045,15 @@ static int intel_iommu_remove_device(u8 + { + struct acpi_rmrr_unit *rmrr; + u16 bdf; +- int i; ++ int ret, i; + + if ( !pdev->domain ) + return -EINVAL; + ++ ret = domain_context_unmap(pdev->domain, devfn, pdev); ++ if ( ret ) ++ return ret; ++ + for_each_rmrr_device ( rmrr, bdf, i ) + { + if ( rmrr->segment != pdev->seg || +@@ -2068,7 +2069,7 @@ static int intel_iommu_remove_device(u8 + rmrr->end_address, 0); + } + +- return domain_context_unmap(pdev->domain, devfn, pdev); ++ return 0; + } + + static int __hwdom_init setup_hwdom_device(u8 devfn, struct pci_dev *pdev) diff --git a/xsa400-4.16-03.patch b/xsa400-4.16-03.patch new file mode 100644 index 0000000..f422ad2 --- /dev/null +++ b/xsa400-4.16-03.patch @@ -0,0 +1,134 @@ +From: Jan Beulich +Subject: IOMMU/x86: tighten iommu_alloc_pgtable()'s parameter + +This is to make more obvious that nothing outside of domain_iommu(d) +actually changes or is otherwise needed by the function. + +No functional change intended. + +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné +Reviewed-by: Paul Durrant +Reviewed-by: Kevin Tian + +--- a/xen/include/asm-x86/iommu.h ++++ b/xen/include/asm-x86/iommu.h +@@ -142,7 +142,8 @@ int pi_update_irte(const struct pi_desc + }) + + int __must_check iommu_free_pgtables(struct domain *d); +-struct page_info *__must_check iommu_alloc_pgtable(struct domain *d); ++struct domain_iommu; ++struct page_info *__must_check iommu_alloc_pgtable(struct domain_iommu *hd); + + #endif /* !__ARCH_X86_IOMMU_H__ */ + /* +--- a/xen/drivers/passthrough/amd/iommu_map.c ++++ b/xen/drivers/passthrough/amd/iommu_map.c +@@ -184,7 +184,7 @@ static int iommu_pde_from_dfn(struct dom + unsigned long next_table_mfn; + unsigned int level; + struct page_info *table; +- const struct domain_iommu *hd = dom_iommu(d); ++ struct domain_iommu *hd = dom_iommu(d); + + table = hd->arch.amd.root_table; + level = hd->arch.amd.paging_mode; +@@ -219,7 +219,7 @@ static int iommu_pde_from_dfn(struct dom + mfn = next_table_mfn; + + /* allocate lower level page table */ +- table = iommu_alloc_pgtable(d); ++ table = iommu_alloc_pgtable(hd); + if ( table == NULL ) + { + AMD_IOMMU_ERROR("cannot allocate I/O page table\n"); +@@ -249,7 +249,7 @@ static int iommu_pde_from_dfn(struct dom + + if ( next_table_mfn == 0 ) + { +- table = iommu_alloc_pgtable(d); ++ table = iommu_alloc_pgtable(hd); + if ( table == NULL ) + { + AMD_IOMMU_ERROR("cannot allocate I/O page table\n"); +@@ -553,7 +553,7 @@ int __init amd_iommu_quarantine_init(str + + spin_lock(&hd->arch.mapping_lock); + +- hd->arch.amd.root_table = iommu_alloc_pgtable(d); ++ hd->arch.amd.root_table = iommu_alloc_pgtable(hd); + if ( !hd->arch.amd.root_table ) + goto out; + +@@ -568,7 +568,7 @@ int __init amd_iommu_quarantine_init(str + * page table pages, and the resulting allocations are always + * zeroed. + */ +- pg = iommu_alloc_pgtable(d); ++ pg = iommu_alloc_pgtable(hd); + if ( !pg ) + break; + +--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c ++++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c +@@ -242,7 +242,7 @@ int amd_iommu_alloc_root(struct domain * + + if ( unlikely(!hd->arch.amd.root_table) ) + { +- hd->arch.amd.root_table = iommu_alloc_pgtable(d); ++ hd->arch.amd.root_table = iommu_alloc_pgtable(hd); + if ( !hd->arch.amd.root_table ) + return -ENOMEM; + } +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -330,7 +330,7 @@ static u64 addr_to_dma_page_maddr(struct + { + struct page_info *pg; + +- if ( !alloc || !(pg = iommu_alloc_pgtable(domain)) ) ++ if ( !alloc || !(pg = iommu_alloc_pgtable(hd)) ) + goto out; + + hd->arch.vtd.pgd_maddr = page_to_maddr(pg); +@@ -350,7 +350,7 @@ static u64 addr_to_dma_page_maddr(struct + if ( !alloc ) + break; + +- pg = iommu_alloc_pgtable(domain); ++ pg = iommu_alloc_pgtable(hd); + if ( !pg ) + break; + +@@ -2766,7 +2766,7 @@ static int __init intel_iommu_quarantine + goto out; + } + +- pg = iommu_alloc_pgtable(d); ++ pg = iommu_alloc_pgtable(hd); + + rc = -ENOMEM; + if ( !pg ) +@@ -2785,7 +2785,7 @@ static int __init intel_iommu_quarantine + * page table pages, and the resulting allocations are always + * zeroed. + */ +- pg = iommu_alloc_pgtable(d); ++ pg = iommu_alloc_pgtable(hd); + + if ( !pg ) + goto out; +--- a/xen/drivers/passthrough/x86/iommu.c ++++ b/xen/drivers/passthrough/x86/iommu.c +@@ -416,9 +416,8 @@ int iommu_free_pgtables(struct domain *d + return 0; + } + +-struct page_info *iommu_alloc_pgtable(struct domain *d) ++struct page_info *iommu_alloc_pgtable(struct domain_iommu *hd) + { +- struct domain_iommu *hd = dom_iommu(d); + unsigned int memflags = 0; + struct page_info *pg; + void *p; diff --git a/xsa400-4.16-04.patch b/xsa400-4.16-04.patch new file mode 100644 index 0000000..41b4f34 --- /dev/null +++ b/xsa400-4.16-04.patch @@ -0,0 +1,94 @@ +From: Jan Beulich +Subject: VT-d: drop ownership checking from domain_context_mapping_one() + +Despite putting in quite a bit of effort it was not possible to +establish why exactly this code exists (beyond possibly sanity +checking). Instead of a subsequent change further complicating this +logic, simply get rid of it. + +Take the opportunity and move the respective unmap_vtd_domain_page() out +of the locked region. + +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné +Reviewed-by: Paul Durrant +Reviewed-by: Kevin Tian + +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -124,28 +124,6 @@ static int context_set_domain_id(struct + return 0; + } + +-static int context_get_domain_id(struct context_entry *context, +- struct vtd_iommu *iommu) +-{ +- unsigned long dom_index, nr_dom; +- int domid = -1; +- +- if (iommu && context) +- { +- nr_dom = cap_ndoms(iommu->cap); +- +- dom_index = context_domain_id(*context); +- +- if ( dom_index < nr_dom && iommu->domid_map ) +- domid = iommu->domid_map[dom_index]; +- else +- dprintk(XENLOG_DEBUG VTDPREFIX, +- "dom_index %lu exceeds nr_dom %lu or iommu has no domid_map\n", +- dom_index, nr_dom); +- } +- return domid; +-} +- + static void cleanup_domid_map(struct domain *domain, struct vtd_iommu *iommu) + { + int iommu_domid = domain_iommu_domid(domain, iommu); +@@ -1416,44 +1394,9 @@ int domain_context_mapping_one( + + if ( context_present(*context) ) + { +- int res = 0; +- +- /* Try to get domain ownership from device structure. If that's +- * not available, try to read it from the context itself. */ +- if ( pdev ) +- { +- if ( pdev->domain != domain ) +- { +- printk(XENLOG_G_INFO VTDPREFIX "%pd: %pp owned by %pd", +- domain, &PCI_SBDF3(seg, bus, devfn), +- pdev->domain); +- res = -EINVAL; +- } +- } +- else +- { +- int cdomain; +- cdomain = context_get_domain_id(context, iommu); +- +- if ( cdomain < 0 ) +- { +- printk(XENLOG_G_WARNING VTDPREFIX +- "%pd: %pp mapped, but can't find owner\n", +- domain, &PCI_SBDF3(seg, bus, devfn)); +- res = -EINVAL; +- } +- else if ( cdomain != domain->domain_id ) +- { +- printk(XENLOG_G_INFO VTDPREFIX +- "%pd: %pp already mapped to d%d", +- domain, &PCI_SBDF3(seg, bus, devfn), cdomain); +- res = -EINVAL; +- } +- } +- +- unmap_vtd_domain_page(context_entries); + spin_unlock(&iommu->lock); +- return res; ++ unmap_vtd_domain_page(context_entries); ++ return 0; + } + + if ( iommu_hwdom_passthrough && is_hardware_domain(domain) ) diff --git a/xsa400-4.16-05.patch b/xsa400-4.16-05.patch new file mode 100644 index 0000000..0eb6619 --- /dev/null +++ b/xsa400-4.16-05.patch @@ -0,0 +1,570 @@ +From: Jan Beulich +Subject: VT-d: re-assign devices directly + +Devices with RMRRs, due to it being unspecified how/when the specified +memory regions may get accessed, may not be left disconnected from their +respective mappings (as long as it's not certain that the device has +been fully quiesced). Hence rather than unmapping the old context and +then mapping the new one, re-assignment needs to be done in a single +step. + +This is CVE-2022-26359 / part of XSA-400. + +Reported-by: Roger Pau Monné + +Similarly quarantining scratch-page mode relies on page tables to be +continuously wired up. + +To avoid complicating things more than necessary, treat all devices +mostly equally, i.e. regardless of their association with any RMRRs. The +main difference is when it comes to updating context entries, which need +to be atomic when there are RMRRs. Yet atomicity can only be achieved +with CMPXCHG16B, availability of which we can't take for given. + +The seemingly complicated choice of non-negative return values for +domain_context_mapping_one() is to limit code churn: This way callers +passing NULL for pdev don't need fiddling with. + +Signed-off-by: Jan Beulich +Reviewed-by: Kevin Tian +Reviewed-by: Roger Pau Monné + +--- a/xen/drivers/passthrough/vtd/extern.h ++++ b/xen/drivers/passthrough/vtd/extern.h +@@ -84,7 +84,8 @@ void free_pgtable_maddr(u64 maddr); + void *map_vtd_domain_page(u64 maddr); + void unmap_vtd_domain_page(const void *va); + int domain_context_mapping_one(struct domain *domain, struct vtd_iommu *iommu, +- u8 bus, u8 devfn, const struct pci_dev *); ++ uint8_t bus, uint8_t devfn, ++ const struct pci_dev *pdev, unsigned int mode); + int domain_context_unmap_one(struct domain *domain, struct vtd_iommu *iommu, + u8 bus, u8 devfn); + int intel_iommu_get_reserved_device_memory(iommu_grdm_t *func, void *ctxt); +@@ -104,8 +105,8 @@ bool is_azalia_tlb_enabled(const struct + void platform_quirks_init(void); + void vtd_ops_preamble_quirk(struct vtd_iommu *iommu); + void vtd_ops_postamble_quirk(struct vtd_iommu *iommu); +-int __must_check me_wifi_quirk(struct domain *domain, +- u8 bus, u8 devfn, int map); ++int __must_check me_wifi_quirk(struct domain *domain, uint8_t bus, ++ uint8_t devfn, unsigned int mode); + void pci_vtd_quirk(const struct pci_dev *); + void quirk_iommu_caps(struct vtd_iommu *iommu); + +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -120,6 +120,7 @@ static int context_set_domain_id(struct + } + + set_bit(i, iommu->domid_bitmap); ++ context->hi &= ~(((1 << DID_FIELD_WIDTH) - 1) << DID_HIGH_OFFSET); + context->hi |= (i & ((1 << DID_FIELD_WIDTH) - 1)) << DID_HIGH_OFFSET; + return 0; + } +@@ -1371,15 +1372,27 @@ static void __hwdom_init intel_iommu_hwd + } + } + ++/* ++ * This function returns ++ * - a negative errno value upon error, ++ * - zero upon success when previously the entry was non-present, or this isn't ++ * the "main" request for a device (pdev == NULL), or for no-op quarantining ++ * assignments, ++ * - positive (one) upon success when previously the entry was present and this ++ * is the "main" request for a device (pdev != NULL). ++ */ + int domain_context_mapping_one( + struct domain *domain, + struct vtd_iommu *iommu, +- u8 bus, u8 devfn, const struct pci_dev *pdev) ++ uint8_t bus, uint8_t devfn, const struct pci_dev *pdev, ++ unsigned int mode) + { + struct domain_iommu *hd = dom_iommu(domain); +- struct context_entry *context, *context_entries; ++ struct context_entry *context, *context_entries, lctxt; ++ __uint128_t old; + u64 maddr, pgd_maddr; +- u16 seg = iommu->drhd->segment; ++ uint16_t seg = iommu->drhd->segment, prev_did = 0; ++ struct domain *prev_dom = NULL; + int rc, ret; + bool_t flush_dev_iotlb; + +@@ -1391,17 +1404,32 @@ int domain_context_mapping_one( + maddr = bus_to_context_maddr(iommu, bus); + context_entries = (struct context_entry *)map_vtd_domain_page(maddr); + context = &context_entries[devfn]; ++ old = (lctxt = *context).full; + +- if ( context_present(*context) ) ++ if ( context_present(lctxt) ) + { +- spin_unlock(&iommu->lock); +- unmap_vtd_domain_page(context_entries); +- return 0; ++ domid_t domid; ++ ++ prev_did = context_domain_id(lctxt); ++ domid = iommu->domid_map[prev_did]; ++ if ( domid < DOMID_FIRST_RESERVED ) ++ prev_dom = rcu_lock_domain_by_id(domid); ++ else if ( domid == DOMID_IO ) ++ prev_dom = rcu_lock_domain(dom_io); ++ if ( !prev_dom ) ++ { ++ spin_unlock(&iommu->lock); ++ unmap_vtd_domain_page(context_entries); ++ dprintk(XENLOG_DEBUG VTDPREFIX, ++ "no domain for did %u (nr_dom %u)\n", ++ prev_did, cap_ndoms(iommu->cap)); ++ return -ESRCH; ++ } + } + + if ( iommu_hwdom_passthrough && is_hardware_domain(domain) ) + { +- context_set_translation_type(*context, CONTEXT_TT_PASS_THRU); ++ context_set_translation_type(lctxt, CONTEXT_TT_PASS_THRU); + } + else + { +@@ -1413,36 +1441,107 @@ int domain_context_mapping_one( + spin_unlock(&hd->arch.mapping_lock); + spin_unlock(&iommu->lock); + unmap_vtd_domain_page(context_entries); ++ if ( prev_dom ) ++ rcu_unlock_domain(prev_dom); + return -ENOMEM; + } + +- context_set_address_root(*context, pgd_maddr); ++ context_set_address_root(lctxt, pgd_maddr); + if ( ats_enabled && ecap_dev_iotlb(iommu->ecap) ) +- context_set_translation_type(*context, CONTEXT_TT_DEV_IOTLB); ++ context_set_translation_type(lctxt, CONTEXT_TT_DEV_IOTLB); + else +- context_set_translation_type(*context, CONTEXT_TT_MULTI_LEVEL); ++ context_set_translation_type(lctxt, CONTEXT_TT_MULTI_LEVEL); + + spin_unlock(&hd->arch.mapping_lock); + } + +- if ( context_set_domain_id(context, domain, iommu) ) ++ if ( context_set_domain_id(&lctxt, domain, iommu) ) + { ++ unlock: + spin_unlock(&iommu->lock); + unmap_vtd_domain_page(context_entries); ++ if ( prev_dom ) ++ rcu_unlock_domain(prev_dom); + return -EFAULT; + } + +- context_set_address_width(*context, level_to_agaw(iommu->nr_pt_levels)); +- context_set_fault_enable(*context); +- context_set_present(*context); ++ if ( !prev_dom ) ++ { ++ context_set_address_width(lctxt, level_to_agaw(iommu->nr_pt_levels)); ++ context_set_fault_enable(lctxt); ++ context_set_present(lctxt); ++ } ++ else if ( prev_dom == domain ) ++ { ++ ASSERT(lctxt.full == context->full); ++ rc = !!pdev; ++ goto unlock; ++ } ++ else ++ { ++ ASSERT(context_address_width(lctxt) == ++ level_to_agaw(iommu->nr_pt_levels)); ++ ASSERT(!context_fault_disable(lctxt)); ++ } ++ ++ if ( cpu_has_cx16 ) ++ { ++ __uint128_t res = cmpxchg16b(context, &old, &lctxt.full); ++ ++ /* ++ * Hardware does not update the context entry behind our backs, ++ * so the return value should match "old". ++ */ ++ if ( res != old ) ++ { ++ if ( pdev ) ++ check_cleanup_domid_map(domain, pdev, iommu); ++ printk(XENLOG_ERR ++ "%pp: unexpected context entry %016lx_%016lx (expected %016lx_%016lx)\n", ++ &PCI_SBDF3(pdev->seg, pdev->bus, devfn), ++ (uint64_t)(res >> 64), (uint64_t)res, ++ (uint64_t)(old >> 64), (uint64_t)old); ++ rc = -EILSEQ; ++ goto unlock; ++ } ++ } ++ else if ( !prev_dom || !(mode & MAP_WITH_RMRR) ) ++ { ++ context_clear_present(*context); ++ iommu_sync_cache(context, sizeof(*context)); ++ ++ write_atomic(&context->hi, lctxt.hi); ++ /* No barrier should be needed between these two. */ ++ write_atomic(&context->lo, lctxt.lo); ++ } ++ else /* Best effort, updating DID last. */ ++ { ++ /* ++ * By non-atomically updating the context entry's DID field last, ++ * during a short window in time TLB entries with the old domain ID ++ * but the new page tables may be inserted. This could affect I/O ++ * of other devices using this same (old) domain ID. Such updating ++ * therefore is not a problem if this was the only device associated ++ * with the old domain ID. Diverting I/O of any of a dying domain's ++ * devices to the quarantine page tables is intended anyway. ++ */ ++ if ( !(mode & (MAP_OWNER_DYING | MAP_SINGLE_DEVICE)) ) ++ printk(XENLOG_WARNING VTDPREFIX ++ " %pp: reassignment may cause %pd data corruption\n", ++ &PCI_SBDF3(seg, bus, devfn), prev_dom); ++ ++ write_atomic(&context->lo, lctxt.lo); ++ /* No barrier should be needed between these two. */ ++ write_atomic(&context->hi, lctxt.hi); ++ } ++ + iommu_sync_cache(context, sizeof(struct context_entry)); + spin_unlock(&iommu->lock); + +- /* Context entry was previously non-present (with domid 0). */ +- rc = iommu_flush_context_device(iommu, 0, PCI_BDF2(bus, devfn), +- DMA_CCMD_MASK_NOBIT, 1); ++ rc = iommu_flush_context_device(iommu, prev_did, PCI_BDF2(bus, devfn), ++ DMA_CCMD_MASK_NOBIT, !prev_dom); + flush_dev_iotlb = !!find_ats_dev_drhd(iommu); +- ret = iommu_flush_iotlb_dsi(iommu, 0, 1, flush_dev_iotlb); ++ ret = iommu_flush_iotlb_dsi(iommu, prev_did, !prev_dom, flush_dev_iotlb); + + /* + * The current logic for returns: +@@ -1463,17 +1562,26 @@ int domain_context_mapping_one( + unmap_vtd_domain_page(context_entries); + + if ( !seg && !rc ) +- rc = me_wifi_quirk(domain, bus, devfn, MAP_ME_PHANTOM_FUNC); ++ rc = me_wifi_quirk(domain, bus, devfn, mode); + + if ( rc ) + { +- ret = domain_context_unmap_one(domain, iommu, bus, devfn); ++ if ( !prev_dom ) ++ ret = domain_context_unmap_one(domain, iommu, bus, devfn); ++ else if ( prev_dom != domain ) /* Avoid infinite recursion. */ ++ ret = domain_context_mapping_one(prev_dom, iommu, bus, devfn, pdev, ++ mode & MAP_WITH_RMRR) < 0; ++ else ++ ret = 1; + + if ( !ret && pdev && pdev->devfn == devfn ) + check_cleanup_domid_map(domain, pdev, iommu); + } + +- return rc; ++ if ( prev_dom ) ++ rcu_unlock_domain(prev_dom); ++ ++ return rc ?: pdev && prev_dom; + } + + static int domain_context_unmap(struct domain *d, uint8_t devfn, +@@ -1483,8 +1591,10 @@ static int domain_context_mapping(struct + struct pci_dev *pdev) + { + const struct acpi_drhd_unit *drhd = acpi_find_matched_drhd_unit(pdev); ++ const struct acpi_rmrr_unit *rmrr; + int ret = 0; +- uint16_t seg = pdev->seg; ++ unsigned int i, mode = 0; ++ uint16_t seg = pdev->seg, bdf; + uint8_t bus = pdev->bus, secbus; + + /* +@@ -1500,8 +1610,29 @@ static int domain_context_mapping(struct + + ASSERT(pcidevs_locked()); + ++ for_each_rmrr_device( rmrr, bdf, i ) ++ { ++ if ( rmrr->segment != pdev->seg || bdf != pdev->sbdf.bdf ) ++ continue; ++ ++ mode |= MAP_WITH_RMRR; ++ break; ++ } ++ ++ if ( domain != pdev->domain ) ++ { ++ if ( pdev->domain->is_dying ) ++ mode |= MAP_OWNER_DYING; ++ else if ( drhd && ++ !any_pdev_behind_iommu(pdev->domain, pdev, drhd->iommu) && ++ !pdev->phantom_stride ) ++ mode |= MAP_SINGLE_DEVICE; ++ } ++ + switch ( pdev->type ) + { ++ bool prev_present; ++ + case DEV_TYPE_PCI_HOST_BRIDGE: + if ( iommu_debug ) + printk(VTDPREFIX "%pd:Hostbridge: skip %pp map\n", +@@ -1523,7 +1654,9 @@ static int domain_context_mapping(struct + printk(VTDPREFIX "%pd:PCIe: map %pp\n", + domain, &PCI_SBDF3(seg, bus, devfn)); + ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn, +- pdev); ++ pdev, mode); ++ if ( ret > 0 ) ++ ret = 0; + if ( !ret && devfn == pdev->devfn && ats_device(pdev, drhd) > 0 ) + enable_ats_device(pdev, &drhd->iommu->ats_devices); + +@@ -1538,9 +1671,10 @@ static int domain_context_mapping(struct + domain, &PCI_SBDF3(seg, bus, devfn)); + + ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn, +- pdev); +- if ( ret ) ++ pdev, mode); ++ if ( ret < 0 ) + break; ++ prev_present = ret; + + if ( (ret = find_upstream_bridge(seg, &bus, &devfn, &secbus)) < 1 ) + { +@@ -1548,6 +1682,15 @@ static int domain_context_mapping(struct + break; + ret = -ENXIO; + } ++ /* ++ * Strictly speaking if the device is the only one behind this bridge ++ * and the only one with this (secbus,0,0) tuple, it could be allowed ++ * to be re-assigned regardless of RMRR presence. But let's deal with ++ * that case only if it is actually found in the wild. ++ */ ++ else if ( prev_present && (mode & MAP_WITH_RMRR) && ++ domain != pdev->domain ) ++ ret = -EOPNOTSUPP; + + /* + * Mapping a bridge should, if anything, pass the struct pci_dev of +@@ -1556,7 +1699,7 @@ static int domain_context_mapping(struct + */ + if ( ret >= 0 ) + ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn, +- NULL); ++ NULL, mode); + + /* + * Devices behind PCIe-to-PCI/PCIx bridge may generate different +@@ -1571,10 +1714,15 @@ static int domain_context_mapping(struct + if ( !ret && pdev_type(seg, bus, devfn) == DEV_TYPE_PCIe2PCI_BRIDGE && + (secbus != pdev->bus || pdev->devfn != 0) ) + ret = domain_context_mapping_one(domain, drhd->iommu, secbus, 0, +- NULL); ++ NULL, mode); + + if ( ret ) +- domain_context_unmap(domain, devfn, pdev); ++ { ++ if ( !prev_present ) ++ domain_context_unmap(domain, devfn, pdev); ++ else if ( pdev->domain != domain ) /* Avoid infinite recursion. */ ++ domain_context_mapping(pdev->domain, devfn, pdev); ++ } + + break; + +@@ -2363,17 +2511,46 @@ static int reassign_device_ownership( + { + int ret; + +- ret = domain_context_unmap(source, devfn, pdev); ++ if ( !QUARANTINE_SKIP(target) ) ++ { ++ if ( !has_arch_pdevs(target) ) ++ vmx_pi_hooks_assign(target); ++ ++ /* ++ * Devices assigned to untrusted domains (here assumed to be any domU) ++ * can attempt to send arbitrary LAPIC/MSI messages. We are unprotected ++ * by the root complex unless interrupt remapping is enabled. ++ */ ++ if ( (target != hardware_domain) && !iommu_intremap ) ++ untrusted_msi = true; ++ ++ ret = domain_context_mapping(target, devfn, pdev); ++ ++ if ( !ret && !QUARANTINE_SKIP(source) && pdev->devfn == devfn ) ++ { ++ const struct acpi_drhd_unit *drhd = acpi_find_matched_drhd_unit(pdev); ++ ++ if ( drhd ) ++ check_cleanup_domid_map(source, pdev, drhd->iommu); ++ } ++ } ++ else ++ ret = domain_context_unmap(source, devfn, pdev); + if ( ret ) ++ { ++ if ( !has_arch_pdevs(target) ) ++ vmx_pi_hooks_deassign(target); + return ret; ++ } + +- /* +- * Devices assigned to untrusted domains (here assumed to be any domU) +- * can attempt to send arbitrary LAPIC/MSI messages. We are unprotected +- * by the root complex unless interrupt remapping is enabled. +- */ +- if ( (target != hardware_domain) && !iommu_intremap ) +- untrusted_msi = true; ++ if ( devfn == pdev->devfn && pdev->domain != target ) ++ { ++ list_move(&pdev->domain_list, &target->pdev_list); ++ pdev->domain = target; ++ } ++ ++ if ( !has_arch_pdevs(source) ) ++ vmx_pi_hooks_deassign(source); + + /* + * If the device belongs to the hardware domain, and it has RMRR, don't +@@ -2403,34 +2580,7 @@ static int reassign_device_ownership( + } + } + +- if ( devfn == pdev->devfn && pdev->domain != dom_io ) +- { +- list_move(&pdev->domain_list, &dom_io->pdev_list); +- pdev->domain = dom_io; +- } +- +- if ( !has_arch_pdevs(source) ) +- vmx_pi_hooks_deassign(source); +- +- if ( !has_arch_pdevs(target) ) +- vmx_pi_hooks_assign(target); +- +- ret = domain_context_mapping(target, devfn, pdev); +- if ( ret ) +- { +- if ( !has_arch_pdevs(target) ) +- vmx_pi_hooks_deassign(target); +- +- return ret; +- } +- +- if ( devfn == pdev->devfn && pdev->domain != target ) +- { +- list_move(&pdev->domain_list, &target->pdev_list); +- pdev->domain = target; +- } +- +- return ret; ++ return 0; + } + + static int intel_iommu_assign_device( +--- a/xen/drivers/passthrough/vtd/iommu.h ++++ b/xen/drivers/passthrough/vtd/iommu.h +@@ -202,8 +202,12 @@ struct root_entry { + do {(root).val |= ((value) & PAGE_MASK_4K);} while(0) + + struct context_entry { +- u64 lo; +- u64 hi; ++ union { ++ struct { ++ uint64_t lo, hi; ++ }; ++ __uint128_t full; ++ }; + }; + #define ROOT_ENTRY_NR (PAGE_SIZE_4K/sizeof(struct root_entry)) + #define context_present(c) ((c).lo & 1) +--- a/xen/drivers/passthrough/vtd/quirks.c ++++ b/xen/drivers/passthrough/vtd/quirks.c +@@ -407,7 +407,8 @@ void __init platform_quirks_init(void) + */ + + static int __must_check map_me_phantom_function(struct domain *domain, +- u32 dev, int map) ++ unsigned int dev, ++ unsigned int mode) + { + struct acpi_drhd_unit *drhd; + struct pci_dev *pdev; +@@ -418,9 +419,9 @@ static int __must_check map_me_phantom_f + drhd = acpi_find_matched_drhd_unit(pdev); + + /* map or unmap ME phantom function */ +- if ( map ) ++ if ( !(mode & UNMAP_ME_PHANTOM_FUNC) ) + rc = domain_context_mapping_one(domain, drhd->iommu, 0, +- PCI_DEVFN(dev, 7), NULL); ++ PCI_DEVFN(dev, 7), NULL, mode); + else + rc = domain_context_unmap_one(domain, drhd->iommu, 0, + PCI_DEVFN(dev, 7)); +@@ -428,7 +429,8 @@ static int __must_check map_me_phantom_f + return rc; + } + +-int me_wifi_quirk(struct domain *domain, u8 bus, u8 devfn, int map) ++int me_wifi_quirk(struct domain *domain, uint8_t bus, uint8_t devfn, ++ unsigned int mode) + { + u32 id; + int rc = 0; +@@ -452,7 +454,7 @@ int me_wifi_quirk(struct domain *domain, + case 0x423b8086: + case 0x423c8086: + case 0x423d8086: +- rc = map_me_phantom_function(domain, 3, map); ++ rc = map_me_phantom_function(domain, 3, mode); + break; + default: + break; +@@ -478,7 +480,7 @@ int me_wifi_quirk(struct domain *domain, + case 0x42388086: /* Puma Peak */ + case 0x422b8086: + case 0x422c8086: +- rc = map_me_phantom_function(domain, 22, map); ++ rc = map_me_phantom_function(domain, 22, mode); + break; + default: + break; +--- a/xen/drivers/passthrough/vtd/vtd.h ++++ b/xen/drivers/passthrough/vtd/vtd.h +@@ -22,8 +22,14 @@ + + #include + +-#define MAP_ME_PHANTOM_FUNC 1 +-#define UNMAP_ME_PHANTOM_FUNC 0 ++/* ++ * Values for domain_context_mapping_one()'s and me_wifi_quirk()'s "mode" ++ * parameters. ++ */ ++#define MAP_WITH_RMRR (1u << 0) ++#define MAP_OWNER_DYING (1u << 1) ++#define MAP_SINGLE_DEVICE (1u << 2) ++#define UNMAP_ME_PHANTOM_FUNC (1u << 3) + + /* Allow for both IOAPIC and IOSAPIC. */ + #define IO_xAPIC_route_entry IO_APIC_route_entry diff --git a/xsa400-4.16-06.patch b/xsa400-4.16-06.patch new file mode 100644 index 0000000..dfde0ee --- /dev/null +++ b/xsa400-4.16-06.patch @@ -0,0 +1,330 @@ +From: Jan Beulich +Subject: AMD/IOMMU: re-assign devices directly + +Devices with unity map ranges, due to it being unspecified how/when +these memory ranges may get accessed, may not be left disconnected from +their unity mappings (as long as it's not certain that the device has +been fully quiesced). Hence rather than tearing down the old root page +table pointer and then establishing the new one, re-assignment needs to +be done in a single step. + +This is CVE-2022-26360 / part of XSA-400. + +Reported-by: Roger Pau Monné + +Similarly quarantining scratch-page mode relies on page tables to be +continuously wired up. + +To avoid complicating things more than necessary, treat all devices +mostly equally, i.e. regardless of their association with any unity map +ranges. The main difference is when it comes to updating DTEs, which need +to be atomic when there are unity mappings. Yet atomicity can only be +achieved with CMPXCHG16B, availability of which we can't take for given. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant +Reviewed-by: Roger Pau Monné + +--- a/xen/drivers/passthrough/amd/iommu.h ++++ b/xen/drivers/passthrough/amd/iommu.h +@@ -262,9 +262,13 @@ void amd_iommu_set_intremap_table(struct + const void *ptr, + const struct amd_iommu *iommu, + bool valid); +-void amd_iommu_set_root_page_table(struct amd_iommu_dte *dte, +- uint64_t root_ptr, uint16_t domain_id, +- uint8_t paging_mode, bool valid); ++#define SET_ROOT_VALID (1u << 0) ++#define SET_ROOT_WITH_UNITY_MAP (1u << 1) ++int __must_check amd_iommu_set_root_page_table(struct amd_iommu_dte *dte, ++ uint64_t root_ptr, ++ uint16_t domain_id, ++ uint8_t paging_mode, ++ unsigned int flags); + void iommu_dte_add_device_entry(struct amd_iommu_dte *dte, + const struct ivrs_mappings *ivrs_dev); + +--- a/xen/drivers/passthrough/amd/iommu_map.c ++++ b/xen/drivers/passthrough/amd/iommu_map.c +@@ -114,10 +114,69 @@ static unsigned int set_iommu_ptes_prese + return flush_flags; + } + +-void amd_iommu_set_root_page_table(struct amd_iommu_dte *dte, +- uint64_t root_ptr, uint16_t domain_id, +- uint8_t paging_mode, bool valid) ++/* ++ * This function returns ++ * - -errno for errors, ++ * - 0 for a successful update, atomic when necessary ++ * - 1 for a successful but non-atomic update, which may need to be warned ++ * about by the caller. ++ */ ++int amd_iommu_set_root_page_table(struct amd_iommu_dte *dte, ++ uint64_t root_ptr, uint16_t domain_id, ++ uint8_t paging_mode, unsigned int flags) + { ++ bool valid = flags & SET_ROOT_VALID; ++ ++ if ( dte->v && dte->tv && ++ (cpu_has_cx16 || (flags & SET_ROOT_WITH_UNITY_MAP)) ) ++ { ++ union { ++ struct amd_iommu_dte dte; ++ uint64_t raw64[4]; ++ __uint128_t raw128[2]; ++ } ldte = { .dte = *dte }; ++ __uint128_t old = ldte.raw128[0]; ++ int ret = 0; ++ ++ ldte.dte.domain_id = domain_id; ++ ldte.dte.pt_root = paddr_to_pfn(root_ptr); ++ ldte.dte.iw = true; ++ ldte.dte.ir = true; ++ ldte.dte.paging_mode = paging_mode; ++ ldte.dte.v = valid; ++ ++ if ( cpu_has_cx16 ) ++ { ++ __uint128_t res = cmpxchg16b(dte, &old, &ldte.raw128[0]); ++ ++ /* ++ * Hardware does not update the DTE behind our backs, so the ++ * return value should match "old". ++ */ ++ if ( res != old ) ++ { ++ printk(XENLOG_ERR ++ "Dom%d: unexpected DTE %016lx_%016lx (expected %016lx_%016lx)\n", ++ domain_id, ++ (uint64_t)(res >> 64), (uint64_t)res, ++ (uint64_t)(old >> 64), (uint64_t)old); ++ ret = -EILSEQ; ++ } ++ } ++ else /* Best effort, updating domain_id last. */ ++ { ++ uint64_t *ptr = (void *)dte; ++ ++ write_atomic(ptr + 0, ldte.raw64[0]); ++ /* No barrier should be needed between these two. */ ++ write_atomic(ptr + 1, ldte.raw64[1]); ++ ++ ret = 1; ++ } ++ ++ return ret; ++ } ++ + if ( valid || dte->v ) + { + dte->tv = false; +@@ -132,6 +191,8 @@ void amd_iommu_set_root_page_table(struc + smp_wmb(); + dte->tv = true; + dte->v = valid; ++ ++ return 0; + } + + void amd_iommu_set_intremap_table( +--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c ++++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c +@@ -96,13 +96,32 @@ static int __must_check allocate_domain_ + return rc; + } + ++static bool any_pdev_behind_iommu(const struct domain *d, ++ const struct pci_dev *exclude, ++ const struct amd_iommu *iommu) ++{ ++ const struct pci_dev *pdev; ++ ++ for_each_pdev ( d, pdev ) ++ { ++ if ( pdev == exclude ) ++ continue; ++ ++ if ( find_iommu_for_device(pdev->seg, pdev->sbdf.bdf) == iommu ) ++ return true; ++ } ++ ++ return false; ++} ++ + static int __must_check amd_iommu_setup_domain_device( + struct domain *domain, struct amd_iommu *iommu, + uint8_t devfn, struct pci_dev *pdev) + { + struct amd_iommu_dte *table, *dte; + unsigned long flags; +- int req_id, valid = 1, rc; ++ unsigned int req_id, sr_flags; ++ int rc; + u8 bus = pdev->bus; + struct domain_iommu *hd = dom_iommu(domain); + const struct ivrs_mappings *ivrs_dev; +@@ -116,8 +135,11 @@ static int __must_check amd_iommu_setup_ + if ( rc ) + return rc; + +- if ( iommu_hwdom_passthrough && is_hardware_domain(domain) ) +- valid = 0; ++ req_id = get_dma_requestor_id(iommu->seg, pdev->sbdf.bdf); ++ ivrs_dev = &get_ivrs_mappings(iommu->seg)[req_id]; ++ sr_flags = (iommu_hwdom_passthrough && is_hardware_domain(domain) ++ ? 0 : SET_ROOT_VALID) ++ | (ivrs_dev->unity_map ? SET_ROOT_WITH_UNITY_MAP : 0); + + /* get device-table entry */ + req_id = get_dma_requestor_id(iommu->seg, PCI_BDF2(bus, devfn)); +@@ -130,9 +152,15 @@ static int __must_check amd_iommu_setup_ + if ( !dte->v || !dte->tv ) + { + /* bind DTE to domain page-tables */ +- amd_iommu_set_root_page_table( +- dte, page_to_maddr(hd->arch.amd.root_table), +- domain->domain_id, hd->arch.amd.paging_mode, valid); ++ rc = amd_iommu_set_root_page_table( ++ dte, page_to_maddr(hd->arch.amd.root_table), ++ domain->domain_id, hd->arch.amd.paging_mode, sr_flags); ++ if ( rc ) ++ { ++ ASSERT(rc < 0); ++ spin_unlock_irqrestore(&iommu->lock, flags); ++ return rc; ++ } + + /* Undo what amd_iommu_disable_domain_device() may have done. */ + if ( dte->it_root ) +@@ -152,17 +180,76 @@ static int __must_check amd_iommu_setup_ + spin_unlock_irqrestore(&iommu->lock, flags); + + amd_iommu_flush_device(iommu, req_id); ++ } ++ else if ( dte->pt_root != mfn_x(page_to_mfn(hd->arch.amd.root_table)) ) ++ { ++ /* ++ * Strictly speaking if the device is the only one with this requestor ++ * ID, it could be allowed to be re-assigned regardless of unity map ++ * presence. But let's deal with that case only if it is actually ++ * found in the wild. ++ */ ++ if ( req_id != PCI_BDF2(bus, devfn) && ++ (sr_flags & SET_ROOT_WITH_UNITY_MAP) ) ++ rc = -EOPNOTSUPP; ++ else ++ rc = amd_iommu_set_root_page_table( ++ dte, page_to_maddr(hd->arch.amd.root_table), ++ domain->domain_id, hd->arch.amd.paging_mode, sr_flags); ++ if ( rc < 0 ) ++ { ++ spin_unlock_irqrestore(&iommu->lock, flags); ++ return rc; ++ } ++ if ( rc && ++ domain != pdev->domain && ++ /* ++ * By non-atomically updating the DTE's domain ID field last, ++ * during a short window in time TLB entries with the old domain ++ * ID but the new page tables may have been inserted. This could ++ * affect I/O of other devices using this same (old) domain ID. ++ * Such updating therefore is not a problem if this was the only ++ * device associated with the old domain ID. Diverting I/O of any ++ * of a dying domain's devices to the quarantine page tables is ++ * intended anyway. ++ */ ++ !pdev->domain->is_dying && ++ (any_pdev_behind_iommu(pdev->domain, pdev, iommu) || ++ pdev->phantom_stride) ) ++ AMD_IOMMU_WARN(" %pp: reassignment may cause %pd data corruption\n", ++ &PCI_SBDF3(pdev->seg, bus, devfn), pdev->domain); ++ ++ /* ++ * Check remaining settings are still in place from an earlier call ++ * here. They're all independent of the domain, so should not have ++ * changed. ++ */ ++ if ( dte->it_root ) ++ ASSERT(dte->int_ctl == IOMMU_DEV_TABLE_INT_CONTROL_TRANSLATED); ++ ASSERT(dte->iv == iommu_intremap); ++ ASSERT(dte->ex == ivrs_dev->dte_allow_exclusion); ++ ASSERT(dte->sys_mgt == MASK_EXTR(ivrs_dev->device_flags, ++ ACPI_IVHD_SYSTEM_MGMT)); + +- AMD_IOMMU_DEBUG("Setup I/O page table: device id = %#x, type = %#x, " +- "root table = %#"PRIx64", " +- "domain = %d, paging mode = %d\n", +- req_id, pdev->type, +- page_to_maddr(hd->arch.amd.root_table), +- domain->domain_id, hd->arch.amd.paging_mode); ++ if ( pci_ats_device(iommu->seg, bus, pdev->devfn) && ++ !ivrs_dev->block_ats && ++ iommu_has_cap(iommu, PCI_CAP_IOTLB_SHIFT) ) ++ ASSERT(dte->i == ats_enabled); ++ ++ spin_unlock_irqrestore(&iommu->lock, flags); ++ ++ amd_iommu_flush_device(iommu, req_id); + } + else + spin_unlock_irqrestore(&iommu->lock, flags); + ++ AMD_IOMMU_DEBUG("Setup I/O page table: device id = %#x, type = %#x, " ++ "root table = %#"PRIx64", " ++ "domain = %d, paging mode = %d\n", ++ req_id, pdev->type, ++ page_to_maddr(hd->arch.amd.root_table), ++ domain->domain_id, hd->arch.amd.paging_mode); ++ + ASSERT(pcidevs_locked()); + + if ( pci_ats_device(iommu->seg, bus, pdev->devfn) && +@@ -366,7 +453,20 @@ static int reassign_device(struct domain + return -ENODEV; + } + +- amd_iommu_disable_domain_device(source, iommu, devfn, pdev); ++ if ( !QUARANTINE_SKIP(target) ) ++ { ++ rc = amd_iommu_setup_domain_device(target, iommu, devfn, pdev); ++ if ( rc ) ++ return rc; ++ } ++ else ++ amd_iommu_disable_domain_device(source, iommu, devfn, pdev); ++ ++ if ( devfn == pdev->devfn && pdev->domain != target ) ++ { ++ list_move(&pdev->domain_list, &target->pdev_list); ++ pdev->domain = target; ++ } + + /* + * If the device belongs to the hardware domain, and it has a unity mapping, +@@ -382,25 +482,9 @@ static int reassign_device(struct domain + return rc; + } + +- if ( devfn == pdev->devfn && pdev->domain != dom_io ) +- { +- list_move(&pdev->domain_list, &dom_io->pdev_list); +- pdev->domain = dom_io; +- } +- +- rc = amd_iommu_setup_domain_device(target, iommu, devfn, pdev); +- if ( rc ) +- return rc; +- + AMD_IOMMU_DEBUG("Re-assign %pp from dom%d to dom%d\n", + &pdev->sbdf, source->domain_id, target->domain_id); + +- if ( devfn == pdev->devfn && pdev->domain != target ) +- { +- list_move(&pdev->domain_list, &target->pdev_list); +- pdev->domain = target; +- } +- + return 0; + } + diff --git a/xsa400-4.16-07.patch b/xsa400-4.16-07.patch new file mode 100644 index 0000000..8af34d5 --- /dev/null +++ b/xsa400-4.16-07.patch @@ -0,0 +1,333 @@ +From: Jan Beulich +Subject: VT-d: prepare for per-device quarantine page tables (part I) + +Arrange for domain ID and page table root to be passed around, the latter in +particular to domain_pgd_maddr() such that taking it from the per-domain +fields can be overridden. + +No functional change intended. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant +Reviewed-by: Roger Pau Monné +Reviewed-by: Kevin Tian + +--- a/xen/drivers/passthrough/vtd/extern.h ++++ b/xen/drivers/passthrough/vtd/extern.h +@@ -85,9 +85,10 @@ void *map_vtd_domain_page(u64 maddr); + void unmap_vtd_domain_page(const void *va); + int domain_context_mapping_one(struct domain *domain, struct vtd_iommu *iommu, + uint8_t bus, uint8_t devfn, +- const struct pci_dev *pdev, unsigned int mode); ++ const struct pci_dev *pdev, domid_t domid, ++ paddr_t pgd_maddr, unsigned int mode); + int domain_context_unmap_one(struct domain *domain, struct vtd_iommu *iommu, +- u8 bus, u8 devfn); ++ uint8_t bus, uint8_t devfn, domid_t domid); + int intel_iommu_get_reserved_device_memory(iommu_grdm_t *func, void *ctxt); + + unsigned int io_apic_read_remap_rte(unsigned int apic, unsigned int reg); +@@ -106,7 +107,8 @@ void platform_quirks_init(void); + void vtd_ops_preamble_quirk(struct vtd_iommu *iommu); + void vtd_ops_postamble_quirk(struct vtd_iommu *iommu); + int __must_check me_wifi_quirk(struct domain *domain, uint8_t bus, +- uint8_t devfn, unsigned int mode); ++ uint8_t devfn, domid_t domid, paddr_t pgd_maddr, ++ unsigned int mode); + void pci_vtd_quirk(const struct pci_dev *); + void quirk_iommu_caps(struct vtd_iommu *iommu); + +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -43,7 +43,7 @@ + #include "../ats.h" + + /* dom_io is used as a sentinel for quarantined devices */ +-#define QUARANTINE_SKIP(d) ((d) == dom_io && !dom_iommu(d)->arch.vtd.pgd_maddr) ++#define QUARANTINE_SKIP(d, pgd_maddr) ((d) == dom_io && !(pgd_maddr)) + + /* Possible unfiltered LAPIC/MSI messages from untrusted sources? */ + bool __read_mostly untrusted_msi; +@@ -358,15 +358,17 @@ static u64 addr_to_dma_page_maddr(struct + return pte_maddr; + } + +-static uint64_t domain_pgd_maddr(struct domain *d, unsigned int nr_pt_levels) ++static paddr_t domain_pgd_maddr(struct domain *d, paddr_t pgd_maddr, ++ unsigned int nr_pt_levels) + { + struct domain_iommu *hd = dom_iommu(d); +- uint64_t pgd_maddr; + unsigned int agaw; + + ASSERT(spin_is_locked(&hd->arch.mapping_lock)); + +- if ( iommu_use_hap_pt(d) ) ++ if ( pgd_maddr ) ++ /* nothing */; ++ else if ( iommu_use_hap_pt(d) ) + { + pagetable_t pgt = p2m_get_pagetable(p2m_get_hostp2m(d)); + +@@ -1385,18 +1387,18 @@ int domain_context_mapping_one( + struct domain *domain, + struct vtd_iommu *iommu, + uint8_t bus, uint8_t devfn, const struct pci_dev *pdev, +- unsigned int mode) ++ domid_t domid, paddr_t pgd_maddr, unsigned int mode) + { + struct domain_iommu *hd = dom_iommu(domain); + struct context_entry *context, *context_entries, lctxt; + __uint128_t old; +- u64 maddr, pgd_maddr; ++ uint64_t maddr; + uint16_t seg = iommu->drhd->segment, prev_did = 0; + struct domain *prev_dom = NULL; + int rc, ret; + bool_t flush_dev_iotlb; + +- if ( QUARANTINE_SKIP(domain) ) ++ if ( QUARANTINE_SKIP(domain, pgd_maddr) ) + return 0; + + ASSERT(pcidevs_locked()); +@@ -1433,10 +1435,12 @@ int domain_context_mapping_one( + } + else + { ++ paddr_t root; ++ + spin_lock(&hd->arch.mapping_lock); + +- pgd_maddr = domain_pgd_maddr(domain, iommu->nr_pt_levels); +- if ( !pgd_maddr ) ++ root = domain_pgd_maddr(domain, pgd_maddr, iommu->nr_pt_levels); ++ if ( !root ) + { + spin_unlock(&hd->arch.mapping_lock); + spin_unlock(&iommu->lock); +@@ -1446,7 +1450,7 @@ int domain_context_mapping_one( + return -ENOMEM; + } + +- context_set_address_root(lctxt, pgd_maddr); ++ context_set_address_root(lctxt, root); + if ( ats_enabled && ecap_dev_iotlb(iommu->ecap) ) + context_set_translation_type(lctxt, CONTEXT_TT_DEV_IOTLB); + else +@@ -1562,15 +1566,21 @@ int domain_context_mapping_one( + unmap_vtd_domain_page(context_entries); + + if ( !seg && !rc ) +- rc = me_wifi_quirk(domain, bus, devfn, mode); ++ rc = me_wifi_quirk(domain, bus, devfn, domid, pgd_maddr, mode); + + if ( rc ) + { + if ( !prev_dom ) +- ret = domain_context_unmap_one(domain, iommu, bus, devfn); ++ ret = domain_context_unmap_one(domain, iommu, bus, devfn, ++ domain->domain_id); + else if ( prev_dom != domain ) /* Avoid infinite recursion. */ ++ { ++ hd = dom_iommu(prev_dom); + ret = domain_context_mapping_one(prev_dom, iommu, bus, devfn, pdev, ++ domain->domain_id, ++ hd->arch.vtd.pgd_maddr, + mode & MAP_WITH_RMRR) < 0; ++ } + else + ret = 1; + +@@ -1592,6 +1602,7 @@ static int domain_context_mapping(struct + { + const struct acpi_drhd_unit *drhd = acpi_find_matched_drhd_unit(pdev); + const struct acpi_rmrr_unit *rmrr; ++ paddr_t pgd_maddr = dom_iommu(domain)->arch.vtd.pgd_maddr; + int ret = 0; + unsigned int i, mode = 0; + uint16_t seg = pdev->seg, bdf; +@@ -1654,7 +1665,8 @@ static int domain_context_mapping(struct + printk(VTDPREFIX "%pd:PCIe: map %pp\n", + domain, &PCI_SBDF3(seg, bus, devfn)); + ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn, +- pdev, mode); ++ pdev, domain->domain_id, pgd_maddr, ++ mode); + if ( ret > 0 ) + ret = 0; + if ( !ret && devfn == pdev->devfn && ats_device(pdev, drhd) > 0 ) +@@ -1671,7 +1683,8 @@ static int domain_context_mapping(struct + domain, &PCI_SBDF3(seg, bus, devfn)); + + ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn, +- pdev, mode); ++ pdev, domain->domain_id, pgd_maddr, ++ mode); + if ( ret < 0 ) + break; + prev_present = ret; +@@ -1699,7 +1712,8 @@ static int domain_context_mapping(struct + */ + if ( ret >= 0 ) + ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn, +- NULL, mode); ++ NULL, domain->domain_id, pgd_maddr, ++ mode); + + /* + * Devices behind PCIe-to-PCI/PCIx bridge may generate different +@@ -1714,7 +1728,8 @@ static int domain_context_mapping(struct + if ( !ret && pdev_type(seg, bus, devfn) == DEV_TYPE_PCIe2PCI_BRIDGE && + (secbus != pdev->bus || pdev->devfn != 0) ) + ret = domain_context_mapping_one(domain, drhd->iommu, secbus, 0, +- NULL, mode); ++ NULL, domain->domain_id, pgd_maddr, ++ mode); + + if ( ret ) + { +@@ -1742,14 +1757,14 @@ static int domain_context_mapping(struct + int domain_context_unmap_one( + struct domain *domain, + struct vtd_iommu *iommu, +- u8 bus, u8 devfn) ++ uint8_t bus, uint8_t devfn, domid_t domid) + { + struct context_entry *context, *context_entries; + u64 maddr; + int iommu_domid, rc, ret; + bool_t flush_dev_iotlb; + +- if ( QUARANTINE_SKIP(domain) ) ++ if ( QUARANTINE_SKIP(domain, dom_iommu(domain)->arch.vtd.pgd_maddr) ) + return 0; + + ASSERT(pcidevs_locked()); +@@ -1803,7 +1818,7 @@ int domain_context_unmap_one( + unmap_vtd_domain_page(context_entries); + + if ( !iommu->drhd->segment && !rc ) +- rc = me_wifi_quirk(domain, bus, devfn, UNMAP_ME_PHANTOM_FUNC); ++ rc = me_wifi_quirk(domain, bus, devfn, domid, 0, UNMAP_ME_PHANTOM_FUNC); + + if ( rc && !is_hardware_domain(domain) && domain != dom_io ) + { +@@ -1850,7 +1865,8 @@ static int domain_context_unmap(struct d + if ( iommu_debug ) + printk(VTDPREFIX "%pd:PCIe: unmap %pp\n", + domain, &PCI_SBDF3(seg, bus, devfn)); +- ret = domain_context_unmap_one(domain, iommu, bus, devfn); ++ ret = domain_context_unmap_one(domain, iommu, bus, devfn, ++ domain->domain_id); + if ( !ret && devfn == pdev->devfn && ats_device(pdev, drhd) > 0 ) + disable_ats_device(pdev); + +@@ -1863,7 +1879,8 @@ static int domain_context_unmap(struct d + if ( iommu_debug ) + printk(VTDPREFIX "%pd:PCI: unmap %pp\n", + domain, &PCI_SBDF3(seg, bus, devfn)); +- ret = domain_context_unmap_one(domain, iommu, bus, devfn); ++ ret = domain_context_unmap_one(domain, iommu, bus, devfn, ++ domain->domain_id); + if ( ret ) + break; + +@@ -1889,12 +1906,15 @@ static int domain_context_unmap(struct d + /* PCIe to PCI/PCIx bridge */ + if ( pdev_type(seg, tmp_bus, tmp_devfn) == DEV_TYPE_PCIe2PCI_BRIDGE ) + { +- ret = domain_context_unmap_one(domain, iommu, tmp_bus, tmp_devfn); ++ ret = domain_context_unmap_one(domain, iommu, tmp_bus, tmp_devfn, ++ domain->domain_id); + if ( !ret ) +- ret = domain_context_unmap_one(domain, iommu, secbus, 0); ++ ret = domain_context_unmap_one(domain, iommu, secbus, 0, ++ domain->domain_id); + } + else /* Legacy PCI bridge */ +- ret = domain_context_unmap_one(domain, iommu, tmp_bus, tmp_devfn); ++ ret = domain_context_unmap_one(domain, iommu, tmp_bus, tmp_devfn, ++ domain->domain_id); + + break; + +@@ -1904,7 +1924,8 @@ static int domain_context_unmap(struct d + return -EINVAL; + } + +- if ( !ret && !QUARANTINE_SKIP(domain) && pdev->devfn == devfn ) ++ if ( !ret && pdev->devfn == devfn && ++ !QUARANTINE_SKIP(domain, dom_iommu(domain)->arch.vtd.pgd_maddr) ) + check_cleanup_domid_map(domain, pdev, iommu); + + return ret; +@@ -2511,7 +2532,7 @@ static int reassign_device_ownership( + { + int ret; + +- if ( !QUARANTINE_SKIP(target) ) ++ if ( !QUARANTINE_SKIP(target, dom_iommu(target)->arch.vtd.pgd_maddr) ) + { + if ( !has_arch_pdevs(target) ) + vmx_pi_hooks_assign(target); +@@ -2526,7 +2547,8 @@ static int reassign_device_ownership( + + ret = domain_context_mapping(target, devfn, pdev); + +- if ( !ret && !QUARANTINE_SKIP(source) && pdev->devfn == devfn ) ++ if ( !ret && pdev->devfn == devfn && ++ !QUARANTINE_SKIP(source, dom_iommu(source)->arch.vtd.pgd_maddr) ) + { + const struct acpi_drhd_unit *drhd = acpi_find_matched_drhd_unit(pdev); + +--- a/xen/drivers/passthrough/vtd/quirks.c ++++ b/xen/drivers/passthrough/vtd/quirks.c +@@ -408,6 +408,8 @@ void __init platform_quirks_init(void) + + static int __must_check map_me_phantom_function(struct domain *domain, + unsigned int dev, ++ domid_t domid, ++ paddr_t pgd_maddr, + unsigned int mode) + { + struct acpi_drhd_unit *drhd; +@@ -421,16 +423,17 @@ static int __must_check map_me_phantom_f + /* map or unmap ME phantom function */ + if ( !(mode & UNMAP_ME_PHANTOM_FUNC) ) + rc = domain_context_mapping_one(domain, drhd->iommu, 0, +- PCI_DEVFN(dev, 7), NULL, mode); ++ PCI_DEVFN(dev, 7), NULL, ++ domid, pgd_maddr, mode); + else + rc = domain_context_unmap_one(domain, drhd->iommu, 0, +- PCI_DEVFN(dev, 7)); ++ PCI_DEVFN(dev, 7), domid); + + return rc; + } + + int me_wifi_quirk(struct domain *domain, uint8_t bus, uint8_t devfn, +- unsigned int mode) ++ domid_t domid, paddr_t pgd_maddr, unsigned int mode) + { + u32 id; + int rc = 0; +@@ -454,7 +457,7 @@ int me_wifi_quirk(struct domain *domain, + case 0x423b8086: + case 0x423c8086: + case 0x423d8086: +- rc = map_me_phantom_function(domain, 3, mode); ++ rc = map_me_phantom_function(domain, 3, domid, pgd_maddr, mode); + break; + default: + break; +@@ -480,7 +483,7 @@ int me_wifi_quirk(struct domain *domain, + case 0x42388086: /* Puma Peak */ + case 0x422b8086: + case 0x422c8086: +- rc = map_me_phantom_function(domain, 22, mode); ++ rc = map_me_phantom_function(domain, 22, domid, pgd_maddr, mode); + break; + default: + break; diff --git a/xsa400-4.16-08.patch b/xsa400-4.16-08.patch new file mode 100644 index 0000000..56a2b32 --- /dev/null +++ b/xsa400-4.16-08.patch @@ -0,0 +1,144 @@ +From: Jan Beulich +Subject: VT-d: prepare for per-device quarantine page tables (part II) + +Replace the passing of struct domain * by domid_t in preparation of +per-device quarantine page tables also requiring per-device pseudo +domain IDs, which aren't going to be associated with any struct domain +instances. + +No functional change intended (except for slightly adjusted log message +text). + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant +Reviewed-by: Kevin Tian +Reviewed-by: Roger Pau Monné + +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -62,8 +62,8 @@ static struct tasklet vtd_fault_tasklet; + static int setup_hwdom_device(u8 devfn, struct pci_dev *); + static void setup_hwdom_rmrr(struct domain *d); + +-static int domain_iommu_domid(struct domain *d, +- struct vtd_iommu *iommu) ++static int get_iommu_did(domid_t domid, const struct vtd_iommu *iommu, ++ bool warn) + { + unsigned long nr_dom, i; + +@@ -71,16 +71,16 @@ static int domain_iommu_domid(struct dom + i = find_first_bit(iommu->domid_bitmap, nr_dom); + while ( i < nr_dom ) + { +- if ( iommu->domid_map[i] == d->domain_id ) ++ if ( iommu->domid_map[i] == domid ) + return i; + + i = find_next_bit(iommu->domid_bitmap, nr_dom, i+1); + } + +- if ( !d->is_dying ) ++ if ( warn ) + dprintk(XENLOG_ERR VTDPREFIX, +- "Cannot get valid iommu %u domid: %pd\n", +- iommu->index, d); ++ "No valid iommu %u domid for Dom%d\n", ++ iommu->index, domid); + + return -1; + } +@@ -88,8 +88,7 @@ static int domain_iommu_domid(struct dom + #define DID_FIELD_WIDTH 16 + #define DID_HIGH_OFFSET 8 + static int context_set_domain_id(struct context_entry *context, +- struct domain *d, +- struct vtd_iommu *iommu) ++ domid_t domid, struct vtd_iommu *iommu) + { + unsigned long nr_dom, i; + int found = 0; +@@ -100,7 +99,7 @@ static int context_set_domain_id(struct + i = find_first_bit(iommu->domid_bitmap, nr_dom); + while ( i < nr_dom ) + { +- if ( iommu->domid_map[i] == d->domain_id ) ++ if ( iommu->domid_map[i] == domid ) + { + found = 1; + break; +@@ -116,7 +115,7 @@ static int context_set_domain_id(struct + dprintk(XENLOG_ERR VTDPREFIX, "IOMMU: no free domain ids\n"); + return -EFAULT; + } +- iommu->domid_map[i] = d->domain_id; ++ iommu->domid_map[i] = domid; + } + + set_bit(i, iommu->domid_bitmap); +@@ -125,9 +124,9 @@ static int context_set_domain_id(struct + return 0; + } + +-static void cleanup_domid_map(struct domain *domain, struct vtd_iommu *iommu) ++static void cleanup_domid_map(domid_t domid, struct vtd_iommu *iommu) + { +- int iommu_domid = domain_iommu_domid(domain, iommu); ++ int iommu_domid = get_iommu_did(domid, iommu, false); + + if ( iommu_domid >= 0 ) + { +@@ -167,7 +166,7 @@ static bool any_pdev_behind_iommu(const + * If no other devices under the same iommu owned by this domain, + * clear iommu in iommu_bitmap and clear domain_id in domid_bitmap. + */ +-static void check_cleanup_domid_map(struct domain *d, ++static void check_cleanup_domid_map(const struct domain *d, + const struct pci_dev *exclude, + struct vtd_iommu *iommu) + { +@@ -183,7 +182,7 @@ static void check_cleanup_domid_map(stru + if ( !found ) + { + clear_bit(iommu->index, dom_iommu(d)->arch.vtd.iommu_bitmap); +- cleanup_domid_map(d, iommu); ++ cleanup_domid_map(d->domain_id, iommu); + } + } + +@@ -683,7 +682,7 @@ static int __must_check iommu_flush_iotl + continue; + + flush_dev_iotlb = !!find_ats_dev_drhd(iommu); +- iommu_domid= domain_iommu_domid(d, iommu); ++ iommu_domid = get_iommu_did(d->domain_id, iommu, !d->is_dying); + if ( iommu_domid == -1 ) + continue; + +@@ -1459,7 +1458,7 @@ int domain_context_mapping_one( + spin_unlock(&hd->arch.mapping_lock); + } + +- if ( context_set_domain_id(&lctxt, domain, iommu) ) ++ if ( context_set_domain_id(&lctxt, domid, iommu) ) + { + unlock: + spin_unlock(&iommu->lock); +@@ -1785,7 +1784,7 @@ int domain_context_unmap_one( + context_clear_entry(*context); + iommu_sync_cache(context, sizeof(struct context_entry)); + +- iommu_domid= domain_iommu_domid(domain, iommu); ++ iommu_domid = get_iommu_did(domid, iommu, !domain->is_dying); + if ( iommu_domid == -1 ) + { + spin_unlock(&iommu->lock); +@@ -1953,7 +1952,7 @@ static void iommu_domain_teardown(struct + ASSERT(!hd->arch.vtd.pgd_maddr); + + for_each_drhd_unit ( drhd ) +- cleanup_domid_map(d, drhd->iommu); ++ cleanup_domid_map(d->domain_id, drhd->iommu); + + XFREE(hd->arch.vtd.iommu_bitmap); + } diff --git a/xsa400-4.16-09.patch b/xsa400-4.16-09.patch new file mode 100644 index 0000000..5cd5732 --- /dev/null +++ b/xsa400-4.16-09.patch @@ -0,0 +1,447 @@ +From: Jan Beulich +Subject: IOMMU/x86: maintain a per-device pseudo domain ID + +In order to subsequently enable per-device quarantine page tables, we'll +need domain-ID-like identifiers to be inserted in the respective device +(AMD) or context (Intel) table entries alongside the per-device page +table root addresses. + +Make use of "real" domain IDs occupying only half of the value range +coverable by domid_t. + +Note that in VT-d's iommu_alloc() I didn't want to introduce new memory +leaks in case of error, but existing ones don't get plugged - that'll be +the subject of a later change. + +The VT-d changes are slightly asymmetric, but this way we can avoid +assigning pseudo domain IDs to devices which would never be mapped while +still avoiding to add a new parameter to domain_context_unmap(). + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant +Reviewed-by: Kevin Tian +Reviewed-by: Roger Pau Monné + +--- a/xen/include/asm-x86/iommu.h ++++ b/xen/include/asm-x86/iommu.h +@@ -141,6 +141,10 @@ int pi_update_irte(const struct pi_desc + iommu_vcall(ops, sync_cache, addr, size); \ + }) + ++unsigned long *iommu_init_domid(void); ++domid_t iommu_alloc_domid(unsigned long *map); ++void iommu_free_domid(domid_t domid, unsigned long *map); ++ + int __must_check iommu_free_pgtables(struct domain *d); + struct domain_iommu; + struct page_info *__must_check iommu_alloc_pgtable(struct domain_iommu *hd); +--- a/xen/include/asm-x86/pci.h ++++ b/xen/include/asm-x86/pci.h +@@ -13,6 +13,12 @@ + + struct arch_pci_dev { + vmask_t used_vectors; ++ /* ++ * These fields are (de)initialized under pcidevs-lock. Other uses of ++ * them don't race (de)initialization and hence don't strictly need any ++ * locking. ++ */ ++ domid_t pseudo_domid; + }; + + int pci_conf_write_intercept(unsigned int seg, unsigned int bdf, +@@ -36,6 +42,6 @@ static always_inline bool is_pci_passthr + return true; + } + +-static inline void arch_pci_init_pdev(struct pci_dev *pdev) {} ++void arch_pci_init_pdev(struct pci_dev *pdev); + + #endif /* __X86_PCI_H__ */ +--- a/xen/drivers/passthrough/amd/iommu.h ++++ b/xen/drivers/passthrough/amd/iommu.h +@@ -96,6 +96,7 @@ struct amd_iommu { + struct ring_buffer cmd_buffer; + struct ring_buffer event_log; + struct ring_buffer ppr_log; ++ unsigned long *domid_map; + + int exclusion_enable; + int exclusion_allow_all; +--- a/xen/drivers/passthrough/amd/iommu_detect.c ++++ b/xen/drivers/passthrough/amd/iommu_detect.c +@@ -223,6 +223,11 @@ int __init amd_iommu_detect_one_acpi( + if ( rt ) + goto out; + ++ iommu->domid_map = iommu_init_domid(); ++ rt = -ENOMEM; ++ if ( !iommu->domid_map ) ++ goto out; ++ + rt = pci_ro_device(iommu->seg, bus, PCI_DEVFN(dev, func)); + if ( rt ) + printk(XENLOG_ERR "Could not mark config space of %pp read-only (%d)\n", +@@ -233,7 +238,10 @@ int __init amd_iommu_detect_one_acpi( + + out: + if ( rt ) ++ { ++ xfree(iommu->domid_map); + xfree(iommu); ++ } + + return rt; + } +--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c ++++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c +@@ -539,6 +539,8 @@ static int amd_iommu_add_device(u8 devfn + struct amd_iommu *iommu; + u16 bdf; + struct ivrs_mappings *ivrs_mappings; ++ bool fresh_domid = false; ++ int ret; + + if ( !pdev->domain ) + return -EINVAL; +@@ -606,7 +608,22 @@ static int amd_iommu_add_device(u8 devfn + AMD_IOMMU_WARN("%pd: unity mapping failed for %pp\n", + pdev->domain, &pdev->sbdf); + +- return amd_iommu_setup_domain_device(pdev->domain, iommu, devfn, pdev); ++ if ( iommu_quarantine && pdev->arch.pseudo_domid == DOMID_INVALID ) ++ { ++ pdev->arch.pseudo_domid = iommu_alloc_domid(iommu->domid_map); ++ if ( pdev->arch.pseudo_domid == DOMID_INVALID ) ++ return -ENOSPC; ++ fresh_domid = true; ++ } ++ ++ ret = amd_iommu_setup_domain_device(pdev->domain, iommu, devfn, pdev); ++ if ( ret && fresh_domid ) ++ { ++ iommu_free_domid(pdev->arch.pseudo_domid, iommu->domid_map); ++ pdev->arch.pseudo_domid = DOMID_INVALID; ++ } ++ ++ return ret; + } + + static int amd_iommu_remove_device(u8 devfn, struct pci_dev *pdev) +@@ -638,6 +655,9 @@ static int amd_iommu_remove_device(u8 de + AMD_IOMMU_WARN("%pd: unity unmapping failed for %pp\n", + pdev->domain, &pdev->sbdf); + ++ iommu_free_domid(pdev->arch.pseudo_domid, iommu->domid_map); ++ pdev->arch.pseudo_domid = DOMID_INVALID; ++ + if ( amd_iommu_perdev_intremap && + ivrs_mappings[bdf].dte_requestor_id == bdf && + ivrs_mappings[bdf].intremap_table ) +--- a/xen/drivers/passthrough/pci.c ++++ b/xen/drivers/passthrough/pci.c +@@ -1271,9 +1271,14 @@ static int _dump_pci_devices(struct pci_ + + list_for_each_entry ( pdev, &pseg->alldevs_list, alldevs_list ) + { +- printk("%pp - %pd - node %-3d", +- &pdev->sbdf, pdev->domain, +- (pdev->node != NUMA_NO_NODE) ? pdev->node : -1); ++ printk("%pp - ", &pdev->sbdf); ++#ifdef CONFIG_X86 ++ if ( pdev->domain == dom_io ) ++ printk("DomIO:%x", pdev->arch.pseudo_domid); ++ else ++#endif ++ printk("%pd", pdev->domain); ++ printk(" - node %-3d", (pdev->node != NUMA_NO_NODE) ? pdev->node : -1); + pdev_dump_msi(pdev); + printk("\n"); + } +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1215,7 +1216,7 @@ int __init iommu_alloc(struct acpi_drhd_ + { + struct vtd_iommu *iommu; + unsigned long sagaw, nr_dom; +- int agaw; ++ int agaw, rc; + + iommu = xzalloc(struct vtd_iommu); + if ( iommu == NULL ) +@@ -1301,7 +1302,16 @@ int __init iommu_alloc(struct acpi_drhd_ + if ( !iommu->domid_map ) + return -ENOMEM; + ++ iommu->pseudo_domid_map = iommu_init_domid(); ++ rc = -ENOMEM; ++ if ( !iommu->pseudo_domid_map ) ++ goto free; ++ + return 0; ++ ++ free: ++ iommu_free(drhd); ++ return rc; + } + + void __init iommu_free(struct acpi_drhd_unit *drhd) +@@ -1324,6 +1334,7 @@ void __init iommu_free(struct acpi_drhd_ + + xfree(iommu->domid_bitmap); + xfree(iommu->domid_map); ++ xfree(iommu->pseudo_domid_map); + + if ( iommu->msi.irq >= 0 ) + destroy_irq(iommu->msi.irq); +@@ -1593,8 +1604,8 @@ int domain_context_mapping_one( + return rc ?: pdev && prev_dom; + } + +-static int domain_context_unmap(struct domain *d, uint8_t devfn, +- struct pci_dev *pdev); ++static const struct acpi_drhd_unit *domain_context_unmap( ++ struct domain *d, uint8_t devfn, struct pci_dev *pdev); + + static int domain_context_mapping(struct domain *domain, u8 devfn, + struct pci_dev *pdev) +@@ -1602,6 +1613,7 @@ static int domain_context_mapping(struct + const struct acpi_drhd_unit *drhd = acpi_find_matched_drhd_unit(pdev); + const struct acpi_rmrr_unit *rmrr; + paddr_t pgd_maddr = dom_iommu(domain)->arch.vtd.pgd_maddr; ++ domid_t orig_domid = pdev->arch.pseudo_domid; + int ret = 0; + unsigned int i, mode = 0; + uint16_t seg = pdev->seg, bdf; +@@ -1660,6 +1672,14 @@ static int domain_context_mapping(struct + if ( !drhd ) + return -ENODEV; + ++ if ( iommu_quarantine && orig_domid == DOMID_INVALID ) ++ { ++ pdev->arch.pseudo_domid = ++ iommu_alloc_domid(drhd->iommu->pseudo_domid_map); ++ if ( pdev->arch.pseudo_domid == DOMID_INVALID ) ++ return -ENOSPC; ++ } ++ + if ( iommu_debug ) + printk(VTDPREFIX "%pd:PCIe: map %pp\n", + domain, &PCI_SBDF3(seg, bus, devfn)); +@@ -1677,6 +1697,14 @@ static int domain_context_mapping(struct + if ( !drhd ) + return -ENODEV; + ++ if ( iommu_quarantine && orig_domid == DOMID_INVALID ) ++ { ++ pdev->arch.pseudo_domid = ++ iommu_alloc_domid(drhd->iommu->pseudo_domid_map); ++ if ( pdev->arch.pseudo_domid == DOMID_INVALID ) ++ return -ENOSPC; ++ } ++ + if ( iommu_debug ) + printk(VTDPREFIX "%pd:PCI: map %pp\n", + domain, &PCI_SBDF3(seg, bus, devfn)); +@@ -1750,6 +1778,13 @@ static int domain_context_mapping(struct + if ( !ret && devfn == pdev->devfn ) + pci_vtd_quirk(pdev); + ++ if ( ret && drhd && orig_domid == DOMID_INVALID ) ++ { ++ iommu_free_domid(pdev->arch.pseudo_domid, ++ drhd->iommu->pseudo_domid_map); ++ pdev->arch.pseudo_domid = DOMID_INVALID; ++ } ++ + return ret; + } + +@@ -1835,8 +1870,10 @@ int domain_context_unmap_one( + return rc; + } + +-static int domain_context_unmap(struct domain *domain, u8 devfn, +- struct pci_dev *pdev) ++static const struct acpi_drhd_unit *domain_context_unmap( ++ struct domain *domain, ++ uint8_t devfn, ++ struct pci_dev *pdev) + { + const struct acpi_drhd_unit *drhd = acpi_find_matched_drhd_unit(pdev); + struct vtd_iommu *iommu = drhd ? drhd->iommu : NULL; +@@ -1850,16 +1887,16 @@ static int domain_context_unmap(struct d + if ( iommu_debug ) + printk(VTDPREFIX "%pd:Hostbridge: skip %pp unmap\n", + domain, &PCI_SBDF3(seg, bus, devfn)); +- return is_hardware_domain(domain) ? 0 : -EPERM; ++ return ERR_PTR(is_hardware_domain(domain) ? 0 : -EPERM); + + case DEV_TYPE_PCIe_BRIDGE: + case DEV_TYPE_PCIe2PCI_BRIDGE: + case DEV_TYPE_LEGACY_PCI_BRIDGE: +- return 0; ++ return ERR_PTR(0); + + case DEV_TYPE_PCIe_ENDPOINT: + if ( !iommu ) +- return -ENODEV; ++ return ERR_PTR(-ENODEV); + + if ( iommu_debug ) + printk(VTDPREFIX "%pd:PCIe: unmap %pp\n", +@@ -1873,7 +1910,7 @@ static int domain_context_unmap(struct d + + case DEV_TYPE_PCI: + if ( !iommu ) +- return -ENODEV; ++ return ERR_PTR(-ENODEV); + + if ( iommu_debug ) + printk(VTDPREFIX "%pd:PCI: unmap %pp\n", +@@ -1920,14 +1957,14 @@ static int domain_context_unmap(struct d + default: + dprintk(XENLOG_ERR VTDPREFIX, "%pd:unknown(%u): %pp\n", + domain, pdev->type, &PCI_SBDF3(seg, bus, devfn)); +- return -EINVAL; ++ return ERR_PTR(-EINVAL); + } + + if ( !ret && pdev->devfn == devfn && + !QUARANTINE_SKIP(domain, dom_iommu(domain)->arch.vtd.pgd_maddr) ) + check_cleanup_domid_map(domain, pdev, iommu); + +- return ret; ++ return drhd; + } + + static void iommu_clear_root_pgtable(struct domain *d) +@@ -2154,16 +2191,17 @@ static int intel_iommu_enable_device(str + + static int intel_iommu_remove_device(u8 devfn, struct pci_dev *pdev) + { ++ const struct acpi_drhd_unit *drhd; + struct acpi_rmrr_unit *rmrr; + u16 bdf; +- int ret, i; ++ unsigned int i; + + if ( !pdev->domain ) + return -EINVAL; + +- ret = domain_context_unmap(pdev->domain, devfn, pdev); +- if ( ret ) +- return ret; ++ drhd = domain_context_unmap(pdev->domain, devfn, pdev); ++ if ( IS_ERR(drhd) ) ++ return PTR_ERR(drhd); + + for_each_rmrr_device ( rmrr, bdf, i ) + { +@@ -2180,6 +2218,13 @@ static int intel_iommu_remove_device(u8 + rmrr->end_address, 0); + } + ++ if ( drhd ) ++ { ++ iommu_free_domid(pdev->arch.pseudo_domid, ++ drhd->iommu->pseudo_domid_map); ++ pdev->arch.pseudo_domid = DOMID_INVALID; ++ } ++ + return 0; + } + +@@ -2556,7 +2601,12 @@ static int reassign_device_ownership( + } + } + else +- ret = domain_context_unmap(source, devfn, pdev); ++ { ++ const struct acpi_drhd_unit *drhd; ++ ++ drhd = domain_context_unmap(source, devfn, pdev); ++ ret = IS_ERR(drhd) ? PTR_ERR(drhd) : 0; ++ } + if ( ret ) + { + if ( !has_arch_pdevs(target) ) +--- a/xen/drivers/passthrough/vtd/iommu.h ++++ b/xen/drivers/passthrough/vtd/iommu.h +@@ -508,6 +508,7 @@ struct vtd_iommu { + } flush; + + struct list_head ats_devices; ++ unsigned long *pseudo_domid_map; /* "pseudo" domain id bitmap */ + unsigned long *domid_bitmap; /* domain id bitmap */ + u16 *domid_map; /* domain id mapping array */ + uint32_t version; +--- a/xen/drivers/passthrough/x86/iommu.c ++++ b/xen/drivers/passthrough/x86/iommu.c +@@ -387,6 +387,58 @@ void __hwdom_init arch_iommu_hwdom_init( + return; + } + ++void arch_pci_init_pdev(struct pci_dev *pdev) ++{ ++ pdev->arch.pseudo_domid = DOMID_INVALID; ++} ++ ++unsigned long *__init iommu_init_domid(void) ++{ ++ if ( !iommu_quarantine ) ++ return ZERO_BLOCK_PTR; ++ ++ BUILD_BUG_ON(DOMID_MASK * 2U >= UINT16_MAX); ++ ++ return xzalloc_array(unsigned long, ++ BITS_TO_LONGS(UINT16_MAX - DOMID_MASK)); ++} ++ ++domid_t iommu_alloc_domid(unsigned long *map) ++{ ++ /* ++ * This is used uniformly across all IOMMUs, such that on typical ++ * systems we wouldn't re-use the same ID very quickly (perhaps never). ++ */ ++ static unsigned int start; ++ unsigned int idx = find_next_zero_bit(map, UINT16_MAX - DOMID_MASK, start); ++ ++ ASSERT(pcidevs_locked()); ++ ++ if ( idx >= UINT16_MAX - DOMID_MASK ) ++ idx = find_first_zero_bit(map, UINT16_MAX - DOMID_MASK); ++ if ( idx >= UINT16_MAX - DOMID_MASK ) ++ return DOMID_INVALID; ++ ++ __set_bit(idx, map); ++ ++ start = idx + 1; ++ ++ return idx | (DOMID_MASK + 1); ++} ++ ++void iommu_free_domid(domid_t domid, unsigned long *map) ++{ ++ ASSERT(pcidevs_locked()); ++ ++ if ( domid == DOMID_INVALID ) ++ return; ++ ++ ASSERT(domid > DOMID_MASK); ++ ++ if ( !__test_and_clear_bit(domid & DOMID_MASK, map) ) ++ BUG(); ++} ++ + int iommu_free_pgtables(struct domain *d) + { + struct domain_iommu *hd = dom_iommu(d); diff --git a/xsa400-4.16-10.patch b/xsa400-4.16-10.patch new file mode 100644 index 0000000..4f5886e --- /dev/null +++ b/xsa400-4.16-10.patch @@ -0,0 +1,38 @@ +From: Jan Beulich +Subject: IOMMU/x86: drop TLB flushes from quarantine_init() hooks + +The page tables just created aren't hooked up yet anywhere, so there's +nothing that could be present in any TLB, and hence nothing to flush. +Dropping this flush is, at least on the VT-d side, a prereq to per- +device domain ID use when quarantining devices, as dom_io isn't going +to be assigned a DID anymore: The warning in get_iommu_did() would +trigger. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant +Reviewed-by: Roger Pau Monné +Reviewed-by: Kevin Tian + +--- a/xen/drivers/passthrough/amd/iommu_map.c ++++ b/xen/drivers/passthrough/amd/iommu_map.c +@@ -654,8 +654,6 @@ int __init amd_iommu_quarantine_init(str + out: + spin_unlock(&hd->arch.mapping_lock); + +- amd_iommu_flush_all_pages(d); +- + /* Pages leaked in failure case */ + return level ? -ENOMEM : 0; + } +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -2975,9 +2975,6 @@ static int __init intel_iommu_quarantine + out: + spin_unlock(&hd->arch.mapping_lock); + +- if ( !rc ) +- rc = iommu_flush_iotlb_all(d); +- + /* Pages may be leaked in failure case */ + return rc; + } diff --git a/xsa400-4.16-11.patch b/xsa400-4.16-11.patch new file mode 100644 index 0000000..831983c --- /dev/null +++ b/xsa400-4.16-11.patch @@ -0,0 +1,29 @@ +From: Jan Beulich +Subject: AMD/IOMMU: abstract maximum number of page table levels + +We will want to use the constant elsewhere. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/amd/iommu-defs.h ++++ b/xen/drivers/passthrough/amd/iommu-defs.h +@@ -106,6 +106,7 @@ struct amd_iommu_dte { + bool tv:1; + unsigned int :5; + unsigned int had:2; ++#define IOMMU_MAX_PT_LEVELS 6 + unsigned int paging_mode:3; + uint64_t pt_root:40; + bool ppr:1; +--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c ++++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c +@@ -337,7 +337,7 @@ int amd_iommu_alloc_root(struct domain * + return 0; + } + +-unsigned int __read_mostly amd_iommu_max_paging_mode = 6; ++unsigned int __read_mostly amd_iommu_max_paging_mode = IOMMU_MAX_PT_LEVELS; + int __read_mostly amd_iommu_min_paging_mode = 1; + + static int amd_iommu_domain_init(struct domain *d) diff --git a/xsa400-4.16-12.patch b/xsa400-4.16-12.patch new file mode 100644 index 0000000..b4836b1 --- /dev/null +++ b/xsa400-4.16-12.patch @@ -0,0 +1,945 @@ +From: Jan Beulich +Subject: IOMMU/x86: use per-device page tables for quarantining + +Devices with RMRRs / unity mapped regions, due to it being unspecified +how/when these memory regions may be accessed, may not be left +disconnected from the mappings of these regions (as long as it's not +certain that the device has been fully quiesced). Hence even the page +tables used when quarantining such devices need to have mappings of +those regions. This implies installing page tables in the first place +even when not in scratch-page quarantining mode. + +This is CVE-2022-26361 / part of XSA-400. + +While for the purpose here it would be sufficient to have devices with +RMRRs / unity mapped regions use per-device page tables, extend this to +all devices (in scratch-page quarantining mode). This allows the leaf +pages to be mapped r/w, thus covering also memory writes (rather than +just reads) issued by non-quiescent devices. + +Set up quarantine page tables as late as possible, yet early enough to +not encounter failure during de-assign. This means setup generally +happens in assign_device(), while (for now) the one in deassign_device() +is there mainly to be on the safe side. + +As to the removal of QUARANTINE_SKIP() from domain_context_unmap_one(): +I think this was never really needed there, as the function explicitly +deals with finding a non-present context entry. Leaving it there would +require propagating pgd_maddr into the function (like was done by "VT-d: +prepare for per-device quarantine page tables" for +domain_context_mapping_one()). + +In VT-d's DID allocation function don't require the IOMMU lock to be +held anymore: All involved code paths hold pcidevs_lock, so this way we +avoid the need to acquire the IOMMU lock around the new call to +context_set_domain_id(). + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant +Reviewed-by: Kevin Tian +Reviewed-by: Roger Pau Monné + +--- a/xen/include/asm-x86/pci.h ++++ b/xen/include/asm-x86/pci.h +@@ -1,6 +1,8 @@ + #ifndef __X86_PCI_H__ + #define __X86_PCI_H__ + ++#include ++ + #define CF8_BDF(cf8) ( ((cf8) & 0x00ffff00) >> 8) + #define CF8_ADDR_LO(cf8) ( (cf8) & 0x000000fc) + #define CF8_ADDR_HI(cf8) ( ((cf8) & 0x0f000000) >> 16) +@@ -18,7 +20,18 @@ struct arch_pci_dev { + * them don't race (de)initialization and hence don't strictly need any + * locking. + */ ++ union { ++ /* Subset of struct arch_iommu's fields, to be used in dom_io. */ ++ struct { ++ uint64_t pgd_maddr; ++ } vtd; ++ struct { ++ struct page_info *root_table; ++ } amd; ++ }; + domid_t pseudo_domid; ++ mfn_t leaf_mfn; ++ struct page_list_head pgtables_list; + }; + + int pci_conf_write_intercept(unsigned int seg, unsigned int bdf, +--- a/xen/drivers/passthrough/amd/iommu.h ++++ b/xen/drivers/passthrough/amd/iommu.h +@@ -237,7 +237,8 @@ int amd_iommu_init_late(void); + int amd_iommu_update_ivrs_mapping_acpi(void); + int iov_adjust_irq_affinities(void); + +-int amd_iommu_quarantine_init(struct domain *d); ++int amd_iommu_quarantine_init(struct pci_dev *pdev, bool scratch_page); ++void amd_iommu_quarantine_teardown(struct pci_dev *pdev); + + /* mapping functions */ + int __must_check amd_iommu_map_page(struct domain *d, dfn_t dfn, +--- a/xen/drivers/passthrough/amd/iommu_map.c ++++ b/xen/drivers/passthrough/amd/iommu_map.c +@@ -598,64 +598,138 @@ int amd_iommu_get_reserved_device_memory + return 0; + } + +-int __init amd_iommu_quarantine_init(struct domain *d) ++static int fill_qpt(union amd_iommu_pte *this, unsigned int level, ++ struct page_info *pgs[IOMMU_MAX_PT_LEVELS]) + { +- struct domain_iommu *hd = dom_iommu(d); ++ struct domain_iommu *hd = dom_iommu(dom_io); ++ unsigned int i; ++ int rc = 0; ++ ++ for ( i = 0; !rc && i < PTE_PER_TABLE_SIZE; ++i ) ++ { ++ union amd_iommu_pte *pte = &this[i], *next; ++ ++ if ( !pte->pr ) ++ { ++ if ( !pgs[level] ) ++ { ++ /* ++ * The pgtable allocator is fine for the leaf page, as well as ++ * page table pages, and the resulting allocations are always ++ * zeroed. ++ */ ++ pgs[level] = iommu_alloc_pgtable(hd); ++ if ( !pgs[level] ) ++ { ++ rc = -ENOMEM; ++ break; ++ } ++ ++ if ( level ) ++ { ++ next = __map_domain_page(pgs[level]); ++ rc = fill_qpt(next, level - 1, pgs); ++ unmap_domain_page(next); ++ } ++ } ++ ++ /* ++ * PDEs are essentially a subset of PTEs, so this function ++ * is fine to use even at the leaf. ++ */ ++ set_iommu_pde_present(pte, mfn_x(page_to_mfn(pgs[level])), level, ++ true, true); ++ } ++ else if ( level && pte->next_level ) ++ { ++ next = map_domain_page(_mfn(pte->mfn)); ++ rc = fill_qpt(next, level - 1, pgs); ++ unmap_domain_page(next); ++ } ++ } ++ ++ return rc; ++} ++ ++int amd_iommu_quarantine_init(struct pci_dev *pdev, bool scratch_page) ++{ ++ struct domain_iommu *hd = dom_iommu(dom_io); + unsigned long end_gfn = + 1ul << (DEFAULT_DOMAIN_ADDRESS_WIDTH - PAGE_SHIFT); + unsigned int level = amd_iommu_get_paging_mode(end_gfn); +- union amd_iommu_pte *table; ++ unsigned int req_id = get_dma_requestor_id(pdev->seg, pdev->sbdf.bdf); ++ const struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(pdev->seg); ++ int rc; ++ ++ ASSERT(pcidevs_locked()); ++ ASSERT(!hd->arch.amd.root_table); ++ ASSERT(page_list_empty(&hd->arch.pgtables.list)); + +- if ( hd->arch.amd.root_table ) +- { +- ASSERT_UNREACHABLE(); ++ if ( !scratch_page && !ivrs_mappings[req_id].unity_map ) + return 0; +- } + +- spin_lock(&hd->arch.mapping_lock); ++ ASSERT(pdev->arch.pseudo_domid != DOMID_INVALID); + +- hd->arch.amd.root_table = iommu_alloc_pgtable(hd); +- if ( !hd->arch.amd.root_table ) +- goto out; ++ if ( pdev->arch.amd.root_table ) ++ { ++ clear_domain_page(pdev->arch.leaf_mfn); ++ return 0; ++ } + +- table = __map_domain_page(hd->arch.amd.root_table); +- while ( level ) ++ pdev->arch.amd.root_table = iommu_alloc_pgtable(hd); ++ if ( !pdev->arch.amd.root_table ) ++ return -ENOMEM; ++ ++ /* Transiently install the root into DomIO, for iommu_identity_mapping(). */ ++ hd->arch.amd.root_table = pdev->arch.amd.root_table; ++ ++ rc = amd_iommu_reserve_domain_unity_map(dom_io, ++ ivrs_mappings[req_id].unity_map, ++ 0); ++ ++ iommu_identity_map_teardown(dom_io); ++ hd->arch.amd.root_table = NULL; ++ ++ if ( rc ) ++ AMD_IOMMU_WARN("%pp: quarantine unity mapping failed\n", &pdev->sbdf); ++ else if ( scratch_page ) + { +- struct page_info *pg; +- unsigned int i; ++ union amd_iommu_pte *root; ++ struct page_info *pgs[IOMMU_MAX_PT_LEVELS] = {}; + +- /* +- * The pgtable allocator is fine for the leaf page, as well as +- * page table pages, and the resulting allocations are always +- * zeroed. +- */ +- pg = iommu_alloc_pgtable(hd); +- if ( !pg ) +- break; ++ spin_lock(&hd->arch.mapping_lock); + +- for ( i = 0; i < PTE_PER_TABLE_SIZE; i++ ) +- { +- union amd_iommu_pte *pde = &table[i]; ++ root = __map_domain_page(pdev->arch.amd.root_table); ++ rc = fill_qpt(root, level - 1, pgs); ++ unmap_domain_page(root); + +- /* +- * PDEs are essentially a subset of PTEs, so this function +- * is fine to use even at the leaf. +- */ +- set_iommu_pde_present(pde, mfn_x(page_to_mfn(pg)), level - 1, +- false, true); +- } ++ pdev->arch.leaf_mfn = page_to_mfn(pgs[0]); + +- unmap_domain_page(table); +- table = __map_domain_page(pg); +- level--; ++ spin_unlock(&hd->arch.mapping_lock); + } +- unmap_domain_page(table); + +- out: +- spin_unlock(&hd->arch.mapping_lock); ++ page_list_move(&pdev->arch.pgtables_list, &hd->arch.pgtables.list); ++ ++ if ( rc ) ++ amd_iommu_quarantine_teardown(pdev); ++ ++ return rc; ++} ++ ++void amd_iommu_quarantine_teardown(struct pci_dev *pdev) ++{ ++ struct domain_iommu *hd = dom_iommu(dom_io); ++ ++ ASSERT(pcidevs_locked()); ++ ++ if ( !pdev->arch.amd.root_table ) ++ return; + +- /* Pages leaked in failure case */ +- return level ? -ENOMEM : 0; ++ ASSERT(page_list_empty(&hd->arch.pgtables.list)); ++ page_list_move(&hd->arch.pgtables.list, &pdev->arch.pgtables_list); ++ while ( iommu_free_pgtables(dom_io) == -ERESTART ) ++ /* nothing */; ++ pdev->arch.amd.root_table = NULL; + } + + /* +--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c ++++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c +@@ -26,7 +26,7 @@ + #include "../ats.h" + + /* dom_io is used as a sentinel for quarantined devices */ +-#define QUARANTINE_SKIP(d) ((d) == dom_io && !dom_iommu(d)->arch.amd.root_table) ++#define QUARANTINE_SKIP(d, p) ((d) == dom_io && !(p)->arch.amd.root_table) + + static bool_t __read_mostly init_done; + +@@ -125,8 +125,10 @@ static int __must_check amd_iommu_setup_ + u8 bus = pdev->bus; + struct domain_iommu *hd = dom_iommu(domain); + const struct ivrs_mappings *ivrs_dev; ++ const struct page_info *root_pg; ++ domid_t domid; + +- if ( QUARANTINE_SKIP(domain) ) ++ if ( QUARANTINE_SKIP(domain, pdev) ) + return 0; + + BUG_ON(!hd->arch.amd.paging_mode || !iommu->dev_table.buffer); +@@ -147,14 +149,25 @@ static int __must_check amd_iommu_setup_ + dte = &table[req_id]; + ivrs_dev = &get_ivrs_mappings(iommu->seg)[req_id]; + ++ if ( domain != dom_io ) ++ { ++ root_pg = hd->arch.amd.root_table; ++ domid = domain->domain_id; ++ } ++ else ++ { ++ root_pg = pdev->arch.amd.root_table; ++ domid = pdev->arch.pseudo_domid; ++ } ++ + spin_lock_irqsave(&iommu->lock, flags); + + if ( !dte->v || !dte->tv ) + { + /* bind DTE to domain page-tables */ + rc = amd_iommu_set_root_page_table( +- dte, page_to_maddr(hd->arch.amd.root_table), +- domain->domain_id, hd->arch.amd.paging_mode, sr_flags); ++ dte, page_to_maddr(root_pg), domid, ++ hd->arch.amd.paging_mode, sr_flags); + if ( rc ) + { + ASSERT(rc < 0); +@@ -181,7 +194,7 @@ static int __must_check amd_iommu_setup_ + + amd_iommu_flush_device(iommu, req_id); + } +- else if ( dte->pt_root != mfn_x(page_to_mfn(hd->arch.amd.root_table)) ) ++ else if ( dte->pt_root != mfn_x(page_to_mfn(root_pg)) ) + { + /* + * Strictly speaking if the device is the only one with this requestor +@@ -194,8 +207,8 @@ static int __must_check amd_iommu_setup_ + rc = -EOPNOTSUPP; + else + rc = amd_iommu_set_root_page_table( +- dte, page_to_maddr(hd->arch.amd.root_table), +- domain->domain_id, hd->arch.amd.paging_mode, sr_flags); ++ dte, page_to_maddr(root_pg), domid, ++ hd->arch.amd.paging_mode, sr_flags); + if ( rc < 0 ) + { + spin_unlock_irqrestore(&iommu->lock, flags); +@@ -214,6 +227,7 @@ static int __must_check amd_iommu_setup_ + * intended anyway. + */ + !pdev->domain->is_dying && ++ pdev->domain != dom_io && + (any_pdev_behind_iommu(pdev->domain, pdev, iommu) || + pdev->phantom_stride) ) + AMD_IOMMU_WARN(" %pp: reassignment may cause %pd data corruption\n", +@@ -246,9 +260,8 @@ static int __must_check amd_iommu_setup_ + AMD_IOMMU_DEBUG("Setup I/O page table: device id = %#x, type = %#x, " + "root table = %#"PRIx64", " + "domain = %d, paging mode = %d\n", +- req_id, pdev->type, +- page_to_maddr(hd->arch.amd.root_table), +- domain->domain_id, hd->arch.amd.paging_mode); ++ req_id, pdev->type, page_to_maddr(root_pg), ++ domid, hd->arch.amd.paging_mode); + + ASSERT(pcidevs_locked()); + +@@ -327,7 +340,7 @@ int amd_iommu_alloc_root(struct domain * + { + struct domain_iommu *hd = dom_iommu(d); + +- if ( unlikely(!hd->arch.amd.root_table) ) ++ if ( unlikely(!hd->arch.amd.root_table) && d != dom_io ) + { + hd->arch.amd.root_table = iommu_alloc_pgtable(hd); + if ( !hd->arch.amd.root_table ) +@@ -391,7 +404,7 @@ static void amd_iommu_disable_domain_dev + int req_id; + u8 bus = pdev->bus; + +- if ( QUARANTINE_SKIP(domain) ) ++ if ( QUARANTINE_SKIP(domain, pdev) ) + return; + + ASSERT(pcidevs_locked()); +@@ -430,7 +443,7 @@ static void amd_iommu_disable_domain_dev + + AMD_IOMMU_DEBUG("Disable: device id = %#x, " + "domain = %d, paging mode = %d\n", +- req_id, domain->domain_id, ++ req_id, dte->domain_id, + dom_iommu(domain)->arch.amd.paging_mode); + } + else +@@ -453,7 +466,7 @@ static int reassign_device(struct domain + return -ENODEV; + } + +- if ( !QUARANTINE_SKIP(target) ) ++ if ( !QUARANTINE_SKIP(target, pdev) ) + { + rc = amd_iommu_setup_domain_device(target, iommu, devfn, pdev); + if ( rc ) +@@ -655,6 +668,8 @@ static int amd_iommu_remove_device(u8 de + AMD_IOMMU_WARN("%pd: unity unmapping failed for %pp\n", + pdev->domain, &pdev->sbdf); + ++ amd_iommu_quarantine_teardown(pdev); ++ + iommu_free_domid(pdev->arch.pseudo_domid, iommu->domid_map); + pdev->arch.pseudo_domid = DOMID_INVALID; + +--- a/xen/drivers/passthrough/iommu.c ++++ b/xen/drivers/passthrough/iommu.c +@@ -443,21 +443,22 @@ int iommu_iotlb_flush_all(struct domain + return rc; + } + +-static int __init iommu_quarantine_init(void) ++int iommu_quarantine_dev_init(device_t *dev) + { + const struct domain_iommu *hd = dom_iommu(dom_io); +- int rc; + +- dom_io->options |= XEN_DOMCTL_CDF_iommu; ++ if ( !iommu_quarantine || !hd->platform_ops->quarantine_init ) ++ return 0; + +- rc = iommu_domain_init(dom_io, 0); +- if ( rc || iommu_quarantine < IOMMU_quarantine_scratch_page ) +- return rc; ++ return iommu_call(hd->platform_ops, quarantine_init, ++ dev, iommu_quarantine == IOMMU_quarantine_scratch_page); ++} + +- if ( !hd->platform_ops->quarantine_init ) +- return 0; ++static int __init iommu_quarantine_init(void) ++{ ++ dom_io->options |= XEN_DOMCTL_CDF_iommu; + +- return hd->platform_ops->quarantine_init(dom_io); ++ return iommu_domain_init(dom_io, 0); + } + + int __init iommu_setup(void) +--- a/xen/drivers/passthrough/pci.c ++++ b/xen/drivers/passthrough/pci.c +@@ -852,9 +852,16 @@ static int deassign_device(struct domain + return -ENODEV; + + /* De-assignment from dom_io should de-quarantine the device */ +- target = ((pdev->quarantine || iommu_quarantine) && +- pdev->domain != dom_io) ? +- dom_io : hardware_domain; ++ if ( (pdev->quarantine || iommu_quarantine) && pdev->domain != dom_io ) ++ { ++ ret = iommu_quarantine_dev_init(pci_to_dev(pdev)); ++ if ( ret ) ++ return ret; ++ ++ target = dom_io; ++ } ++ else ++ target = hardware_domain; + + while ( pdev->phantom_stride ) + { +@@ -1424,6 +1431,13 @@ static int assign_device(struct domain * + if ( rc ) + goto done; + ++ if ( pdev->domain != dom_io ) ++ { ++ rc = iommu_quarantine_dev_init(pci_to_dev(pdev)); ++ if ( rc ) ++ goto done; ++ } ++ + pdev->fault.count = 0; + + if ( (rc = hd->platform_ops->assign_device(d, devfn, pci_to_dev(pdev), flag)) ) +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -45,6 +45,11 @@ + + /* dom_io is used as a sentinel for quarantined devices */ + #define QUARANTINE_SKIP(d, pgd_maddr) ((d) == dom_io && !(pgd_maddr)) ++#define DEVICE_DOMID(d, pdev) ((d) != dom_io ? (d)->domain_id \ ++ : (pdev)->arch.pseudo_domid) ++#define DEVICE_PGTABLE(d, pdev) ((d) != dom_io \ ++ ? dom_iommu(d)->arch.vtd.pgd_maddr \ ++ : (pdev)->arch.vtd.pgd_maddr) + + /* Possible unfiltered LAPIC/MSI messages from untrusted sources? */ + bool __read_mostly untrusted_msi; +@@ -88,13 +93,18 @@ static int get_iommu_did(domid_t domid, + + #define DID_FIELD_WIDTH 16 + #define DID_HIGH_OFFSET 8 ++ ++/* ++ * This function may have "context" passed as NULL, to merely obtain a DID ++ * for "domid". ++ */ + static int context_set_domain_id(struct context_entry *context, + domid_t domid, struct vtd_iommu *iommu) + { + unsigned long nr_dom, i; + int found = 0; + +- ASSERT(spin_is_locked(&iommu->lock)); ++ ASSERT(pcidevs_locked()); + + nr_dom = cap_ndoms(iommu->cap); + i = find_first_bit(iommu->domid_bitmap, nr_dom); +@@ -120,8 +130,13 @@ static int context_set_domain_id(struct + } + + set_bit(i, iommu->domid_bitmap); +- context->hi &= ~(((1 << DID_FIELD_WIDTH) - 1) << DID_HIGH_OFFSET); +- context->hi |= (i & ((1 << DID_FIELD_WIDTH) - 1)) << DID_HIGH_OFFSET; ++ ++ if ( context ) ++ { ++ context->hi &= ~(((1 << DID_FIELD_WIDTH) - 1) << DID_HIGH_OFFSET); ++ context->hi |= (i & ((1 << DID_FIELD_WIDTH) - 1)) << DID_HIGH_OFFSET; ++ } ++ + return 0; + } + +@@ -171,8 +186,12 @@ static void check_cleanup_domid_map(cons + const struct pci_dev *exclude, + struct vtd_iommu *iommu) + { +- bool found = any_pdev_behind_iommu(d, exclude, iommu); ++ bool found; ++ ++ if ( d == dom_io ) ++ return; + ++ found = any_pdev_behind_iommu(d, exclude, iommu); + /* + * Hidden devices are associated with DomXEN but usable by the hardware + * domain. Hence they need considering here as well. +@@ -1426,7 +1445,7 @@ int domain_context_mapping_one( + domid = iommu->domid_map[prev_did]; + if ( domid < DOMID_FIRST_RESERVED ) + prev_dom = rcu_lock_domain_by_id(domid); +- else if ( domid == DOMID_IO ) ++ else if ( pdev ? domid == pdev->arch.pseudo_domid : domid > DOMID_MASK ) + prev_dom = rcu_lock_domain(dom_io); + if ( !prev_dom ) + { +@@ -1582,15 +1601,12 @@ int domain_context_mapping_one( + { + if ( !prev_dom ) + ret = domain_context_unmap_one(domain, iommu, bus, devfn, +- domain->domain_id); ++ DEVICE_DOMID(domain, pdev)); + else if ( prev_dom != domain ) /* Avoid infinite recursion. */ +- { +- hd = dom_iommu(prev_dom); + ret = domain_context_mapping_one(prev_dom, iommu, bus, devfn, pdev, +- domain->domain_id, +- hd->arch.vtd.pgd_maddr, ++ DEVICE_DOMID(prev_dom, pdev), ++ DEVICE_PGTABLE(prev_dom, pdev), + mode & MAP_WITH_RMRR) < 0; +- } + else + ret = 1; + +@@ -1612,7 +1628,7 @@ static int domain_context_mapping(struct + { + const struct acpi_drhd_unit *drhd = acpi_find_matched_drhd_unit(pdev); + const struct acpi_rmrr_unit *rmrr; +- paddr_t pgd_maddr = dom_iommu(domain)->arch.vtd.pgd_maddr; ++ paddr_t pgd_maddr = DEVICE_PGTABLE(domain, pdev); + domid_t orig_domid = pdev->arch.pseudo_domid; + int ret = 0; + unsigned int i, mode = 0; +@@ -1641,7 +1657,7 @@ static int domain_context_mapping(struct + break; + } + +- if ( domain != pdev->domain ) ++ if ( domain != pdev->domain && pdev->domain != dom_io ) + { + if ( pdev->domain->is_dying ) + mode |= MAP_OWNER_DYING; +@@ -1683,8 +1699,8 @@ static int domain_context_mapping(struct + if ( iommu_debug ) + printk(VTDPREFIX "%pd:PCIe: map %pp\n", + domain, &PCI_SBDF3(seg, bus, devfn)); +- ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn, +- pdev, domain->domain_id, pgd_maddr, ++ ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn, pdev, ++ DEVICE_DOMID(domain, pdev), pgd_maddr, + mode); + if ( ret > 0 ) + ret = 0; +@@ -1710,8 +1726,8 @@ static int domain_context_mapping(struct + domain, &PCI_SBDF3(seg, bus, devfn)); + + ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn, +- pdev, domain->domain_id, pgd_maddr, +- mode); ++ pdev, DEVICE_DOMID(domain, pdev), ++ pgd_maddr, mode); + if ( ret < 0 ) + break; + prev_present = ret; +@@ -1739,8 +1755,8 @@ static int domain_context_mapping(struct + */ + if ( ret >= 0 ) + ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn, +- NULL, domain->domain_id, pgd_maddr, +- mode); ++ NULL, DEVICE_DOMID(domain, pdev), ++ pgd_maddr, mode); + + /* + * Devices behind PCIe-to-PCI/PCIx bridge may generate different +@@ -1755,8 +1771,8 @@ static int domain_context_mapping(struct + if ( !ret && pdev_type(seg, bus, devfn) == DEV_TYPE_PCIe2PCI_BRIDGE && + (secbus != pdev->bus || pdev->devfn != 0) ) + ret = domain_context_mapping_one(domain, drhd->iommu, secbus, 0, +- NULL, domain->domain_id, pgd_maddr, +- mode); ++ NULL, DEVICE_DOMID(domain, pdev), ++ pgd_maddr, mode); + + if ( ret ) + { +@@ -1798,9 +1814,6 @@ int domain_context_unmap_one( + int iommu_domid, rc, ret; + bool_t flush_dev_iotlb; + +- if ( QUARANTINE_SKIP(domain, dom_iommu(domain)->arch.vtd.pgd_maddr) ) +- return 0; +- + ASSERT(pcidevs_locked()); + spin_lock(&iommu->lock); + +@@ -1902,7 +1915,7 @@ static const struct acpi_drhd_unit *doma + printk(VTDPREFIX "%pd:PCIe: unmap %pp\n", + domain, &PCI_SBDF3(seg, bus, devfn)); + ret = domain_context_unmap_one(domain, iommu, bus, devfn, +- domain->domain_id); ++ DEVICE_DOMID(domain, pdev)); + if ( !ret && devfn == pdev->devfn && ats_device(pdev, drhd) > 0 ) + disable_ats_device(pdev); + +@@ -1916,7 +1929,7 @@ static const struct acpi_drhd_unit *doma + printk(VTDPREFIX "%pd:PCI: unmap %pp\n", + domain, &PCI_SBDF3(seg, bus, devfn)); + ret = domain_context_unmap_one(domain, iommu, bus, devfn, +- domain->domain_id); ++ DEVICE_DOMID(domain, pdev)); + if ( ret ) + break; + +@@ -1939,18 +1952,12 @@ static const struct acpi_drhd_unit *doma + break; + } + ++ ret = domain_context_unmap_one(domain, iommu, tmp_bus, tmp_devfn, ++ DEVICE_DOMID(domain, pdev)); + /* PCIe to PCI/PCIx bridge */ +- if ( pdev_type(seg, tmp_bus, tmp_devfn) == DEV_TYPE_PCIe2PCI_BRIDGE ) +- { +- ret = domain_context_unmap_one(domain, iommu, tmp_bus, tmp_devfn, +- domain->domain_id); +- if ( !ret ) +- ret = domain_context_unmap_one(domain, iommu, secbus, 0, +- domain->domain_id); +- } +- else /* Legacy PCI bridge */ +- ret = domain_context_unmap_one(domain, iommu, tmp_bus, tmp_devfn, +- domain->domain_id); ++ if ( !ret && pdev_type(seg, tmp_bus, tmp_devfn) == DEV_TYPE_PCIe2PCI_BRIDGE ) ++ ret = domain_context_unmap_one(domain, iommu, secbus, 0, ++ DEVICE_DOMID(domain, pdev)); + + break; + +@@ -1961,7 +1968,7 @@ static const struct acpi_drhd_unit *doma + } + + if ( !ret && pdev->devfn == devfn && +- !QUARANTINE_SKIP(domain, dom_iommu(domain)->arch.vtd.pgd_maddr) ) ++ !QUARANTINE_SKIP(domain, pdev->arch.vtd.pgd_maddr) ) + check_cleanup_domid_map(domain, pdev, iommu); + + return drhd; +@@ -1994,6 +2001,26 @@ static void iommu_domain_teardown(struct + XFREE(hd->arch.vtd.iommu_bitmap); + } + ++static void quarantine_teardown(struct pci_dev *pdev, ++ const struct acpi_drhd_unit *drhd) ++{ ++ struct domain_iommu *hd = dom_iommu(dom_io); ++ ++ ASSERT(pcidevs_locked()); ++ ++ if ( !pdev->arch.vtd.pgd_maddr ) ++ return; ++ ++ ASSERT(page_list_empty(&hd->arch.pgtables.list)); ++ page_list_move(&hd->arch.pgtables.list, &pdev->arch.pgtables_list); ++ while ( iommu_free_pgtables(dom_io) == -ERESTART ) ++ /* nothing */; ++ pdev->arch.vtd.pgd_maddr = 0; ++ ++ if ( drhd ) ++ cleanup_domid_map(pdev->arch.pseudo_domid, drhd->iommu); ++} ++ + static int __must_check intel_iommu_map_page(struct domain *d, dfn_t dfn, + mfn_t mfn, unsigned int flags, + unsigned int *flush_flags) +@@ -2218,6 +2245,8 @@ static int intel_iommu_remove_device(u8 + rmrr->end_address, 0); + } + ++ quarantine_teardown(pdev, drhd); ++ + if ( drhd ) + { + iommu_free_domid(pdev->arch.pseudo_domid, +@@ -2576,7 +2605,7 @@ static int reassign_device_ownership( + { + int ret; + +- if ( !QUARANTINE_SKIP(target, dom_iommu(target)->arch.vtd.pgd_maddr) ) ++ if ( !QUARANTINE_SKIP(target, pdev->arch.vtd.pgd_maddr) ) + { + if ( !has_arch_pdevs(target) ) + vmx_pi_hooks_assign(target); +@@ -2592,7 +2621,7 @@ static int reassign_device_ownership( + ret = domain_context_mapping(target, devfn, pdev); + + if ( !ret && pdev->devfn == devfn && +- !QUARANTINE_SKIP(source, dom_iommu(source)->arch.vtd.pgd_maddr) ) ++ !QUARANTINE_SKIP(source, pdev->arch.vtd.pgd_maddr) ) + { + const struct acpi_drhd_unit *drhd = acpi_find_matched_drhd_unit(pdev); + +@@ -2913,69 +2942,135 @@ static void vtd_dump_page_tables(struct + agaw_to_level(hd->arch.vtd.agaw), 0, 0); + } + +-static int __init intel_iommu_quarantine_init(struct domain *d) ++static int fill_qpt(struct dma_pte *this, unsigned int level, ++ struct page_info *pgs[6]) + { +- struct domain_iommu *hd = dom_iommu(d); ++ struct domain_iommu *hd = dom_iommu(dom_io); ++ unsigned int i; ++ int rc = 0; ++ ++ for ( i = 0; !rc && i < PTE_NUM; ++i ) ++ { ++ struct dma_pte *pte = &this[i], *next; ++ ++ if ( !dma_pte_present(*pte) ) ++ { ++ if ( !pgs[level] ) ++ { ++ /* ++ * The pgtable allocator is fine for the leaf page, as well as ++ * page table pages, and the resulting allocations are always ++ * zeroed. ++ */ ++ pgs[level] = iommu_alloc_pgtable(hd); ++ if ( !pgs[level] ) ++ { ++ rc = -ENOMEM; ++ break; ++ } ++ ++ if ( level ) ++ { ++ next = map_vtd_domain_page(page_to_maddr(pgs[level])); ++ rc = fill_qpt(next, level - 1, pgs); ++ unmap_vtd_domain_page(next); ++ } ++ } ++ ++ dma_set_pte_addr(*pte, page_to_maddr(pgs[level])); ++ dma_set_pte_readable(*pte); ++ dma_set_pte_writable(*pte); ++ } ++ else if ( level && !dma_pte_superpage(*pte) ) ++ { ++ next = map_vtd_domain_page(dma_pte_addr(*pte)); ++ rc = fill_qpt(next, level - 1, pgs); ++ unmap_vtd_domain_page(next); ++ } ++ } ++ ++ return rc; ++} ++ ++static int intel_iommu_quarantine_init(struct pci_dev *pdev, bool scratch_page) ++{ ++ struct domain_iommu *hd = dom_iommu(dom_io); + struct page_info *pg; +- struct dma_pte *parent; + unsigned int agaw = width_to_agaw(DEFAULT_DOMAIN_ADDRESS_WIDTH); + unsigned int level = agaw_to_level(agaw); +- int rc = 0; ++ const struct acpi_drhd_unit *drhd; ++ const struct acpi_rmrr_unit *rmrr; ++ unsigned int i, bdf; ++ bool rmrr_found = false; ++ int rc; + +- spin_lock(&hd->arch.mapping_lock); ++ ASSERT(pcidevs_locked()); ++ ASSERT(!hd->arch.vtd.pgd_maddr); ++ ASSERT(page_list_empty(&hd->arch.pgtables.list)); + +- if ( hd->arch.vtd.pgd_maddr ) ++ if ( pdev->arch.vtd.pgd_maddr ) + { +- ASSERT_UNREACHABLE(); +- goto out; ++ clear_domain_page(pdev->arch.leaf_mfn); ++ return 0; + } + +- pg = iommu_alloc_pgtable(hd); ++ drhd = acpi_find_matched_drhd_unit(pdev); ++ if ( !drhd ) ++ return -ENODEV; + +- rc = -ENOMEM; ++ pg = iommu_alloc_pgtable(hd); + if ( !pg ) +- goto out; ++ return -ENOMEM; + ++ rc = context_set_domain_id(NULL, pdev->arch.pseudo_domid, drhd->iommu); ++ ++ /* Transiently install the root into DomIO, for iommu_identity_mapping(). */ + hd->arch.vtd.pgd_maddr = page_to_maddr(pg); + +- parent = map_vtd_domain_page(hd->arch.vtd.pgd_maddr); +- while ( level ) ++ for_each_rmrr_device ( rmrr, bdf, i ) + { +- uint64_t maddr; +- unsigned int offset; +- +- /* +- * The pgtable allocator is fine for the leaf page, as well as +- * page table pages, and the resulting allocations are always +- * zeroed. +- */ +- pg = iommu_alloc_pgtable(hd); +- +- if ( !pg ) +- goto out; ++ if ( rc ) ++ break; + +- maddr = page_to_maddr(pg); +- for ( offset = 0; offset < PTE_NUM; offset++ ) ++ if ( rmrr->segment == pdev->seg && bdf == pdev->sbdf.bdf ) + { +- struct dma_pte *pte = &parent[offset]; ++ rmrr_found = true; + +- dma_set_pte_addr(*pte, maddr); +- dma_set_pte_readable(*pte); ++ rc = iommu_identity_mapping(dom_io, p2m_access_rw, ++ rmrr->base_address, rmrr->end_address, ++ 0); ++ if ( rc ) ++ printk(XENLOG_ERR VTDPREFIX ++ "%pp: RMRR quarantine mapping failed\n", ++ &pdev->sbdf); + } +- iommu_sync_cache(parent, PAGE_SIZE); ++ } + +- unmap_vtd_domain_page(parent); +- parent = map_vtd_domain_page(maddr); +- level--; ++ iommu_identity_map_teardown(dom_io); ++ hd->arch.vtd.pgd_maddr = 0; ++ pdev->arch.vtd.pgd_maddr = page_to_maddr(pg); ++ ++ if ( !rc && scratch_page ) ++ { ++ struct dma_pte *root; ++ struct page_info *pgs[6] = {}; ++ ++ spin_lock(&hd->arch.mapping_lock); ++ ++ root = map_vtd_domain_page(pdev->arch.vtd.pgd_maddr); ++ rc = fill_qpt(root, level - 1, pgs); ++ unmap_vtd_domain_page(root); ++ ++ pdev->arch.leaf_mfn = page_to_mfn(pgs[0]); ++ ++ spin_unlock(&hd->arch.mapping_lock); + } +- unmap_vtd_domain_page(parent); + +- rc = 0; ++ page_list_move(&pdev->arch.pgtables_list, &hd->arch.pgtables.list); + +- out: +- spin_unlock(&hd->arch.mapping_lock); ++ if ( rc || (!scratch_page && !rmrr_found) ) ++ quarantine_teardown(pdev, drhd); + +- /* Pages may be leaked in failure case */ + return rc; + } + +--- a/xen/drivers/passthrough/vtd/iommu.h ++++ b/xen/drivers/passthrough/vtd/iommu.h +@@ -482,7 +482,7 @@ struct vtd_iommu { + u32 nr_pt_levels; + u64 cap; + u64 ecap; +- spinlock_t lock; /* protect context, domain ids */ ++ spinlock_t lock; /* protect context */ + spinlock_t register_lock; /* protect iommu register handling */ + u64 root_maddr; /* root entry machine address */ + nodeid_t node; +--- a/xen/include/xen/iommu.h ++++ b/xen/include/xen/iommu.h +@@ -233,7 +233,7 @@ typedef int iommu_grdm_t(xen_pfn_t start + struct iommu_ops { + int (*init)(struct domain *d); + void (*hwdom_init)(struct domain *d); +- int (*quarantine_init)(struct domain *d); ++ int (*quarantine_init)(device_t *dev, bool scratch_page); + int (*add_device)(u8 devfn, device_t *dev); + int (*enable_device)(device_t *dev); + int (*remove_device)(u8 devfn, device_t *dev); +@@ -350,6 +350,7 @@ int __must_check iommu_suspend(void); + void iommu_resume(void); + void iommu_crash_shutdown(void); + int iommu_get_reserved_device_memory(iommu_grdm_t *, void *); ++int iommu_quarantine_dev_init(device_t *dev); + + #ifdef CONFIG_HAS_PCI + int iommu_do_pci_domctl(struct xen_domctl *, struct domain *d, From 7c54ce20c6caed4e4a38a26aa38b517156ae3914 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 14 Apr 2022 21:06:05 +0100 Subject: [PATCH 036/137] update to xen-4.16.1 strip .efi file to help EFI partitions with limited space --- .gitignore | 2 +- sources | 2 +- ...03c855c071e9b1aaaa96403f2a90433336a7.patch | 94 -- ...0feed10571543061abeaedd21ce8adc60114.patch | 125 --- ...26a2c5ad64c05281dc8ed2f1f57c0ee5988c.patch | 32 - ...77b4f367269dc1b768a335cfa16f48f7f02f.patch | 209 ---- ...32726add103ee8f63293e326ad43b1643239.patch | 148 --- ...02da23861f686c349a6808b2f4c9adc15f9f.patch | 45 - ...b6a895d10744bab32fc843246c45da444d8b.patch | 62 -- ...7e8b53935de9a1123f677213f1b146843a0e.patch | 41 - ...518e952c3a8fe5b9ec6a2d007cda73fd05a4.patch | 36 - ...f6b5560e738955993fe061a04d64c6f71c14.patch | 142 --- xen.spec | 84 +- xsa393.patch | 49 - xsa394.patch | 63 -- xsa395.patch | 42 - xsa397-4.16.patch | 98 -- ...xen-arm-Introduce-new-Arm-processors.patch | 63 -- ...n-arm-move-errata-CSV2-check-earlier.patch | 53 - ...arm-Add-ECBHB-and-CLEARBHB-ID-fields.patch | 94 -- ...6-4-xen-arm-Add-Spectre-BHB-handling.patch | 351 ------- ...-discover-and-use-SMCCC_ARCH_WORKARO.patch | 91 -- ...ctrl-Cease-using-thunk-lfence-on-AMD.patch | 118 --- xsa399-4.16.patch | 45 - xsa400-4.16-01.patch | 105 -- xsa400-4.16-02.patch | 80 -- xsa400-4.16-03.patch | 134 --- xsa400-4.16-04.patch | 94 -- xsa400-4.16-05.patch | 570 ----------- xsa400-4.16-06.patch | 330 ------ xsa400-4.16-07.patch | 333 ------ xsa400-4.16-08.patch | 144 --- xsa400-4.16-09.patch | 447 --------- xsa400-4.16-10.patch | 38 - xsa400-4.16-11.patch | 29 - xsa400-4.16-12.patch | 945 ------------------ 36 files changed, 16 insertions(+), 5322 deletions(-) delete mode 100644 xen.git-08fc03c855c071e9b1aaaa96403f2a90433336a7.patch delete mode 100644 xen.git-21d70feed10571543061abeaedd21ce8adc60114.patch delete mode 100644 xen.git-243026a2c5ad64c05281dc8ed2f1f57c0ee5988c.patch delete mode 100644 xen.git-41e477b4f367269dc1b768a335cfa16f48f7f02f.patch delete mode 100644 xen.git-6ef732726add103ee8f63293e326ad43b1643239.patch delete mode 100644 xen.git-72ef02da23861f686c349a6808b2f4c9adc15f9f.patch delete mode 100644 xen.git-7f34b6a895d10744bab32fc843246c45da444d8b.patch delete mode 100644 xen.git-84977e8b53935de9a1123f677213f1b146843a0e.patch delete mode 100644 xen.git-b2db518e952c3a8fe5b9ec6a2d007cda73fd05a4.patch delete mode 100644 xen.git-fa45f6b5560e738955993fe061a04d64c6f71c14.patch delete mode 100644 xsa393.patch delete mode 100644 xsa394.patch delete mode 100644 xsa395.patch delete mode 100644 xsa397-4.16.patch delete mode 100644 xsa398-4.16-1-xen-arm-Introduce-new-Arm-processors.patch delete mode 100644 xsa398-4.16-2-xen-arm-move-errata-CSV2-check-earlier.patch delete mode 100644 xsa398-4.16-3-xen-arm-Add-ECBHB-and-CLEARBHB-ID-fields.patch delete mode 100644 xsa398-4.16-4-xen-arm-Add-Spectre-BHB-handling.patch delete mode 100644 xsa398-4.16-5-xen-arm-Allow-to-discover-and-use-SMCCC_ARCH_WORKARO.patch delete mode 100644 xsa398-4.16-6-x86-spec-ctrl-Cease-using-thunk-lfence-on-AMD.patch delete mode 100644 xsa399-4.16.patch delete mode 100644 xsa400-4.16-01.patch delete mode 100644 xsa400-4.16-02.patch delete mode 100644 xsa400-4.16-03.patch delete mode 100644 xsa400-4.16-04.patch delete mode 100644 xsa400-4.16-05.patch delete mode 100644 xsa400-4.16-06.patch delete mode 100644 xsa400-4.16-07.patch delete mode 100644 xsa400-4.16-08.patch delete mode 100644 xsa400-4.16-09.patch delete mode 100644 xsa400-4.16-10.patch delete mode 100644 xsa400-4.16-11.patch delete mode 100644 xsa400-4.16-12.patch diff --git a/.gitignore b/.gitignore index dcc3690..8f4e85c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.16.0.tar.gz +/xen-4.16.1.tar.gz diff --git a/sources b/sources index 10bfdea..6a26e23 100644 --- a/sources +++ b/sources @@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.16.0.tar.gz) = 2869ed90d1779c9754d7f2397f5fc67a655304d9c32953ac20655ef96cb154521d8fce9f23915ac0c91f984dc54f72c67e5e619e2da318b5997748f44cf21b87 +SHA512 (xen-4.16.1.tar.gz) = eeabba9c263cd2425bca083e32b5ebfc6c716c00553759c144fd4b6f64a89836b260787fa25ba22c1f5c4ea65aaad7c95b8c2c1070d3377b1c43c9517aa7032a diff --git a/xen.git-08fc03c855c071e9b1aaaa96403f2a90433336a7.patch b/xen.git-08fc03c855c071e9b1aaaa96403f2a90433336a7.patch deleted file mode 100644 index 84514e0..0000000 --- a/xen.git-08fc03c855c071e9b1aaaa96403f2a90433336a7.patch +++ /dev/null @@ -1,94 +0,0 @@ -From: Andrew Cooper -Date: Tue, 25 Jan 2022 17:14:48 +0000 (+0000) -Subject: x86/spec-ctrl: Introduce new has_spec_ctrl boolean -X-Git-Url: http://xenbits.xenproject.org/gitweb/?p=xen.git;a=commitdiff_plain;h=08fc03c855c071e9b1aaaa96403f2a90433336a7 - -x86/spec-ctrl: Introduce new has_spec_ctrl boolean - -Most MSR_SPEC_CTRL setup will be common between Intel and AMD. Instead of -opencoding an OR of two features everywhere, introduce has_spec_ctrl instead. - -Reword the comment above the Intel specific alternatives block to highlight -that it is Intel specific, and pull the setting of default_xen_spec_ctrl.IBRS -out because it will want to be common. - -No functional change. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -(cherry picked from commit 5d9eff3a312763d889cfbf3c8468b6dfb3ab490c) ---- - -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 8a550d0a09..2072daf662 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -927,7 +927,7 @@ static __init void mds_calculations(uint64_t caps) - void __init init_speculation_mitigations(void) - { - enum ind_thunk thunk = THUNK_DEFAULT; -- bool ibrs = false, hw_smt_enabled; -+ bool has_spec_ctrl, ibrs = false, hw_smt_enabled; - bool cpu_has_bug_taa; - uint64_t caps = 0; - -@@ -936,6 +936,8 @@ void __init init_speculation_mitigations(void) - - hw_smt_enabled = check_smt_enabled(); - -+ has_spec_ctrl = boot_cpu_has(X86_FEATURE_IBRSB); -+ - /* - * First, disable the use of retpolines if Xen is using shadow stacks, as - * they are incompatible. -@@ -973,11 +975,11 @@ void __init init_speculation_mitigations(void) - */ - else if ( retpoline_safe(caps) ) - thunk = THUNK_RETPOLINE; -- else if ( boot_cpu_has(X86_FEATURE_IBRSB) ) -+ else if ( has_spec_ctrl ) - ibrs = true; - } - /* Without compiler thunk support, use IBRS if available. */ -- else if ( boot_cpu_has(X86_FEATURE_IBRSB) ) -+ else if ( has_spec_ctrl ) - ibrs = true; - } - -@@ -1008,10 +1010,7 @@ void __init init_speculation_mitigations(void) - else if ( thunk == THUNK_JMP ) - setup_force_cpu_cap(X86_FEATURE_IND_THUNK_JMP); - -- /* -- * If we are on hardware supporting MSR_SPEC_CTRL, see about setting up -- * the alternatives blocks so we can virtualise support for guests. -- */ -+ /* Intel hardware: MSR_SPEC_CTRL alternatives setup. */ - if ( boot_cpu_has(X86_FEATURE_IBRSB) ) - { - if ( opt_msr_sc_pv ) -@@ -1030,11 +1029,12 @@ void __init init_speculation_mitigations(void) - default_spec_ctrl_flags |= SCF_ist_wrmsr; - setup_force_cpu_cap(X86_FEATURE_SC_MSR_HVM); - } -- -- if ( ibrs ) -- default_xen_spec_ctrl |= SPEC_CTRL_IBRS; - } - -+ /* If we have IBRS available, see whether we should use it. */ -+ if ( has_spec_ctrl && ibrs ) -+ default_xen_spec_ctrl |= SPEC_CTRL_IBRS; -+ - /* If we have SSBD available, see whether we should use it. */ - if ( boot_cpu_has(X86_FEATURE_SSBD) && opt_ssbd ) - default_xen_spec_ctrl |= SPEC_CTRL_SSBD; -@@ -1268,7 +1268,7 @@ void __init init_speculation_mitigations(void) - * boot won't have any other code running in a position to mount an - * attack. - */ -- if ( boot_cpu_has(X86_FEATURE_IBRSB) ) -+ if ( has_spec_ctrl ) - { - bsp_delay_spec_ctrl = !cpu_has_hypervisor && default_xen_spec_ctrl; - diff --git a/xen.git-21d70feed10571543061abeaedd21ce8adc60114.patch b/xen.git-21d70feed10571543061abeaedd21ce8adc60114.patch deleted file mode 100644 index 2164b76..0000000 --- a/xen.git-21d70feed10571543061abeaedd21ce8adc60114.patch +++ /dev/null @@ -1,125 +0,0 @@ -From: Andrew Cooper -Date: Tue, 25 Jan 2022 12:39:31 +0000 (+0100) -Subject: x86/spec-ctrl: Drop SPEC_CTRL_{ENTRY_FROM,EXIT_TO}_HVM -X-Git-Url: http://xenbits.xenproject.org/gitweb/?p=xen.git;a=commitdiff_plain;h=21d70feed10571543061abeaedd21ce8adc60114 - -x86/spec-ctrl: Drop SPEC_CTRL_{ENTRY_FROM,EXIT_TO}_HVM - -These were written before Spectre/Meltdown went public, and there was large -uncertainty in how the protections would evolve. As it turns out, they're -very specific to Intel hardware, and not very suitable for AMD. - -Drop the macros, opencoding the relevant subset of functionality, and leaving -grep-fodder to locate the logic. No change at all for VT-x. - -For AMD, the only relevant piece of functionality is DO_OVERWRITE_RSB, -although we will soon be adding (different) logic to handle MSR_SPEC_CTRL. - -This has a marginal improvement of removing an unconditional pile of long-nops -from the vmentry/exit path. - -Signed-off-by: Andrew Cooper -Reviewed-by: Roger Pau Monné -master commit: 95b13fa43e0753b7514bef13abe28253e8614f62 -master date: 2022-01-20 16:32:11 +0000 ---- - -diff --git a/xen/arch/x86/hvm/svm/entry.S b/xen/arch/x86/hvm/svm/entry.S -index e208a4b32a..276215d36a 100644 ---- a/xen/arch/x86/hvm/svm/entry.S -+++ b/xen/arch/x86/hvm/svm/entry.S -@@ -59,7 +59,7 @@ __UNLIKELY_END(nsvm_hap) - mov VCPUMSR_spec_ctrl_raw(%rax), %eax - - /* WARNING! `ret`, `call *`, `jmp *` not safe beyond this point. */ -- SPEC_CTRL_EXIT_TO_HVM /* Req: a=spec_ctrl %rsp=regs/cpuinfo, Clob: cd */ -+ /* SPEC_CTRL_EXIT_TO_SVM (nothing currently) */ - - pop %r15 - pop %r14 -@@ -86,7 +86,8 @@ __UNLIKELY_END(nsvm_hap) - - GET_CURRENT(bx) - -- SPEC_CTRL_ENTRY_FROM_HVM /* Req: b=curr %rsp=regs/cpuinfo, Clob: acd */ -+ /* SPEC_CTRL_ENTRY_FROM_SVM Req: b=curr %rsp=regs/cpuinfo, Clob: ac */ -+ ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_HVM - /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ - - stgi -diff --git a/xen/arch/x86/hvm/vmx/entry.S b/xen/arch/x86/hvm/vmx/entry.S -index 27c8c5ca49..30139ae58e 100644 ---- a/xen/arch/x86/hvm/vmx/entry.S -+++ b/xen/arch/x86/hvm/vmx/entry.S -@@ -33,7 +33,9 @@ ENTRY(vmx_asm_vmexit_handler) - movb $1,VCPU_vmx_launched(%rbx) - mov %rax,VCPU_hvm_guest_cr2(%rbx) - -- SPEC_CTRL_ENTRY_FROM_HVM /* Req: b=curr %rsp=regs/cpuinfo, Clob: acd */ -+ /* SPEC_CTRL_ENTRY_FROM_VMX Req: b=curr %rsp=regs/cpuinfo, Clob: acd */ -+ ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_HVM -+ ALTERNATIVE "", DO_SPEC_CTRL_ENTRY_FROM_HVM, X86_FEATURE_SC_MSR_HVM - /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ - - /* Hardware clears MSR_DEBUGCTL on VMExit. Reinstate it if debugging Xen. */ -@@ -80,7 +82,9 @@ UNLIKELY_END(realmode) - mov VCPUMSR_spec_ctrl_raw(%rax), %eax - - /* WARNING! `ret`, `call *`, `jmp *` not safe beyond this point. */ -- SPEC_CTRL_EXIT_TO_HVM /* Req: a=spec_ctrl %rsp=regs/cpuinfo, Clob: cd */ -+ /* SPEC_CTRL_EXIT_TO_VMX Req: a=spec_ctrl %rsp=regs/cpuinfo, Clob: cd */ -+ ALTERNATIVE "", DO_SPEC_CTRL_EXIT_TO_GUEST, X86_FEATURE_SC_MSR_HVM -+ ALTERNATIVE "", __stringify(verw CPUINFO_verw_sel(%rsp)), X86_FEATURE_SC_VERW_HVM - - mov VCPU_hvm_guest_cr2(%rbx),%rax - -diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h -index cb34299a86..2b3f123cb5 100644 ---- a/xen/include/asm-x86/spec_ctrl_asm.h -+++ b/xen/include/asm-x86/spec_ctrl_asm.h -@@ -68,14 +68,16 @@ - * - * The following ASM fragments implement this algorithm. See their local - * comments for further details. -- * - SPEC_CTRL_ENTRY_FROM_HVM - * - SPEC_CTRL_ENTRY_FROM_PV - * - SPEC_CTRL_ENTRY_FROM_INTR - * - SPEC_CTRL_ENTRY_FROM_INTR_IST - * - SPEC_CTRL_EXIT_TO_XEN_IST - * - SPEC_CTRL_EXIT_TO_XEN - * - SPEC_CTRL_EXIT_TO_PV -- * - SPEC_CTRL_EXIT_TO_HVM -+ * -+ * Additionally, the following grep-fodder exists to find the HVM logic. -+ * - SPEC_CTRL_ENTRY_FROM_{SVM,VMX} -+ * - SPEC_CTRL_EXIT_TO_{SVM,VMX} - */ - - .macro DO_OVERWRITE_RSB tmp=rax -@@ -225,12 +227,6 @@ - wrmsr - .endm - --/* Use after a VMEXIT from an HVM guest. */ --#define SPEC_CTRL_ENTRY_FROM_HVM \ -- ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_HVM; \ -- ALTERNATIVE "", DO_SPEC_CTRL_ENTRY_FROM_HVM, \ -- X86_FEATURE_SC_MSR_HVM -- - /* Use after an entry from PV context (syscall/sysenter/int80/int82/etc). */ - #define SPEC_CTRL_ENTRY_FROM_PV \ - ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_PV; \ -@@ -255,13 +251,6 @@ - ALTERNATIVE "", __stringify(verw CPUINFO_verw_sel(%rsp)), \ - X86_FEATURE_SC_VERW_PV - --/* Use when exiting to HVM guest context. */ --#define SPEC_CTRL_EXIT_TO_HVM \ -- ALTERNATIVE "", \ -- DO_SPEC_CTRL_EXIT_TO_GUEST, X86_FEATURE_SC_MSR_HVM; \ -- ALTERNATIVE "", __stringify(verw CPUINFO_verw_sel(%rsp)), \ -- X86_FEATURE_SC_VERW_HVM -- - /* - * Use in IST interrupt/exception context. May interrupt Xen or PV context. - * Fine grain control of SCF_ist_wrmsr is needed for safety in the S3 resume diff --git a/xen.git-243026a2c5ad64c05281dc8ed2f1f57c0ee5988c.patch b/xen.git-243026a2c5ad64c05281dc8ed2f1f57c0ee5988c.patch deleted file mode 100644 index 5693970..0000000 --- a/xen.git-243026a2c5ad64c05281dc8ed2f1f57c0ee5988c.patch +++ /dev/null @@ -1,32 +0,0 @@ -From: Andrew Cooper -Date: Thu, 6 Jan 2022 13:15:14 +0000 (+0100) -Subject: x86/spec-ctrl: Fix default calculation of opt_srb_lock -X-Git-Url: http://xenbits.xenproject.org/gitweb/?p=xen.git;a=commitdiff_plain;h=243026a2c5ad64c05281dc8ed2f1f57c0ee5988c - -x86/spec-ctrl: Fix default calculation of opt_srb_lock - -Since this logic was introduced, opt_tsx has become more complicated and -shouldn't be compared to 0 directly. While there are no buggy logic paths, -the correct expression is !(opt_tsx & 1) but the rtm_disabled boolean is -easier and clearer to use. - -Fixes: 8fe24090d940 ("x86/cpuid: Rework HLE and RTM handling") -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -master commit: 31f3bc97f4508687215e459a5e35676eecf1772b -master date: 2022-01-05 09:44:26 +0000 ---- - -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index a5569c7f2b..c18cc8aa49 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -1245,7 +1245,7 @@ void __init init_speculation_mitigations(void) - */ - if ( opt_srb_lock == -1 && - (caps & (ARCH_CAPS_MDS_NO|ARCH_CAPS_TAA_NO)) == ARCH_CAPS_MDS_NO && -- (!cpu_has_hle || ((caps & ARCH_CAPS_TSX_CTRL) && opt_tsx == 0)) ) -+ (!cpu_has_hle || ((caps & ARCH_CAPS_TSX_CTRL) && rtm_disabled)) ) - opt_srb_lock = 0; - - val &= ~MCU_OPT_CTRL_RNGDS_MITG_DIS; diff --git a/xen.git-41e477b4f367269dc1b768a335cfa16f48f7f02f.patch b/xen.git-41e477b4f367269dc1b768a335cfa16f48f7f02f.patch deleted file mode 100644 index 4bd651a..0000000 --- a/xen.git-41e477b4f367269dc1b768a335cfa16f48f7f02f.patch +++ /dev/null @@ -1,209 +0,0 @@ -From: Andrew Cooper -Date: Wed, 19 May 2021 18:40:28 +0000 (+0100) -Subject: x86/spec-ctrl: Clean up MSR_MCU_OPT_CTRL handling -X-Git-Url: http://xenbits.xenproject.org/gitweb/?p=xen.git;a=commitdiff_plain;h=41e477b4f367269dc1b768a335cfa16f48f7f02f - -x86/spec-ctrl: Clean up MSR_MCU_OPT_CTRL handling - -Introduce cpu_has_srbds_ctrl as more users are going to appear shortly. - -MSR_MCU_OPT_CTRL is gaining extra functionality, meaning that the current -default_xen_mcu_opt_ctrl is no longer a good fit. - -Introduce two new helpers, update_mcu_opt_ctrl() which does a full RMW cycle -on the MSR, and set_in_mcu_opt_ctrl() which lets callers configure specific -bits at a time without clobbering each others settings. - -No functional change. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -(cherry picked from commit 39a40f3835efcc25c1b05a25c321a01d7e11cbd7) ---- - -diff --git a/xen/arch/x86/acpi/power.c b/xen/arch/x86/acpi/power.c -index d4bdc3e7df..5eaa77f66a 100644 ---- a/xen/arch/x86/acpi/power.c -+++ b/xen/arch/x86/acpi/power.c -@@ -301,8 +301,7 @@ static int enter_state(u32 state) - ci->last_spec_ctrl = default_xen_spec_ctrl; - } - -- if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) -- wrmsrl(MSR_MCU_OPT_CTRL, default_xen_mcu_opt_ctrl); -+ update_mcu_opt_ctrl(); - - /* (re)initialise SYSCALL/SYSENTER state, amongst other things. */ - percpu_traps_init(); -diff --git a/xen/arch/x86/cpu/intel.c b/xen/arch/x86/cpu/intel.c -index 9b011c3446..e7d4dd652f 100644 ---- a/xen/arch/x86/cpu/intel.c -+++ b/xen/arch/x86/cpu/intel.c -@@ -14,6 +14,38 @@ - - #include "cpu.h" - -+/* -+ * MSR_MCU_OPT_CTRL is a collection of unrelated functionality, with separate -+ * enablement requirements, but which want to be consistent across the system. -+ */ -+static uint32_t __read_mostly mcu_opt_ctrl_mask; -+static uint32_t __read_mostly mcu_opt_ctrl_val; -+ -+void update_mcu_opt_ctrl(void) -+{ -+ uint32_t mask = mcu_opt_ctrl_mask, lo, hi; -+ -+ if ( !mask ) -+ return; -+ -+ rdmsr(MSR_MCU_OPT_CTRL, lo, hi); -+ -+ lo &= ~mask; -+ lo |= mcu_opt_ctrl_val; -+ -+ wrmsr(MSR_MCU_OPT_CTRL, lo, hi); -+} -+ -+void __init set_in_mcu_opt_ctrl(uint32_t mask, uint32_t val) -+{ -+ mcu_opt_ctrl_mask |= mask; -+ -+ mcu_opt_ctrl_val &= ~mask; -+ mcu_opt_ctrl_val |= (val & mask); -+ -+ update_mcu_opt_ctrl(); -+} -+ - /* - * Processors which have self-snooping capability can handle conflicting - * memory type across CPUs by snooping its own cache. However, there exists -diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c -index 54237c6c6d..2596e4374b 100644 ---- a/xen/arch/x86/smpboot.c -+++ b/xen/arch/x86/smpboot.c -@@ -384,8 +384,7 @@ void start_secondary(void *unused) - wrmsrl(MSR_SPEC_CTRL, default_xen_spec_ctrl); - info->last_spec_ctrl = default_xen_spec_ctrl; - } -- if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) -- wrmsrl(MSR_MCU_OPT_CTRL, default_xen_mcu_opt_ctrl); -+ update_mcu_opt_ctrl(); - - tsx_init(); /* Needs microcode. May change HLE/RTM feature bits. */ - -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index ee862089b7..3628b4b415 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -67,7 +67,6 @@ static bool __initdata cpu_has_bug_msbds_only; /* => minimal HT impact. */ - static bool __initdata cpu_has_bug_mds; /* Any other M{LP,SB,FB}DS combination. */ - - static int8_t __initdata opt_srb_lock = -1; --uint64_t __read_mostly default_xen_mcu_opt_ctrl; - - static int __init parse_spec_ctrl(const char *s) - { -@@ -376,7 +375,7 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - (default_xen_spec_ctrl & SPEC_CTRL_SSBD) ? " SSBD+" : " SSBD-", - !(caps & ARCH_CAPS_TSX_CTRL) ? "" : - (opt_tsx & 1) ? " TSX+" : " TSX-", -- !boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ? "" : -+ !cpu_has_srbds_ctrl ? "" : - opt_srb_lock ? " SRB_LOCK+" : " SRB_LOCK-", - opt_ibpb ? " IBPB" : "", - opt_l1d_flush ? " L1D_FLUSH" : "", -@@ -1251,32 +1250,24 @@ void __init init_speculation_mitigations(void) - tsx_init(); - } - -- /* Calculate suitable defaults for MSR_MCU_OPT_CTRL */ -- if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) -+ /* -+ * On some SRBDS-affected hardware, it may be safe to relax srb-lock by -+ * default. -+ * -+ * On parts which enumerate MDS_NO and not TAA_NO, TSX is the only known -+ * way to access the Fill Buffer. If TSX isn't available (inc. SKU -+ * reasons on some models), or TSX is explicitly disabled, then there is -+ * no need for the extra overhead to protect RDRAND/RDSEED. -+ */ -+ if ( cpu_has_srbds_ctrl ) - { -- uint64_t val; -- -- rdmsrl(MSR_MCU_OPT_CTRL, val); -- -- /* -- * On some SRBDS-affected hardware, it may be safe to relax srb-lock -- * by default. -- * -- * On parts which enumerate MDS_NO and not TAA_NO, TSX is the only way -- * to access the Fill Buffer. If TSX isn't available (inc. SKU -- * reasons on some models), or TSX is explicitly disabled, then there -- * is no need for the extra overhead to protect RDRAND/RDSEED. -- */ - if ( opt_srb_lock == -1 && - (caps & (ARCH_CAPS_MDS_NO|ARCH_CAPS_TAA_NO)) == ARCH_CAPS_MDS_NO && - (!cpu_has_hle || ((caps & ARCH_CAPS_TSX_CTRL) && rtm_disabled)) ) - opt_srb_lock = 0; - -- val &= ~MCU_OPT_CTRL_RNGDS_MITG_DIS; -- if ( !opt_srb_lock ) -- val |= MCU_OPT_CTRL_RNGDS_MITG_DIS; -- -- default_xen_mcu_opt_ctrl = val; -+ set_in_mcu_opt_ctrl(MCU_OPT_CTRL_RNGDS_MITG_DIS, -+ opt_srb_lock ? 0 : MCU_OPT_CTRL_RNGDS_MITG_DIS); - } - - print_details(thunk, caps); -@@ -1314,9 +1305,6 @@ void __init init_speculation_mitigations(void) - wrmsrl(MSR_SPEC_CTRL, val); - info->last_spec_ctrl = val; - } -- -- if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) -- wrmsrl(MSR_MCU_OPT_CTRL, default_xen_mcu_opt_ctrl); - } - - static void __init __maybe_unused build_assertions(void) -diff --git a/xen/include/asm-x86/cpufeature.h b/xen/include/asm-x86/cpufeature.h -index ba0fe7c0aa..0ff6d899f9 100644 ---- a/xen/include/asm-x86/cpufeature.h -+++ b/xen/include/asm-x86/cpufeature.h -@@ -133,6 +133,7 @@ - #define cpu_has_avx512_4vnniw boot_cpu_has(X86_FEATURE_AVX512_4VNNIW) - #define cpu_has_avx512_4fmaps boot_cpu_has(X86_FEATURE_AVX512_4FMAPS) - #define cpu_has_avx512_vp2intersect boot_cpu_has(X86_FEATURE_AVX512_VP2INTERSECT) -+#define cpu_has_srbds_ctrl boot_cpu_has(X86_FEATURE_SRBDS_CTRL) - #define cpu_has_rtm_always_abort boot_cpu_has(X86_FEATURE_RTM_ALWAYS_ABORT) - #define cpu_has_tsx_force_abort boot_cpu_has(X86_FEATURE_TSX_FORCE_ABORT) - #define cpu_has_serialize boot_cpu_has(X86_FEATURE_SERIALIZE) -diff --git a/xen/include/asm-x86/processor.h b/xen/include/asm-x86/processor.h -index bc4dc69253..3d8aacd3aa 100644 ---- a/xen/include/asm-x86/processor.h -+++ b/xen/include/asm-x86/processor.h -@@ -630,6 +630,9 @@ extern int8_t opt_tsx, cpu_has_tsx_ctrl; - extern bool rtm_disabled; - void tsx_init(void); - -+void update_mcu_opt_ctrl(void); -+void set_in_mcu_opt_ctrl(uint32_t mask, uint32_t val); -+ - enum ap_boot_method { - AP_BOOT_NORMAL, - AP_BOOT_SKINIT, -diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h -index a803d16f90..f760295236 100644 ---- a/xen/include/asm-x86/spec_ctrl.h -+++ b/xen/include/asm-x86/spec_ctrl.h -@@ -54,8 +54,6 @@ extern int8_t opt_pv_l1tf_hwdom, opt_pv_l1tf_domu; - */ - extern paddr_t l1tf_addr_mask, l1tf_safe_maddr; - --extern uint64_t default_xen_mcu_opt_ctrl; -- - static inline void init_shadow_spec_ctrl_state(void) - { - struct cpu_info *info = get_cpu_info(); diff --git a/xen.git-6ef732726add103ee8f63293e326ad43b1643239.patch b/xen.git-6ef732726add103ee8f63293e326ad43b1643239.patch deleted file mode 100644 index c5983a1..0000000 --- a/xen.git-6ef732726add103ee8f63293e326ad43b1643239.patch +++ /dev/null @@ -1,148 +0,0 @@ -From: Andrew Cooper -Date: Fri, 28 Jan 2022 11:57:19 +0000 (+0000) -Subject: x86/spec-ctrl: Record the last write to MSR_SPEC_CTRL -X-Git-Url: http://xenbits.xenproject.org/gitweb/?p=xen.git;a=commitdiff_plain;h=6ef732726add103ee8f63293e326ad43b1643239 - -x86/spec-ctrl: Record the last write to MSR_SPEC_CTRL - -In some cases, writes to MSR_SPEC_CTRL do not have interesting side effects, -and we should implement lazy context switching like we do with other MSRs. - -In the short term, this will be used by the SVM infrastructure, but I expect -to extend it to other contexts in due course. - -Introduce cpu_info.last_spec_ctrl for the purpose, and cache writes made from -the boot/resume paths. The value can't live in regular per-cpu data when it -is eventually used for PV guests when XPTI might be active. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -(cherry picked from commit 00f2992b6c7a9d4090443c1a85bf83224a87eeb9) ---- - -diff --git a/xen/arch/x86/acpi/power.c b/xen/arch/x86/acpi/power.c -index 0837a3ead4..bac9c16389 100644 ---- a/xen/arch/x86/acpi/power.c -+++ b/xen/arch/x86/acpi/power.c -@@ -296,7 +296,10 @@ static int enter_state(u32 state) - ci->spec_ctrl_flags |= (default_spec_ctrl_flags & SCF_ist_wrmsr); - - if ( boot_cpu_has(X86_FEATURE_IBRSB) ) -+ { - wrmsrl(MSR_SPEC_CTRL, default_xen_spec_ctrl); -+ ci->last_spec_ctrl = default_xen_spec_ctrl; -+ } - - if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) - wrmsrl(MSR_MCU_OPT_CTRL, default_xen_mcu_opt_ctrl); -diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c -index da47cdea14..369691dd13 100644 ---- a/xen/arch/x86/setup.c -+++ b/xen/arch/x86/setup.c -@@ -1944,9 +1944,12 @@ void __init noreturn __start_xen(unsigned long mbi_p) - - if ( bsp_delay_spec_ctrl ) - { -- get_cpu_info()->spec_ctrl_flags &= ~SCF_use_shadow; -+ struct cpu_info *info = get_cpu_info(); -+ -+ info->spec_ctrl_flags &= ~SCF_use_shadow; - barrier(); - wrmsrl(MSR_SPEC_CTRL, default_xen_spec_ctrl); -+ info->last_spec_ctrl = default_xen_spec_ctrl; - } - - /* Jump to the 1:1 virtual mappings of cpu0_stack. */ -diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c -index 329cfdb6c9..ee3e86cc78 100644 ---- a/xen/arch/x86/smpboot.c -+++ b/xen/arch/x86/smpboot.c -@@ -322,6 +322,8 @@ static void set_cpu_sibling_map(unsigned int cpu) - - void start_secondary(void *unused) - { -+ struct cpu_info *info = get_cpu_info(); -+ - /* - * Dont put anything before smp_callin(), SMP booting is so fragile that we - * want to limit the things done here to the most necessary things. -@@ -378,7 +380,10 @@ void start_secondary(void *unused) - * microcode. - */ - if ( boot_cpu_has(X86_FEATURE_IBRSB) ) -+ { - wrmsrl(MSR_SPEC_CTRL, default_xen_spec_ctrl); -+ info->last_spec_ctrl = default_xen_spec_ctrl; -+ } - if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) - wrmsrl(MSR_MCU_OPT_CTRL, default_xen_mcu_opt_ctrl); - -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 2072daf662..b2fd86ebe5 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -1270,6 +1270,9 @@ void __init init_speculation_mitigations(void) - */ - if ( has_spec_ctrl ) - { -+ struct cpu_info *info = get_cpu_info(); -+ unsigned int val; -+ - bsp_delay_spec_ctrl = !cpu_has_hypervisor && default_xen_spec_ctrl; - - /* -@@ -1278,15 +1281,16 @@ void __init init_speculation_mitigations(void) - */ - if ( bsp_delay_spec_ctrl ) - { -- struct cpu_info *info = get_cpu_info(); -- - info->shadow_spec_ctrl = 0; - barrier(); - info->spec_ctrl_flags |= SCF_use_shadow; - barrier(); - } - -- wrmsrl(MSR_SPEC_CTRL, bsp_delay_spec_ctrl ? 0 : default_xen_spec_ctrl); -+ val = bsp_delay_spec_ctrl ? 0 : default_xen_spec_ctrl; -+ -+ wrmsrl(MSR_SPEC_CTRL, val); -+ info->last_spec_ctrl = val; - } - - if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) -diff --git a/xen/include/asm-x86/current.h b/xen/include/asm-x86/current.h -index a74ad4bc4c..8ea4aecc5e 100644 ---- a/xen/include/asm-x86/current.h -+++ b/xen/include/asm-x86/current.h -@@ -56,6 +56,7 @@ struct cpu_info { - /* See asm-x86/spec_ctrl_asm.h for usage. */ - unsigned int shadow_spec_ctrl; - uint8_t xen_spec_ctrl; -+ uint8_t last_spec_ctrl; - uint8_t spec_ctrl_flags; - - /* -@@ -73,7 +74,6 @@ struct cpu_info { - */ - bool use_pv_cr3; - -- unsigned long __pad; - /* get_stack_bottom() must be 16-byte aligned */ - }; - -diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h -index bf82528a12..9c0c7622c4 100644 ---- a/xen/include/asm-x86/spec_ctrl_asm.h -+++ b/xen/include/asm-x86/spec_ctrl_asm.h -@@ -67,6 +67,10 @@ - * steps 2 and 6 will restore the shadow value rather than leaving Xen's value - * loaded and corrupting the value used in guest context. - * -+ * Additionally, in some cases it is safe to skip writes to MSR_SPEC_CTRL when -+ * we don't require any of the side effects of an identical write. Maintain a -+ * per-cpu last_spec_ctrl value for this purpose. -+ * - * The following ASM fragments implement this algorithm. See their local - * comments for further details. - * - SPEC_CTRL_ENTRY_FROM_PV diff --git a/xen.git-72ef02da23861f686c349a6808b2f4c9adc15f9f.patch b/xen.git-72ef02da23861f686c349a6808b2f4c9adc15f9f.patch deleted file mode 100644 index 3231b6a..0000000 --- a/xen.git-72ef02da23861f686c349a6808b2f4c9adc15f9f.patch +++ /dev/null @@ -1,45 +0,0 @@ -From: Andrew Cooper -Date: Fri, 28 Jan 2022 12:03:42 +0000 (+0000) -Subject: x86/spec-ctrl: Don't use spec_ctrl_{enter,exit}_idle() for S3 -X-Git-Url: http://xenbits.xenproject.org/gitweb/?p=xen.git;a=commitdiff_plain;h=72ef02da23861f686c349a6808b2f4c9adc15f9f - -x86/spec-ctrl: Don't use spec_ctrl_{enter,exit}_idle() for S3 - -'idle' here refers to hlt/mwait. The S3 path isn't an idle path - it is a -platform reset. - -We need to load default_xen_spec_ctrl unilaterally on the way back up. -Currently it happens as a side effect of X86_FEATURE_SC_MSR_IDLE or the next -return-to-guest, but that's fragile behaviour. - -Conversely, there is no need to clear IBRS and flush the store buffers on the -way down; we're microseconds away from cutting power. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -(cherry picked from commit 71fac402e05ade7b0af2c34f77517449f6f7e2c1) ---- - -diff --git a/xen/arch/x86/acpi/power.c b/xen/arch/x86/acpi/power.c -index 31a56f02d0..0837a3ead4 100644 ---- a/xen/arch/x86/acpi/power.c -+++ b/xen/arch/x86/acpi/power.c -@@ -248,7 +248,6 @@ static int enter_state(u32 state) - error = 0; - - ci = get_cpu_info(); -- spec_ctrl_enter_idle(ci); - /* Avoid NMI/#MC using MSR_SPEC_CTRL until we've reloaded microcode. */ - ci->spec_ctrl_flags &= ~SCF_ist_wrmsr; - -@@ -295,7 +294,9 @@ static int enter_state(u32 state) - - /* Re-enabled default NMI/#MC use of MSR_SPEC_CTRL. */ - ci->spec_ctrl_flags |= (default_spec_ctrl_flags & SCF_ist_wrmsr); -- spec_ctrl_exit_idle(ci); -+ -+ if ( boot_cpu_has(X86_FEATURE_IBRSB) ) -+ wrmsrl(MSR_SPEC_CTRL, default_xen_spec_ctrl); - - if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) - wrmsrl(MSR_MCU_OPT_CTRL, default_xen_mcu_opt_ctrl); diff --git a/xen.git-7f34b6a895d10744bab32fc843246c45da444d8b.patch b/xen.git-7f34b6a895d10744bab32fc843246c45da444d8b.patch deleted file mode 100644 index 8ce520d..0000000 --- a/xen.git-7f34b6a895d10744bab32fc843246c45da444d8b.patch +++ /dev/null @@ -1,62 +0,0 @@ -From: Andrew Cooper -Date: Tue, 25 Jan 2022 16:09:59 +0000 (+0000) -Subject: x86/spec-ctrl: Drop use_spec_ctrl boolean -X-Git-Url: http://xenbits.xenproject.org/gitweb/?p=xen.git;a=commitdiff_plain;h=7f34b6a895d10744bab32fc843246c45da444d8b - -x86/spec-ctrl: Drop use_spec_ctrl boolean - -Several bugfixes have reduced the utility of this variable from it's original -purpose, and now all it does is aid in the setup of SCF_ist_wrmsr. - -Simplify the logic by drop the variable, and doubling up the setting of -SCF_ist_wrmsr for the PV and HVM blocks, which will make the AMD SPEC_CTRL -support easier to follow. Leave a comment explaining why SCF_ist_wrmsr is -still necessary for the VMExit case. - -No functional change. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -(cherry picked from commit ec083bf552c35e10347449e21809f4780f8155d2) ---- - -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index c18cc8aa49..8a550d0a09 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -927,7 +927,7 @@ static __init void mds_calculations(uint64_t caps) - void __init init_speculation_mitigations(void) - { - enum ind_thunk thunk = THUNK_DEFAULT; -- bool use_spec_ctrl = false, ibrs = false, hw_smt_enabled; -+ bool ibrs = false, hw_smt_enabled; - bool cpu_has_bug_taa; - uint64_t caps = 0; - -@@ -1016,19 +1016,21 @@ void __init init_speculation_mitigations(void) - { - if ( opt_msr_sc_pv ) - { -- use_spec_ctrl = true; -+ default_spec_ctrl_flags |= SCF_ist_wrmsr; - setup_force_cpu_cap(X86_FEATURE_SC_MSR_PV); - } - - if ( opt_msr_sc_hvm ) - { -- use_spec_ctrl = true; -+ /* -+ * While the guest MSR_SPEC_CTRL value is loaded/saved atomically, -+ * Xen's value is not restored atomically. An early NMI hitting -+ * the VMExit path needs to restore Xen's value for safety. -+ */ -+ default_spec_ctrl_flags |= SCF_ist_wrmsr; - setup_force_cpu_cap(X86_FEATURE_SC_MSR_HVM); - } - -- if ( use_spec_ctrl ) -- default_spec_ctrl_flags |= SCF_ist_wrmsr; -- - if ( ibrs ) - default_xen_spec_ctrl |= SPEC_CTRL_IBRS; - } diff --git a/xen.git-84977e8b53935de9a1123f677213f1b146843a0e.patch b/xen.git-84977e8b53935de9a1123f677213f1b146843a0e.patch deleted file mode 100644 index 1b73e73..0000000 --- a/xen.git-84977e8b53935de9a1123f677213f1b146843a0e.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 84977e8b53935de9a1123f677213f1b146843a0e Mon Sep 17 00:00:00 2001 -From: Jan Beulich -Date: Thu, 6 Jan 2022 14:11:23 +0100 -Subject: [PATCH] VT-d: don't leak domid mapping on error path - -While domain_context_mapping() invokes domain_context_unmap() in a sub- -case of handling DEV_TYPE_PCI when encountering an error, thus avoiding -a leak, individual calls to domain_context_mapping_one() aren't -similarly covered. Such a leak might persist until domain destruction. -Leverage that these cases can be recognized by pdev being non-NULL. - -Fixes: dec403cc668f ("VT-d: fix iommu_domid for PCI/PCIx devices assignment") -Signed-off-by: Jan Beulich -Reviewed-by: Kevin Tian -master commit: e6252a51faf42c892eb5fc71f8a2617580832196 -master date: 2021-11-24 11:07:11 +0100 ---- - xen/drivers/passthrough/vtd/iommu.c | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - -diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c -index de11c258ca..3b37bad25e 100644 ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -1517,7 +1517,12 @@ int domain_context_mapping_one( - rc = me_wifi_quirk(domain, bus, devfn, MAP_ME_PHANTOM_FUNC); - - if ( rc ) -- domain_context_unmap_one(domain, iommu, bus, devfn); -+ { -+ ret = domain_context_unmap_one(domain, iommu, bus, devfn); -+ -+ if ( !ret && pdev && pdev->devfn == devfn ) -+ check_cleanup_domid_map(domain, pdev, iommu); -+ } - - return rc; - } --- -2.30.2 - diff --git a/xen.git-b2db518e952c3a8fe5b9ec6a2d007cda73fd05a4.patch b/xen.git-b2db518e952c3a8fe5b9ec6a2d007cda73fd05a4.patch deleted file mode 100644 index 0b2d15a..0000000 --- a/xen.git-b2db518e952c3a8fe5b9ec6a2d007cda73fd05a4.patch +++ /dev/null @@ -1,36 +0,0 @@ -From b2db518e952c3a8fe5b9ec6a2d007cda73fd05a4 Mon Sep 17 00:00:00 2001 -From: Jan Beulich -Date: Thu, 10 Mar 2022 09:43:50 +0100 -Subject: [PATCH] VT-d: drop undue address-of from check_cleanup_domid_map() -MIME-Version: 1.0 -Content-Type: text/plain; charset=utf8 -Content-Transfer-Encoding: 8bit - -For an unknown reason I added back the operator while backporting, -despite 4.16 having c06e3d810314 ("VT-d: per-domain IOMMU bitmap needs -to have dynamic size"). I can only assume that I mistakenly took the -4.15 backport as basis and/or reference. - -Fixes: fa45f6b5560e ("VT-d: split domid map cleanup check into a function") -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné ---- - xen/drivers/passthrough/vtd/iommu.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c -index 3b37bad25e..ead12db6a4 100644 ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -197,7 +197,7 @@ static void check_cleanup_domid_map(struct domain *d, - - if ( !found ) - { -- clear_bit(iommu->index, &dom_iommu(d)->arch.vtd.iommu_bitmap); -+ clear_bit(iommu->index, dom_iommu(d)->arch.vtd.iommu_bitmap); - cleanup_domid_map(d, iommu); - } - } --- -2.30.2 - diff --git a/xen.git-fa45f6b5560e738955993fe061a04d64c6f71c14.patch b/xen.git-fa45f6b5560e738955993fe061a04d64c6f71c14.patch deleted file mode 100644 index a5fad75..0000000 --- a/xen.git-fa45f6b5560e738955993fe061a04d64c6f71c14.patch +++ /dev/null @@ -1,142 +0,0 @@ -From: Jan Beulich -Date: Thu, 6 Jan 2022 13:10:05 +0000 (+0100) -Subject: VT-d: split domid map cleanup check into a function -X-Git-Url: http://xenbits.xenproject.org/gitweb/?p=xen.git;a=commitdiff_plain;h=fa45f6b5560e738955993fe061a04d64c6f71c14 - -VT-d: split domid map cleanup check into a function - -This logic will want invoking from elsewhere. - -No functional change intended. - -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné -Reviewed-by: Kevin Tian -master commit: 9fdc10abe9457e4c9879a266f82372cb08e88ffb -master date: 2021-11-24 11:06:20 +0100 ---- - -diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c -index f9ce402f22..de11c258ca 100644 ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -157,6 +157,51 @@ static void cleanup_domid_map(struct domain *domain, struct vtd_iommu *iommu) - } - } - -+static bool any_pdev_behind_iommu(const struct domain *d, -+ const struct pci_dev *exclude, -+ const struct vtd_iommu *iommu) -+{ -+ const struct pci_dev *pdev; -+ -+ for_each_pdev ( d, pdev ) -+ { -+ const struct acpi_drhd_unit *drhd; -+ -+ if ( pdev == exclude ) -+ continue; -+ -+ drhd = acpi_find_matched_drhd_unit(pdev); -+ if ( drhd && drhd->iommu == iommu ) -+ return true; -+ } -+ -+ return false; -+} -+ -+/* -+ * If no other devices under the same iommu owned by this domain, -+ * clear iommu in iommu_bitmap and clear domain_id in domid_bitmap. -+ */ -+static void check_cleanup_domid_map(struct domain *d, -+ const struct pci_dev *exclude, -+ struct vtd_iommu *iommu) -+{ -+ bool found = any_pdev_behind_iommu(d, exclude, iommu); -+ -+ /* -+ * Hidden devices are associated with DomXEN but usable by the hardware -+ * domain. Hence they need considering here as well. -+ */ -+ if ( !found && is_hardware_domain(d) ) -+ found = any_pdev_behind_iommu(dom_xen, exclude, iommu); -+ -+ if ( !found ) -+ { -+ clear_bit(iommu->index, &dom_iommu(d)->arch.vtd.iommu_bitmap); -+ cleanup_domid_map(d, iommu); -+ } -+} -+ - static void sync_cache(const void *addr, unsigned int size) - { - static unsigned long clflush_size = 0; -@@ -1674,27 +1719,6 @@ int domain_context_unmap_one( - return rc; - } - --static bool any_pdev_behind_iommu(const struct domain *d, -- const struct pci_dev *exclude, -- const struct vtd_iommu *iommu) --{ -- const struct pci_dev *pdev; -- -- for_each_pdev ( d, pdev ) -- { -- const struct acpi_drhd_unit *drhd; -- -- if ( pdev == exclude ) -- continue; -- -- drhd = acpi_find_matched_drhd_unit(pdev); -- if ( drhd && drhd->iommu == iommu ) -- return true; -- } -- -- return false; --} -- - static int domain_context_unmap(struct domain *domain, u8 devfn, - struct pci_dev *pdev) - { -@@ -1703,7 +1727,6 @@ static int domain_context_unmap(struct domain *domain, u8 devfn, - int ret; - uint16_t seg = pdev->seg; - uint8_t bus = pdev->bus, tmp_bus, tmp_devfn, secbus; -- bool found; - - switch ( pdev->type ) - { -@@ -1779,28 +1802,10 @@ static int domain_context_unmap(struct domain *domain, u8 devfn, - return -EINVAL; - } - -- if ( ret || QUARANTINE_SKIP(domain) || pdev->devfn != devfn ) -- return ret; -+ if ( !ret && !QUARANTINE_SKIP(domain) && pdev->devfn == devfn ) -+ check_cleanup_domid_map(domain, pdev, iommu); - -- /* -- * If no other devices under the same iommu owned by this domain, -- * clear iommu in iommu_bitmap and clear domain_id in domid_bitmap. -- */ -- found = any_pdev_behind_iommu(domain, pdev, iommu); -- /* -- * Hidden devices are associated with DomXEN but usable by the hardware -- * domain. Hence they need considering here as well. -- */ -- if ( !found && is_hardware_domain(domain) ) -- found = any_pdev_behind_iommu(dom_xen, pdev, iommu); -- -- if ( !found ) -- { -- clear_bit(iommu->index, dom_iommu(domain)->arch.vtd.iommu_bitmap); -- cleanup_domid_map(domain, iommu); -- } -- -- return 0; -+ return ret; - } - - static void iommu_clear_root_pgtable(struct domain *d) diff --git a/xen.spec b/xen.spec index d14f502..01ae49b 100644 --- a/xen.spec +++ b/xen.spec @@ -54,8 +54,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.16.0 -Release: 6%{?dist} +Version: 4.16.1 +Release: 1%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -109,41 +109,8 @@ Patch41: xen.gcc9.fixes.patch Patch42: xen.gcc10.fixes.patch Patch43: xen.gcc11.fixes.patch Patch44: xsa376.patch -Patch45: xsa393.patch -Patch46: xsa394.patch -Patch47: xsa395.patch -Patch48: xen.gcc12.fixes.patch -Patch49: xsa398-4.16-1-xen-arm-Introduce-new-Arm-processors.patch -Patch50: xsa398-4.16-2-xen-arm-move-errata-CSV2-check-earlier.patch -Patch51: xsa398-4.16-3-xen-arm-Add-ECBHB-and-CLEARBHB-ID-fields.patch -Patch52: xsa398-4.16-4-xen-arm-Add-Spectre-BHB-handling.patch -Patch53: xsa398-4.16-5-xen-arm-Allow-to-discover-and-use-SMCCC_ARCH_WORKARO.patch -Patch54: xen.git-7f34b6a895d10744bab32fc843246c45da444d8b.patch -Patch55: xen.git-08fc03c855c071e9b1aaaa96403f2a90433336a7.patch -Patch56: xen.git-21d70feed10571543061abeaedd21ce8adc60114.patch -Patch57: xen.git-72ef02da23861f686c349a6808b2f4c9adc15f9f.patch -Patch58: xen.git-6ef732726add103ee8f63293e326ad43b1643239.patch -Patch59: xen.git-243026a2c5ad64c05281dc8ed2f1f57c0ee5988c.patch -Patch60: xen.git-41e477b4f367269dc1b768a335cfa16f48f7f02f.patch -Patch61: xsa398-4.16-6-x86-spec-ctrl-Cease-using-thunk-lfence-on-AMD.patch -Patch62: xen.efi.build.patch -Patch63: xsa397-4.16.patch -Patch64: xsa399-4.16.patch -Patch65: xen.git-fa45f6b5560e738955993fe061a04d64c6f71c14.patch -Patch66: xen.git-84977e8b53935de9a1123f677213f1b146843a0e.patch -Patch67: xen.git-b2db518e952c3a8fe5b9ec6a2d007cda73fd05a4.patch -Patch68: xsa400-4.16-01.patch -Patch69: xsa400-4.16-02.patch -Patch70: xsa400-4.16-03.patch -Patch71: xsa400-4.16-04.patch -Patch72: xsa400-4.16-05.patch -Patch73: xsa400-4.16-06.patch -Patch74: xsa400-4.16-07.patch -Patch75: xsa400-4.16-08.patch -Patch76: xsa400-4.16-09.patch -Patch77: xsa400-4.16-10.patch -Patch78: xsa400-4.16-11.patch -Patch79: xsa400-4.16-12.patch +Patch45: xen.gcc12.fixes.patch +Patch46: xen.efi.build.patch %if %build_qemutrad @@ -356,39 +323,6 @@ manage Xen virtual machines. %patch44 -p1 %patch45 -p1 %patch46 -p1 -%patch47 -p1 -%patch48 -p1 -%patch49 -p1 -%patch50 -p1 -%patch51 -p1 -%patch52 -p1 -%patch53 -p1 -%patch54 -p1 -%patch55 -p1 -%patch56 -p1 -%patch57 -p1 -%patch58 -p1 -%patch59 -p1 -%patch60 -p1 -%patch61 -p1 -%patch62 -p1 -%patch63 -p1 -%patch64 -p1 -%patch65 -p1 -%patch66 -p1 -%patch67 -p1 -%patch68 -p1 -%patch69 -p1 -%patch70 -p1 -%patch71 -p1 -%patch72 -p1 -%patch73 -p1 -%patch74 -p1 -%patch75 -p1 -%patch76 -p1 -%patch77 -p1 -%patch78 -p1 -%patch79 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -559,10 +493,14 @@ rm -rf %{buildroot}/%{_libdir}/*.a rm -f %{buildroot}/usr/lib64/efi/xen-%{hv_abi}.efi rm -f %{buildroot}/usr/lib64/efi/xen-4.efi rm -f %{buildroot}/usr/lib64/efi/xen.efi +cp -p %{buildroot}/usr/lib64/efi/xen-%{version}{,.notstripped}.efi +strip -s %{buildroot}/usr/lib64/efi/xen-%{version}.efi %else rm -f %{buildroot}/%{_libdir}/efi/xen-%{hv_abi}.efi rm -f %{buildroot}/%{_libdir}/efi/xen-4.efi rm -f %{buildroot}/%{_libdir}/efi/xen.efi +cp -p %{buildroot}/%{_libdir}/efi/xen-%{version}{,.notstripped}.efi +strip -s %{buildroot}/%{_libdir}/efi/xen-%{version}.efi %endif %endif @@ -1000,6 +938,12 @@ fi %endif %changelog +* Thu Apr 14 2022 Michael Young - 4.16.1-1 +- update to xen-4.16.1 + remove or adjust patches now included or superceded upstream + renumber patches +- strip .efi file to help EFI partitions with limited space + * Tue Apr 05 2022 Michael Young - 4.16.0-6 - Racy interactions between dirty vram tracking and paging log dirty hypercalls [XSA-397, CVE-2022-26356] diff --git a/xsa393.patch b/xsa393.patch deleted file mode 100644 index 57af36b..0000000 --- a/xsa393.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 7ff58ab770157a03c92604155a0c745bcab834c2 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Tue, 14 Dec 2021 09:53:44 +0000 -Subject: [PATCH] xen/arm: p2m: Always clear the P2M entry when the mapping is - removed - -Commit 2148a125b73b ("xen/arm: Track page accessed between batch of -Set/Way operations") allowed an entry to be invalid from the CPU PoV -(lpae_is_valid()) but valid for Xen (p2m_is_valid()). This is useful -to track which page is accessed and only perform an action on them -(e.g. clean & invalidate the cache after a set/way instruction). - -Unfortunately, __p2m_set_entry() is only zeroing the P2M entry when -lpae_is_valid() returns true. This means the entry will not be zeroed -if the entry was valid from Xen PoV but invalid from the CPU PoV for -tracking purpose. - -As a consequence, this will allow a domain to continue to access the -page after it was removed. - -Resolve the issue by always zeroing the entry if it the LPAE bit is -set or the entry is about to be removed. - -This is CVE-2022-23033 / XSA-393. - -Reported-by: Dmytro Firsov -Fixes: 2148a125b73b ("xen/arm: Track page accessed between batch of Set/Way operations") -Reviewed-by: Stefano Stabellini -Signed-off-by: Julien Grall ---- - xen/arch/arm/p2m.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c -index 8b20b430777e..fb71fa4c1c90 100644 ---- a/xen/arch/arm/p2m.c -+++ b/xen/arch/arm/p2m.c -@@ -1016,7 +1016,7 @@ static int __p2m_set_entry(struct p2m_domain *p2m, - * sequence when updating the translation table (D4.7.1 in ARM DDI - * 0487A.j). - */ -- if ( lpae_is_valid(orig_pte) ) -+ if ( lpae_is_valid(orig_pte) || removing_mapping ) - p2m_remove_pte(entry, p2m->clean_pte); - - if ( removing_mapping ) --- -2.32.0 - diff --git a/xsa394.patch b/xsa394.patch deleted file mode 100644 index 1704c5b..0000000 --- a/xsa394.patch +++ /dev/null @@ -1,63 +0,0 @@ -From a8bdee7a30d0cd13341d2ca1753569b171daf5b8 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Fri, 19 Nov 2021 11:27:47 +0000 -Subject: [PATCH] xen/grant-table: Only decrement the refcounter when grant is - fully unmapped - -The grant unmapping hypercall (GNTTABOP_unmap_grant_ref) is not a -simple revert of the changes done by the grant mapping hypercall -(GNTTABOP_map_grant_ref). - -Instead, it is possible to partially (or even not) clear some flags. -This will leave the grant is mapped until a future call where all -the flags would be cleared. - -XSA-380 introduced a refcounting that is meant to only be dropped -when the grant is fully unmapped. Unfortunately, unmap_common() will -decrement the refcount for every successful call. - -A consequence is a domain would be able to underflow the refcount -and trigger a BUG(). - -Looking at the code, it is not clear to me why a domain would -want to partially clear some flags in the grant-table. But as -this is part of the ABI, it is better to not change the behavior -for now. - -Fix it by checking if the maptrack handle has been released before -decrementing the refcounting. - -This is CVE-2022-23034 / XSA-394. - -Fixes: 9781b51efde2 ("gnttab: replace mapkind()") -Signed-off-by: Julien Grall -Reviewed-by: Jan Beulich ---- - xen/common/grant_table.c | 11 +++++++++-- - 1 file changed, 9 insertions(+), 2 deletions(-) - -diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c -index 0262f2c48af8..ed1e2fabcea6 100644 ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -1488,8 +1488,15 @@ unmap_common( - if ( put_handle ) - put_maptrack_handle(lgt, op->handle); - -- /* See the respective comment in map_grant_ref(). */ -- if ( rc == GNTST_okay && ld != rd && gnttab_need_iommu_mapping(ld) ) -+ /* -+ * map_grant_ref() will only increment the refcount (and update the -+ * IOMMU) once per mapping. So we only want to decrement it once the -+ * maptrack handle has been put, alongside the further IOMMU update. -+ * -+ * For the second and third check, see the respective comment in -+ * map_grant_ref(). -+ */ -+ if ( put_handle && ld != rd && gnttab_need_iommu_mapping(ld) ) - { - void **slot; - union maptrack_node node; --- -2.32.0 - diff --git a/xsa395.patch b/xsa395.patch deleted file mode 100644 index 13b7311..0000000 --- a/xsa395.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 4cc924c3e3a0d53306d08b04720c427d1c298ba8 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Wed, 5 Jan 2022 18:09:20 +0000 -Subject: [PATCH] passthrough/x86: stop pirq iteration immediately in case of - error - -pt_pirq_iterate() will iterate in batch over all the PIRQs. The outer -loop will bail out if 'rc' is non-zero but the inner loop will continue. - -This means 'rc' will get clobbered and we may miss any errors (such as --ERESTART in the case of the callback pci_clean_dpci_irq()). - -This is CVE-2022-23035 / XSA-395. - -Fixes: c24536b636f2 ("replace d->nr_pirqs sized arrays with radix tree") -Fixes: f6dd295381f4 ("dpci: replace tasklet with softirq") -Signed-off-by: Julien Grall -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné ---- - xen/drivers/passthrough/x86/hvm.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/xen/drivers/passthrough/x86/hvm.c b/xen/drivers/passthrough/x86/hvm.c -index 351daafdc9bf..0b37cd145b60 100644 ---- a/xen/drivers/passthrough/x86/hvm.c -+++ b/xen/drivers/passthrough/x86/hvm.c -@@ -732,7 +732,11 @@ int pt_pirq_iterate(struct domain *d, - - pirq = pirqs[i]->pirq; - if ( (pirq_dpci->flags & HVM_IRQ_DPCI_MAPPED) ) -+ { - rc = cb(d, pirq_dpci, arg); -+ if ( rc ) -+ break; -+ } - } - } while ( !rc && ++pirq < d->nr_pirqs && n == ARRAY_SIZE(pirqs) ); - --- -2.32.0 - diff --git a/xsa397-4.16.patch b/xsa397-4.16.patch deleted file mode 100644 index c04cd54..0000000 --- a/xsa397-4.16.patch +++ /dev/null @@ -1,98 +0,0 @@ -From: Roger Pau Monne -Subject: x86/hap: do not switch on log dirty for VRAM tracking - -XEN_DMOP_track_dirty_vram possibly calls into paging_log_dirty_enable -when using HAP mode, and it can interact badly with other ongoing -paging domctls, as XEN_DMOP_track_dirty_vram is not holding the domctl -lock. - -This was detected as a result of the following assert triggering when -doing repeated migrations of a HAP HVM domain with a stubdom: - -Assertion 'd->arch.paging.log_dirty.allocs == 0' failed at paging.c:198 -----[ Xen-4.17-unstable x86_64 debug=y Not tainted ]---- -CPU: 34 -RIP: e008:[] arch/x86/mm/paging.c#paging_free_log_dirty_bitmap+0x606/0x6 -RFLAGS: 0000000000010206 CONTEXT: hypervisor (d0v23) -[...] -Xen call trace: - [] R arch/x86/mm/paging.c#paging_free_log_dirty_bitmap+0x606/0x63a - [] S xsm/flask/hooks.c#domain_has_perm+0x5a/0x67 - [] F paging_domctl+0x251/0xd41 - [] F paging_domctl_continuation+0x19d/0x202 - [] F pv_hypercall+0x150/0x2a7 - [] F lstar_enter+0x12d/0x140 - -Such assert triggered because the stubdom used -XEN_DMOP_track_dirty_vram while dom0 was in the middle of executing -XEN_DOMCTL_SHADOW_OP_OFF, and so log dirty become enabled while -retiring the old structures, thus leading to new entries being -populated in already clear slots. - -Fix this by not enabling log dirty for VRAM tracking, similar to what -is done when using shadow instead of HAP. Call -p2m_enable_hardware_log_dirty when enabling VRAM tracking in order to -get some hardware assistance if available. As a side effect the memory -pressure on the p2m pool should go down if only VRAM tracking is -enabled, as the dirty bitmap is no longer allocated. - -Note that paging_log_dirty_range (used to get the dirty bitmap for -VRAM tracking) doesn't use the log dirty bitmap, and instead relies on -checking whether each gfn on the range has been switched from -p2m_ram_logdirty to p2m_ram_rw in order to account for dirty pages. - -This is CVE-2022-26356 / XSA-397. - -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich - ---- a/xen/include/asm-x86/paging.h -+++ b/xen/include/asm-x86/paging.h -@@ -162,9 +162,6 @@ void paging_log_dirty_range(struct domai - unsigned long nr, - uint8_t *dirty_bitmap); - --/* enable log dirty */ --int paging_log_dirty_enable(struct domain *d, bool log_global); -- - /* log dirty initialization */ - void paging_log_dirty_init(struct domain *d, const struct log_dirty_ops *ops); - ---- a/xen/arch/x86/mm/hap/hap.c -+++ b/xen/arch/x86/mm/hap/hap.c -@@ -69,13 +69,6 @@ int hap_track_dirty_vram(struct domain * - { - unsigned int size = DIV_ROUND_UP(nr_frames, BITS_PER_BYTE); - -- if ( !paging_mode_log_dirty(d) ) -- { -- rc = paging_log_dirty_enable(d, false); -- if ( rc ) -- goto out; -- } -- - rc = -ENOMEM; - dirty_bitmap = vzalloc(size); - if ( !dirty_bitmap ) -@@ -107,6 +100,10 @@ int hap_track_dirty_vram(struct domain * - - paging_unlock(d); - -+ domain_pause(d); -+ p2m_enable_hardware_log_dirty(d); -+ domain_unpause(d); -+ - if ( oend > ostart ) - p2m_change_type_range(d, ostart, oend, - p2m_ram_logdirty, p2m_ram_rw); ---- a/xen/arch/x86/mm/paging.c -+++ b/xen/arch/x86/mm/paging.c -@@ -211,7 +211,7 @@ static int paging_free_log_dirty_bitmap( - return rc; - } - --int paging_log_dirty_enable(struct domain *d, bool log_global) -+static int paging_log_dirty_enable(struct domain *d, bool log_global) - { - int ret; - diff --git a/xsa398-4.16-1-xen-arm-Introduce-new-Arm-processors.patch b/xsa398-4.16-1-xen-arm-Introduce-new-Arm-processors.patch deleted file mode 100644 index 7f61307..0000000 --- a/xsa398-4.16-1-xen-arm-Introduce-new-Arm-processors.patch +++ /dev/null @@ -1,63 +0,0 @@ -From 8d18b03c95850239a1a9ebaeb565936c7c9ae070 Mon Sep 17 00:00:00 2001 -From: Bertrand Marquis -Date: Tue, 15 Feb 2022 10:37:51 +0000 -Subject: xen/arm: Introduce new Arm processors - -Add some new processor identifiers in processor.h and sync Xen -definitions with status of Linux 5.17 (declared in -arch/arm64/include/asm/cputype.h). - -This is part of XSA-398 / CVE-2022-23960. - -Signed-off-by: Bertrand Marquis -Acked-by: Julien Grall -(cherry picked from commit 35d1b85a6b43483f6bd007d48757434e54743e98) - -diff --git a/xen/include/asm-arm/processor.h b/xen/include/asm-arm/processor.h -index 8ab2940f688e..852b5f3c24b8 100644 ---- a/xen/include/asm-arm/processor.h -+++ b/xen/include/asm-arm/processor.h -@@ -65,6 +65,7 @@ - #define ARM_CPU_PART_CORTEX_A17 0xC0E - #define ARM_CPU_PART_CORTEX_A15 0xC0F - #define ARM_CPU_PART_CORTEX_A53 0xD03 -+#define ARM_CPU_PART_CORTEX_A35 0xD04 - #define ARM_CPU_PART_CORTEX_A55 0xD05 - #define ARM_CPU_PART_CORTEX_A57 0xD07 - #define ARM_CPU_PART_CORTEX_A72 0xD08 -@@ -72,11 +73,20 @@ - #define ARM_CPU_PART_CORTEX_A75 0xD0A - #define ARM_CPU_PART_CORTEX_A76 0xD0B - #define ARM_CPU_PART_NEOVERSE_N1 0xD0C -+#define ARM_CPU_PART_CORTEX_A77 0xD0D -+#define ARM_CPU_PART_NEOVERSE_V1 0xD40 -+#define ARM_CPU_PART_CORTEX_A78 0xD41 -+#define ARM_CPU_PART_CORTEX_X1 0xD44 -+#define ARM_CPU_PART_CORTEX_A710 0xD47 -+#define ARM_CPU_PART_CORTEX_X2 0xD48 -+#define ARM_CPU_PART_NEOVERSE_N2 0xD49 -+#define ARM_CPU_PART_CORTEX_A78C 0xD4B - - #define MIDR_CORTEX_A12 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A12) - #define MIDR_CORTEX_A17 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A17) - #define MIDR_CORTEX_A15 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A15) - #define MIDR_CORTEX_A53 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A53) -+#define MIDR_CORTEX_A35 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A35) - #define MIDR_CORTEX_A55 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A55) - #define MIDR_CORTEX_A57 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A57) - #define MIDR_CORTEX_A72 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A72) -@@ -84,6 +94,14 @@ - #define MIDR_CORTEX_A75 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A75) - #define MIDR_CORTEX_A76 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A76) - #define MIDR_NEOVERSE_N1 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_N1) -+#define MIDR_CORTEX_A77 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A77) -+#define MIDR_NEOVERSE_V1 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V1) -+#define MIDR_CORTEX_A78 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A78) -+#define MIDR_CORTEX_X1 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X1) -+#define MIDR_CORTEX_A710 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A710) -+#define MIDR_CORTEX_X2 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X2) -+#define MIDR_NEOVERSE_N2 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_N2) -+#define MIDR_CORTEX_A78C MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A78C) - - /* MPIDR Multiprocessor Affinity Register */ - #define _MPIDR_UP (30) diff --git a/xsa398-4.16-2-xen-arm-move-errata-CSV2-check-earlier.patch b/xsa398-4.16-2-xen-arm-move-errata-CSV2-check-earlier.patch deleted file mode 100644 index 4d84830..0000000 --- a/xsa398-4.16-2-xen-arm-move-errata-CSV2-check-earlier.patch +++ /dev/null @@ -1,53 +0,0 @@ -From 3d963874461b3001e33f3ff90e285670f04d16c4 Mon Sep 17 00:00:00 2001 -From: Bertrand Marquis -Date: Tue, 15 Feb 2022 10:39:47 +0000 -Subject: xen/arm: move errata CSV2 check earlier - -CSV2 availability check is done after printing to the user that -workaround 1 will be used. Move the check before to prevent saying to the -user that workaround 1 is used when it is not because it is not needed. -This will also allow to reuse install_bp_hardening_vec function for -other use cases. - -Code previously returning "true", now returns "0" to conform to -enable_smccc_arch_workaround_1 returning an int and surrounding code -doing a "return 0" if workaround is not needed. - -This is part of XSA-398 / CVE-2022-23960. - -Signed-off-by: Bertrand Marquis -Reviewed-by: Julien Grall -(cherry picked from commit 599616d70eb886b9ad0ef9d6b51693ce790504ba) - -diff --git a/xen/arch/arm/cpuerrata.c b/xen/arch/arm/cpuerrata.c -index b398d480f113..00f9ebe9cee0 100644 ---- a/xen/arch/arm/cpuerrata.c -+++ b/xen/arch/arm/cpuerrata.c -@@ -103,13 +103,6 @@ install_bp_hardening_vec(const struct arm_cpu_capabilities *entry, - printk(XENLOG_INFO "CPU%u will %s on exception entry\n", - smp_processor_id(), desc); - -- /* -- * No need to install hardened vector when the processor has -- * ID_AA64PRF0_EL1.CSV2 set. -- */ -- if ( cpu_data[smp_processor_id()].pfr64.csv2 ) -- return true; -- - spin_lock(&bp_lock); - - /* -@@ -167,6 +160,13 @@ static int enable_smccc_arch_workaround_1(void *data) - if ( !entry->matches(entry) ) - return 0; - -+ /* -+ * No need to install hardened vector when the processor has -+ * ID_AA64PRF0_EL1.CSV2 set. -+ */ -+ if ( cpu_data[smp_processor_id()].pfr64.csv2 ) -+ return 0; -+ - if ( smccc_ver < SMCCC_VERSION(1, 1) ) - goto warn; - diff --git a/xsa398-4.16-3-xen-arm-Add-ECBHB-and-CLEARBHB-ID-fields.patch b/xsa398-4.16-3-xen-arm-Add-ECBHB-and-CLEARBHB-ID-fields.patch deleted file mode 100644 index d5f360c..0000000 --- a/xsa398-4.16-3-xen-arm-Add-ECBHB-and-CLEARBHB-ID-fields.patch +++ /dev/null @@ -1,94 +0,0 @@ -From 8aa3833db97e8fe1143c5ece110b9321ce1494ea Mon Sep 17 00:00:00 2001 -From: Bertrand Marquis -Date: Wed, 23 Feb 2022 09:42:18 +0000 -Subject: xen/arm: Add ECBHB and CLEARBHB ID fields - -Introduce ID coprocessor register ID_AA64ISAR2_EL1. -Add definitions in cpufeature and sysregs of ECBHB field in mmfr1 and -CLEARBHB in isar2 ID coprocessor registers. - -This is part of XSA-398 / CVE-2022-23960. - -Signed-off-by: Bertrand Marquis -Acked-by: Julien Grall -(cherry picked from commit 4b68d12d98b8790d8002fcc2c25a9d713374a4d7) - -diff --git a/xen/arch/arm/cpufeature.c b/xen/arch/arm/cpufeature.c -index 6e51f530a80e..a58965f7b9bf 100644 ---- a/xen/arch/arm/cpufeature.c -+++ b/xen/arch/arm/cpufeature.c -@@ -122,6 +122,7 @@ void identify_cpu(struct cpuinfo_arm *c) - - c->isa64.bits[0] = READ_SYSREG(ID_AA64ISAR0_EL1); - c->isa64.bits[1] = READ_SYSREG(ID_AA64ISAR1_EL1); -+ c->isa64.bits[2] = READ_SYSREG(ID_AA64ISAR2_EL1); - - c->zfr64.bits[0] = READ_SYSREG(ID_AA64ZFR0_EL1); - -diff --git a/xen/include/asm-arm/arm64/sysregs.h b/xen/include/asm-arm/arm64/sysregs.h -index d7e4772f217f..eac08ed33f53 100644 ---- a/xen/include/asm-arm/arm64/sysregs.h -+++ b/xen/include/asm-arm/arm64/sysregs.h -@@ -84,6 +84,9 @@ - #ifndef ID_DFR1_EL1 - #define ID_DFR1_EL1 S3_0_C0_C3_5 - #endif -+#ifndef ID_AA64ISAR2_EL1 -+#define ID_AA64ISAR2_EL1 S3_0_C0_C6_2 -+#endif - - /* ID registers (imported from arm64/include/asm/sysreg.h in Linux) */ - -@@ -139,6 +142,9 @@ - #define ID_AA64ISAR1_GPI_NI 0x0 - #define ID_AA64ISAR1_GPI_IMP_DEF 0x1 - -+/* id_aa64isar2 */ -+#define ID_AA64ISAR2_CLEARBHB_SHIFT 28 -+ - /* id_aa64pfr0 */ - #define ID_AA64PFR0_CSV3_SHIFT 60 - #define ID_AA64PFR0_CSV2_SHIFT 56 -@@ -232,6 +238,7 @@ - #define ID_AA64MMFR0_PARANGE_52 0x6 - - /* id_aa64mmfr1 */ -+#define ID_AA64MMFR1_ECBHB_SHIFT 60 - #define ID_AA64MMFR1_ETS_SHIFT 36 - #define ID_AA64MMFR1_TWED_SHIFT 32 - #define ID_AA64MMFR1_XNX_SHIFT 28 -diff --git a/xen/include/asm-arm/cpufeature.h b/xen/include/asm-arm/cpufeature.h -index 8a5afbaf0baf..db126508f159 100644 ---- a/xen/include/asm-arm/cpufeature.h -+++ b/xen/include/asm-arm/cpufeature.h -@@ -243,14 +243,15 @@ struct cpuinfo_arm { - unsigned long lo:4; - unsigned long pan:4; - unsigned long __res1:8; -- unsigned long __res2:32; -+ unsigned long __res2:28; -+ unsigned long ecbhb:4; - - unsigned long __res3:64; - }; - } mm64; - - union { -- register_t bits[2]; -+ register_t bits[3]; - struct { - /* ISAR0 */ - unsigned long __res0:4; -@@ -286,6 +287,12 @@ struct cpuinfo_arm { - unsigned long dgh:4; - unsigned long i8mm:4; - unsigned long __res2:8; -+ -+ /* ISAR2 */ -+ unsigned long __res3:28; -+ unsigned long clearbhb:4; -+ -+ unsigned long __res4:32; - }; - } isa64; - diff --git a/xsa398-4.16-4-xen-arm-Add-Spectre-BHB-handling.patch b/xsa398-4.16-4-xen-arm-Add-Spectre-BHB-handling.patch deleted file mode 100644 index 6959081..0000000 --- a/xsa398-4.16-4-xen-arm-Add-Spectre-BHB-handling.patch +++ /dev/null @@ -1,351 +0,0 @@ -From 789523a2aac88e3668f9c4ad892fa47b5f6bf1a7 Mon Sep 17 00:00:00 2001 -From: Rahul Singh -Date: Mon, 14 Feb 2022 18:47:32 +0000 -Subject: xen/arm: Add Spectre BHB handling - -This commit is adding Spectre BHB handling to Xen on Arm. -The commit is introducing new alternative code to be executed during -exception entry: -- SMCC workaround 3 call -- loop workaround (with 8, 24 or 32 iterations) -- use of new clearbhb instruction - -Cpuerrata is modified by this patch to apply the required workaround for -CPU affected by Spectre BHB when CONFIG_ARM64_HARDEN_BRANCH_PREDICTOR is -enabled. - -To do this the system previously used to apply smcc workaround 1 is -reused and new alternative code to be copied in the exception handler is -introduced. - -To define the type of workaround required by a processor, 4 new cpu -capabilities are introduced (for each number of loop and for smcc -workaround 3). - -When a processor is affected, enable_spectre_bhb_workaround is called -and if the processor does not have CSV2 set to 3 or ECBHB feature (which -would mean that the processor is doing what is required in hardware), -the proper code is enabled at exception entry. - -In the case where workaround 3 is not supported by the firmware, we -enable workaround 1 when possible as it will also mitigate Spectre BHB -on systems without CSV2. - -This is part of XSA-398 / CVE-2022-23960. - -Signed-off-by: Bertrand Marquis -Signed-off-by: Rahul Singh -Acked-by: Julien Grall -(cherry picked from commit 62c91eb66a2904eefb1d1d9642e3697a1e3c3a3c) - -diff --git a/xen/arch/arm/arm64/bpi.S b/xen/arch/arm/arm64/bpi.S -index d8743d955c4a..4e6382522048 100644 ---- a/xen/arch/arm/arm64/bpi.S -+++ b/xen/arch/arm/arm64/bpi.S -@@ -58,16 +58,42 @@ ENTRY(__bp_harden_hyp_vecs_start) - .endr - ENTRY(__bp_harden_hyp_vecs_end) - --ENTRY(__smccc_workaround_1_smc_start) -+.macro mitigate_spectre_bhb_loop count -+ENTRY(__mitigate_spectre_bhb_loop_start_\count) -+ stp x0, x1, [sp, #-16]! -+ mov x0, \count -+.Lspectre_bhb_loop\@: -+ b . + 4 -+ subs x0, x0, #1 -+ b.ne .Lspectre_bhb_loop\@ -+ sb -+ ldp x0, x1, [sp], #16 -+ENTRY(__mitigate_spectre_bhb_loop_end_\count) -+.endm -+ -+.macro smccc_workaround num smcc_id -+ENTRY(__smccc_workaround_smc_start_\num) - sub sp, sp, #(8 * 4) - stp x0, x1, [sp, #(8 * 2)] - stp x2, x3, [sp, #(8 * 0)] -- mov w0, #ARM_SMCCC_ARCH_WORKAROUND_1_FID -+ mov w0, \smcc_id - smc #0 - ldp x2, x3, [sp, #(8 * 0)] - ldp x0, x1, [sp, #(8 * 2)] - add sp, sp, #(8 * 4) --ENTRY(__smccc_workaround_1_smc_end) -+ENTRY(__smccc_workaround_smc_end_\num) -+.endm -+ -+ENTRY(__mitigate_spectre_bhb_clear_insn_start) -+ clearbhb -+ isb -+ENTRY(__mitigate_spectre_bhb_clear_insn_end) -+ -+mitigate_spectre_bhb_loop 8 -+mitigate_spectre_bhb_loop 24 -+mitigate_spectre_bhb_loop 32 -+smccc_workaround 1, #ARM_SMCCC_ARCH_WORKAROUND_1_FID -+smccc_workaround 3, #ARM_SMCCC_ARCH_WORKAROUND_3_FID - - /* - * Local variables: -diff --git a/xen/arch/arm/cpuerrata.c b/xen/arch/arm/cpuerrata.c -index 00f9ebe9cee0..ae649d16ef02 100644 ---- a/xen/arch/arm/cpuerrata.c -+++ b/xen/arch/arm/cpuerrata.c -@@ -145,7 +145,16 @@ install_bp_hardening_vec(const struct arm_cpu_capabilities *entry, - return ret; - } - --extern char __smccc_workaround_1_smc_start[], __smccc_workaround_1_smc_end[]; -+extern char __smccc_workaround_smc_start_1[], __smccc_workaround_smc_end_1[]; -+extern char __smccc_workaround_smc_start_3[], __smccc_workaround_smc_end_3[]; -+extern char __mitigate_spectre_bhb_clear_insn_start[], -+ __mitigate_spectre_bhb_clear_insn_end[]; -+extern char __mitigate_spectre_bhb_loop_start_8[], -+ __mitigate_spectre_bhb_loop_end_8[]; -+extern char __mitigate_spectre_bhb_loop_start_24[], -+ __mitigate_spectre_bhb_loop_end_24[]; -+extern char __mitigate_spectre_bhb_loop_start_32[], -+ __mitigate_spectre_bhb_loop_end_32[]; - - static int enable_smccc_arch_workaround_1(void *data) - { -@@ -176,8 +185,8 @@ static int enable_smccc_arch_workaround_1(void *data) - if ( (int)res.a0 < 0 ) - goto warn; - -- return !install_bp_hardening_vec(entry,__smccc_workaround_1_smc_start, -- __smccc_workaround_1_smc_end, -+ return !install_bp_hardening_vec(entry,__smccc_workaround_smc_start_1, -+ __smccc_workaround_smc_end_1, - "call ARM_SMCCC_ARCH_WORKAROUND_1"); - - warn: -@@ -187,6 +196,93 @@ static int enable_smccc_arch_workaround_1(void *data) - return 0; - } - -+/* -+ * Spectre BHB Mitigation -+ * -+ * CPU is either: -+ * - Having CVS2.3 so it is not affected. -+ * - Having ECBHB and is clearing the branch history buffer when an exception -+ * to a different exception level is happening so no mitigation is needed. -+ * - Mitigating using a loop on exception entry (number of loop depending on -+ * the CPU). -+ * - Mitigating using the firmware. -+ */ -+static int enable_spectre_bhb_workaround(void *data) -+{ -+ const struct arm_cpu_capabilities *entry = data; -+ -+ /* -+ * Enable callbacks are called on every CPU based on the capabilities, so -+ * double-check whether the CPU matches the entry. -+ */ -+ if ( !entry->matches(entry) ) -+ return 0; -+ -+ if ( cpu_data[smp_processor_id()].pfr64.csv2 == 3 ) -+ return 0; -+ -+ if ( cpu_data[smp_processor_id()].mm64.ecbhb ) -+ return 0; -+ -+ if ( cpu_data[smp_processor_id()].isa64.clearbhb ) -+ return !install_bp_hardening_vec(entry, -+ __mitigate_spectre_bhb_clear_insn_start, -+ __mitigate_spectre_bhb_clear_insn_end, -+ "use clearBHB instruction"); -+ -+ /* Apply solution depending on hwcaps set on arm_errata */ -+ if ( cpus_have_cap(ARM_WORKAROUND_BHB_LOOP_8) ) -+ return !install_bp_hardening_vec(entry, -+ __mitigate_spectre_bhb_loop_start_8, -+ __mitigate_spectre_bhb_loop_end_8, -+ "use 8 loops workaround"); -+ -+ if ( cpus_have_cap(ARM_WORKAROUND_BHB_LOOP_24) ) -+ return !install_bp_hardening_vec(entry, -+ __mitigate_spectre_bhb_loop_start_24, -+ __mitigate_spectre_bhb_loop_end_24, -+ "use 24 loops workaround"); -+ -+ if ( cpus_have_cap(ARM_WORKAROUND_BHB_LOOP_32) ) -+ return !install_bp_hardening_vec(entry, -+ __mitigate_spectre_bhb_loop_start_32, -+ __mitigate_spectre_bhb_loop_end_32, -+ "use 32 loops workaround"); -+ -+ if ( cpus_have_cap(ARM_WORKAROUND_BHB_SMCC_3) ) -+ { -+ struct arm_smccc_res res; -+ -+ if ( smccc_ver < SMCCC_VERSION(1, 1) ) -+ goto warn; -+ -+ arm_smccc_1_1_smc(ARM_SMCCC_ARCH_FEATURES_FID, -+ ARM_SMCCC_ARCH_WORKAROUND_3_FID, &res); -+ /* The return value is in the lower 32-bits. */ -+ if ( (int)res.a0 < 0 ) -+ { -+ /* -+ * On processor affected with CSV2=0, workaround 1 will mitigate -+ * both Spectre v2 and BHB so use it when available -+ */ -+ if ( enable_smccc_arch_workaround_1(data) ) -+ return 1; -+ -+ goto warn; -+ } -+ -+ return !install_bp_hardening_vec(entry,__smccc_workaround_smc_start_3, -+ __smccc_workaround_smc_end_3, -+ "call ARM_SMCCC_ARCH_WORKAROUND_3"); -+ } -+ -+warn: -+ printk_once("**** No support for any spectre BHB workaround. ****\n" -+ "**** Please update your firmware. ****\n"); -+ -+ return 0; -+} -+ - #endif /* CONFIG_ARM64_HARDEN_BRANCH_PREDICTOR */ - - /* Hardening Branch predictor code for Arm32 */ -@@ -446,19 +542,77 @@ static const struct arm_cpu_capabilities arm_errata[] = { - }, - { - .capability = ARM_HARDEN_BRANCH_PREDICTOR, -- MIDR_ALL_VERSIONS(MIDR_CORTEX_A72), -+ MIDR_RANGE(MIDR_CORTEX_A72, 0, 1 << MIDR_VARIANT_SHIFT), - .enable = enable_smccc_arch_workaround_1, - }, - { -- .capability = ARM_HARDEN_BRANCH_PREDICTOR, -+ .capability = ARM_WORKAROUND_BHB_SMCC_3, - MIDR_ALL_VERSIONS(MIDR_CORTEX_A73), -- .enable = enable_smccc_arch_workaround_1, -+ .enable = enable_spectre_bhb_workaround, - }, - { -- .capability = ARM_HARDEN_BRANCH_PREDICTOR, -+ .capability = ARM_WORKAROUND_BHB_SMCC_3, - MIDR_ALL_VERSIONS(MIDR_CORTEX_A75), -- .enable = enable_smccc_arch_workaround_1, -+ .enable = enable_spectre_bhb_workaround, -+ }, -+ /* spectre BHB */ -+ { -+ .capability = ARM_WORKAROUND_BHB_LOOP_8, -+ MIDR_RANGE(MIDR_CORTEX_A72, 1 << MIDR_VARIANT_SHIFT, -+ (MIDR_VARIANT_MASK | MIDR_REVISION_MASK)), -+ .enable = enable_spectre_bhb_workaround, -+ }, -+ { -+ .capability = ARM_WORKAROUND_BHB_LOOP_24, -+ MIDR_ALL_VERSIONS(MIDR_CORTEX_A76), -+ .enable = enable_spectre_bhb_workaround, -+ }, -+ { -+ .capability = ARM_WORKAROUND_BHB_LOOP_24, -+ MIDR_ALL_VERSIONS(MIDR_CORTEX_A77), -+ .enable = enable_spectre_bhb_workaround, -+ }, -+ { -+ .capability = ARM_WORKAROUND_BHB_LOOP_32, -+ MIDR_ALL_VERSIONS(MIDR_CORTEX_A78), -+ .enable = enable_spectre_bhb_workaround, -+ }, -+ { -+ .capability = ARM_WORKAROUND_BHB_LOOP_32, -+ MIDR_ALL_VERSIONS(MIDR_CORTEX_A78C), -+ .enable = enable_spectre_bhb_workaround, -+ }, -+ { -+ .capability = ARM_WORKAROUND_BHB_LOOP_32, -+ MIDR_ALL_VERSIONS(MIDR_CORTEX_X1), -+ .enable = enable_spectre_bhb_workaround, -+ }, -+ { -+ .capability = ARM_WORKAROUND_BHB_LOOP_32, -+ MIDR_ALL_VERSIONS(MIDR_CORTEX_X2), -+ .enable = enable_spectre_bhb_workaround, -+ }, -+ { -+ .capability = ARM_WORKAROUND_BHB_LOOP_32, -+ MIDR_ALL_VERSIONS(MIDR_CORTEX_A710), -+ .enable = enable_spectre_bhb_workaround, - }, -+ { -+ .capability = ARM_WORKAROUND_BHB_LOOP_24, -+ MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N1), -+ .enable = enable_spectre_bhb_workaround, -+ }, -+ { -+ .capability = ARM_WORKAROUND_BHB_LOOP_32, -+ MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N2), -+ .enable = enable_spectre_bhb_workaround, -+ }, -+ { -+ .capability = ARM_WORKAROUND_BHB_LOOP_32, -+ MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V1), -+ .enable = enable_spectre_bhb_workaround, -+ }, -+ - #endif - #ifdef CONFIG_ARM32_HARDEN_BRANCH_PREDICTOR - { -diff --git a/xen/include/asm-arm/arm64/macros.h b/xen/include/asm-arm/arm64/macros.h -index 5ad66efd6ba4..140e223b4c99 100644 ---- a/xen/include/asm-arm/arm64/macros.h -+++ b/xen/include/asm-arm/arm64/macros.h -@@ -27,6 +27,11 @@ - sb - .endm - -+ /* clearbhb instruction clearing the branch history */ -+ .macro clearbhb -+ hint #22 -+ .endm -+ - /* - * Register aliases. - */ -diff --git a/xen/include/asm-arm/cpufeature.h b/xen/include/asm-arm/cpufeature.h -index db126508f159..f7368766c07c 100644 ---- a/xen/include/asm-arm/cpufeature.h -+++ b/xen/include/asm-arm/cpufeature.h -@@ -63,8 +63,12 @@ - #define ARM64_WORKAROUND_AT_SPECULATE 9 - #define ARM_WORKAROUND_858921 10 - #define ARM64_WORKAROUND_REPEAT_TLBI 11 -+#define ARM_WORKAROUND_BHB_LOOP_8 12 -+#define ARM_WORKAROUND_BHB_LOOP_24 13 -+#define ARM_WORKAROUND_BHB_LOOP_32 14 -+#define ARM_WORKAROUND_BHB_SMCC_3 15 - --#define ARM_NCAPS 12 -+#define ARM_NCAPS 16 - - #ifndef __ASSEMBLY__ - -diff --git a/xen/include/asm-arm/smccc.h b/xen/include/asm-arm/smccc.h -index 9d94beb3df2d..b3dbeecc90ad 100644 ---- a/xen/include/asm-arm/smccc.h -+++ b/xen/include/asm-arm/smccc.h -@@ -334,6 +334,12 @@ void __arm_smccc_1_0_smc(register_t a0, register_t a1, register_t a2, - ARM_SMCCC_OWNER_ARCH, \ - 0x7FFF) - -+#define ARM_SMCCC_ARCH_WORKAROUND_3_FID \ -+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ -+ ARM_SMCCC_CONV_32, \ -+ ARM_SMCCC_OWNER_ARCH, \ -+ 0x3FFF) -+ - /* SMCCC error codes */ - #define ARM_SMCCC_NOT_REQUIRED (-2) - #define ARM_SMCCC_ERR_UNKNOWN_FUNCTION (-1) diff --git a/xsa398-4.16-5-xen-arm-Allow-to-discover-and-use-SMCCC_ARCH_WORKARO.patch b/xsa398-4.16-5-xen-arm-Allow-to-discover-and-use-SMCCC_ARCH_WORKARO.patch deleted file mode 100644 index efe99ac..0000000 --- a/xsa398-4.16-5-xen-arm-Allow-to-discover-and-use-SMCCC_ARCH_WORKARO.patch +++ /dev/null @@ -1,91 +0,0 @@ -From dab616cd3d4856a7a4d4f3a429a82dbdbf1aeeb9 Mon Sep 17 00:00:00 2001 -From: Bertrand Marquis -Date: Thu, 17 Feb 2022 14:52:54 +0000 -Subject: xen/arm: Allow to discover and use SMCCC_ARCH_WORKAROUND_3 - -Allow guest to discover whether or not SMCCC_ARCH_WORKAROUND_3 is -supported and create a fastpath in the code to handle guests request to -do the workaround. - -The function SMCCC_ARCH_WORKAROUND_3 will be called by the guest for -flushing the branch history. So we want the handling to be as fast as -possible. - -As the mitigation is applied on every guest exit, we can check for the -call before saving all context and return very early. - -This is part of XSA-398 / CVE-2022-23960. - -Signed-off-by: Bertrand Marquis -Reviewed-by: Julien Grall -(cherry picked from commit c0a56ea0fd92ecb471936b7355ddbecbaea3707c) - -diff --git a/xen/arch/arm/arm64/entry.S b/xen/arch/arm/arm64/entry.S -index fc3811ad0ad5..cf7b9d826f54 100644 ---- a/xen/arch/arm/arm64/entry.S -+++ b/xen/arch/arm/arm64/entry.S -@@ -336,16 +336,26 @@ guest_sync: - cbnz x1, guest_sync_slowpath /* should be 0 for HVC #0 */ - - /* -- * Fastest path possible for ARM_SMCCC_ARCH_WORKAROUND_1. -- * The workaround has already been applied on the exception -+ * Fastest path possible for ARM_SMCCC_ARCH_WORKAROUND_1 and -+ * ARM_SMCCC_ARCH_WORKAROUND_3. -+ * The workaround needed has already been applied on the exception - * entry from the guest, so let's quickly get back to the guest. - * - * Note that eor is used because the function identifier cannot - * be encoded as an immediate for cmp. - */ - eor w0, w0, #ARM_SMCCC_ARCH_WORKAROUND_1_FID -- cbnz w0, check_wa2 -+ cbz w0, fastpath_out_workaround - -+ /* ARM_SMCCC_ARCH_WORKAROUND_2 handling */ -+ eor w0, w0, #(ARM_SMCCC_ARCH_WORKAROUND_1_FID ^ ARM_SMCCC_ARCH_WORKAROUND_2_FID) -+ cbz w0, wa2_ssbd -+ -+ /* Fastpath out for ARM_SMCCC_ARCH_WORKAROUND_3 */ -+ eor w0, w0, #(ARM_SMCCC_ARCH_WORKAROUND_2_FID ^ ARM_SMCCC_ARCH_WORKAROUND_3_FID) -+ cbnz w0, guest_sync_slowpath -+ -+fastpath_out_workaround: - /* - * Clobber both x0 and x1 to prevent leakage. Note that thanks - * the eor, x0 = 0. -@@ -354,10 +364,7 @@ guest_sync: - eret - sb - --check_wa2: -- /* ARM_SMCCC_ARCH_WORKAROUND_2 handling */ -- eor w0, w0, #(ARM_SMCCC_ARCH_WORKAROUND_1_FID ^ ARM_SMCCC_ARCH_WORKAROUND_2_FID) -- cbnz w0, guest_sync_slowpath -+wa2_ssbd: - #ifdef CONFIG_ARM_SSBD - alternative_cb arm_enable_wa2_handling - b wa2_end -diff --git a/xen/arch/arm/vsmc.c b/xen/arch/arm/vsmc.c -index a36db15fffc0..b633ff2fe897 100644 ---- a/xen/arch/arm/vsmc.c -+++ b/xen/arch/arm/vsmc.c -@@ -124,6 +124,10 @@ static bool handle_arch(struct cpu_user_regs *regs) - break; - } - break; -+ case ARM_SMCCC_ARCH_WORKAROUND_3_FID: -+ if ( cpus_have_cap(ARM_WORKAROUND_BHB_SMCC_3) ) -+ ret = 0; -+ break; - } - - set_user_reg(regs, 0, ret); -@@ -132,6 +136,7 @@ static bool handle_arch(struct cpu_user_regs *regs) - } - - case ARM_SMCCC_ARCH_WORKAROUND_1_FID: -+ case ARM_SMCCC_ARCH_WORKAROUND_3_FID: - /* No return value */ - return true; - diff --git a/xsa398-4.16-6-x86-spec-ctrl-Cease-using-thunk-lfence-on-AMD.patch b/xsa398-4.16-6-x86-spec-ctrl-Cease-using-thunk-lfence-on-AMD.patch deleted file mode 100644 index 7c28ac0..0000000 --- a/xsa398-4.16-6-x86-spec-ctrl-Cease-using-thunk-lfence-on-AMD.patch +++ /dev/null @@ -1,118 +0,0 @@ -From c374a8c5cc74535e16410b7a0d9e92bf5de54f79 Mon Sep 17 00:00:00 2001 -From: Andrew Cooper -Date: Mon, 7 Mar 2022 16:35:52 +0000 -Subject: x86/spec-ctrl: Cease using thunk=lfence on AMD - -AMD have updated their Spectre v2 guidance, and lfence/jmp is no longer -considered safe. AMD are recommending using retpoline everywhere. - -Retpoline is incompatible with CET. All CET-capable hardware has efficient -IBRS (specifically, not something retrofitted in microcode), so use IBRS (and -STIBP for consistency sake). - -This is a logical change on AMD, but not on Intel as the default calculations -would end up with these settings anyway. Leave behind a message if IBRS is -found to be missing. - -Also update the default heuristics to never select THUNK_LFENCE. This causes -AMD CPUs to change their default to retpoline. - -Also update the printed message to include the AMD MSR_SPEC_CTRL settings, and -STIBP now that we set it for consistency sake. - -This is part of XSA-398 / CVE-2021-26401. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -(cherry picked from commit 8d03080d2a339840d3a59e0932a94f804e45110d) - -diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc -index 995197f4b23e..f606dc0e14c1 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -2269,9 +2269,9 @@ to use. - - If Xen was compiled with INDIRECT_THUNK support, `bti-thunk=` can be used to - select which of the thunks gets patched into the `__x86_indirect_thunk_%reg` --locations. The default thunk is `retpoline` (generally preferred for Intel --hardware), with the alternatives being `jmp` (a `jmp *%reg` gadget, minimal --overhead), and `lfence` (an `lfence; jmp *%reg` gadget, preferred for AMD). -+locations. The default thunk is `retpoline` (generally preferred), with the -+alternatives being `jmp` (a `jmp *%reg` gadget, minimal overhead), and -+`lfence` (an `lfence; jmp *%reg` gadget). - - On hardware supporting IBRS (Indirect Branch Restricted Speculation), the - `ibrs=` option can be used to force or prevent Xen using the feature itself. -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index cbeeb199037e..ae076bec3ab0 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -367,14 +367,19 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - "\n"); - - /* Settings for Xen's protection, irrespective of guests. */ -- printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s, Other:%s%s%s%s%s\n", -+ printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s%s, Other:%s%s%s%s%s\n", - thunk == THUNK_NONE ? "N/A" : - thunk == THUNK_RETPOLINE ? "RETPOLINE" : - thunk == THUNK_LFENCE ? "LFENCE" : - thunk == THUNK_JMP ? "JMP" : "?", -- !boot_cpu_has(X86_FEATURE_IBRSB) ? "No" : -+ (!boot_cpu_has(X86_FEATURE_IBRSB) && -+ !boot_cpu_has(X86_FEATURE_IBRS)) ? "No" : - (default_xen_spec_ctrl & SPEC_CTRL_IBRS) ? "IBRS+" : "IBRS-", -- !boot_cpu_has(X86_FEATURE_SSBD) ? "" : -+ (!boot_cpu_has(X86_FEATURE_STIBP) && -+ !boot_cpu_has(X86_FEATURE_AMD_STIBP)) ? "" : -+ (default_xen_spec_ctrl & SPEC_CTRL_STIBP) ? " STIBP+" : " STIBP-", -+ (!boot_cpu_has(X86_FEATURE_SSBD) && -+ !boot_cpu_has(X86_FEATURE_AMD_SSBD)) ? "" : - (default_xen_spec_ctrl & SPEC_CTRL_SSBD) ? " SSBD+" : " SSBD-", - !(caps & ARCH_CAPS_TSX_CTRL) ? "" : - (opt_tsx & 1) ? " TSX+" : " TSX-", -@@ -945,10 +950,23 @@ void __init init_speculation_mitigations(void) - /* - * First, disable the use of retpolines if Xen is using shadow stacks, as - * they are incompatible. -+ * -+ * In the absence of retpolines, IBRS needs to be used for speculative -+ * safety. All CET-capable hardware has efficient IBRS. - */ -- if ( cpu_has_xen_shstk && -- (opt_thunk == THUNK_DEFAULT || opt_thunk == THUNK_RETPOLINE) ) -- thunk = THUNK_JMP; -+ if ( cpu_has_xen_shstk ) -+ { -+ if ( !has_spec_ctrl ) -+ printk(XENLOG_WARNING "?!? CET active, but no MSR_SPEC_CTRL?\n"); -+ else if ( opt_ibrs == -1 ) -+ { -+ opt_ibrs = ibrs = true; -+ default_xen_spec_ctrl |= SPEC_CTRL_IBRS | SPEC_CTRL_STIBP; -+ } -+ -+ if ( opt_thunk == THUNK_DEFAULT || opt_thunk == THUNK_RETPOLINE ) -+ thunk = THUNK_JMP; -+ } - - /* - * Has the user specified any custom BTI mitigations? If so, follow their -@@ -968,16 +986,10 @@ void __init init_speculation_mitigations(void) - if ( IS_ENABLED(CONFIG_INDIRECT_THUNK) ) - { - /* -- * AMD's recommended mitigation is to set lfence as being dispatch -- * serialising, and to use IND_THUNK_LFENCE. -- */ -- if ( cpu_has_lfence_dispatch ) -- thunk = THUNK_LFENCE; -- /* -- * On Intel hardware, we'd like to use retpoline in preference to -+ * On all hardware, we'd like to use retpoline in preference to - * IBRS, but only if it is safe on this hardware. - */ -- else if ( retpoline_safe(caps) ) -+ if ( retpoline_safe(caps) ) - thunk = THUNK_RETPOLINE; - else if ( has_spec_ctrl ) - ibrs = true; diff --git a/xsa399-4.16.patch b/xsa399-4.16.patch deleted file mode 100644 index 5f3850e..0000000 --- a/xsa399-4.16.patch +++ /dev/null @@ -1,45 +0,0 @@ -From: Jan Beulich -Subject: VT-d: correct ordering of operations in cleanup_domid_map() - -The function may be called without any locks held (leaving aside the -domctl one, which we surely don't want to depend on here), so needs to -play safe wrt other accesses to domid_map[] and domid_bitmap[]. This is -to avoid context_set_domain_id()'s writing of domid_map[] to be reset to -zero right away in the case of it racing the freeing of a DID. - -For the interaction with context_set_domain_id() and ->domid_map[] reads -see the code comment. - -{check_,}cleanup_domid_map() are called with pcidevs_lock held or during -domain cleanup only (and pcidevs_lock is also held around -context_set_domain_id()), i.e. racing calls with the same (dom, iommu) -tuple cannot occur. - -domain_iommu_domid(), besides its use by cleanup_domid_map(), has its -result used only to control flushing, and hence a stale result would -only lead to a stray extra flush. - -This is CVE-2022-26357 / XSA-399. - -Fixes: b9c20c78789f ("VT-d: per-iommu domain-id") -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné - ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -152,8 +152,14 @@ static void cleanup_domid_map(struct dom - - if ( iommu_domid >= 0 ) - { -+ /* -+ * Update domid_map[] /before/ domid_bitmap[] to avoid a race with -+ * context_set_domain_id(), setting the slot to DOMID_INVALID for -+ * ->domid_map[] reads to produce a suitable value while the bit is -+ * still set. -+ */ -+ iommu->domid_map[iommu_domid] = DOMID_INVALID; - clear_bit(iommu_domid, iommu->domid_bitmap); -- iommu->domid_map[iommu_domid] = 0; - } - } - diff --git a/xsa400-4.16-01.patch b/xsa400-4.16-01.patch deleted file mode 100644 index f44f08e..0000000 --- a/xsa400-4.16-01.patch +++ /dev/null @@ -1,105 +0,0 @@ -From: Jan Beulich -Subject: VT-d: fix (de)assign ordering when RMRRs are in use - -In the event that the RMRR mappings are essential for device operation, -they should be established before updating the device's context entry, -while they should be torn down only after the device's context entry was -successfully updated. - -Also adjust a related log message. - -This is CVE-2022-26358 / part of XSA-400. - -Fixes: 8b99f4400b69 ("VT-d: fix RMRR related error handling") -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné -Reviewed-by: Paul Durrant -Reviewed-by: Kevin Tian - ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -2419,6 +2419,10 @@ static int reassign_device_ownership( - { - int ret; - -+ ret = domain_context_unmap(source, devfn, pdev); -+ if ( ret ) -+ return ret; -+ - /* - * Devices assigned to untrusted domains (here assumed to be any domU) - * can attempt to send arbitrary LAPIC/MSI messages. We are unprotected -@@ -2455,10 +2459,6 @@ static int reassign_device_ownership( - } - } - -- ret = domain_context_unmap(source, devfn, pdev); -- if ( ret ) -- return ret; -- - if ( devfn == pdev->devfn && pdev->domain != dom_io ) - { - list_move(&pdev->domain_list, &dom_io->pdev_list); -@@ -2534,9 +2534,8 @@ static int intel_iommu_assign_device( - } - } - -- ret = reassign_device_ownership(s, d, devfn, pdev); -- if ( ret || d == dom_io ) -- return ret; -+ if ( d == dom_io ) -+ return reassign_device_ownership(s, d, devfn, pdev); - - /* Setup rmrr identity mapping */ - for_each_rmrr_device( rmrr, bdf, i ) -@@ -2549,20 +2548,37 @@ static int intel_iommu_assign_device( - rmrr->end_address, flag); - if ( ret ) - { -- int rc; -- -- rc = reassign_device_ownership(d, s, devfn, pdev); - printk(XENLOG_G_ERR VTDPREFIX -- " cannot map reserved region (%"PRIx64",%"PRIx64"] for Dom%d (%d)\n", -- rmrr->base_address, rmrr->end_address, -- d->domain_id, ret); -- if ( rc ) -- { -- printk(XENLOG_ERR VTDPREFIX -- " failed to reclaim %pp from %pd (%d)\n", -- &PCI_SBDF3(seg, bus, devfn), d, rc); -- domain_crash(d); -- } -+ "%pd: cannot map reserved region [%"PRIx64",%"PRIx64"]: %d\n", -+ d, rmrr->base_address, rmrr->end_address, ret); -+ break; -+ } -+ } -+ } -+ -+ if ( !ret ) -+ ret = reassign_device_ownership(s, d, devfn, pdev); -+ -+ /* See reassign_device_ownership() for the hwdom aspect. */ -+ if ( !ret || is_hardware_domain(d) ) -+ return ret; -+ -+ for_each_rmrr_device( rmrr, bdf, i ) -+ { -+ if ( rmrr->segment == seg && -+ PCI_BUS(bdf) == bus && -+ PCI_DEVFN2(bdf) == devfn ) -+ { -+ int rc = iommu_identity_mapping(d, p2m_access_x, -+ rmrr->base_address, -+ rmrr->end_address, 0); -+ -+ if ( rc && rc != -ENOENT ) -+ { -+ printk(XENLOG_ERR VTDPREFIX -+ "%pd: cannot unmap reserved region [%"PRIx64",%"PRIx64"]: %d\n", -+ d, rmrr->base_address, rmrr->end_address, rc); -+ domain_crash(d); - break; - } - } diff --git a/xsa400-4.16-02.patch b/xsa400-4.16-02.patch deleted file mode 100644 index e8de874..0000000 --- a/xsa400-4.16-02.patch +++ /dev/null @@ -1,80 +0,0 @@ -From: Jan Beulich -Subject: VT-d: fix add/remove ordering when RMRRs are in use - -In the event that the RMRR mappings are essential for device operation, -they should be established before updating the device's context entry, -while they should be torn down only after the device's context entry was -successfully cleared. - -Also switch to %pd in related log messages. - -Fixes: fa88cfadf918 ("vt-d: Map RMRR in intel_iommu_add_device() if the device has RMRR") -Fixes: 8b99f4400b69 ("VT-d: fix RMRR related error handling") -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné -Reviewed-by: Kevin Tian - ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -1997,14 +1997,6 @@ static int intel_iommu_add_device(u8 dev - if ( !pdev->domain ) - return -EINVAL; - -- ret = domain_context_mapping(pdev->domain, devfn, pdev); -- if ( ret ) -- { -- dprintk(XENLOG_ERR VTDPREFIX, "d%d: context mapping failed\n", -- pdev->domain->domain_id); -- return ret; -- } -- - for_each_rmrr_device ( rmrr, bdf, i ) - { - if ( rmrr->segment == pdev->seg && -@@ -2021,12 +2013,17 @@ static int intel_iommu_add_device(u8 dev - rmrr->base_address, rmrr->end_address, - 0); - if ( ret ) -- dprintk(XENLOG_ERR VTDPREFIX, "d%d: RMRR mapping failed\n", -- pdev->domain->domain_id); -+ dprintk(XENLOG_ERR VTDPREFIX, "%pd: RMRR mapping failed\n", -+ pdev->domain); - } - } - -- return 0; -+ ret = domain_context_mapping(pdev->domain, devfn, pdev); -+ if ( ret ) -+ dprintk(XENLOG_ERR VTDPREFIX, "%pd: context mapping failed\n", -+ pdev->domain); -+ -+ return ret; - } - - static int intel_iommu_enable_device(struct pci_dev *pdev) -@@ -2048,11 +2045,15 @@ static int intel_iommu_remove_device(u8 - { - struct acpi_rmrr_unit *rmrr; - u16 bdf; -- int i; -+ int ret, i; - - if ( !pdev->domain ) - return -EINVAL; - -+ ret = domain_context_unmap(pdev->domain, devfn, pdev); -+ if ( ret ) -+ return ret; -+ - for_each_rmrr_device ( rmrr, bdf, i ) - { - if ( rmrr->segment != pdev->seg || -@@ -2068,7 +2069,7 @@ static int intel_iommu_remove_device(u8 - rmrr->end_address, 0); - } - -- return domain_context_unmap(pdev->domain, devfn, pdev); -+ return 0; - } - - static int __hwdom_init setup_hwdom_device(u8 devfn, struct pci_dev *pdev) diff --git a/xsa400-4.16-03.patch b/xsa400-4.16-03.patch deleted file mode 100644 index f422ad2..0000000 --- a/xsa400-4.16-03.patch +++ /dev/null @@ -1,134 +0,0 @@ -From: Jan Beulich -Subject: IOMMU/x86: tighten iommu_alloc_pgtable()'s parameter - -This is to make more obvious that nothing outside of domain_iommu(d) -actually changes or is otherwise needed by the function. - -No functional change intended. - -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné -Reviewed-by: Paul Durrant -Reviewed-by: Kevin Tian - ---- a/xen/include/asm-x86/iommu.h -+++ b/xen/include/asm-x86/iommu.h -@@ -142,7 +142,8 @@ int pi_update_irte(const struct pi_desc - }) - - int __must_check iommu_free_pgtables(struct domain *d); --struct page_info *__must_check iommu_alloc_pgtable(struct domain *d); -+struct domain_iommu; -+struct page_info *__must_check iommu_alloc_pgtable(struct domain_iommu *hd); - - #endif /* !__ARCH_X86_IOMMU_H__ */ - /* ---- a/xen/drivers/passthrough/amd/iommu_map.c -+++ b/xen/drivers/passthrough/amd/iommu_map.c -@@ -184,7 +184,7 @@ static int iommu_pde_from_dfn(struct dom - unsigned long next_table_mfn; - unsigned int level; - struct page_info *table; -- const struct domain_iommu *hd = dom_iommu(d); -+ struct domain_iommu *hd = dom_iommu(d); - - table = hd->arch.amd.root_table; - level = hd->arch.amd.paging_mode; -@@ -219,7 +219,7 @@ static int iommu_pde_from_dfn(struct dom - mfn = next_table_mfn; - - /* allocate lower level page table */ -- table = iommu_alloc_pgtable(d); -+ table = iommu_alloc_pgtable(hd); - if ( table == NULL ) - { - AMD_IOMMU_ERROR("cannot allocate I/O page table\n"); -@@ -249,7 +249,7 @@ static int iommu_pde_from_dfn(struct dom - - if ( next_table_mfn == 0 ) - { -- table = iommu_alloc_pgtable(d); -+ table = iommu_alloc_pgtable(hd); - if ( table == NULL ) - { - AMD_IOMMU_ERROR("cannot allocate I/O page table\n"); -@@ -553,7 +553,7 @@ int __init amd_iommu_quarantine_init(str - - spin_lock(&hd->arch.mapping_lock); - -- hd->arch.amd.root_table = iommu_alloc_pgtable(d); -+ hd->arch.amd.root_table = iommu_alloc_pgtable(hd); - if ( !hd->arch.amd.root_table ) - goto out; - -@@ -568,7 +568,7 @@ int __init amd_iommu_quarantine_init(str - * page table pages, and the resulting allocations are always - * zeroed. - */ -- pg = iommu_alloc_pgtable(d); -+ pg = iommu_alloc_pgtable(hd); - if ( !pg ) - break; - ---- a/xen/drivers/passthrough/amd/pci_amd_iommu.c -+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c -@@ -242,7 +242,7 @@ int amd_iommu_alloc_root(struct domain * - - if ( unlikely(!hd->arch.amd.root_table) ) - { -- hd->arch.amd.root_table = iommu_alloc_pgtable(d); -+ hd->arch.amd.root_table = iommu_alloc_pgtable(hd); - if ( !hd->arch.amd.root_table ) - return -ENOMEM; - } ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -330,7 +330,7 @@ static u64 addr_to_dma_page_maddr(struct - { - struct page_info *pg; - -- if ( !alloc || !(pg = iommu_alloc_pgtable(domain)) ) -+ if ( !alloc || !(pg = iommu_alloc_pgtable(hd)) ) - goto out; - - hd->arch.vtd.pgd_maddr = page_to_maddr(pg); -@@ -350,7 +350,7 @@ static u64 addr_to_dma_page_maddr(struct - if ( !alloc ) - break; - -- pg = iommu_alloc_pgtable(domain); -+ pg = iommu_alloc_pgtable(hd); - if ( !pg ) - break; - -@@ -2766,7 +2766,7 @@ static int __init intel_iommu_quarantine - goto out; - } - -- pg = iommu_alloc_pgtable(d); -+ pg = iommu_alloc_pgtable(hd); - - rc = -ENOMEM; - if ( !pg ) -@@ -2785,7 +2785,7 @@ static int __init intel_iommu_quarantine - * page table pages, and the resulting allocations are always - * zeroed. - */ -- pg = iommu_alloc_pgtable(d); -+ pg = iommu_alloc_pgtable(hd); - - if ( !pg ) - goto out; ---- a/xen/drivers/passthrough/x86/iommu.c -+++ b/xen/drivers/passthrough/x86/iommu.c -@@ -416,9 +416,8 @@ int iommu_free_pgtables(struct domain *d - return 0; - } - --struct page_info *iommu_alloc_pgtable(struct domain *d) -+struct page_info *iommu_alloc_pgtable(struct domain_iommu *hd) - { -- struct domain_iommu *hd = dom_iommu(d); - unsigned int memflags = 0; - struct page_info *pg; - void *p; diff --git a/xsa400-4.16-04.patch b/xsa400-4.16-04.patch deleted file mode 100644 index 41b4f34..0000000 --- a/xsa400-4.16-04.patch +++ /dev/null @@ -1,94 +0,0 @@ -From: Jan Beulich -Subject: VT-d: drop ownership checking from domain_context_mapping_one() - -Despite putting in quite a bit of effort it was not possible to -establish why exactly this code exists (beyond possibly sanity -checking). Instead of a subsequent change further complicating this -logic, simply get rid of it. - -Take the opportunity and move the respective unmap_vtd_domain_page() out -of the locked region. - -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné -Reviewed-by: Paul Durrant -Reviewed-by: Kevin Tian - ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -124,28 +124,6 @@ static int context_set_domain_id(struct - return 0; - } - --static int context_get_domain_id(struct context_entry *context, -- struct vtd_iommu *iommu) --{ -- unsigned long dom_index, nr_dom; -- int domid = -1; -- -- if (iommu && context) -- { -- nr_dom = cap_ndoms(iommu->cap); -- -- dom_index = context_domain_id(*context); -- -- if ( dom_index < nr_dom && iommu->domid_map ) -- domid = iommu->domid_map[dom_index]; -- else -- dprintk(XENLOG_DEBUG VTDPREFIX, -- "dom_index %lu exceeds nr_dom %lu or iommu has no domid_map\n", -- dom_index, nr_dom); -- } -- return domid; --} -- - static void cleanup_domid_map(struct domain *domain, struct vtd_iommu *iommu) - { - int iommu_domid = domain_iommu_domid(domain, iommu); -@@ -1416,44 +1394,9 @@ int domain_context_mapping_one( - - if ( context_present(*context) ) - { -- int res = 0; -- -- /* Try to get domain ownership from device structure. If that's -- * not available, try to read it from the context itself. */ -- if ( pdev ) -- { -- if ( pdev->domain != domain ) -- { -- printk(XENLOG_G_INFO VTDPREFIX "%pd: %pp owned by %pd", -- domain, &PCI_SBDF3(seg, bus, devfn), -- pdev->domain); -- res = -EINVAL; -- } -- } -- else -- { -- int cdomain; -- cdomain = context_get_domain_id(context, iommu); -- -- if ( cdomain < 0 ) -- { -- printk(XENLOG_G_WARNING VTDPREFIX -- "%pd: %pp mapped, but can't find owner\n", -- domain, &PCI_SBDF3(seg, bus, devfn)); -- res = -EINVAL; -- } -- else if ( cdomain != domain->domain_id ) -- { -- printk(XENLOG_G_INFO VTDPREFIX -- "%pd: %pp already mapped to d%d", -- domain, &PCI_SBDF3(seg, bus, devfn), cdomain); -- res = -EINVAL; -- } -- } -- -- unmap_vtd_domain_page(context_entries); - spin_unlock(&iommu->lock); -- return res; -+ unmap_vtd_domain_page(context_entries); -+ return 0; - } - - if ( iommu_hwdom_passthrough && is_hardware_domain(domain) ) diff --git a/xsa400-4.16-05.patch b/xsa400-4.16-05.patch deleted file mode 100644 index 0eb6619..0000000 --- a/xsa400-4.16-05.patch +++ /dev/null @@ -1,570 +0,0 @@ -From: Jan Beulich -Subject: VT-d: re-assign devices directly - -Devices with RMRRs, due to it being unspecified how/when the specified -memory regions may get accessed, may not be left disconnected from their -respective mappings (as long as it's not certain that the device has -been fully quiesced). Hence rather than unmapping the old context and -then mapping the new one, re-assignment needs to be done in a single -step. - -This is CVE-2022-26359 / part of XSA-400. - -Reported-by: Roger Pau Monné - -Similarly quarantining scratch-page mode relies on page tables to be -continuously wired up. - -To avoid complicating things more than necessary, treat all devices -mostly equally, i.e. regardless of their association with any RMRRs. The -main difference is when it comes to updating context entries, which need -to be atomic when there are RMRRs. Yet atomicity can only be achieved -with CMPXCHG16B, availability of which we can't take for given. - -The seemingly complicated choice of non-negative return values for -domain_context_mapping_one() is to limit code churn: This way callers -passing NULL for pdev don't need fiddling with. - -Signed-off-by: Jan Beulich -Reviewed-by: Kevin Tian -Reviewed-by: Roger Pau Monné - ---- a/xen/drivers/passthrough/vtd/extern.h -+++ b/xen/drivers/passthrough/vtd/extern.h -@@ -84,7 +84,8 @@ void free_pgtable_maddr(u64 maddr); - void *map_vtd_domain_page(u64 maddr); - void unmap_vtd_domain_page(const void *va); - int domain_context_mapping_one(struct domain *domain, struct vtd_iommu *iommu, -- u8 bus, u8 devfn, const struct pci_dev *); -+ uint8_t bus, uint8_t devfn, -+ const struct pci_dev *pdev, unsigned int mode); - int domain_context_unmap_one(struct domain *domain, struct vtd_iommu *iommu, - u8 bus, u8 devfn); - int intel_iommu_get_reserved_device_memory(iommu_grdm_t *func, void *ctxt); -@@ -104,8 +105,8 @@ bool is_azalia_tlb_enabled(const struct - void platform_quirks_init(void); - void vtd_ops_preamble_quirk(struct vtd_iommu *iommu); - void vtd_ops_postamble_quirk(struct vtd_iommu *iommu); --int __must_check me_wifi_quirk(struct domain *domain, -- u8 bus, u8 devfn, int map); -+int __must_check me_wifi_quirk(struct domain *domain, uint8_t bus, -+ uint8_t devfn, unsigned int mode); - void pci_vtd_quirk(const struct pci_dev *); - void quirk_iommu_caps(struct vtd_iommu *iommu); - ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -120,6 +120,7 @@ static int context_set_domain_id(struct - } - - set_bit(i, iommu->domid_bitmap); -+ context->hi &= ~(((1 << DID_FIELD_WIDTH) - 1) << DID_HIGH_OFFSET); - context->hi |= (i & ((1 << DID_FIELD_WIDTH) - 1)) << DID_HIGH_OFFSET; - return 0; - } -@@ -1371,15 +1372,27 @@ static void __hwdom_init intel_iommu_hwd - } - } - -+/* -+ * This function returns -+ * - a negative errno value upon error, -+ * - zero upon success when previously the entry was non-present, or this isn't -+ * the "main" request for a device (pdev == NULL), or for no-op quarantining -+ * assignments, -+ * - positive (one) upon success when previously the entry was present and this -+ * is the "main" request for a device (pdev != NULL). -+ */ - int domain_context_mapping_one( - struct domain *domain, - struct vtd_iommu *iommu, -- u8 bus, u8 devfn, const struct pci_dev *pdev) -+ uint8_t bus, uint8_t devfn, const struct pci_dev *pdev, -+ unsigned int mode) - { - struct domain_iommu *hd = dom_iommu(domain); -- struct context_entry *context, *context_entries; -+ struct context_entry *context, *context_entries, lctxt; -+ __uint128_t old; - u64 maddr, pgd_maddr; -- u16 seg = iommu->drhd->segment; -+ uint16_t seg = iommu->drhd->segment, prev_did = 0; -+ struct domain *prev_dom = NULL; - int rc, ret; - bool_t flush_dev_iotlb; - -@@ -1391,17 +1404,32 @@ int domain_context_mapping_one( - maddr = bus_to_context_maddr(iommu, bus); - context_entries = (struct context_entry *)map_vtd_domain_page(maddr); - context = &context_entries[devfn]; -+ old = (lctxt = *context).full; - -- if ( context_present(*context) ) -+ if ( context_present(lctxt) ) - { -- spin_unlock(&iommu->lock); -- unmap_vtd_domain_page(context_entries); -- return 0; -+ domid_t domid; -+ -+ prev_did = context_domain_id(lctxt); -+ domid = iommu->domid_map[prev_did]; -+ if ( domid < DOMID_FIRST_RESERVED ) -+ prev_dom = rcu_lock_domain_by_id(domid); -+ else if ( domid == DOMID_IO ) -+ prev_dom = rcu_lock_domain(dom_io); -+ if ( !prev_dom ) -+ { -+ spin_unlock(&iommu->lock); -+ unmap_vtd_domain_page(context_entries); -+ dprintk(XENLOG_DEBUG VTDPREFIX, -+ "no domain for did %u (nr_dom %u)\n", -+ prev_did, cap_ndoms(iommu->cap)); -+ return -ESRCH; -+ } - } - - if ( iommu_hwdom_passthrough && is_hardware_domain(domain) ) - { -- context_set_translation_type(*context, CONTEXT_TT_PASS_THRU); -+ context_set_translation_type(lctxt, CONTEXT_TT_PASS_THRU); - } - else - { -@@ -1413,36 +1441,107 @@ int domain_context_mapping_one( - spin_unlock(&hd->arch.mapping_lock); - spin_unlock(&iommu->lock); - unmap_vtd_domain_page(context_entries); -+ if ( prev_dom ) -+ rcu_unlock_domain(prev_dom); - return -ENOMEM; - } - -- context_set_address_root(*context, pgd_maddr); -+ context_set_address_root(lctxt, pgd_maddr); - if ( ats_enabled && ecap_dev_iotlb(iommu->ecap) ) -- context_set_translation_type(*context, CONTEXT_TT_DEV_IOTLB); -+ context_set_translation_type(lctxt, CONTEXT_TT_DEV_IOTLB); - else -- context_set_translation_type(*context, CONTEXT_TT_MULTI_LEVEL); -+ context_set_translation_type(lctxt, CONTEXT_TT_MULTI_LEVEL); - - spin_unlock(&hd->arch.mapping_lock); - } - -- if ( context_set_domain_id(context, domain, iommu) ) -+ if ( context_set_domain_id(&lctxt, domain, iommu) ) - { -+ unlock: - spin_unlock(&iommu->lock); - unmap_vtd_domain_page(context_entries); -+ if ( prev_dom ) -+ rcu_unlock_domain(prev_dom); - return -EFAULT; - } - -- context_set_address_width(*context, level_to_agaw(iommu->nr_pt_levels)); -- context_set_fault_enable(*context); -- context_set_present(*context); -+ if ( !prev_dom ) -+ { -+ context_set_address_width(lctxt, level_to_agaw(iommu->nr_pt_levels)); -+ context_set_fault_enable(lctxt); -+ context_set_present(lctxt); -+ } -+ else if ( prev_dom == domain ) -+ { -+ ASSERT(lctxt.full == context->full); -+ rc = !!pdev; -+ goto unlock; -+ } -+ else -+ { -+ ASSERT(context_address_width(lctxt) == -+ level_to_agaw(iommu->nr_pt_levels)); -+ ASSERT(!context_fault_disable(lctxt)); -+ } -+ -+ if ( cpu_has_cx16 ) -+ { -+ __uint128_t res = cmpxchg16b(context, &old, &lctxt.full); -+ -+ /* -+ * Hardware does not update the context entry behind our backs, -+ * so the return value should match "old". -+ */ -+ if ( res != old ) -+ { -+ if ( pdev ) -+ check_cleanup_domid_map(domain, pdev, iommu); -+ printk(XENLOG_ERR -+ "%pp: unexpected context entry %016lx_%016lx (expected %016lx_%016lx)\n", -+ &PCI_SBDF3(pdev->seg, pdev->bus, devfn), -+ (uint64_t)(res >> 64), (uint64_t)res, -+ (uint64_t)(old >> 64), (uint64_t)old); -+ rc = -EILSEQ; -+ goto unlock; -+ } -+ } -+ else if ( !prev_dom || !(mode & MAP_WITH_RMRR) ) -+ { -+ context_clear_present(*context); -+ iommu_sync_cache(context, sizeof(*context)); -+ -+ write_atomic(&context->hi, lctxt.hi); -+ /* No barrier should be needed between these two. */ -+ write_atomic(&context->lo, lctxt.lo); -+ } -+ else /* Best effort, updating DID last. */ -+ { -+ /* -+ * By non-atomically updating the context entry's DID field last, -+ * during a short window in time TLB entries with the old domain ID -+ * but the new page tables may be inserted. This could affect I/O -+ * of other devices using this same (old) domain ID. Such updating -+ * therefore is not a problem if this was the only device associated -+ * with the old domain ID. Diverting I/O of any of a dying domain's -+ * devices to the quarantine page tables is intended anyway. -+ */ -+ if ( !(mode & (MAP_OWNER_DYING | MAP_SINGLE_DEVICE)) ) -+ printk(XENLOG_WARNING VTDPREFIX -+ " %pp: reassignment may cause %pd data corruption\n", -+ &PCI_SBDF3(seg, bus, devfn), prev_dom); -+ -+ write_atomic(&context->lo, lctxt.lo); -+ /* No barrier should be needed between these two. */ -+ write_atomic(&context->hi, lctxt.hi); -+ } -+ - iommu_sync_cache(context, sizeof(struct context_entry)); - spin_unlock(&iommu->lock); - -- /* Context entry was previously non-present (with domid 0). */ -- rc = iommu_flush_context_device(iommu, 0, PCI_BDF2(bus, devfn), -- DMA_CCMD_MASK_NOBIT, 1); -+ rc = iommu_flush_context_device(iommu, prev_did, PCI_BDF2(bus, devfn), -+ DMA_CCMD_MASK_NOBIT, !prev_dom); - flush_dev_iotlb = !!find_ats_dev_drhd(iommu); -- ret = iommu_flush_iotlb_dsi(iommu, 0, 1, flush_dev_iotlb); -+ ret = iommu_flush_iotlb_dsi(iommu, prev_did, !prev_dom, flush_dev_iotlb); - - /* - * The current logic for returns: -@@ -1463,17 +1562,26 @@ int domain_context_mapping_one( - unmap_vtd_domain_page(context_entries); - - if ( !seg && !rc ) -- rc = me_wifi_quirk(domain, bus, devfn, MAP_ME_PHANTOM_FUNC); -+ rc = me_wifi_quirk(domain, bus, devfn, mode); - - if ( rc ) - { -- ret = domain_context_unmap_one(domain, iommu, bus, devfn); -+ if ( !prev_dom ) -+ ret = domain_context_unmap_one(domain, iommu, bus, devfn); -+ else if ( prev_dom != domain ) /* Avoid infinite recursion. */ -+ ret = domain_context_mapping_one(prev_dom, iommu, bus, devfn, pdev, -+ mode & MAP_WITH_RMRR) < 0; -+ else -+ ret = 1; - - if ( !ret && pdev && pdev->devfn == devfn ) - check_cleanup_domid_map(domain, pdev, iommu); - } - -- return rc; -+ if ( prev_dom ) -+ rcu_unlock_domain(prev_dom); -+ -+ return rc ?: pdev && prev_dom; - } - - static int domain_context_unmap(struct domain *d, uint8_t devfn, -@@ -1483,8 +1591,10 @@ static int domain_context_mapping(struct - struct pci_dev *pdev) - { - const struct acpi_drhd_unit *drhd = acpi_find_matched_drhd_unit(pdev); -+ const struct acpi_rmrr_unit *rmrr; - int ret = 0; -- uint16_t seg = pdev->seg; -+ unsigned int i, mode = 0; -+ uint16_t seg = pdev->seg, bdf; - uint8_t bus = pdev->bus, secbus; - - /* -@@ -1500,8 +1610,29 @@ static int domain_context_mapping(struct - - ASSERT(pcidevs_locked()); - -+ for_each_rmrr_device( rmrr, bdf, i ) -+ { -+ if ( rmrr->segment != pdev->seg || bdf != pdev->sbdf.bdf ) -+ continue; -+ -+ mode |= MAP_WITH_RMRR; -+ break; -+ } -+ -+ if ( domain != pdev->domain ) -+ { -+ if ( pdev->domain->is_dying ) -+ mode |= MAP_OWNER_DYING; -+ else if ( drhd && -+ !any_pdev_behind_iommu(pdev->domain, pdev, drhd->iommu) && -+ !pdev->phantom_stride ) -+ mode |= MAP_SINGLE_DEVICE; -+ } -+ - switch ( pdev->type ) - { -+ bool prev_present; -+ - case DEV_TYPE_PCI_HOST_BRIDGE: - if ( iommu_debug ) - printk(VTDPREFIX "%pd:Hostbridge: skip %pp map\n", -@@ -1523,7 +1654,9 @@ static int domain_context_mapping(struct - printk(VTDPREFIX "%pd:PCIe: map %pp\n", - domain, &PCI_SBDF3(seg, bus, devfn)); - ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn, -- pdev); -+ pdev, mode); -+ if ( ret > 0 ) -+ ret = 0; - if ( !ret && devfn == pdev->devfn && ats_device(pdev, drhd) > 0 ) - enable_ats_device(pdev, &drhd->iommu->ats_devices); - -@@ -1538,9 +1671,10 @@ static int domain_context_mapping(struct - domain, &PCI_SBDF3(seg, bus, devfn)); - - ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn, -- pdev); -- if ( ret ) -+ pdev, mode); -+ if ( ret < 0 ) - break; -+ prev_present = ret; - - if ( (ret = find_upstream_bridge(seg, &bus, &devfn, &secbus)) < 1 ) - { -@@ -1548,6 +1682,15 @@ static int domain_context_mapping(struct - break; - ret = -ENXIO; - } -+ /* -+ * Strictly speaking if the device is the only one behind this bridge -+ * and the only one with this (secbus,0,0) tuple, it could be allowed -+ * to be re-assigned regardless of RMRR presence. But let's deal with -+ * that case only if it is actually found in the wild. -+ */ -+ else if ( prev_present && (mode & MAP_WITH_RMRR) && -+ domain != pdev->domain ) -+ ret = -EOPNOTSUPP; - - /* - * Mapping a bridge should, if anything, pass the struct pci_dev of -@@ -1556,7 +1699,7 @@ static int domain_context_mapping(struct - */ - if ( ret >= 0 ) - ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn, -- NULL); -+ NULL, mode); - - /* - * Devices behind PCIe-to-PCI/PCIx bridge may generate different -@@ -1571,10 +1714,15 @@ static int domain_context_mapping(struct - if ( !ret && pdev_type(seg, bus, devfn) == DEV_TYPE_PCIe2PCI_BRIDGE && - (secbus != pdev->bus || pdev->devfn != 0) ) - ret = domain_context_mapping_one(domain, drhd->iommu, secbus, 0, -- NULL); -+ NULL, mode); - - if ( ret ) -- domain_context_unmap(domain, devfn, pdev); -+ { -+ if ( !prev_present ) -+ domain_context_unmap(domain, devfn, pdev); -+ else if ( pdev->domain != domain ) /* Avoid infinite recursion. */ -+ domain_context_mapping(pdev->domain, devfn, pdev); -+ } - - break; - -@@ -2363,17 +2511,46 @@ static int reassign_device_ownership( - { - int ret; - -- ret = domain_context_unmap(source, devfn, pdev); -+ if ( !QUARANTINE_SKIP(target) ) -+ { -+ if ( !has_arch_pdevs(target) ) -+ vmx_pi_hooks_assign(target); -+ -+ /* -+ * Devices assigned to untrusted domains (here assumed to be any domU) -+ * can attempt to send arbitrary LAPIC/MSI messages. We are unprotected -+ * by the root complex unless interrupt remapping is enabled. -+ */ -+ if ( (target != hardware_domain) && !iommu_intremap ) -+ untrusted_msi = true; -+ -+ ret = domain_context_mapping(target, devfn, pdev); -+ -+ if ( !ret && !QUARANTINE_SKIP(source) && pdev->devfn == devfn ) -+ { -+ const struct acpi_drhd_unit *drhd = acpi_find_matched_drhd_unit(pdev); -+ -+ if ( drhd ) -+ check_cleanup_domid_map(source, pdev, drhd->iommu); -+ } -+ } -+ else -+ ret = domain_context_unmap(source, devfn, pdev); - if ( ret ) -+ { -+ if ( !has_arch_pdevs(target) ) -+ vmx_pi_hooks_deassign(target); - return ret; -+ } - -- /* -- * Devices assigned to untrusted domains (here assumed to be any domU) -- * can attempt to send arbitrary LAPIC/MSI messages. We are unprotected -- * by the root complex unless interrupt remapping is enabled. -- */ -- if ( (target != hardware_domain) && !iommu_intremap ) -- untrusted_msi = true; -+ if ( devfn == pdev->devfn && pdev->domain != target ) -+ { -+ list_move(&pdev->domain_list, &target->pdev_list); -+ pdev->domain = target; -+ } -+ -+ if ( !has_arch_pdevs(source) ) -+ vmx_pi_hooks_deassign(source); - - /* - * If the device belongs to the hardware domain, and it has RMRR, don't -@@ -2403,34 +2580,7 @@ static int reassign_device_ownership( - } - } - -- if ( devfn == pdev->devfn && pdev->domain != dom_io ) -- { -- list_move(&pdev->domain_list, &dom_io->pdev_list); -- pdev->domain = dom_io; -- } -- -- if ( !has_arch_pdevs(source) ) -- vmx_pi_hooks_deassign(source); -- -- if ( !has_arch_pdevs(target) ) -- vmx_pi_hooks_assign(target); -- -- ret = domain_context_mapping(target, devfn, pdev); -- if ( ret ) -- { -- if ( !has_arch_pdevs(target) ) -- vmx_pi_hooks_deassign(target); -- -- return ret; -- } -- -- if ( devfn == pdev->devfn && pdev->domain != target ) -- { -- list_move(&pdev->domain_list, &target->pdev_list); -- pdev->domain = target; -- } -- -- return ret; -+ return 0; - } - - static int intel_iommu_assign_device( ---- a/xen/drivers/passthrough/vtd/iommu.h -+++ b/xen/drivers/passthrough/vtd/iommu.h -@@ -202,8 +202,12 @@ struct root_entry { - do {(root).val |= ((value) & PAGE_MASK_4K);} while(0) - - struct context_entry { -- u64 lo; -- u64 hi; -+ union { -+ struct { -+ uint64_t lo, hi; -+ }; -+ __uint128_t full; -+ }; - }; - #define ROOT_ENTRY_NR (PAGE_SIZE_4K/sizeof(struct root_entry)) - #define context_present(c) ((c).lo & 1) ---- a/xen/drivers/passthrough/vtd/quirks.c -+++ b/xen/drivers/passthrough/vtd/quirks.c -@@ -407,7 +407,8 @@ void __init platform_quirks_init(void) - */ - - static int __must_check map_me_phantom_function(struct domain *domain, -- u32 dev, int map) -+ unsigned int dev, -+ unsigned int mode) - { - struct acpi_drhd_unit *drhd; - struct pci_dev *pdev; -@@ -418,9 +419,9 @@ static int __must_check map_me_phantom_f - drhd = acpi_find_matched_drhd_unit(pdev); - - /* map or unmap ME phantom function */ -- if ( map ) -+ if ( !(mode & UNMAP_ME_PHANTOM_FUNC) ) - rc = domain_context_mapping_one(domain, drhd->iommu, 0, -- PCI_DEVFN(dev, 7), NULL); -+ PCI_DEVFN(dev, 7), NULL, mode); - else - rc = domain_context_unmap_one(domain, drhd->iommu, 0, - PCI_DEVFN(dev, 7)); -@@ -428,7 +429,8 @@ static int __must_check map_me_phantom_f - return rc; - } - --int me_wifi_quirk(struct domain *domain, u8 bus, u8 devfn, int map) -+int me_wifi_quirk(struct domain *domain, uint8_t bus, uint8_t devfn, -+ unsigned int mode) - { - u32 id; - int rc = 0; -@@ -452,7 +454,7 @@ int me_wifi_quirk(struct domain *domain, - case 0x423b8086: - case 0x423c8086: - case 0x423d8086: -- rc = map_me_phantom_function(domain, 3, map); -+ rc = map_me_phantom_function(domain, 3, mode); - break; - default: - break; -@@ -478,7 +480,7 @@ int me_wifi_quirk(struct domain *domain, - case 0x42388086: /* Puma Peak */ - case 0x422b8086: - case 0x422c8086: -- rc = map_me_phantom_function(domain, 22, map); -+ rc = map_me_phantom_function(domain, 22, mode); - break; - default: - break; ---- a/xen/drivers/passthrough/vtd/vtd.h -+++ b/xen/drivers/passthrough/vtd/vtd.h -@@ -22,8 +22,14 @@ - - #include - --#define MAP_ME_PHANTOM_FUNC 1 --#define UNMAP_ME_PHANTOM_FUNC 0 -+/* -+ * Values for domain_context_mapping_one()'s and me_wifi_quirk()'s "mode" -+ * parameters. -+ */ -+#define MAP_WITH_RMRR (1u << 0) -+#define MAP_OWNER_DYING (1u << 1) -+#define MAP_SINGLE_DEVICE (1u << 2) -+#define UNMAP_ME_PHANTOM_FUNC (1u << 3) - - /* Allow for both IOAPIC and IOSAPIC. */ - #define IO_xAPIC_route_entry IO_APIC_route_entry diff --git a/xsa400-4.16-06.patch b/xsa400-4.16-06.patch deleted file mode 100644 index dfde0ee..0000000 --- a/xsa400-4.16-06.patch +++ /dev/null @@ -1,330 +0,0 @@ -From: Jan Beulich -Subject: AMD/IOMMU: re-assign devices directly - -Devices with unity map ranges, due to it being unspecified how/when -these memory ranges may get accessed, may not be left disconnected from -their unity mappings (as long as it's not certain that the device has -been fully quiesced). Hence rather than tearing down the old root page -table pointer and then establishing the new one, re-assignment needs to -be done in a single step. - -This is CVE-2022-26360 / part of XSA-400. - -Reported-by: Roger Pau Monné - -Similarly quarantining scratch-page mode relies on page tables to be -continuously wired up. - -To avoid complicating things more than necessary, treat all devices -mostly equally, i.e. regardless of their association with any unity map -ranges. The main difference is when it comes to updating DTEs, which need -to be atomic when there are unity mappings. Yet atomicity can only be -achieved with CMPXCHG16B, availability of which we can't take for given. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant -Reviewed-by: Roger Pau Monné - ---- a/xen/drivers/passthrough/amd/iommu.h -+++ b/xen/drivers/passthrough/amd/iommu.h -@@ -262,9 +262,13 @@ void amd_iommu_set_intremap_table(struct - const void *ptr, - const struct amd_iommu *iommu, - bool valid); --void amd_iommu_set_root_page_table(struct amd_iommu_dte *dte, -- uint64_t root_ptr, uint16_t domain_id, -- uint8_t paging_mode, bool valid); -+#define SET_ROOT_VALID (1u << 0) -+#define SET_ROOT_WITH_UNITY_MAP (1u << 1) -+int __must_check amd_iommu_set_root_page_table(struct amd_iommu_dte *dte, -+ uint64_t root_ptr, -+ uint16_t domain_id, -+ uint8_t paging_mode, -+ unsigned int flags); - void iommu_dte_add_device_entry(struct amd_iommu_dte *dte, - const struct ivrs_mappings *ivrs_dev); - ---- a/xen/drivers/passthrough/amd/iommu_map.c -+++ b/xen/drivers/passthrough/amd/iommu_map.c -@@ -114,10 +114,69 @@ static unsigned int set_iommu_ptes_prese - return flush_flags; - } - --void amd_iommu_set_root_page_table(struct amd_iommu_dte *dte, -- uint64_t root_ptr, uint16_t domain_id, -- uint8_t paging_mode, bool valid) -+/* -+ * This function returns -+ * - -errno for errors, -+ * - 0 for a successful update, atomic when necessary -+ * - 1 for a successful but non-atomic update, which may need to be warned -+ * about by the caller. -+ */ -+int amd_iommu_set_root_page_table(struct amd_iommu_dte *dte, -+ uint64_t root_ptr, uint16_t domain_id, -+ uint8_t paging_mode, unsigned int flags) - { -+ bool valid = flags & SET_ROOT_VALID; -+ -+ if ( dte->v && dte->tv && -+ (cpu_has_cx16 || (flags & SET_ROOT_WITH_UNITY_MAP)) ) -+ { -+ union { -+ struct amd_iommu_dte dte; -+ uint64_t raw64[4]; -+ __uint128_t raw128[2]; -+ } ldte = { .dte = *dte }; -+ __uint128_t old = ldte.raw128[0]; -+ int ret = 0; -+ -+ ldte.dte.domain_id = domain_id; -+ ldte.dte.pt_root = paddr_to_pfn(root_ptr); -+ ldte.dte.iw = true; -+ ldte.dte.ir = true; -+ ldte.dte.paging_mode = paging_mode; -+ ldte.dte.v = valid; -+ -+ if ( cpu_has_cx16 ) -+ { -+ __uint128_t res = cmpxchg16b(dte, &old, &ldte.raw128[0]); -+ -+ /* -+ * Hardware does not update the DTE behind our backs, so the -+ * return value should match "old". -+ */ -+ if ( res != old ) -+ { -+ printk(XENLOG_ERR -+ "Dom%d: unexpected DTE %016lx_%016lx (expected %016lx_%016lx)\n", -+ domain_id, -+ (uint64_t)(res >> 64), (uint64_t)res, -+ (uint64_t)(old >> 64), (uint64_t)old); -+ ret = -EILSEQ; -+ } -+ } -+ else /* Best effort, updating domain_id last. */ -+ { -+ uint64_t *ptr = (void *)dte; -+ -+ write_atomic(ptr + 0, ldte.raw64[0]); -+ /* No barrier should be needed between these two. */ -+ write_atomic(ptr + 1, ldte.raw64[1]); -+ -+ ret = 1; -+ } -+ -+ return ret; -+ } -+ - if ( valid || dte->v ) - { - dte->tv = false; -@@ -132,6 +191,8 @@ void amd_iommu_set_root_page_table(struc - smp_wmb(); - dte->tv = true; - dte->v = valid; -+ -+ return 0; - } - - void amd_iommu_set_intremap_table( ---- a/xen/drivers/passthrough/amd/pci_amd_iommu.c -+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c -@@ -96,13 +96,32 @@ static int __must_check allocate_domain_ - return rc; - } - -+static bool any_pdev_behind_iommu(const struct domain *d, -+ const struct pci_dev *exclude, -+ const struct amd_iommu *iommu) -+{ -+ const struct pci_dev *pdev; -+ -+ for_each_pdev ( d, pdev ) -+ { -+ if ( pdev == exclude ) -+ continue; -+ -+ if ( find_iommu_for_device(pdev->seg, pdev->sbdf.bdf) == iommu ) -+ return true; -+ } -+ -+ return false; -+} -+ - static int __must_check amd_iommu_setup_domain_device( - struct domain *domain, struct amd_iommu *iommu, - uint8_t devfn, struct pci_dev *pdev) - { - struct amd_iommu_dte *table, *dte; - unsigned long flags; -- int req_id, valid = 1, rc; -+ unsigned int req_id, sr_flags; -+ int rc; - u8 bus = pdev->bus; - struct domain_iommu *hd = dom_iommu(domain); - const struct ivrs_mappings *ivrs_dev; -@@ -116,8 +135,11 @@ static int __must_check amd_iommu_setup_ - if ( rc ) - return rc; - -- if ( iommu_hwdom_passthrough && is_hardware_domain(domain) ) -- valid = 0; -+ req_id = get_dma_requestor_id(iommu->seg, pdev->sbdf.bdf); -+ ivrs_dev = &get_ivrs_mappings(iommu->seg)[req_id]; -+ sr_flags = (iommu_hwdom_passthrough && is_hardware_domain(domain) -+ ? 0 : SET_ROOT_VALID) -+ | (ivrs_dev->unity_map ? SET_ROOT_WITH_UNITY_MAP : 0); - - /* get device-table entry */ - req_id = get_dma_requestor_id(iommu->seg, PCI_BDF2(bus, devfn)); -@@ -130,9 +152,15 @@ static int __must_check amd_iommu_setup_ - if ( !dte->v || !dte->tv ) - { - /* bind DTE to domain page-tables */ -- amd_iommu_set_root_page_table( -- dte, page_to_maddr(hd->arch.amd.root_table), -- domain->domain_id, hd->arch.amd.paging_mode, valid); -+ rc = amd_iommu_set_root_page_table( -+ dte, page_to_maddr(hd->arch.amd.root_table), -+ domain->domain_id, hd->arch.amd.paging_mode, sr_flags); -+ if ( rc ) -+ { -+ ASSERT(rc < 0); -+ spin_unlock_irqrestore(&iommu->lock, flags); -+ return rc; -+ } - - /* Undo what amd_iommu_disable_domain_device() may have done. */ - if ( dte->it_root ) -@@ -152,17 +180,76 @@ static int __must_check amd_iommu_setup_ - spin_unlock_irqrestore(&iommu->lock, flags); - - amd_iommu_flush_device(iommu, req_id); -+ } -+ else if ( dte->pt_root != mfn_x(page_to_mfn(hd->arch.amd.root_table)) ) -+ { -+ /* -+ * Strictly speaking if the device is the only one with this requestor -+ * ID, it could be allowed to be re-assigned regardless of unity map -+ * presence. But let's deal with that case only if it is actually -+ * found in the wild. -+ */ -+ if ( req_id != PCI_BDF2(bus, devfn) && -+ (sr_flags & SET_ROOT_WITH_UNITY_MAP) ) -+ rc = -EOPNOTSUPP; -+ else -+ rc = amd_iommu_set_root_page_table( -+ dte, page_to_maddr(hd->arch.amd.root_table), -+ domain->domain_id, hd->arch.amd.paging_mode, sr_flags); -+ if ( rc < 0 ) -+ { -+ spin_unlock_irqrestore(&iommu->lock, flags); -+ return rc; -+ } -+ if ( rc && -+ domain != pdev->domain && -+ /* -+ * By non-atomically updating the DTE's domain ID field last, -+ * during a short window in time TLB entries with the old domain -+ * ID but the new page tables may have been inserted. This could -+ * affect I/O of other devices using this same (old) domain ID. -+ * Such updating therefore is not a problem if this was the only -+ * device associated with the old domain ID. Diverting I/O of any -+ * of a dying domain's devices to the quarantine page tables is -+ * intended anyway. -+ */ -+ !pdev->domain->is_dying && -+ (any_pdev_behind_iommu(pdev->domain, pdev, iommu) || -+ pdev->phantom_stride) ) -+ AMD_IOMMU_WARN(" %pp: reassignment may cause %pd data corruption\n", -+ &PCI_SBDF3(pdev->seg, bus, devfn), pdev->domain); -+ -+ /* -+ * Check remaining settings are still in place from an earlier call -+ * here. They're all independent of the domain, so should not have -+ * changed. -+ */ -+ if ( dte->it_root ) -+ ASSERT(dte->int_ctl == IOMMU_DEV_TABLE_INT_CONTROL_TRANSLATED); -+ ASSERT(dte->iv == iommu_intremap); -+ ASSERT(dte->ex == ivrs_dev->dte_allow_exclusion); -+ ASSERT(dte->sys_mgt == MASK_EXTR(ivrs_dev->device_flags, -+ ACPI_IVHD_SYSTEM_MGMT)); - -- AMD_IOMMU_DEBUG("Setup I/O page table: device id = %#x, type = %#x, " -- "root table = %#"PRIx64", " -- "domain = %d, paging mode = %d\n", -- req_id, pdev->type, -- page_to_maddr(hd->arch.amd.root_table), -- domain->domain_id, hd->arch.amd.paging_mode); -+ if ( pci_ats_device(iommu->seg, bus, pdev->devfn) && -+ !ivrs_dev->block_ats && -+ iommu_has_cap(iommu, PCI_CAP_IOTLB_SHIFT) ) -+ ASSERT(dte->i == ats_enabled); -+ -+ spin_unlock_irqrestore(&iommu->lock, flags); -+ -+ amd_iommu_flush_device(iommu, req_id); - } - else - spin_unlock_irqrestore(&iommu->lock, flags); - -+ AMD_IOMMU_DEBUG("Setup I/O page table: device id = %#x, type = %#x, " -+ "root table = %#"PRIx64", " -+ "domain = %d, paging mode = %d\n", -+ req_id, pdev->type, -+ page_to_maddr(hd->arch.amd.root_table), -+ domain->domain_id, hd->arch.amd.paging_mode); -+ - ASSERT(pcidevs_locked()); - - if ( pci_ats_device(iommu->seg, bus, pdev->devfn) && -@@ -366,7 +453,20 @@ static int reassign_device(struct domain - return -ENODEV; - } - -- amd_iommu_disable_domain_device(source, iommu, devfn, pdev); -+ if ( !QUARANTINE_SKIP(target) ) -+ { -+ rc = amd_iommu_setup_domain_device(target, iommu, devfn, pdev); -+ if ( rc ) -+ return rc; -+ } -+ else -+ amd_iommu_disable_domain_device(source, iommu, devfn, pdev); -+ -+ if ( devfn == pdev->devfn && pdev->domain != target ) -+ { -+ list_move(&pdev->domain_list, &target->pdev_list); -+ pdev->domain = target; -+ } - - /* - * If the device belongs to the hardware domain, and it has a unity mapping, -@@ -382,25 +482,9 @@ static int reassign_device(struct domain - return rc; - } - -- if ( devfn == pdev->devfn && pdev->domain != dom_io ) -- { -- list_move(&pdev->domain_list, &dom_io->pdev_list); -- pdev->domain = dom_io; -- } -- -- rc = amd_iommu_setup_domain_device(target, iommu, devfn, pdev); -- if ( rc ) -- return rc; -- - AMD_IOMMU_DEBUG("Re-assign %pp from dom%d to dom%d\n", - &pdev->sbdf, source->domain_id, target->domain_id); - -- if ( devfn == pdev->devfn && pdev->domain != target ) -- { -- list_move(&pdev->domain_list, &target->pdev_list); -- pdev->domain = target; -- } -- - return 0; - } - diff --git a/xsa400-4.16-07.patch b/xsa400-4.16-07.patch deleted file mode 100644 index 8af34d5..0000000 --- a/xsa400-4.16-07.patch +++ /dev/null @@ -1,333 +0,0 @@ -From: Jan Beulich -Subject: VT-d: prepare for per-device quarantine page tables (part I) - -Arrange for domain ID and page table root to be passed around, the latter in -particular to domain_pgd_maddr() such that taking it from the per-domain -fields can be overridden. - -No functional change intended. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant -Reviewed-by: Roger Pau Monné -Reviewed-by: Kevin Tian - ---- a/xen/drivers/passthrough/vtd/extern.h -+++ b/xen/drivers/passthrough/vtd/extern.h -@@ -85,9 +85,10 @@ void *map_vtd_domain_page(u64 maddr); - void unmap_vtd_domain_page(const void *va); - int domain_context_mapping_one(struct domain *domain, struct vtd_iommu *iommu, - uint8_t bus, uint8_t devfn, -- const struct pci_dev *pdev, unsigned int mode); -+ const struct pci_dev *pdev, domid_t domid, -+ paddr_t pgd_maddr, unsigned int mode); - int domain_context_unmap_one(struct domain *domain, struct vtd_iommu *iommu, -- u8 bus, u8 devfn); -+ uint8_t bus, uint8_t devfn, domid_t domid); - int intel_iommu_get_reserved_device_memory(iommu_grdm_t *func, void *ctxt); - - unsigned int io_apic_read_remap_rte(unsigned int apic, unsigned int reg); -@@ -106,7 +107,8 @@ void platform_quirks_init(void); - void vtd_ops_preamble_quirk(struct vtd_iommu *iommu); - void vtd_ops_postamble_quirk(struct vtd_iommu *iommu); - int __must_check me_wifi_quirk(struct domain *domain, uint8_t bus, -- uint8_t devfn, unsigned int mode); -+ uint8_t devfn, domid_t domid, paddr_t pgd_maddr, -+ unsigned int mode); - void pci_vtd_quirk(const struct pci_dev *); - void quirk_iommu_caps(struct vtd_iommu *iommu); - ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -43,7 +43,7 @@ - #include "../ats.h" - - /* dom_io is used as a sentinel for quarantined devices */ --#define QUARANTINE_SKIP(d) ((d) == dom_io && !dom_iommu(d)->arch.vtd.pgd_maddr) -+#define QUARANTINE_SKIP(d, pgd_maddr) ((d) == dom_io && !(pgd_maddr)) - - /* Possible unfiltered LAPIC/MSI messages from untrusted sources? */ - bool __read_mostly untrusted_msi; -@@ -358,15 +358,17 @@ static u64 addr_to_dma_page_maddr(struct - return pte_maddr; - } - --static uint64_t domain_pgd_maddr(struct domain *d, unsigned int nr_pt_levels) -+static paddr_t domain_pgd_maddr(struct domain *d, paddr_t pgd_maddr, -+ unsigned int nr_pt_levels) - { - struct domain_iommu *hd = dom_iommu(d); -- uint64_t pgd_maddr; - unsigned int agaw; - - ASSERT(spin_is_locked(&hd->arch.mapping_lock)); - -- if ( iommu_use_hap_pt(d) ) -+ if ( pgd_maddr ) -+ /* nothing */; -+ else if ( iommu_use_hap_pt(d) ) - { - pagetable_t pgt = p2m_get_pagetable(p2m_get_hostp2m(d)); - -@@ -1385,18 +1387,18 @@ int domain_context_mapping_one( - struct domain *domain, - struct vtd_iommu *iommu, - uint8_t bus, uint8_t devfn, const struct pci_dev *pdev, -- unsigned int mode) -+ domid_t domid, paddr_t pgd_maddr, unsigned int mode) - { - struct domain_iommu *hd = dom_iommu(domain); - struct context_entry *context, *context_entries, lctxt; - __uint128_t old; -- u64 maddr, pgd_maddr; -+ uint64_t maddr; - uint16_t seg = iommu->drhd->segment, prev_did = 0; - struct domain *prev_dom = NULL; - int rc, ret; - bool_t flush_dev_iotlb; - -- if ( QUARANTINE_SKIP(domain) ) -+ if ( QUARANTINE_SKIP(domain, pgd_maddr) ) - return 0; - - ASSERT(pcidevs_locked()); -@@ -1433,10 +1435,12 @@ int domain_context_mapping_one( - } - else - { -+ paddr_t root; -+ - spin_lock(&hd->arch.mapping_lock); - -- pgd_maddr = domain_pgd_maddr(domain, iommu->nr_pt_levels); -- if ( !pgd_maddr ) -+ root = domain_pgd_maddr(domain, pgd_maddr, iommu->nr_pt_levels); -+ if ( !root ) - { - spin_unlock(&hd->arch.mapping_lock); - spin_unlock(&iommu->lock); -@@ -1446,7 +1450,7 @@ int domain_context_mapping_one( - return -ENOMEM; - } - -- context_set_address_root(lctxt, pgd_maddr); -+ context_set_address_root(lctxt, root); - if ( ats_enabled && ecap_dev_iotlb(iommu->ecap) ) - context_set_translation_type(lctxt, CONTEXT_TT_DEV_IOTLB); - else -@@ -1562,15 +1566,21 @@ int domain_context_mapping_one( - unmap_vtd_domain_page(context_entries); - - if ( !seg && !rc ) -- rc = me_wifi_quirk(domain, bus, devfn, mode); -+ rc = me_wifi_quirk(domain, bus, devfn, domid, pgd_maddr, mode); - - if ( rc ) - { - if ( !prev_dom ) -- ret = domain_context_unmap_one(domain, iommu, bus, devfn); -+ ret = domain_context_unmap_one(domain, iommu, bus, devfn, -+ domain->domain_id); - else if ( prev_dom != domain ) /* Avoid infinite recursion. */ -+ { -+ hd = dom_iommu(prev_dom); - ret = domain_context_mapping_one(prev_dom, iommu, bus, devfn, pdev, -+ domain->domain_id, -+ hd->arch.vtd.pgd_maddr, - mode & MAP_WITH_RMRR) < 0; -+ } - else - ret = 1; - -@@ -1592,6 +1602,7 @@ static int domain_context_mapping(struct - { - const struct acpi_drhd_unit *drhd = acpi_find_matched_drhd_unit(pdev); - const struct acpi_rmrr_unit *rmrr; -+ paddr_t pgd_maddr = dom_iommu(domain)->arch.vtd.pgd_maddr; - int ret = 0; - unsigned int i, mode = 0; - uint16_t seg = pdev->seg, bdf; -@@ -1654,7 +1665,8 @@ static int domain_context_mapping(struct - printk(VTDPREFIX "%pd:PCIe: map %pp\n", - domain, &PCI_SBDF3(seg, bus, devfn)); - ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn, -- pdev, mode); -+ pdev, domain->domain_id, pgd_maddr, -+ mode); - if ( ret > 0 ) - ret = 0; - if ( !ret && devfn == pdev->devfn && ats_device(pdev, drhd) > 0 ) -@@ -1671,7 +1683,8 @@ static int domain_context_mapping(struct - domain, &PCI_SBDF3(seg, bus, devfn)); - - ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn, -- pdev, mode); -+ pdev, domain->domain_id, pgd_maddr, -+ mode); - if ( ret < 0 ) - break; - prev_present = ret; -@@ -1699,7 +1712,8 @@ static int domain_context_mapping(struct - */ - if ( ret >= 0 ) - ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn, -- NULL, mode); -+ NULL, domain->domain_id, pgd_maddr, -+ mode); - - /* - * Devices behind PCIe-to-PCI/PCIx bridge may generate different -@@ -1714,7 +1728,8 @@ static int domain_context_mapping(struct - if ( !ret && pdev_type(seg, bus, devfn) == DEV_TYPE_PCIe2PCI_BRIDGE && - (secbus != pdev->bus || pdev->devfn != 0) ) - ret = domain_context_mapping_one(domain, drhd->iommu, secbus, 0, -- NULL, mode); -+ NULL, domain->domain_id, pgd_maddr, -+ mode); - - if ( ret ) - { -@@ -1742,14 +1757,14 @@ static int domain_context_mapping(struct - int domain_context_unmap_one( - struct domain *domain, - struct vtd_iommu *iommu, -- u8 bus, u8 devfn) -+ uint8_t bus, uint8_t devfn, domid_t domid) - { - struct context_entry *context, *context_entries; - u64 maddr; - int iommu_domid, rc, ret; - bool_t flush_dev_iotlb; - -- if ( QUARANTINE_SKIP(domain) ) -+ if ( QUARANTINE_SKIP(domain, dom_iommu(domain)->arch.vtd.pgd_maddr) ) - return 0; - - ASSERT(pcidevs_locked()); -@@ -1803,7 +1818,7 @@ int domain_context_unmap_one( - unmap_vtd_domain_page(context_entries); - - if ( !iommu->drhd->segment && !rc ) -- rc = me_wifi_quirk(domain, bus, devfn, UNMAP_ME_PHANTOM_FUNC); -+ rc = me_wifi_quirk(domain, bus, devfn, domid, 0, UNMAP_ME_PHANTOM_FUNC); - - if ( rc && !is_hardware_domain(domain) && domain != dom_io ) - { -@@ -1850,7 +1865,8 @@ static int domain_context_unmap(struct d - if ( iommu_debug ) - printk(VTDPREFIX "%pd:PCIe: unmap %pp\n", - domain, &PCI_SBDF3(seg, bus, devfn)); -- ret = domain_context_unmap_one(domain, iommu, bus, devfn); -+ ret = domain_context_unmap_one(domain, iommu, bus, devfn, -+ domain->domain_id); - if ( !ret && devfn == pdev->devfn && ats_device(pdev, drhd) > 0 ) - disable_ats_device(pdev); - -@@ -1863,7 +1879,8 @@ static int domain_context_unmap(struct d - if ( iommu_debug ) - printk(VTDPREFIX "%pd:PCI: unmap %pp\n", - domain, &PCI_SBDF3(seg, bus, devfn)); -- ret = domain_context_unmap_one(domain, iommu, bus, devfn); -+ ret = domain_context_unmap_one(domain, iommu, bus, devfn, -+ domain->domain_id); - if ( ret ) - break; - -@@ -1889,12 +1906,15 @@ static int domain_context_unmap(struct d - /* PCIe to PCI/PCIx bridge */ - if ( pdev_type(seg, tmp_bus, tmp_devfn) == DEV_TYPE_PCIe2PCI_BRIDGE ) - { -- ret = domain_context_unmap_one(domain, iommu, tmp_bus, tmp_devfn); -+ ret = domain_context_unmap_one(domain, iommu, tmp_bus, tmp_devfn, -+ domain->domain_id); - if ( !ret ) -- ret = domain_context_unmap_one(domain, iommu, secbus, 0); -+ ret = domain_context_unmap_one(domain, iommu, secbus, 0, -+ domain->domain_id); - } - else /* Legacy PCI bridge */ -- ret = domain_context_unmap_one(domain, iommu, tmp_bus, tmp_devfn); -+ ret = domain_context_unmap_one(domain, iommu, tmp_bus, tmp_devfn, -+ domain->domain_id); - - break; - -@@ -1904,7 +1924,8 @@ static int domain_context_unmap(struct d - return -EINVAL; - } - -- if ( !ret && !QUARANTINE_SKIP(domain) && pdev->devfn == devfn ) -+ if ( !ret && pdev->devfn == devfn && -+ !QUARANTINE_SKIP(domain, dom_iommu(domain)->arch.vtd.pgd_maddr) ) - check_cleanup_domid_map(domain, pdev, iommu); - - return ret; -@@ -2511,7 +2532,7 @@ static int reassign_device_ownership( - { - int ret; - -- if ( !QUARANTINE_SKIP(target) ) -+ if ( !QUARANTINE_SKIP(target, dom_iommu(target)->arch.vtd.pgd_maddr) ) - { - if ( !has_arch_pdevs(target) ) - vmx_pi_hooks_assign(target); -@@ -2526,7 +2547,8 @@ static int reassign_device_ownership( - - ret = domain_context_mapping(target, devfn, pdev); - -- if ( !ret && !QUARANTINE_SKIP(source) && pdev->devfn == devfn ) -+ if ( !ret && pdev->devfn == devfn && -+ !QUARANTINE_SKIP(source, dom_iommu(source)->arch.vtd.pgd_maddr) ) - { - const struct acpi_drhd_unit *drhd = acpi_find_matched_drhd_unit(pdev); - ---- a/xen/drivers/passthrough/vtd/quirks.c -+++ b/xen/drivers/passthrough/vtd/quirks.c -@@ -408,6 +408,8 @@ void __init platform_quirks_init(void) - - static int __must_check map_me_phantom_function(struct domain *domain, - unsigned int dev, -+ domid_t domid, -+ paddr_t pgd_maddr, - unsigned int mode) - { - struct acpi_drhd_unit *drhd; -@@ -421,16 +423,17 @@ static int __must_check map_me_phantom_f - /* map or unmap ME phantom function */ - if ( !(mode & UNMAP_ME_PHANTOM_FUNC) ) - rc = domain_context_mapping_one(domain, drhd->iommu, 0, -- PCI_DEVFN(dev, 7), NULL, mode); -+ PCI_DEVFN(dev, 7), NULL, -+ domid, pgd_maddr, mode); - else - rc = domain_context_unmap_one(domain, drhd->iommu, 0, -- PCI_DEVFN(dev, 7)); -+ PCI_DEVFN(dev, 7), domid); - - return rc; - } - - int me_wifi_quirk(struct domain *domain, uint8_t bus, uint8_t devfn, -- unsigned int mode) -+ domid_t domid, paddr_t pgd_maddr, unsigned int mode) - { - u32 id; - int rc = 0; -@@ -454,7 +457,7 @@ int me_wifi_quirk(struct domain *domain, - case 0x423b8086: - case 0x423c8086: - case 0x423d8086: -- rc = map_me_phantom_function(domain, 3, mode); -+ rc = map_me_phantom_function(domain, 3, domid, pgd_maddr, mode); - break; - default: - break; -@@ -480,7 +483,7 @@ int me_wifi_quirk(struct domain *domain, - case 0x42388086: /* Puma Peak */ - case 0x422b8086: - case 0x422c8086: -- rc = map_me_phantom_function(domain, 22, mode); -+ rc = map_me_phantom_function(domain, 22, domid, pgd_maddr, mode); - break; - default: - break; diff --git a/xsa400-4.16-08.patch b/xsa400-4.16-08.patch deleted file mode 100644 index 56a2b32..0000000 --- a/xsa400-4.16-08.patch +++ /dev/null @@ -1,144 +0,0 @@ -From: Jan Beulich -Subject: VT-d: prepare for per-device quarantine page tables (part II) - -Replace the passing of struct domain * by domid_t in preparation of -per-device quarantine page tables also requiring per-device pseudo -domain IDs, which aren't going to be associated with any struct domain -instances. - -No functional change intended (except for slightly adjusted log message -text). - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant -Reviewed-by: Kevin Tian -Reviewed-by: Roger Pau Monné - ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -62,8 +62,8 @@ static struct tasklet vtd_fault_tasklet; - static int setup_hwdom_device(u8 devfn, struct pci_dev *); - static void setup_hwdom_rmrr(struct domain *d); - --static int domain_iommu_domid(struct domain *d, -- struct vtd_iommu *iommu) -+static int get_iommu_did(domid_t domid, const struct vtd_iommu *iommu, -+ bool warn) - { - unsigned long nr_dom, i; - -@@ -71,16 +71,16 @@ static int domain_iommu_domid(struct dom - i = find_first_bit(iommu->domid_bitmap, nr_dom); - while ( i < nr_dom ) - { -- if ( iommu->domid_map[i] == d->domain_id ) -+ if ( iommu->domid_map[i] == domid ) - return i; - - i = find_next_bit(iommu->domid_bitmap, nr_dom, i+1); - } - -- if ( !d->is_dying ) -+ if ( warn ) - dprintk(XENLOG_ERR VTDPREFIX, -- "Cannot get valid iommu %u domid: %pd\n", -- iommu->index, d); -+ "No valid iommu %u domid for Dom%d\n", -+ iommu->index, domid); - - return -1; - } -@@ -88,8 +88,7 @@ static int domain_iommu_domid(struct dom - #define DID_FIELD_WIDTH 16 - #define DID_HIGH_OFFSET 8 - static int context_set_domain_id(struct context_entry *context, -- struct domain *d, -- struct vtd_iommu *iommu) -+ domid_t domid, struct vtd_iommu *iommu) - { - unsigned long nr_dom, i; - int found = 0; -@@ -100,7 +99,7 @@ static int context_set_domain_id(struct - i = find_first_bit(iommu->domid_bitmap, nr_dom); - while ( i < nr_dom ) - { -- if ( iommu->domid_map[i] == d->domain_id ) -+ if ( iommu->domid_map[i] == domid ) - { - found = 1; - break; -@@ -116,7 +115,7 @@ static int context_set_domain_id(struct - dprintk(XENLOG_ERR VTDPREFIX, "IOMMU: no free domain ids\n"); - return -EFAULT; - } -- iommu->domid_map[i] = d->domain_id; -+ iommu->domid_map[i] = domid; - } - - set_bit(i, iommu->domid_bitmap); -@@ -125,9 +124,9 @@ static int context_set_domain_id(struct - return 0; - } - --static void cleanup_domid_map(struct domain *domain, struct vtd_iommu *iommu) -+static void cleanup_domid_map(domid_t domid, struct vtd_iommu *iommu) - { -- int iommu_domid = domain_iommu_domid(domain, iommu); -+ int iommu_domid = get_iommu_did(domid, iommu, false); - - if ( iommu_domid >= 0 ) - { -@@ -167,7 +166,7 @@ static bool any_pdev_behind_iommu(const - * If no other devices under the same iommu owned by this domain, - * clear iommu in iommu_bitmap and clear domain_id in domid_bitmap. - */ --static void check_cleanup_domid_map(struct domain *d, -+static void check_cleanup_domid_map(const struct domain *d, - const struct pci_dev *exclude, - struct vtd_iommu *iommu) - { -@@ -183,7 +182,7 @@ static void check_cleanup_domid_map(stru - if ( !found ) - { - clear_bit(iommu->index, dom_iommu(d)->arch.vtd.iommu_bitmap); -- cleanup_domid_map(d, iommu); -+ cleanup_domid_map(d->domain_id, iommu); - } - } - -@@ -683,7 +682,7 @@ static int __must_check iommu_flush_iotl - continue; - - flush_dev_iotlb = !!find_ats_dev_drhd(iommu); -- iommu_domid= domain_iommu_domid(d, iommu); -+ iommu_domid = get_iommu_did(d->domain_id, iommu, !d->is_dying); - if ( iommu_domid == -1 ) - continue; - -@@ -1459,7 +1458,7 @@ int domain_context_mapping_one( - spin_unlock(&hd->arch.mapping_lock); - } - -- if ( context_set_domain_id(&lctxt, domain, iommu) ) -+ if ( context_set_domain_id(&lctxt, domid, iommu) ) - { - unlock: - spin_unlock(&iommu->lock); -@@ -1785,7 +1784,7 @@ int domain_context_unmap_one( - context_clear_entry(*context); - iommu_sync_cache(context, sizeof(struct context_entry)); - -- iommu_domid= domain_iommu_domid(domain, iommu); -+ iommu_domid = get_iommu_did(domid, iommu, !domain->is_dying); - if ( iommu_domid == -1 ) - { - spin_unlock(&iommu->lock); -@@ -1953,7 +1952,7 @@ static void iommu_domain_teardown(struct - ASSERT(!hd->arch.vtd.pgd_maddr); - - for_each_drhd_unit ( drhd ) -- cleanup_domid_map(d, drhd->iommu); -+ cleanup_domid_map(d->domain_id, drhd->iommu); - - XFREE(hd->arch.vtd.iommu_bitmap); - } diff --git a/xsa400-4.16-09.patch b/xsa400-4.16-09.patch deleted file mode 100644 index 5cd5732..0000000 --- a/xsa400-4.16-09.patch +++ /dev/null @@ -1,447 +0,0 @@ -From: Jan Beulich -Subject: IOMMU/x86: maintain a per-device pseudo domain ID - -In order to subsequently enable per-device quarantine page tables, we'll -need domain-ID-like identifiers to be inserted in the respective device -(AMD) or context (Intel) table entries alongside the per-device page -table root addresses. - -Make use of "real" domain IDs occupying only half of the value range -coverable by domid_t. - -Note that in VT-d's iommu_alloc() I didn't want to introduce new memory -leaks in case of error, but existing ones don't get plugged - that'll be -the subject of a later change. - -The VT-d changes are slightly asymmetric, but this way we can avoid -assigning pseudo domain IDs to devices which would never be mapped while -still avoiding to add a new parameter to domain_context_unmap(). - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant -Reviewed-by: Kevin Tian -Reviewed-by: Roger Pau Monné - ---- a/xen/include/asm-x86/iommu.h -+++ b/xen/include/asm-x86/iommu.h -@@ -141,6 +141,10 @@ int pi_update_irte(const struct pi_desc - iommu_vcall(ops, sync_cache, addr, size); \ - }) - -+unsigned long *iommu_init_domid(void); -+domid_t iommu_alloc_domid(unsigned long *map); -+void iommu_free_domid(domid_t domid, unsigned long *map); -+ - int __must_check iommu_free_pgtables(struct domain *d); - struct domain_iommu; - struct page_info *__must_check iommu_alloc_pgtable(struct domain_iommu *hd); ---- a/xen/include/asm-x86/pci.h -+++ b/xen/include/asm-x86/pci.h -@@ -13,6 +13,12 @@ - - struct arch_pci_dev { - vmask_t used_vectors; -+ /* -+ * These fields are (de)initialized under pcidevs-lock. Other uses of -+ * them don't race (de)initialization and hence don't strictly need any -+ * locking. -+ */ -+ domid_t pseudo_domid; - }; - - int pci_conf_write_intercept(unsigned int seg, unsigned int bdf, -@@ -36,6 +42,6 @@ static always_inline bool is_pci_passthr - return true; - } - --static inline void arch_pci_init_pdev(struct pci_dev *pdev) {} -+void arch_pci_init_pdev(struct pci_dev *pdev); - - #endif /* __X86_PCI_H__ */ ---- a/xen/drivers/passthrough/amd/iommu.h -+++ b/xen/drivers/passthrough/amd/iommu.h -@@ -96,6 +96,7 @@ struct amd_iommu { - struct ring_buffer cmd_buffer; - struct ring_buffer event_log; - struct ring_buffer ppr_log; -+ unsigned long *domid_map; - - int exclusion_enable; - int exclusion_allow_all; ---- a/xen/drivers/passthrough/amd/iommu_detect.c -+++ b/xen/drivers/passthrough/amd/iommu_detect.c -@@ -223,6 +223,11 @@ int __init amd_iommu_detect_one_acpi( - if ( rt ) - goto out; - -+ iommu->domid_map = iommu_init_domid(); -+ rt = -ENOMEM; -+ if ( !iommu->domid_map ) -+ goto out; -+ - rt = pci_ro_device(iommu->seg, bus, PCI_DEVFN(dev, func)); - if ( rt ) - printk(XENLOG_ERR "Could not mark config space of %pp read-only (%d)\n", -@@ -233,7 +238,10 @@ int __init amd_iommu_detect_one_acpi( - - out: - if ( rt ) -+ { -+ xfree(iommu->domid_map); - xfree(iommu); -+ } - - return rt; - } ---- a/xen/drivers/passthrough/amd/pci_amd_iommu.c -+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c -@@ -539,6 +539,8 @@ static int amd_iommu_add_device(u8 devfn - struct amd_iommu *iommu; - u16 bdf; - struct ivrs_mappings *ivrs_mappings; -+ bool fresh_domid = false; -+ int ret; - - if ( !pdev->domain ) - return -EINVAL; -@@ -606,7 +608,22 @@ static int amd_iommu_add_device(u8 devfn - AMD_IOMMU_WARN("%pd: unity mapping failed for %pp\n", - pdev->domain, &pdev->sbdf); - -- return amd_iommu_setup_domain_device(pdev->domain, iommu, devfn, pdev); -+ if ( iommu_quarantine && pdev->arch.pseudo_domid == DOMID_INVALID ) -+ { -+ pdev->arch.pseudo_domid = iommu_alloc_domid(iommu->domid_map); -+ if ( pdev->arch.pseudo_domid == DOMID_INVALID ) -+ return -ENOSPC; -+ fresh_domid = true; -+ } -+ -+ ret = amd_iommu_setup_domain_device(pdev->domain, iommu, devfn, pdev); -+ if ( ret && fresh_domid ) -+ { -+ iommu_free_domid(pdev->arch.pseudo_domid, iommu->domid_map); -+ pdev->arch.pseudo_domid = DOMID_INVALID; -+ } -+ -+ return ret; - } - - static int amd_iommu_remove_device(u8 devfn, struct pci_dev *pdev) -@@ -638,6 +655,9 @@ static int amd_iommu_remove_device(u8 de - AMD_IOMMU_WARN("%pd: unity unmapping failed for %pp\n", - pdev->domain, &pdev->sbdf); - -+ iommu_free_domid(pdev->arch.pseudo_domid, iommu->domid_map); -+ pdev->arch.pseudo_domid = DOMID_INVALID; -+ - if ( amd_iommu_perdev_intremap && - ivrs_mappings[bdf].dte_requestor_id == bdf && - ivrs_mappings[bdf].intremap_table ) ---- a/xen/drivers/passthrough/pci.c -+++ b/xen/drivers/passthrough/pci.c -@@ -1271,9 +1271,14 @@ static int _dump_pci_devices(struct pci_ - - list_for_each_entry ( pdev, &pseg->alldevs_list, alldevs_list ) - { -- printk("%pp - %pd - node %-3d", -- &pdev->sbdf, pdev->domain, -- (pdev->node != NUMA_NO_NODE) ? pdev->node : -1); -+ printk("%pp - ", &pdev->sbdf); -+#ifdef CONFIG_X86 -+ if ( pdev->domain == dom_io ) -+ printk("DomIO:%x", pdev->arch.pseudo_domid); -+ else -+#endif -+ printk("%pd", pdev->domain); -+ printk(" - node %-3d", (pdev->node != NUMA_NO_NODE) ? pdev->node : -1); - pdev_dump_msi(pdev); - printk("\n"); - } ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -22,6 +22,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -1215,7 +1216,7 @@ int __init iommu_alloc(struct acpi_drhd_ - { - struct vtd_iommu *iommu; - unsigned long sagaw, nr_dom; -- int agaw; -+ int agaw, rc; - - iommu = xzalloc(struct vtd_iommu); - if ( iommu == NULL ) -@@ -1301,7 +1302,16 @@ int __init iommu_alloc(struct acpi_drhd_ - if ( !iommu->domid_map ) - return -ENOMEM; - -+ iommu->pseudo_domid_map = iommu_init_domid(); -+ rc = -ENOMEM; -+ if ( !iommu->pseudo_domid_map ) -+ goto free; -+ - return 0; -+ -+ free: -+ iommu_free(drhd); -+ return rc; - } - - void __init iommu_free(struct acpi_drhd_unit *drhd) -@@ -1324,6 +1334,7 @@ void __init iommu_free(struct acpi_drhd_ - - xfree(iommu->domid_bitmap); - xfree(iommu->domid_map); -+ xfree(iommu->pseudo_domid_map); - - if ( iommu->msi.irq >= 0 ) - destroy_irq(iommu->msi.irq); -@@ -1593,8 +1604,8 @@ int domain_context_mapping_one( - return rc ?: pdev && prev_dom; - } - --static int domain_context_unmap(struct domain *d, uint8_t devfn, -- struct pci_dev *pdev); -+static const struct acpi_drhd_unit *domain_context_unmap( -+ struct domain *d, uint8_t devfn, struct pci_dev *pdev); - - static int domain_context_mapping(struct domain *domain, u8 devfn, - struct pci_dev *pdev) -@@ -1602,6 +1613,7 @@ static int domain_context_mapping(struct - const struct acpi_drhd_unit *drhd = acpi_find_matched_drhd_unit(pdev); - const struct acpi_rmrr_unit *rmrr; - paddr_t pgd_maddr = dom_iommu(domain)->arch.vtd.pgd_maddr; -+ domid_t orig_domid = pdev->arch.pseudo_domid; - int ret = 0; - unsigned int i, mode = 0; - uint16_t seg = pdev->seg, bdf; -@@ -1660,6 +1672,14 @@ static int domain_context_mapping(struct - if ( !drhd ) - return -ENODEV; - -+ if ( iommu_quarantine && orig_domid == DOMID_INVALID ) -+ { -+ pdev->arch.pseudo_domid = -+ iommu_alloc_domid(drhd->iommu->pseudo_domid_map); -+ if ( pdev->arch.pseudo_domid == DOMID_INVALID ) -+ return -ENOSPC; -+ } -+ - if ( iommu_debug ) - printk(VTDPREFIX "%pd:PCIe: map %pp\n", - domain, &PCI_SBDF3(seg, bus, devfn)); -@@ -1677,6 +1697,14 @@ static int domain_context_mapping(struct - if ( !drhd ) - return -ENODEV; - -+ if ( iommu_quarantine && orig_domid == DOMID_INVALID ) -+ { -+ pdev->arch.pseudo_domid = -+ iommu_alloc_domid(drhd->iommu->pseudo_domid_map); -+ if ( pdev->arch.pseudo_domid == DOMID_INVALID ) -+ return -ENOSPC; -+ } -+ - if ( iommu_debug ) - printk(VTDPREFIX "%pd:PCI: map %pp\n", - domain, &PCI_SBDF3(seg, bus, devfn)); -@@ -1750,6 +1778,13 @@ static int domain_context_mapping(struct - if ( !ret && devfn == pdev->devfn ) - pci_vtd_quirk(pdev); - -+ if ( ret && drhd && orig_domid == DOMID_INVALID ) -+ { -+ iommu_free_domid(pdev->arch.pseudo_domid, -+ drhd->iommu->pseudo_domid_map); -+ pdev->arch.pseudo_domid = DOMID_INVALID; -+ } -+ - return ret; - } - -@@ -1835,8 +1870,10 @@ int domain_context_unmap_one( - return rc; - } - --static int domain_context_unmap(struct domain *domain, u8 devfn, -- struct pci_dev *pdev) -+static const struct acpi_drhd_unit *domain_context_unmap( -+ struct domain *domain, -+ uint8_t devfn, -+ struct pci_dev *pdev) - { - const struct acpi_drhd_unit *drhd = acpi_find_matched_drhd_unit(pdev); - struct vtd_iommu *iommu = drhd ? drhd->iommu : NULL; -@@ -1850,16 +1887,16 @@ static int domain_context_unmap(struct d - if ( iommu_debug ) - printk(VTDPREFIX "%pd:Hostbridge: skip %pp unmap\n", - domain, &PCI_SBDF3(seg, bus, devfn)); -- return is_hardware_domain(domain) ? 0 : -EPERM; -+ return ERR_PTR(is_hardware_domain(domain) ? 0 : -EPERM); - - case DEV_TYPE_PCIe_BRIDGE: - case DEV_TYPE_PCIe2PCI_BRIDGE: - case DEV_TYPE_LEGACY_PCI_BRIDGE: -- return 0; -+ return ERR_PTR(0); - - case DEV_TYPE_PCIe_ENDPOINT: - if ( !iommu ) -- return -ENODEV; -+ return ERR_PTR(-ENODEV); - - if ( iommu_debug ) - printk(VTDPREFIX "%pd:PCIe: unmap %pp\n", -@@ -1873,7 +1910,7 @@ static int domain_context_unmap(struct d - - case DEV_TYPE_PCI: - if ( !iommu ) -- return -ENODEV; -+ return ERR_PTR(-ENODEV); - - if ( iommu_debug ) - printk(VTDPREFIX "%pd:PCI: unmap %pp\n", -@@ -1920,14 +1957,14 @@ static int domain_context_unmap(struct d - default: - dprintk(XENLOG_ERR VTDPREFIX, "%pd:unknown(%u): %pp\n", - domain, pdev->type, &PCI_SBDF3(seg, bus, devfn)); -- return -EINVAL; -+ return ERR_PTR(-EINVAL); - } - - if ( !ret && pdev->devfn == devfn && - !QUARANTINE_SKIP(domain, dom_iommu(domain)->arch.vtd.pgd_maddr) ) - check_cleanup_domid_map(domain, pdev, iommu); - -- return ret; -+ return drhd; - } - - static void iommu_clear_root_pgtable(struct domain *d) -@@ -2154,16 +2191,17 @@ static int intel_iommu_enable_device(str - - static int intel_iommu_remove_device(u8 devfn, struct pci_dev *pdev) - { -+ const struct acpi_drhd_unit *drhd; - struct acpi_rmrr_unit *rmrr; - u16 bdf; -- int ret, i; -+ unsigned int i; - - if ( !pdev->domain ) - return -EINVAL; - -- ret = domain_context_unmap(pdev->domain, devfn, pdev); -- if ( ret ) -- return ret; -+ drhd = domain_context_unmap(pdev->domain, devfn, pdev); -+ if ( IS_ERR(drhd) ) -+ return PTR_ERR(drhd); - - for_each_rmrr_device ( rmrr, bdf, i ) - { -@@ -2180,6 +2218,13 @@ static int intel_iommu_remove_device(u8 - rmrr->end_address, 0); - } - -+ if ( drhd ) -+ { -+ iommu_free_domid(pdev->arch.pseudo_domid, -+ drhd->iommu->pseudo_domid_map); -+ pdev->arch.pseudo_domid = DOMID_INVALID; -+ } -+ - return 0; - } - -@@ -2556,7 +2601,12 @@ static int reassign_device_ownership( - } - } - else -- ret = domain_context_unmap(source, devfn, pdev); -+ { -+ const struct acpi_drhd_unit *drhd; -+ -+ drhd = domain_context_unmap(source, devfn, pdev); -+ ret = IS_ERR(drhd) ? PTR_ERR(drhd) : 0; -+ } - if ( ret ) - { - if ( !has_arch_pdevs(target) ) ---- a/xen/drivers/passthrough/vtd/iommu.h -+++ b/xen/drivers/passthrough/vtd/iommu.h -@@ -508,6 +508,7 @@ struct vtd_iommu { - } flush; - - struct list_head ats_devices; -+ unsigned long *pseudo_domid_map; /* "pseudo" domain id bitmap */ - unsigned long *domid_bitmap; /* domain id bitmap */ - u16 *domid_map; /* domain id mapping array */ - uint32_t version; ---- a/xen/drivers/passthrough/x86/iommu.c -+++ b/xen/drivers/passthrough/x86/iommu.c -@@ -387,6 +387,58 @@ void __hwdom_init arch_iommu_hwdom_init( - return; - } - -+void arch_pci_init_pdev(struct pci_dev *pdev) -+{ -+ pdev->arch.pseudo_domid = DOMID_INVALID; -+} -+ -+unsigned long *__init iommu_init_domid(void) -+{ -+ if ( !iommu_quarantine ) -+ return ZERO_BLOCK_PTR; -+ -+ BUILD_BUG_ON(DOMID_MASK * 2U >= UINT16_MAX); -+ -+ return xzalloc_array(unsigned long, -+ BITS_TO_LONGS(UINT16_MAX - DOMID_MASK)); -+} -+ -+domid_t iommu_alloc_domid(unsigned long *map) -+{ -+ /* -+ * This is used uniformly across all IOMMUs, such that on typical -+ * systems we wouldn't re-use the same ID very quickly (perhaps never). -+ */ -+ static unsigned int start; -+ unsigned int idx = find_next_zero_bit(map, UINT16_MAX - DOMID_MASK, start); -+ -+ ASSERT(pcidevs_locked()); -+ -+ if ( idx >= UINT16_MAX - DOMID_MASK ) -+ idx = find_first_zero_bit(map, UINT16_MAX - DOMID_MASK); -+ if ( idx >= UINT16_MAX - DOMID_MASK ) -+ return DOMID_INVALID; -+ -+ __set_bit(idx, map); -+ -+ start = idx + 1; -+ -+ return idx | (DOMID_MASK + 1); -+} -+ -+void iommu_free_domid(domid_t domid, unsigned long *map) -+{ -+ ASSERT(pcidevs_locked()); -+ -+ if ( domid == DOMID_INVALID ) -+ return; -+ -+ ASSERT(domid > DOMID_MASK); -+ -+ if ( !__test_and_clear_bit(domid & DOMID_MASK, map) ) -+ BUG(); -+} -+ - int iommu_free_pgtables(struct domain *d) - { - struct domain_iommu *hd = dom_iommu(d); diff --git a/xsa400-4.16-10.patch b/xsa400-4.16-10.patch deleted file mode 100644 index 4f5886e..0000000 --- a/xsa400-4.16-10.patch +++ /dev/null @@ -1,38 +0,0 @@ -From: Jan Beulich -Subject: IOMMU/x86: drop TLB flushes from quarantine_init() hooks - -The page tables just created aren't hooked up yet anywhere, so there's -nothing that could be present in any TLB, and hence nothing to flush. -Dropping this flush is, at least on the VT-d side, a prereq to per- -device domain ID use when quarantining devices, as dom_io isn't going -to be assigned a DID anymore: The warning in get_iommu_did() would -trigger. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant -Reviewed-by: Roger Pau Monné -Reviewed-by: Kevin Tian - ---- a/xen/drivers/passthrough/amd/iommu_map.c -+++ b/xen/drivers/passthrough/amd/iommu_map.c -@@ -654,8 +654,6 @@ int __init amd_iommu_quarantine_init(str - out: - spin_unlock(&hd->arch.mapping_lock); - -- amd_iommu_flush_all_pages(d); -- - /* Pages leaked in failure case */ - return level ? -ENOMEM : 0; - } ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -2975,9 +2975,6 @@ static int __init intel_iommu_quarantine - out: - spin_unlock(&hd->arch.mapping_lock); - -- if ( !rc ) -- rc = iommu_flush_iotlb_all(d); -- - /* Pages may be leaked in failure case */ - return rc; - } diff --git a/xsa400-4.16-11.patch b/xsa400-4.16-11.patch deleted file mode 100644 index 831983c..0000000 --- a/xsa400-4.16-11.patch +++ /dev/null @@ -1,29 +0,0 @@ -From: Jan Beulich -Subject: AMD/IOMMU: abstract maximum number of page table levels - -We will want to use the constant elsewhere. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/amd/iommu-defs.h -+++ b/xen/drivers/passthrough/amd/iommu-defs.h -@@ -106,6 +106,7 @@ struct amd_iommu_dte { - bool tv:1; - unsigned int :5; - unsigned int had:2; -+#define IOMMU_MAX_PT_LEVELS 6 - unsigned int paging_mode:3; - uint64_t pt_root:40; - bool ppr:1; ---- a/xen/drivers/passthrough/amd/pci_amd_iommu.c -+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c -@@ -337,7 +337,7 @@ int amd_iommu_alloc_root(struct domain * - return 0; - } - --unsigned int __read_mostly amd_iommu_max_paging_mode = 6; -+unsigned int __read_mostly amd_iommu_max_paging_mode = IOMMU_MAX_PT_LEVELS; - int __read_mostly amd_iommu_min_paging_mode = 1; - - static int amd_iommu_domain_init(struct domain *d) diff --git a/xsa400-4.16-12.patch b/xsa400-4.16-12.patch deleted file mode 100644 index b4836b1..0000000 --- a/xsa400-4.16-12.patch +++ /dev/null @@ -1,945 +0,0 @@ -From: Jan Beulich -Subject: IOMMU/x86: use per-device page tables for quarantining - -Devices with RMRRs / unity mapped regions, due to it being unspecified -how/when these memory regions may be accessed, may not be left -disconnected from the mappings of these regions (as long as it's not -certain that the device has been fully quiesced). Hence even the page -tables used when quarantining such devices need to have mappings of -those regions. This implies installing page tables in the first place -even when not in scratch-page quarantining mode. - -This is CVE-2022-26361 / part of XSA-400. - -While for the purpose here it would be sufficient to have devices with -RMRRs / unity mapped regions use per-device page tables, extend this to -all devices (in scratch-page quarantining mode). This allows the leaf -pages to be mapped r/w, thus covering also memory writes (rather than -just reads) issued by non-quiescent devices. - -Set up quarantine page tables as late as possible, yet early enough to -not encounter failure during de-assign. This means setup generally -happens in assign_device(), while (for now) the one in deassign_device() -is there mainly to be on the safe side. - -As to the removal of QUARANTINE_SKIP() from domain_context_unmap_one(): -I think this was never really needed there, as the function explicitly -deals with finding a non-present context entry. Leaving it there would -require propagating pgd_maddr into the function (like was done by "VT-d: -prepare for per-device quarantine page tables" for -domain_context_mapping_one()). - -In VT-d's DID allocation function don't require the IOMMU lock to be -held anymore: All involved code paths hold pcidevs_lock, so this way we -avoid the need to acquire the IOMMU lock around the new call to -context_set_domain_id(). - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant -Reviewed-by: Kevin Tian -Reviewed-by: Roger Pau Monné - ---- a/xen/include/asm-x86/pci.h -+++ b/xen/include/asm-x86/pci.h -@@ -1,6 +1,8 @@ - #ifndef __X86_PCI_H__ - #define __X86_PCI_H__ - -+#include -+ - #define CF8_BDF(cf8) ( ((cf8) & 0x00ffff00) >> 8) - #define CF8_ADDR_LO(cf8) ( (cf8) & 0x000000fc) - #define CF8_ADDR_HI(cf8) ( ((cf8) & 0x0f000000) >> 16) -@@ -18,7 +20,18 @@ struct arch_pci_dev { - * them don't race (de)initialization and hence don't strictly need any - * locking. - */ -+ union { -+ /* Subset of struct arch_iommu's fields, to be used in dom_io. */ -+ struct { -+ uint64_t pgd_maddr; -+ } vtd; -+ struct { -+ struct page_info *root_table; -+ } amd; -+ }; - domid_t pseudo_domid; -+ mfn_t leaf_mfn; -+ struct page_list_head pgtables_list; - }; - - int pci_conf_write_intercept(unsigned int seg, unsigned int bdf, ---- a/xen/drivers/passthrough/amd/iommu.h -+++ b/xen/drivers/passthrough/amd/iommu.h -@@ -237,7 +237,8 @@ int amd_iommu_init_late(void); - int amd_iommu_update_ivrs_mapping_acpi(void); - int iov_adjust_irq_affinities(void); - --int amd_iommu_quarantine_init(struct domain *d); -+int amd_iommu_quarantine_init(struct pci_dev *pdev, bool scratch_page); -+void amd_iommu_quarantine_teardown(struct pci_dev *pdev); - - /* mapping functions */ - int __must_check amd_iommu_map_page(struct domain *d, dfn_t dfn, ---- a/xen/drivers/passthrough/amd/iommu_map.c -+++ b/xen/drivers/passthrough/amd/iommu_map.c -@@ -598,64 +598,138 @@ int amd_iommu_get_reserved_device_memory - return 0; - } - --int __init amd_iommu_quarantine_init(struct domain *d) -+static int fill_qpt(union amd_iommu_pte *this, unsigned int level, -+ struct page_info *pgs[IOMMU_MAX_PT_LEVELS]) - { -- struct domain_iommu *hd = dom_iommu(d); -+ struct domain_iommu *hd = dom_iommu(dom_io); -+ unsigned int i; -+ int rc = 0; -+ -+ for ( i = 0; !rc && i < PTE_PER_TABLE_SIZE; ++i ) -+ { -+ union amd_iommu_pte *pte = &this[i], *next; -+ -+ if ( !pte->pr ) -+ { -+ if ( !pgs[level] ) -+ { -+ /* -+ * The pgtable allocator is fine for the leaf page, as well as -+ * page table pages, and the resulting allocations are always -+ * zeroed. -+ */ -+ pgs[level] = iommu_alloc_pgtable(hd); -+ if ( !pgs[level] ) -+ { -+ rc = -ENOMEM; -+ break; -+ } -+ -+ if ( level ) -+ { -+ next = __map_domain_page(pgs[level]); -+ rc = fill_qpt(next, level - 1, pgs); -+ unmap_domain_page(next); -+ } -+ } -+ -+ /* -+ * PDEs are essentially a subset of PTEs, so this function -+ * is fine to use even at the leaf. -+ */ -+ set_iommu_pde_present(pte, mfn_x(page_to_mfn(pgs[level])), level, -+ true, true); -+ } -+ else if ( level && pte->next_level ) -+ { -+ next = map_domain_page(_mfn(pte->mfn)); -+ rc = fill_qpt(next, level - 1, pgs); -+ unmap_domain_page(next); -+ } -+ } -+ -+ return rc; -+} -+ -+int amd_iommu_quarantine_init(struct pci_dev *pdev, bool scratch_page) -+{ -+ struct domain_iommu *hd = dom_iommu(dom_io); - unsigned long end_gfn = - 1ul << (DEFAULT_DOMAIN_ADDRESS_WIDTH - PAGE_SHIFT); - unsigned int level = amd_iommu_get_paging_mode(end_gfn); -- union amd_iommu_pte *table; -+ unsigned int req_id = get_dma_requestor_id(pdev->seg, pdev->sbdf.bdf); -+ const struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(pdev->seg); -+ int rc; -+ -+ ASSERT(pcidevs_locked()); -+ ASSERT(!hd->arch.amd.root_table); -+ ASSERT(page_list_empty(&hd->arch.pgtables.list)); - -- if ( hd->arch.amd.root_table ) -- { -- ASSERT_UNREACHABLE(); -+ if ( !scratch_page && !ivrs_mappings[req_id].unity_map ) - return 0; -- } - -- spin_lock(&hd->arch.mapping_lock); -+ ASSERT(pdev->arch.pseudo_domid != DOMID_INVALID); - -- hd->arch.amd.root_table = iommu_alloc_pgtable(hd); -- if ( !hd->arch.amd.root_table ) -- goto out; -+ if ( pdev->arch.amd.root_table ) -+ { -+ clear_domain_page(pdev->arch.leaf_mfn); -+ return 0; -+ } - -- table = __map_domain_page(hd->arch.amd.root_table); -- while ( level ) -+ pdev->arch.amd.root_table = iommu_alloc_pgtable(hd); -+ if ( !pdev->arch.amd.root_table ) -+ return -ENOMEM; -+ -+ /* Transiently install the root into DomIO, for iommu_identity_mapping(). */ -+ hd->arch.amd.root_table = pdev->arch.amd.root_table; -+ -+ rc = amd_iommu_reserve_domain_unity_map(dom_io, -+ ivrs_mappings[req_id].unity_map, -+ 0); -+ -+ iommu_identity_map_teardown(dom_io); -+ hd->arch.amd.root_table = NULL; -+ -+ if ( rc ) -+ AMD_IOMMU_WARN("%pp: quarantine unity mapping failed\n", &pdev->sbdf); -+ else if ( scratch_page ) - { -- struct page_info *pg; -- unsigned int i; -+ union amd_iommu_pte *root; -+ struct page_info *pgs[IOMMU_MAX_PT_LEVELS] = {}; - -- /* -- * The pgtable allocator is fine for the leaf page, as well as -- * page table pages, and the resulting allocations are always -- * zeroed. -- */ -- pg = iommu_alloc_pgtable(hd); -- if ( !pg ) -- break; -+ spin_lock(&hd->arch.mapping_lock); - -- for ( i = 0; i < PTE_PER_TABLE_SIZE; i++ ) -- { -- union amd_iommu_pte *pde = &table[i]; -+ root = __map_domain_page(pdev->arch.amd.root_table); -+ rc = fill_qpt(root, level - 1, pgs); -+ unmap_domain_page(root); - -- /* -- * PDEs are essentially a subset of PTEs, so this function -- * is fine to use even at the leaf. -- */ -- set_iommu_pde_present(pde, mfn_x(page_to_mfn(pg)), level - 1, -- false, true); -- } -+ pdev->arch.leaf_mfn = page_to_mfn(pgs[0]); - -- unmap_domain_page(table); -- table = __map_domain_page(pg); -- level--; -+ spin_unlock(&hd->arch.mapping_lock); - } -- unmap_domain_page(table); - -- out: -- spin_unlock(&hd->arch.mapping_lock); -+ page_list_move(&pdev->arch.pgtables_list, &hd->arch.pgtables.list); -+ -+ if ( rc ) -+ amd_iommu_quarantine_teardown(pdev); -+ -+ return rc; -+} -+ -+void amd_iommu_quarantine_teardown(struct pci_dev *pdev) -+{ -+ struct domain_iommu *hd = dom_iommu(dom_io); -+ -+ ASSERT(pcidevs_locked()); -+ -+ if ( !pdev->arch.amd.root_table ) -+ return; - -- /* Pages leaked in failure case */ -- return level ? -ENOMEM : 0; -+ ASSERT(page_list_empty(&hd->arch.pgtables.list)); -+ page_list_move(&hd->arch.pgtables.list, &pdev->arch.pgtables_list); -+ while ( iommu_free_pgtables(dom_io) == -ERESTART ) -+ /* nothing */; -+ pdev->arch.amd.root_table = NULL; - } - - /* ---- a/xen/drivers/passthrough/amd/pci_amd_iommu.c -+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c -@@ -26,7 +26,7 @@ - #include "../ats.h" - - /* dom_io is used as a sentinel for quarantined devices */ --#define QUARANTINE_SKIP(d) ((d) == dom_io && !dom_iommu(d)->arch.amd.root_table) -+#define QUARANTINE_SKIP(d, p) ((d) == dom_io && !(p)->arch.amd.root_table) - - static bool_t __read_mostly init_done; - -@@ -125,8 +125,10 @@ static int __must_check amd_iommu_setup_ - u8 bus = pdev->bus; - struct domain_iommu *hd = dom_iommu(domain); - const struct ivrs_mappings *ivrs_dev; -+ const struct page_info *root_pg; -+ domid_t domid; - -- if ( QUARANTINE_SKIP(domain) ) -+ if ( QUARANTINE_SKIP(domain, pdev) ) - return 0; - - BUG_ON(!hd->arch.amd.paging_mode || !iommu->dev_table.buffer); -@@ -147,14 +149,25 @@ static int __must_check amd_iommu_setup_ - dte = &table[req_id]; - ivrs_dev = &get_ivrs_mappings(iommu->seg)[req_id]; - -+ if ( domain != dom_io ) -+ { -+ root_pg = hd->arch.amd.root_table; -+ domid = domain->domain_id; -+ } -+ else -+ { -+ root_pg = pdev->arch.amd.root_table; -+ domid = pdev->arch.pseudo_domid; -+ } -+ - spin_lock_irqsave(&iommu->lock, flags); - - if ( !dte->v || !dte->tv ) - { - /* bind DTE to domain page-tables */ - rc = amd_iommu_set_root_page_table( -- dte, page_to_maddr(hd->arch.amd.root_table), -- domain->domain_id, hd->arch.amd.paging_mode, sr_flags); -+ dte, page_to_maddr(root_pg), domid, -+ hd->arch.amd.paging_mode, sr_flags); - if ( rc ) - { - ASSERT(rc < 0); -@@ -181,7 +194,7 @@ static int __must_check amd_iommu_setup_ - - amd_iommu_flush_device(iommu, req_id); - } -- else if ( dte->pt_root != mfn_x(page_to_mfn(hd->arch.amd.root_table)) ) -+ else if ( dte->pt_root != mfn_x(page_to_mfn(root_pg)) ) - { - /* - * Strictly speaking if the device is the only one with this requestor -@@ -194,8 +207,8 @@ static int __must_check amd_iommu_setup_ - rc = -EOPNOTSUPP; - else - rc = amd_iommu_set_root_page_table( -- dte, page_to_maddr(hd->arch.amd.root_table), -- domain->domain_id, hd->arch.amd.paging_mode, sr_flags); -+ dte, page_to_maddr(root_pg), domid, -+ hd->arch.amd.paging_mode, sr_flags); - if ( rc < 0 ) - { - spin_unlock_irqrestore(&iommu->lock, flags); -@@ -214,6 +227,7 @@ static int __must_check amd_iommu_setup_ - * intended anyway. - */ - !pdev->domain->is_dying && -+ pdev->domain != dom_io && - (any_pdev_behind_iommu(pdev->domain, pdev, iommu) || - pdev->phantom_stride) ) - AMD_IOMMU_WARN(" %pp: reassignment may cause %pd data corruption\n", -@@ -246,9 +260,8 @@ static int __must_check amd_iommu_setup_ - AMD_IOMMU_DEBUG("Setup I/O page table: device id = %#x, type = %#x, " - "root table = %#"PRIx64", " - "domain = %d, paging mode = %d\n", -- req_id, pdev->type, -- page_to_maddr(hd->arch.amd.root_table), -- domain->domain_id, hd->arch.amd.paging_mode); -+ req_id, pdev->type, page_to_maddr(root_pg), -+ domid, hd->arch.amd.paging_mode); - - ASSERT(pcidevs_locked()); - -@@ -327,7 +340,7 @@ int amd_iommu_alloc_root(struct domain * - { - struct domain_iommu *hd = dom_iommu(d); - -- if ( unlikely(!hd->arch.amd.root_table) ) -+ if ( unlikely(!hd->arch.amd.root_table) && d != dom_io ) - { - hd->arch.amd.root_table = iommu_alloc_pgtable(hd); - if ( !hd->arch.amd.root_table ) -@@ -391,7 +404,7 @@ static void amd_iommu_disable_domain_dev - int req_id; - u8 bus = pdev->bus; - -- if ( QUARANTINE_SKIP(domain) ) -+ if ( QUARANTINE_SKIP(domain, pdev) ) - return; - - ASSERT(pcidevs_locked()); -@@ -430,7 +443,7 @@ static void amd_iommu_disable_domain_dev - - AMD_IOMMU_DEBUG("Disable: device id = %#x, " - "domain = %d, paging mode = %d\n", -- req_id, domain->domain_id, -+ req_id, dte->domain_id, - dom_iommu(domain)->arch.amd.paging_mode); - } - else -@@ -453,7 +466,7 @@ static int reassign_device(struct domain - return -ENODEV; - } - -- if ( !QUARANTINE_SKIP(target) ) -+ if ( !QUARANTINE_SKIP(target, pdev) ) - { - rc = amd_iommu_setup_domain_device(target, iommu, devfn, pdev); - if ( rc ) -@@ -655,6 +668,8 @@ static int amd_iommu_remove_device(u8 de - AMD_IOMMU_WARN("%pd: unity unmapping failed for %pp\n", - pdev->domain, &pdev->sbdf); - -+ amd_iommu_quarantine_teardown(pdev); -+ - iommu_free_domid(pdev->arch.pseudo_domid, iommu->domid_map); - pdev->arch.pseudo_domid = DOMID_INVALID; - ---- a/xen/drivers/passthrough/iommu.c -+++ b/xen/drivers/passthrough/iommu.c -@@ -443,21 +443,22 @@ int iommu_iotlb_flush_all(struct domain - return rc; - } - --static int __init iommu_quarantine_init(void) -+int iommu_quarantine_dev_init(device_t *dev) - { - const struct domain_iommu *hd = dom_iommu(dom_io); -- int rc; - -- dom_io->options |= XEN_DOMCTL_CDF_iommu; -+ if ( !iommu_quarantine || !hd->platform_ops->quarantine_init ) -+ return 0; - -- rc = iommu_domain_init(dom_io, 0); -- if ( rc || iommu_quarantine < IOMMU_quarantine_scratch_page ) -- return rc; -+ return iommu_call(hd->platform_ops, quarantine_init, -+ dev, iommu_quarantine == IOMMU_quarantine_scratch_page); -+} - -- if ( !hd->platform_ops->quarantine_init ) -- return 0; -+static int __init iommu_quarantine_init(void) -+{ -+ dom_io->options |= XEN_DOMCTL_CDF_iommu; - -- return hd->platform_ops->quarantine_init(dom_io); -+ return iommu_domain_init(dom_io, 0); - } - - int __init iommu_setup(void) ---- a/xen/drivers/passthrough/pci.c -+++ b/xen/drivers/passthrough/pci.c -@@ -852,9 +852,16 @@ static int deassign_device(struct domain - return -ENODEV; - - /* De-assignment from dom_io should de-quarantine the device */ -- target = ((pdev->quarantine || iommu_quarantine) && -- pdev->domain != dom_io) ? -- dom_io : hardware_domain; -+ if ( (pdev->quarantine || iommu_quarantine) && pdev->domain != dom_io ) -+ { -+ ret = iommu_quarantine_dev_init(pci_to_dev(pdev)); -+ if ( ret ) -+ return ret; -+ -+ target = dom_io; -+ } -+ else -+ target = hardware_domain; - - while ( pdev->phantom_stride ) - { -@@ -1424,6 +1431,13 @@ static int assign_device(struct domain * - if ( rc ) - goto done; - -+ if ( pdev->domain != dom_io ) -+ { -+ rc = iommu_quarantine_dev_init(pci_to_dev(pdev)); -+ if ( rc ) -+ goto done; -+ } -+ - pdev->fault.count = 0; - - if ( (rc = hd->platform_ops->assign_device(d, devfn, pci_to_dev(pdev), flag)) ) ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -45,6 +45,11 @@ - - /* dom_io is used as a sentinel for quarantined devices */ - #define QUARANTINE_SKIP(d, pgd_maddr) ((d) == dom_io && !(pgd_maddr)) -+#define DEVICE_DOMID(d, pdev) ((d) != dom_io ? (d)->domain_id \ -+ : (pdev)->arch.pseudo_domid) -+#define DEVICE_PGTABLE(d, pdev) ((d) != dom_io \ -+ ? dom_iommu(d)->arch.vtd.pgd_maddr \ -+ : (pdev)->arch.vtd.pgd_maddr) - - /* Possible unfiltered LAPIC/MSI messages from untrusted sources? */ - bool __read_mostly untrusted_msi; -@@ -88,13 +93,18 @@ static int get_iommu_did(domid_t domid, - - #define DID_FIELD_WIDTH 16 - #define DID_HIGH_OFFSET 8 -+ -+/* -+ * This function may have "context" passed as NULL, to merely obtain a DID -+ * for "domid". -+ */ - static int context_set_domain_id(struct context_entry *context, - domid_t domid, struct vtd_iommu *iommu) - { - unsigned long nr_dom, i; - int found = 0; - -- ASSERT(spin_is_locked(&iommu->lock)); -+ ASSERT(pcidevs_locked()); - - nr_dom = cap_ndoms(iommu->cap); - i = find_first_bit(iommu->domid_bitmap, nr_dom); -@@ -120,8 +130,13 @@ static int context_set_domain_id(struct - } - - set_bit(i, iommu->domid_bitmap); -- context->hi &= ~(((1 << DID_FIELD_WIDTH) - 1) << DID_HIGH_OFFSET); -- context->hi |= (i & ((1 << DID_FIELD_WIDTH) - 1)) << DID_HIGH_OFFSET; -+ -+ if ( context ) -+ { -+ context->hi &= ~(((1 << DID_FIELD_WIDTH) - 1) << DID_HIGH_OFFSET); -+ context->hi |= (i & ((1 << DID_FIELD_WIDTH) - 1)) << DID_HIGH_OFFSET; -+ } -+ - return 0; - } - -@@ -171,8 +186,12 @@ static void check_cleanup_domid_map(cons - const struct pci_dev *exclude, - struct vtd_iommu *iommu) - { -- bool found = any_pdev_behind_iommu(d, exclude, iommu); -+ bool found; -+ -+ if ( d == dom_io ) -+ return; - -+ found = any_pdev_behind_iommu(d, exclude, iommu); - /* - * Hidden devices are associated with DomXEN but usable by the hardware - * domain. Hence they need considering here as well. -@@ -1426,7 +1445,7 @@ int domain_context_mapping_one( - domid = iommu->domid_map[prev_did]; - if ( domid < DOMID_FIRST_RESERVED ) - prev_dom = rcu_lock_domain_by_id(domid); -- else if ( domid == DOMID_IO ) -+ else if ( pdev ? domid == pdev->arch.pseudo_domid : domid > DOMID_MASK ) - prev_dom = rcu_lock_domain(dom_io); - if ( !prev_dom ) - { -@@ -1582,15 +1601,12 @@ int domain_context_mapping_one( - { - if ( !prev_dom ) - ret = domain_context_unmap_one(domain, iommu, bus, devfn, -- domain->domain_id); -+ DEVICE_DOMID(domain, pdev)); - else if ( prev_dom != domain ) /* Avoid infinite recursion. */ -- { -- hd = dom_iommu(prev_dom); - ret = domain_context_mapping_one(prev_dom, iommu, bus, devfn, pdev, -- domain->domain_id, -- hd->arch.vtd.pgd_maddr, -+ DEVICE_DOMID(prev_dom, pdev), -+ DEVICE_PGTABLE(prev_dom, pdev), - mode & MAP_WITH_RMRR) < 0; -- } - else - ret = 1; - -@@ -1612,7 +1628,7 @@ static int domain_context_mapping(struct - { - const struct acpi_drhd_unit *drhd = acpi_find_matched_drhd_unit(pdev); - const struct acpi_rmrr_unit *rmrr; -- paddr_t pgd_maddr = dom_iommu(domain)->arch.vtd.pgd_maddr; -+ paddr_t pgd_maddr = DEVICE_PGTABLE(domain, pdev); - domid_t orig_domid = pdev->arch.pseudo_domid; - int ret = 0; - unsigned int i, mode = 0; -@@ -1641,7 +1657,7 @@ static int domain_context_mapping(struct - break; - } - -- if ( domain != pdev->domain ) -+ if ( domain != pdev->domain && pdev->domain != dom_io ) - { - if ( pdev->domain->is_dying ) - mode |= MAP_OWNER_DYING; -@@ -1683,8 +1699,8 @@ static int domain_context_mapping(struct - if ( iommu_debug ) - printk(VTDPREFIX "%pd:PCIe: map %pp\n", - domain, &PCI_SBDF3(seg, bus, devfn)); -- ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn, -- pdev, domain->domain_id, pgd_maddr, -+ ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn, pdev, -+ DEVICE_DOMID(domain, pdev), pgd_maddr, - mode); - if ( ret > 0 ) - ret = 0; -@@ -1710,8 +1726,8 @@ static int domain_context_mapping(struct - domain, &PCI_SBDF3(seg, bus, devfn)); - - ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn, -- pdev, domain->domain_id, pgd_maddr, -- mode); -+ pdev, DEVICE_DOMID(domain, pdev), -+ pgd_maddr, mode); - if ( ret < 0 ) - break; - prev_present = ret; -@@ -1739,8 +1755,8 @@ static int domain_context_mapping(struct - */ - if ( ret >= 0 ) - ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn, -- NULL, domain->domain_id, pgd_maddr, -- mode); -+ NULL, DEVICE_DOMID(domain, pdev), -+ pgd_maddr, mode); - - /* - * Devices behind PCIe-to-PCI/PCIx bridge may generate different -@@ -1755,8 +1771,8 @@ static int domain_context_mapping(struct - if ( !ret && pdev_type(seg, bus, devfn) == DEV_TYPE_PCIe2PCI_BRIDGE && - (secbus != pdev->bus || pdev->devfn != 0) ) - ret = domain_context_mapping_one(domain, drhd->iommu, secbus, 0, -- NULL, domain->domain_id, pgd_maddr, -- mode); -+ NULL, DEVICE_DOMID(domain, pdev), -+ pgd_maddr, mode); - - if ( ret ) - { -@@ -1798,9 +1814,6 @@ int domain_context_unmap_one( - int iommu_domid, rc, ret; - bool_t flush_dev_iotlb; - -- if ( QUARANTINE_SKIP(domain, dom_iommu(domain)->arch.vtd.pgd_maddr) ) -- return 0; -- - ASSERT(pcidevs_locked()); - spin_lock(&iommu->lock); - -@@ -1902,7 +1915,7 @@ static const struct acpi_drhd_unit *doma - printk(VTDPREFIX "%pd:PCIe: unmap %pp\n", - domain, &PCI_SBDF3(seg, bus, devfn)); - ret = domain_context_unmap_one(domain, iommu, bus, devfn, -- domain->domain_id); -+ DEVICE_DOMID(domain, pdev)); - if ( !ret && devfn == pdev->devfn && ats_device(pdev, drhd) > 0 ) - disable_ats_device(pdev); - -@@ -1916,7 +1929,7 @@ static const struct acpi_drhd_unit *doma - printk(VTDPREFIX "%pd:PCI: unmap %pp\n", - domain, &PCI_SBDF3(seg, bus, devfn)); - ret = domain_context_unmap_one(domain, iommu, bus, devfn, -- domain->domain_id); -+ DEVICE_DOMID(domain, pdev)); - if ( ret ) - break; - -@@ -1939,18 +1952,12 @@ static const struct acpi_drhd_unit *doma - break; - } - -+ ret = domain_context_unmap_one(domain, iommu, tmp_bus, tmp_devfn, -+ DEVICE_DOMID(domain, pdev)); - /* PCIe to PCI/PCIx bridge */ -- if ( pdev_type(seg, tmp_bus, tmp_devfn) == DEV_TYPE_PCIe2PCI_BRIDGE ) -- { -- ret = domain_context_unmap_one(domain, iommu, tmp_bus, tmp_devfn, -- domain->domain_id); -- if ( !ret ) -- ret = domain_context_unmap_one(domain, iommu, secbus, 0, -- domain->domain_id); -- } -- else /* Legacy PCI bridge */ -- ret = domain_context_unmap_one(domain, iommu, tmp_bus, tmp_devfn, -- domain->domain_id); -+ if ( !ret && pdev_type(seg, tmp_bus, tmp_devfn) == DEV_TYPE_PCIe2PCI_BRIDGE ) -+ ret = domain_context_unmap_one(domain, iommu, secbus, 0, -+ DEVICE_DOMID(domain, pdev)); - - break; - -@@ -1961,7 +1968,7 @@ static const struct acpi_drhd_unit *doma - } - - if ( !ret && pdev->devfn == devfn && -- !QUARANTINE_SKIP(domain, dom_iommu(domain)->arch.vtd.pgd_maddr) ) -+ !QUARANTINE_SKIP(domain, pdev->arch.vtd.pgd_maddr) ) - check_cleanup_domid_map(domain, pdev, iommu); - - return drhd; -@@ -1994,6 +2001,26 @@ static void iommu_domain_teardown(struct - XFREE(hd->arch.vtd.iommu_bitmap); - } - -+static void quarantine_teardown(struct pci_dev *pdev, -+ const struct acpi_drhd_unit *drhd) -+{ -+ struct domain_iommu *hd = dom_iommu(dom_io); -+ -+ ASSERT(pcidevs_locked()); -+ -+ if ( !pdev->arch.vtd.pgd_maddr ) -+ return; -+ -+ ASSERT(page_list_empty(&hd->arch.pgtables.list)); -+ page_list_move(&hd->arch.pgtables.list, &pdev->arch.pgtables_list); -+ while ( iommu_free_pgtables(dom_io) == -ERESTART ) -+ /* nothing */; -+ pdev->arch.vtd.pgd_maddr = 0; -+ -+ if ( drhd ) -+ cleanup_domid_map(pdev->arch.pseudo_domid, drhd->iommu); -+} -+ - static int __must_check intel_iommu_map_page(struct domain *d, dfn_t dfn, - mfn_t mfn, unsigned int flags, - unsigned int *flush_flags) -@@ -2218,6 +2245,8 @@ static int intel_iommu_remove_device(u8 - rmrr->end_address, 0); - } - -+ quarantine_teardown(pdev, drhd); -+ - if ( drhd ) - { - iommu_free_domid(pdev->arch.pseudo_domid, -@@ -2576,7 +2605,7 @@ static int reassign_device_ownership( - { - int ret; - -- if ( !QUARANTINE_SKIP(target, dom_iommu(target)->arch.vtd.pgd_maddr) ) -+ if ( !QUARANTINE_SKIP(target, pdev->arch.vtd.pgd_maddr) ) - { - if ( !has_arch_pdevs(target) ) - vmx_pi_hooks_assign(target); -@@ -2592,7 +2621,7 @@ static int reassign_device_ownership( - ret = domain_context_mapping(target, devfn, pdev); - - if ( !ret && pdev->devfn == devfn && -- !QUARANTINE_SKIP(source, dom_iommu(source)->arch.vtd.pgd_maddr) ) -+ !QUARANTINE_SKIP(source, pdev->arch.vtd.pgd_maddr) ) - { - const struct acpi_drhd_unit *drhd = acpi_find_matched_drhd_unit(pdev); - -@@ -2913,69 +2942,135 @@ static void vtd_dump_page_tables(struct - agaw_to_level(hd->arch.vtd.agaw), 0, 0); - } - --static int __init intel_iommu_quarantine_init(struct domain *d) -+static int fill_qpt(struct dma_pte *this, unsigned int level, -+ struct page_info *pgs[6]) - { -- struct domain_iommu *hd = dom_iommu(d); -+ struct domain_iommu *hd = dom_iommu(dom_io); -+ unsigned int i; -+ int rc = 0; -+ -+ for ( i = 0; !rc && i < PTE_NUM; ++i ) -+ { -+ struct dma_pte *pte = &this[i], *next; -+ -+ if ( !dma_pte_present(*pte) ) -+ { -+ if ( !pgs[level] ) -+ { -+ /* -+ * The pgtable allocator is fine for the leaf page, as well as -+ * page table pages, and the resulting allocations are always -+ * zeroed. -+ */ -+ pgs[level] = iommu_alloc_pgtable(hd); -+ if ( !pgs[level] ) -+ { -+ rc = -ENOMEM; -+ break; -+ } -+ -+ if ( level ) -+ { -+ next = map_vtd_domain_page(page_to_maddr(pgs[level])); -+ rc = fill_qpt(next, level - 1, pgs); -+ unmap_vtd_domain_page(next); -+ } -+ } -+ -+ dma_set_pte_addr(*pte, page_to_maddr(pgs[level])); -+ dma_set_pte_readable(*pte); -+ dma_set_pte_writable(*pte); -+ } -+ else if ( level && !dma_pte_superpage(*pte) ) -+ { -+ next = map_vtd_domain_page(dma_pte_addr(*pte)); -+ rc = fill_qpt(next, level - 1, pgs); -+ unmap_vtd_domain_page(next); -+ } -+ } -+ -+ return rc; -+} -+ -+static int intel_iommu_quarantine_init(struct pci_dev *pdev, bool scratch_page) -+{ -+ struct domain_iommu *hd = dom_iommu(dom_io); - struct page_info *pg; -- struct dma_pte *parent; - unsigned int agaw = width_to_agaw(DEFAULT_DOMAIN_ADDRESS_WIDTH); - unsigned int level = agaw_to_level(agaw); -- int rc = 0; -+ const struct acpi_drhd_unit *drhd; -+ const struct acpi_rmrr_unit *rmrr; -+ unsigned int i, bdf; -+ bool rmrr_found = false; -+ int rc; - -- spin_lock(&hd->arch.mapping_lock); -+ ASSERT(pcidevs_locked()); -+ ASSERT(!hd->arch.vtd.pgd_maddr); -+ ASSERT(page_list_empty(&hd->arch.pgtables.list)); - -- if ( hd->arch.vtd.pgd_maddr ) -+ if ( pdev->arch.vtd.pgd_maddr ) - { -- ASSERT_UNREACHABLE(); -- goto out; -+ clear_domain_page(pdev->arch.leaf_mfn); -+ return 0; - } - -- pg = iommu_alloc_pgtable(hd); -+ drhd = acpi_find_matched_drhd_unit(pdev); -+ if ( !drhd ) -+ return -ENODEV; - -- rc = -ENOMEM; -+ pg = iommu_alloc_pgtable(hd); - if ( !pg ) -- goto out; -+ return -ENOMEM; - -+ rc = context_set_domain_id(NULL, pdev->arch.pseudo_domid, drhd->iommu); -+ -+ /* Transiently install the root into DomIO, for iommu_identity_mapping(). */ - hd->arch.vtd.pgd_maddr = page_to_maddr(pg); - -- parent = map_vtd_domain_page(hd->arch.vtd.pgd_maddr); -- while ( level ) -+ for_each_rmrr_device ( rmrr, bdf, i ) - { -- uint64_t maddr; -- unsigned int offset; -- -- /* -- * The pgtable allocator is fine for the leaf page, as well as -- * page table pages, and the resulting allocations are always -- * zeroed. -- */ -- pg = iommu_alloc_pgtable(hd); -- -- if ( !pg ) -- goto out; -+ if ( rc ) -+ break; - -- maddr = page_to_maddr(pg); -- for ( offset = 0; offset < PTE_NUM; offset++ ) -+ if ( rmrr->segment == pdev->seg && bdf == pdev->sbdf.bdf ) - { -- struct dma_pte *pte = &parent[offset]; -+ rmrr_found = true; - -- dma_set_pte_addr(*pte, maddr); -- dma_set_pte_readable(*pte); -+ rc = iommu_identity_mapping(dom_io, p2m_access_rw, -+ rmrr->base_address, rmrr->end_address, -+ 0); -+ if ( rc ) -+ printk(XENLOG_ERR VTDPREFIX -+ "%pp: RMRR quarantine mapping failed\n", -+ &pdev->sbdf); - } -- iommu_sync_cache(parent, PAGE_SIZE); -+ } - -- unmap_vtd_domain_page(parent); -- parent = map_vtd_domain_page(maddr); -- level--; -+ iommu_identity_map_teardown(dom_io); -+ hd->arch.vtd.pgd_maddr = 0; -+ pdev->arch.vtd.pgd_maddr = page_to_maddr(pg); -+ -+ if ( !rc && scratch_page ) -+ { -+ struct dma_pte *root; -+ struct page_info *pgs[6] = {}; -+ -+ spin_lock(&hd->arch.mapping_lock); -+ -+ root = map_vtd_domain_page(pdev->arch.vtd.pgd_maddr); -+ rc = fill_qpt(root, level - 1, pgs); -+ unmap_vtd_domain_page(root); -+ -+ pdev->arch.leaf_mfn = page_to_mfn(pgs[0]); -+ -+ spin_unlock(&hd->arch.mapping_lock); - } -- unmap_vtd_domain_page(parent); - -- rc = 0; -+ page_list_move(&pdev->arch.pgtables_list, &hd->arch.pgtables.list); - -- out: -- spin_unlock(&hd->arch.mapping_lock); -+ if ( rc || (!scratch_page && !rmrr_found) ) -+ quarantine_teardown(pdev, drhd); - -- /* Pages may be leaked in failure case */ - return rc; - } - ---- a/xen/drivers/passthrough/vtd/iommu.h -+++ b/xen/drivers/passthrough/vtd/iommu.h -@@ -482,7 +482,7 @@ struct vtd_iommu { - u32 nr_pt_levels; - u64 cap; - u64 ecap; -- spinlock_t lock; /* protect context, domain ids */ -+ spinlock_t lock; /* protect context */ - spinlock_t register_lock; /* protect iommu register handling */ - u64 root_maddr; /* root entry machine address */ - nodeid_t node; ---- a/xen/include/xen/iommu.h -+++ b/xen/include/xen/iommu.h -@@ -233,7 +233,7 @@ typedef int iommu_grdm_t(xen_pfn_t start - struct iommu_ops { - int (*init)(struct domain *d); - void (*hwdom_init)(struct domain *d); -- int (*quarantine_init)(struct domain *d); -+ int (*quarantine_init)(device_t *dev, bool scratch_page); - int (*add_device)(u8 devfn, device_t *dev); - int (*enable_device)(device_t *dev); - int (*remove_device)(u8 devfn, device_t *dev); -@@ -350,6 +350,7 @@ int __must_check iommu_suspend(void); - void iommu_resume(void); - void iommu_crash_shutdown(void); - int iommu_get_reserved_device_memory(iommu_grdm_t *, void *); -+int iommu_quarantine_dev_init(device_t *dev); - - #ifdef CONFIG_HAS_PCI - int iommu_do_pci_domctl(struct xen_domctl *, struct domain *d, From 161d5c0fe933f5dbcedf45a419f44be32d0e4185 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Sat, 11 Jun 2022 17:13:19 +0100 Subject: [PATCH 037/137] stop building for ix86 and armv7hl and 2 security updates stop building for ix86 and armv7hl due to missing build dependency x86 pv: Race condition in typeref acquisition [XSA-401, CVE-2022-26362] x86 pv: Insufficient care with non-coherent mappings [ XSA-402, CVE-2022-26363, CVE-2022-26364] --- ...ee56c7e0bb5eeb35dcc55b3d29e5f50eb566.patch | 43 +++ xen.spec | 27 +- xsa401-4.16-1.patch | 170 +++++++++++ xsa401-4.16-2.patch | 191 ++++++++++++ xsa402-4.16-1.patch | 43 +++ xsa402-4.16-2.patch | 213 +++++++++++++ xsa402-4.16-3.patch | 284 ++++++++++++++++++ xsa402-4.16-4.patch | 83 +++++ xsa402-4.16-5.patch | 148 +++++++++ 9 files changed, 1200 insertions(+), 2 deletions(-) create mode 100644 xen.git-b378ee56c7e0bb5eeb35dcc55b3d29e5f50eb566.patch create mode 100644 xsa401-4.16-1.patch create mode 100644 xsa401-4.16-2.patch create mode 100644 xsa402-4.16-1.patch create mode 100644 xsa402-4.16-2.patch create mode 100644 xsa402-4.16-3.patch create mode 100644 xsa402-4.16-4.patch create mode 100644 xsa402-4.16-5.patch diff --git a/xen.git-b378ee56c7e0bb5eeb35dcc55b3d29e5f50eb566.patch b/xen.git-b378ee56c7e0bb5eeb35dcc55b3d29e5f50eb566.patch new file mode 100644 index 0000000..2f1b7f5 --- /dev/null +++ b/xen.git-b378ee56c7e0bb5eeb35dcc55b3d29e5f50eb566.patch @@ -0,0 +1,43 @@ +From: Jan Beulich +Date: Tue, 7 Jun 2022 11:58:16 +0000 (+0200) +Subject: VT-d: refuse to use IOMMU with reserved CAP.ND value +X-Git-Url: http://xenbits.xenproject.org/gitweb/?p=xen.git;a=commitdiff_plain;h=b378ee56c7e0bb5eeb35dcc55b3d29e5f50eb566 + +VT-d: refuse to use IOMMU with reserved CAP.ND value + +The field taking the value 7 (resulting in 18-bit DIDs when using the +calculation in cap_ndoms(), when the DID fields are only 16 bits wide) +is reserved. Instead of misbehaving in case we would encounter such an +IOMMU, refuse to use it. + +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné +Reviewed-by: Kevin Tian +master commit: a1545fbf45c689aff39ce76a6eaa609d32ef72a7 +master date: 2022-04-20 10:54:26 +0200 +--- + +diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c +index 93dd8aa643..8975c1de61 100644 +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -1279,8 +1279,11 @@ int __init iommu_alloc(struct acpi_drhd_unit *drhd) + + quirk_iommu_caps(iommu); + ++ nr_dom = cap_ndoms(iommu->cap); ++ + if ( cap_fault_reg_offset(iommu->cap) + + cap_num_fault_regs(iommu->cap) * PRIMARY_FAULT_REG_LEN >= PAGE_SIZE || ++ ((nr_dom - 1) >> 16) /* I.e. cap.nd > 6 */ || + ecap_iotlb_offset(iommu->ecap) >= PAGE_SIZE ) + { + printk(XENLOG_ERR VTDPREFIX "IOMMU: unsupported\n"); +@@ -1305,7 +1308,6 @@ int __init iommu_alloc(struct acpi_drhd_unit *drhd) + vtd_ops.sync_cache = sync_cache; + + /* allocate domain id bitmap */ +- nr_dom = cap_ndoms(iommu->cap); + iommu->domid_bitmap = xzalloc_array(unsigned long, BITS_TO_LONGS(nr_dom)); + if ( !iommu->domid_bitmap ) + return -ENOMEM; diff --git a/xen.spec b/xen.spec index 01ae49b..9332a75 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.16.1 -Release: 1%{?dist} +Release: 2%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -111,6 +111,14 @@ Patch43: xen.gcc11.fixes.patch Patch44: xsa376.patch Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch +Patch47: xsa401-4.16-1.patch +Patch48: xsa401-4.16-2.patch +Patch49: xsa402-4.16-1.patch +Patch50: xsa402-4.16-2.patch +Patch51: xen.git-b378ee56c7e0bb5eeb35dcc55b3d29e5f50eb566.patch +Patch52: xsa402-4.16-3.patch +Patch53: xsa402-4.16-4.patch +Patch54: xsa402-4.16-5.patch %if %build_qemutrad @@ -162,7 +170,7 @@ Requires: xen-runtime = %{version}-%{release} # now for accessing domU data from within a dom0 so bring it in when the user # installs xen. Requires: kpartx -ExclusiveArch: %{ix86} x86_64 armv7hl aarch64 +ExclusiveArch: x86_64 aarch64 #ExclusiveArch: %#{ix86} x86_64 ia64 noarch %if %with_ocaml BuildRequires: ocaml, ocaml-findlib @@ -323,6 +331,14 @@ manage Xen virtual machines. %patch44 -p1 %patch45 -p1 %patch46 -p1 +%patch47 -p1 +%patch48 -p1 +%patch49 -p1 +%patch50 -p1 +%patch51 -p1 +%patch52 -p1 +%patch53 -p1 +%patch54 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -938,6 +954,13 @@ fi %endif %changelog +* Sat Jun 11 2022 Michael Young - 4.16.1-2 +- stop building for ix86 and armv7hl due to missing build dependency +- x86 pv: Race condition in typeref acquisition [XSA-401, CVE-2022-26362] +- x86 pv: Insufficient care with non-coherent mappings [ XSA-402, + CVE-2022-26363, CVE-2022-26364] +- additional patches so above applies cleanly + * Thu Apr 14 2022 Michael Young - 4.16.1-1 - update to xen-4.16.1 remove or adjust patches now included or superceded upstream diff --git a/xsa401-4.16-1.patch b/xsa401-4.16-1.patch new file mode 100644 index 0000000..5c8c506 --- /dev/null +++ b/xsa401-4.16-1.patch @@ -0,0 +1,170 @@ +From: Andrew Cooper +Subject: x86/pv: Clean up _get_page_type() + +Various fixes for clarity, ahead of making complicated changes. + + * Split the overflow check out of the if/else chain for type handling, as + it's somewhat unrelated. + * Comment the main if/else chain to explain what is going on. Adjust one + ASSERT() and state the bit layout for validate-locked and partial states. + * Correct the comment about TLB flushing, as it's backwards. The problem + case is when writeable mappings are retained to a page becoming read-only, + as it allows the guest to bypass Xen's safety checks for updates. + * Reduce the scope of 'y'. It is an artefact of the cmpxchg loop and not + valid for use by subsequent logic. Switch to using ACCESS_ONCE() to treat + all reads as explicitly volatile. The only thing preventing the validated + wait-loop being infinite is the compiler barrier hidden in cpu_relax(). + * Replace one page_get_owner(page) with the already-calculated 'd' already in + scope. + +No functional change. + +This is part of XSA-401 / CVE-2022-26362. + +Signed-off-by: Andrew Cooper +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +Reviewed-by: George Dunlap + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 796faca64103..ddd32f88c798 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -2935,16 +2935,17 @@ static int _put_page_type(struct page_info *page, unsigned int flags, + static int _get_page_type(struct page_info *page, unsigned long type, + bool preemptible) + { +- unsigned long nx, x, y = page->u.inuse.type_info; ++ unsigned long nx, x; + int rc = 0; + + ASSERT(!(type & ~(PGT_type_mask | PGT_pae_xen_l2))); + ASSERT(!in_irq()); + +- for ( ; ; ) ++ for ( unsigned long y = ACCESS_ONCE(page->u.inuse.type_info); ; ) + { + x = y; + nx = x + 1; ++ + if ( unlikely((nx & PGT_count_mask) == 0) ) + { + gdprintk(XENLOG_WARNING, +@@ -2952,8 +2953,15 @@ static int _get_page_type(struct page_info *page, unsigned long type, + mfn_x(page_to_mfn(page))); + return -EINVAL; + } +- else if ( unlikely((x & PGT_count_mask) == 0) ) ++ ++ if ( unlikely((x & PGT_count_mask) == 0) ) + { ++ /* ++ * Typeref 0 -> 1. ++ * ++ * Type changes are permitted when the typeref is 0. If the type ++ * actually changes, the page needs re-validating. ++ */ + struct domain *d = page_get_owner(page); + + if ( d && shadow_mode_enabled(d) ) +@@ -2964,8 +2972,8 @@ static int _get_page_type(struct page_info *page, unsigned long type, + { + /* + * On type change we check to flush stale TLB entries. It is +- * vital that no other CPUs are left with mappings of a frame +- * which is about to become writeable to the guest. ++ * vital that no other CPUs are left with writeable mappings ++ * to a frame which is intending to become pgtable/segdesc. + */ + cpumask_t *mask = this_cpu(scratch_cpumask); + +@@ -2977,7 +2985,7 @@ static int _get_page_type(struct page_info *page, unsigned long type, + + if ( unlikely(!cpumask_empty(mask)) && + /* Shadow mode: track only writable pages. */ +- (!shadow_mode_enabled(page_get_owner(page)) || ++ (!shadow_mode_enabled(d) || + ((nx & PGT_type_mask) == PGT_writable_page)) ) + { + perfc_incr(need_flush_tlb_flush); +@@ -3008,7 +3016,14 @@ static int _get_page_type(struct page_info *page, unsigned long type, + } + else if ( unlikely((x & (PGT_type_mask|PGT_pae_xen_l2)) != type) ) + { +- /* Don't log failure if it could be a recursive-mapping attempt. */ ++ /* ++ * else, we're trying to take a new reference, of the wrong type. ++ * ++ * This (being able to prohibit use of the wrong type) is what the ++ * typeref system exists for, but skip printing the failure if it ++ * looks like a recursive mapping, as subsequent logic might ++ * ultimately permit the attempt. ++ */ + if ( ((x & PGT_type_mask) == PGT_l2_page_table) && + (type == PGT_l1_page_table) ) + return -EINVAL; +@@ -3027,18 +3042,46 @@ static int _get_page_type(struct page_info *page, unsigned long type, + } + else if ( unlikely(!(x & PGT_validated)) ) + { ++ /* ++ * else, the count is non-zero, and we're grabbing the right type; ++ * but the page hasn't been validated yet. ++ * ++ * The page is in one of two states (depending on PGT_partial), ++ * and should have exactly one reference. ++ */ ++ ASSERT((x & (PGT_type_mask | PGT_count_mask)) == (type | 1)); ++ + if ( !(x & PGT_partial) ) + { +- /* Someone else is updating validation of this page. Wait... */ ++ /* ++ * The page has been left in the "validate locked" state ++ * (i.e. PGT_[type] | 1) which means that a concurrent caller ++ * of _get_page_type() is in the middle of validation. ++ * ++ * Spin waiting for the concurrent user to complete (partial ++ * or fully validated), then restart our attempt to acquire a ++ * type reference. ++ */ + do { + if ( preemptible && hypercall_preempt_check() ) + return -EINTR; + cpu_relax(); +- } while ( (y = page->u.inuse.type_info) == x ); ++ } while ( (y = ACCESS_ONCE(page->u.inuse.type_info)) == x ); + continue; + } +- /* Type ref count was left at 1 when PGT_partial got set. */ +- ASSERT((x & PGT_count_mask) == 1); ++ ++ /* ++ * The page has been left in the "partial" state ++ * (i.e., PGT_[type] | PGT_partial | 1). ++ * ++ * Rather than bumping the type count, we need to try to grab the ++ * validation lock; if we succeed, we need to validate the page, ++ * then drop the general ref associated with the PGT_partial bit. ++ * ++ * We grab the validation lock by setting nx to (PGT_[type] | 1) ++ * (i.e., non-zero type count, neither PGT_validated nor ++ * PGT_partial set). ++ */ + nx = x & ~PGT_partial; + } + +@@ -3087,6 +3130,13 @@ static int _get_page_type(struct page_info *page, unsigned long type, + } + + out: ++ /* ++ * Did we drop the PGT_partial bit when acquiring the typeref? If so, ++ * drop the general reference that went along with it. ++ * ++ * N.B. validate_page() may have have re-set PGT_partial, not reflected in ++ * nx, but will have taken an extra ref when doing so. ++ */ + if ( (x & PGT_partial) && !(nx & PGT_partial) ) + put_page(page); + diff --git a/xsa401-4.16-2.patch b/xsa401-4.16-2.patch new file mode 100644 index 0000000..be58db5 --- /dev/null +++ b/xsa401-4.16-2.patch @@ -0,0 +1,191 @@ +From: Andrew Cooper +Subject: x86/pv: Fix ABAC cmpxchg() race in _get_page_type() + +_get_page_type() suffers from a race condition where it incorrectly assumes +that because 'x' was read and a subsequent a cmpxchg() succeeds, the type +cannot have changed in-between. Consider: + +CPU A: + 1. Creates an L2e referencing pg + `-> _get_page_type(pg, PGT_l1_page_table), sees count 0, type PGT_writable_page + 2. Issues flush_tlb_mask() +CPU B: + 3. Creates a writeable mapping of pg + `-> _get_page_type(pg, PGT_writable_page), count increases to 1 + 4. Writes into new mapping, creating a TLB entry for pg + 5. Removes the writeable mapping of pg + `-> _put_page_type(pg), count goes back down to 0 +CPU A: + 7. Issues cmpxchg(), setting count 1, type PGT_l1_page_table + +CPU B now has a writeable mapping to pg, which Xen believes is a pagetable and +suitably protected (i.e. read-only). The TLB flush in step 2 must be deferred +until after the guest is prohibited from creating new writeable mappings, +which is after step 7. + +Defer all safety actions until after the cmpxchg() has successfully taken the +intended typeref, because that is what prevents concurrent users from using +the old type. + +Also remove the early validation for writeable and shared pages. This removes +race conditions where one half of a parallel mapping attempt can return +successfully before: + * The IOMMU pagetables are in sync with the new page type + * Writeable mappings to shared pages have been torn down + +This is part of XSA-401 / CVE-2022-26362. + +Reported-by: Jann Horn +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +Reviewed-by: George Dunlap + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index ddd32f88c798..1693b580b152 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -2962,56 +2962,12 @@ static int _get_page_type(struct page_info *page, unsigned long type, + * Type changes are permitted when the typeref is 0. If the type + * actually changes, the page needs re-validating. + */ +- struct domain *d = page_get_owner(page); +- +- if ( d && shadow_mode_enabled(d) ) +- shadow_prepare_page_type_change(d, page, type); + + ASSERT(!(x & PGT_pae_xen_l2)); + if ( (x & PGT_type_mask) != type ) + { +- /* +- * On type change we check to flush stale TLB entries. It is +- * vital that no other CPUs are left with writeable mappings +- * to a frame which is intending to become pgtable/segdesc. +- */ +- cpumask_t *mask = this_cpu(scratch_cpumask); +- +- BUG_ON(in_irq()); +- cpumask_copy(mask, d->dirty_cpumask); +- +- /* Don't flush if the timestamp is old enough */ +- tlbflush_filter(mask, page->tlbflush_timestamp); +- +- if ( unlikely(!cpumask_empty(mask)) && +- /* Shadow mode: track only writable pages. */ +- (!shadow_mode_enabled(d) || +- ((nx & PGT_type_mask) == PGT_writable_page)) ) +- { +- perfc_incr(need_flush_tlb_flush); +- /* +- * If page was a page table make sure the flush is +- * performed using an IPI in order to avoid changing the +- * type of a page table page under the feet of +- * spurious_page_fault(). +- */ +- flush_mask(mask, +- (x & PGT_type_mask) && +- (x & PGT_type_mask) <= PGT_root_page_table +- ? FLUSH_TLB | FLUSH_FORCE_IPI +- : FLUSH_TLB); +- } +- +- /* We lose existing type and validity. */ + nx &= ~(PGT_type_mask | PGT_validated); + nx |= type; +- +- /* +- * No special validation needed for writable pages. +- * Page tables and GDT/LDT need to be scanned for validity. +- */ +- if ( type == PGT_writable_page || type == PGT_shared_page ) +- nx |= PGT_validated; + } + } + else if ( unlikely((x & (PGT_type_mask|PGT_pae_xen_l2)) != type) ) +@@ -3092,6 +3048,56 @@ static int _get_page_type(struct page_info *page, unsigned long type, + return -EINTR; + } + ++ /* ++ * One typeref has been taken and is now globally visible. ++ * ++ * The page is either in the "validate locked" state (PGT_[type] | 1) or ++ * fully validated (PGT_[type] | PGT_validated | >0). ++ */ ++ ++ if ( unlikely((x & PGT_count_mask) == 0) ) ++ { ++ struct domain *d = page_get_owner(page); ++ ++ if ( d && shadow_mode_enabled(d) ) ++ shadow_prepare_page_type_change(d, page, type); ++ ++ if ( (x & PGT_type_mask) != type ) ++ { ++ /* ++ * On type change we check to flush stale TLB entries. It is ++ * vital that no other CPUs are left with writeable mappings ++ * to a frame which is intending to become pgtable/segdesc. ++ */ ++ cpumask_t *mask = this_cpu(scratch_cpumask); ++ ++ BUG_ON(in_irq()); ++ cpumask_copy(mask, d->dirty_cpumask); ++ ++ /* Don't flush if the timestamp is old enough */ ++ tlbflush_filter(mask, page->tlbflush_timestamp); ++ ++ if ( unlikely(!cpumask_empty(mask)) && ++ /* Shadow mode: track only writable pages. */ ++ (!shadow_mode_enabled(d) || ++ ((nx & PGT_type_mask) == PGT_writable_page)) ) ++ { ++ perfc_incr(need_flush_tlb_flush); ++ /* ++ * If page was a page table make sure the flush is ++ * performed using an IPI in order to avoid changing the ++ * type of a page table page under the feet of ++ * spurious_page_fault(). ++ */ ++ flush_mask(mask, ++ (x & PGT_type_mask) && ++ (x & PGT_type_mask) <= PGT_root_page_table ++ ? FLUSH_TLB | FLUSH_FORCE_IPI ++ : FLUSH_TLB); ++ } ++ } ++ } ++ + if ( unlikely(((x & PGT_type_mask) == PGT_writable_page) != + (type == PGT_writable_page)) ) + { +@@ -3120,13 +3126,25 @@ static int _get_page_type(struct page_info *page, unsigned long type, + + if ( unlikely(!(nx & PGT_validated)) ) + { +- if ( !(x & PGT_partial) ) ++ /* ++ * No special validation needed for writable or shared pages. Page ++ * tables and GDT/LDT need to have their contents audited. ++ * ++ * per validate_page(), non-atomic updates are fine here. ++ */ ++ if ( type == PGT_writable_page || type == PGT_shared_page ) ++ page->u.inuse.type_info |= PGT_validated; ++ else + { +- page->nr_validated_ptes = 0; +- page->partial_flags = 0; +- page->linear_pt_count = 0; ++ if ( !(x & PGT_partial) ) ++ { ++ page->nr_validated_ptes = 0; ++ page->partial_flags = 0; ++ page->linear_pt_count = 0; ++ } ++ ++ rc = validate_page(page, type, preemptible); + } +- rc = validate_page(page, type, preemptible); + } + + out: diff --git a/xsa402-4.16-1.patch b/xsa402-4.16-1.patch new file mode 100644 index 0000000..b783383 --- /dev/null +++ b/xsa402-4.16-1.patch @@ -0,0 +1,43 @@ +From: Andrew Cooper +Subject: x86/page: Introduce _PAGE_* constants for memory types + +... rather than opencoding the PAT/PCD/PWT attributes in __PAGE_HYPERVISOR_* +constants. These are going to be needed by forthcoming logic. + +No functional change. + +This is part of XSA-402. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/include/asm-x86/page.h b/xen/include/asm-x86/page.h +index 1d080cffbe84..2e542050f65a 100644 +--- a/xen/include/asm-x86/page.h ++++ b/xen/include/asm-x86/page.h +@@ -331,6 +331,14 @@ void efi_update_l4_pgtable(unsigned int l4idx, l4_pgentry_t); + + #define PAGE_CACHE_ATTRS (_PAGE_PAT | _PAGE_PCD | _PAGE_PWT) + ++/* Memory types, encoded under Xen's choice of MSR_PAT. */ ++#define _PAGE_WB ( 0) ++#define _PAGE_WT ( _PAGE_PWT) ++#define _PAGE_UCM ( _PAGE_PCD ) ++#define _PAGE_UC ( _PAGE_PCD | _PAGE_PWT) ++#define _PAGE_WC (_PAGE_PAT ) ++#define _PAGE_WP (_PAGE_PAT | _PAGE_PWT) ++ + /* + * Debug option: Ensure that granted mappings are not implicitly unmapped. + * WARNING: This will need to be disabled to run OSes that use the spare PTE +@@ -349,8 +357,8 @@ void efi_update_l4_pgtable(unsigned int l4idx, l4_pgentry_t); + #define __PAGE_HYPERVISOR_RX (_PAGE_PRESENT | _PAGE_ACCESSED) + #define __PAGE_HYPERVISOR (__PAGE_HYPERVISOR_RX | \ + _PAGE_DIRTY | _PAGE_RW) +-#define __PAGE_HYPERVISOR_UCMINUS (__PAGE_HYPERVISOR | _PAGE_PCD) +-#define __PAGE_HYPERVISOR_UC (__PAGE_HYPERVISOR | _PAGE_PCD | _PAGE_PWT) ++#define __PAGE_HYPERVISOR_UCMINUS (__PAGE_HYPERVISOR | _PAGE_UCM) ++#define __PAGE_HYPERVISOR_UC (__PAGE_HYPERVISOR | _PAGE_UC) + #define __PAGE_HYPERVISOR_SHSTK (__PAGE_HYPERVISOR_RO | _PAGE_DIRTY) + + #define MAP_SMALL_PAGES _PAGE_AVAIL0 /* don't use superpages mappings */ diff --git a/xsa402-4.16-2.patch b/xsa402-4.16-2.patch new file mode 100644 index 0000000..ebb2f5e --- /dev/null +++ b/xsa402-4.16-2.patch @@ -0,0 +1,213 @@ +From: Andrew Cooper +Subject: x86: Don't change the cacheability of the directmap + +Changeset 55f97f49b7ce ("x86: Change cache attributes of Xen 1:1 page mappings +in response to guest mapping requests") attempted to keep the cacheability +consistent between different mappings of the same page. + +The reason wasn't described in the changelog, but it is understood to be in +regards to a concern over machine check exceptions, owing to errata when using +mixed cacheabilities. It did this primarily by updating Xen's mapping of the +page in the direct map when the guest mapped a page with reduced cacheability. + +Unfortunately, the logic didn't actually prevent mixed cacheability from +occurring: + * A guest could map a page normally, and then map the same page with + different cacheability; nothing prevented this. + * The cacheability of the directmap was always latest-takes-precedence in + terms of guest requests. + * Grant-mapped frames with lesser cacheability didn't adjust the page's + cacheattr settings. + * The map_domain_page() function still unconditionally created WB mappings, + irrespective of the page's cacheattr settings. + +Additionally, update_xen_mappings() had a bug where the alias calculation was +wrong for mfn's which were .init content, which should have been treated as +fully guest pages, not Xen pages. + +Worse yet, the logic introduced a vulnerability whereby necessary +pagetable/segdesc adjustments made by Xen in the validation logic could become +non-coherent between the cache and main memory. The CPU could subsequently +operate on the stale value in the cache, rather than the safe value in main +memory. + +The directmap contains primarily mappings of RAM. PAT/MTRR conflict +resolution is asymmetric, and generally for MTRR=WB ranges, PAT of lesser +cacheability resolves to being coherent. The special case is WC mappings, +which are non-coherent against MTRR=WB regions (except for fully-coherent +CPUs). + +Xen must not have any WC cacheability in the directmap, to prevent Xen's +actions from creating non-coherency. (Guest actions creating non-coherency is +dealt with in subsequent patches.) As all memory types for MTRR=WB ranges +inter-operate coherently, so leave Xen's directmap mappings as WB. + +Only PV guests with access to devices can use reduced-cacheability mappings to +begin with, and they're trusted not to mount DoSs against the system anyway. + +Drop PGC_cacheattr_{base,mask} entirely, and the logic to manipulate them. +Shift the later PGC_* constants up, to gain 3 extra bits in the main reference +count. Retain the check in get_page_from_l1e() for special_pages() because a +guest has no business using reduced cacheability on these. + +This reverts changeset 55f97f49b7ce6c3520c555d19caac6cf3f9a5df0 + +This is CVE-2022-26363, part of XSA-402. + +Signed-off-by: Andrew Cooper +Reviewed-by: George Dunlap + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index c6429b0f749a..ab32d13a1a0d 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -783,28 +783,6 @@ bool is_iomem_page(mfn_t mfn) + return (page_get_owner(page) == dom_io); + } + +-static int update_xen_mappings(unsigned long mfn, unsigned int cacheattr) +-{ +- int err = 0; +- bool alias = mfn >= PFN_DOWN(xen_phys_start) && +- mfn < PFN_UP(xen_phys_start + xen_virt_end - XEN_VIRT_START); +- unsigned long xen_va = +- XEN_VIRT_START + ((mfn - PFN_DOWN(xen_phys_start)) << PAGE_SHIFT); +- +- if ( boot_cpu_has(X86_FEATURE_XEN_SELFSNOOP) ) +- return 0; +- +- if ( unlikely(alias) && cacheattr ) +- err = map_pages_to_xen(xen_va, _mfn(mfn), 1, 0); +- if ( !err ) +- err = map_pages_to_xen((unsigned long)mfn_to_virt(mfn), _mfn(mfn), 1, +- PAGE_HYPERVISOR | cacheattr_to_pte_flags(cacheattr)); +- if ( unlikely(alias) && !cacheattr && !err ) +- err = map_pages_to_xen(xen_va, _mfn(mfn), 1, PAGE_HYPERVISOR); +- +- return err; +-} +- + #ifndef NDEBUG + struct mmio_emul_range_ctxt { + const struct domain *d; +@@ -1009,47 +987,14 @@ get_page_from_l1e( + goto could_not_pin; + } + +- if ( pte_flags_to_cacheattr(l1f) != +- ((page->count_info & PGC_cacheattr_mask) >> PGC_cacheattr_base) ) ++ if ( (l1f & PAGE_CACHE_ATTRS) != _PAGE_WB && is_special_page(page) ) + { +- unsigned long x, nx, y = page->count_info; +- unsigned long cacheattr = pte_flags_to_cacheattr(l1f); +- int err; +- +- if ( is_special_page(page) ) +- { +- if ( write ) +- put_page_type(page); +- put_page(page); +- gdprintk(XENLOG_WARNING, +- "Attempt to change cache attributes of Xen heap page\n"); +- return -EACCES; +- } +- +- do { +- x = y; +- nx = (x & ~PGC_cacheattr_mask) | (cacheattr << PGC_cacheattr_base); +- } while ( (y = cmpxchg(&page->count_info, x, nx)) != x ); +- +- err = update_xen_mappings(mfn, cacheattr); +- if ( unlikely(err) ) +- { +- cacheattr = y & PGC_cacheattr_mask; +- do { +- x = y; +- nx = (x & ~PGC_cacheattr_mask) | cacheattr; +- } while ( (y = cmpxchg(&page->count_info, x, nx)) != x ); +- +- if ( write ) +- put_page_type(page); +- put_page(page); +- +- gdprintk(XENLOG_WARNING, "Error updating mappings for mfn %" PRI_mfn +- " (pfn %" PRI_pfn ", from L1 entry %" PRIpte ") for d%d\n", +- mfn, get_gpfn_from_mfn(mfn), +- l1e_get_intpte(l1e), l1e_owner->domain_id); +- return err; +- } ++ if ( write ) ++ put_page_type(page); ++ put_page(page); ++ gdprintk(XENLOG_WARNING, ++ "Attempt to change cache attributes of Xen heap page\n"); ++ return -EACCES; + } + + return 0; +@@ -2467,25 +2412,10 @@ static int mod_l4_entry(l4_pgentry_t *pl4e, + */ + static int cleanup_page_mappings(struct page_info *page) + { +- unsigned int cacheattr = +- (page->count_info & PGC_cacheattr_mask) >> PGC_cacheattr_base; + int rc = 0; + unsigned long mfn = mfn_x(page_to_mfn(page)); + + /* +- * If we've modified xen mappings as a result of guest cache +- * attributes, restore them to the "normal" state. +- */ +- if ( unlikely(cacheattr) ) +- { +- page->count_info &= ~PGC_cacheattr_mask; +- +- BUG_ON(is_special_page(page)); +- +- rc = update_xen_mappings(mfn, 0); +- } +- +- /* + * If this may be in a PV domain's IOMMU, remove it. + * + * NB that writable xenheap pages have their type set and cleared by +diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h +index cb9052749963..8a9a43bb0a9d 100644 +--- a/xen/include/asm-x86/mm.h ++++ b/xen/include/asm-x86/mm.h +@@ -69,25 +69,22 @@ + /* Set when is using a page as a page table */ + #define _PGC_page_table PG_shift(3) + #define PGC_page_table PG_mask(1, 3) +- /* 3-bit PAT/PCD/PWT cache-attribute hint. */ +-#define PGC_cacheattr_base PG_shift(6) +-#define PGC_cacheattr_mask PG_mask(7, 6) + /* Page is broken? */ +-#define _PGC_broken PG_shift(7) +-#define PGC_broken PG_mask(1, 7) ++#define _PGC_broken PG_shift(4) ++#define PGC_broken PG_mask(1, 4) + /* Mutually-exclusive page states: { inuse, offlining, offlined, free }. */ +-#define PGC_state PG_mask(3, 9) +-#define PGC_state_inuse PG_mask(0, 9) +-#define PGC_state_offlining PG_mask(1, 9) +-#define PGC_state_offlined PG_mask(2, 9) +-#define PGC_state_free PG_mask(3, 9) ++#define PGC_state PG_mask(3, 6) ++#define PGC_state_inuse PG_mask(0, 6) ++#define PGC_state_offlining PG_mask(1, 6) ++#define PGC_state_offlined PG_mask(2, 6) ++#define PGC_state_free PG_mask(3, 6) + #define page_state_is(pg, st) (((pg)->count_info&PGC_state) == PGC_state_##st) + /* Page is not reference counted (see below for caveats) */ +-#define _PGC_extra PG_shift(10) +-#define PGC_extra PG_mask(1, 10) ++#define _PGC_extra PG_shift(7) ++#define PGC_extra PG_mask(1, 7) + + /* Count of references to this frame. */ +-#define PGC_count_width PG_shift(10) ++#define PGC_count_width PG_shift(7) + #define PGC_count_mask ((1UL< +Subject: x86: Split cache_flush() out of cache_writeback() + +Subsequent changes will want a fully flushing version. + +Use the new helper rather than opencoding it in flush_area_local(). This +resolves an outstanding issue where the conditional sfence is on the wrong +side of the clflushopt loop. clflushopt is ordered with respect to older +stores, not to younger stores. + +Rename gnttab_cache_flush()'s helper to avoid colliding in name. +grant_table.c can see the prototype from cache.h so the build fails +otherwise. + +This is part of XSA-402. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +Xen 4.16 and earlier: + * Also backport half of c/s 3330013e67396 "VT-d / x86: re-arrange cache + syncing" to split cache_writeback() out of the IOMMU logic, but without the + associated hooks changes. + +diff --git a/xen/arch/x86/flushtlb.c b/xen/arch/x86/flushtlb.c +index 25798df50f54..0c912b8669f8 100644 +--- a/xen/arch/x86/flushtlb.c ++++ b/xen/arch/x86/flushtlb.c +@@ -234,7 +234,7 @@ unsigned int flush_area_local(const void *va, unsigned int flags) + if ( flags & FLUSH_CACHE ) + { + const struct cpuinfo_x86 *c = ¤t_cpu_data; +- unsigned long i, sz = 0; ++ unsigned long sz = 0; + + if ( order < (BITS_PER_LONG - PAGE_SHIFT) ) + sz = 1UL << (order + PAGE_SHIFT); +@@ -244,13 +244,7 @@ unsigned int flush_area_local(const void *va, unsigned int flags) + c->x86_clflush_size && c->x86_cache_size && sz && + ((sz >> 10) < c->x86_cache_size) ) + { +- alternative("", "sfence", X86_FEATURE_CLFLUSHOPT); +- for ( i = 0; i < sz; i += c->x86_clflush_size ) +- alternative_input(".byte " __stringify(NOP_DS_PREFIX) ";" +- " clflush %0", +- "data16 clflush %0", /* clflushopt */ +- X86_FEATURE_CLFLUSHOPT, +- "m" (((const char *)va)[i])); ++ cache_flush(va, sz); + flags &= ~FLUSH_CACHE; + } + else +@@ -265,6 +259,80 @@ unsigned int flush_area_local(const void *va, unsigned int flags) + return flags; + } + ++void cache_flush(const void *addr, unsigned int size) ++{ ++ /* ++ * This function may be called before current_cpu_data is established. ++ * Hence a fallback is needed to prevent the loop below becoming infinite. ++ */ ++ unsigned int clflush_size = current_cpu_data.x86_clflush_size ?: 16; ++ const void *end = addr + size; ++ ++ addr -= (unsigned long)addr & (clflush_size - 1); ++ for ( ; addr < end; addr += clflush_size ) ++ { ++ /* ++ * Note regarding the "ds" prefix use: it's faster to do a clflush ++ * + prefix than a clflush + nop, and hence the prefix is added instead ++ * of letting the alternative framework fill the gap by appending nops. ++ */ ++ alternative_io("ds; clflush %[p]", ++ "data16 clflush %[p]", /* clflushopt */ ++ X86_FEATURE_CLFLUSHOPT, ++ /* no outputs */, ++ [p] "m" (*(const char *)(addr))); ++ } ++ ++ alternative("", "sfence", X86_FEATURE_CLFLUSHOPT); ++} ++ ++void cache_writeback(const void *addr, unsigned int size) ++{ ++ unsigned int clflush_size; ++ const void *end = addr + size; ++ ++ /* Fall back to CLFLUSH{,OPT} when CLWB isn't available. */ ++ if ( !boot_cpu_has(X86_FEATURE_CLWB) ) ++ return cache_flush(addr, size); ++ ++ /* ++ * This function may be called before current_cpu_data is established. ++ * Hence a fallback is needed to prevent the loop below becoming infinite. ++ */ ++ clflush_size = current_cpu_data.x86_clflush_size ?: 16; ++ addr -= (unsigned long)addr & (clflush_size - 1); ++ for ( ; addr < end; addr += clflush_size ) ++ { ++/* ++ * The arguments to a macro must not include preprocessor directives. Doing so ++ * results in undefined behavior, so we have to create some defines here in ++ * order to avoid it. ++ */ ++#if defined(HAVE_AS_CLWB) ++# define CLWB_ENCODING "clwb %[p]" ++#elif defined(HAVE_AS_XSAVEOPT) ++# define CLWB_ENCODING "data16 xsaveopt %[p]" /* clwb */ ++#else ++# define CLWB_ENCODING ".byte 0x66, 0x0f, 0xae, 0x30" /* clwb (%%rax) */ ++#endif ++ ++#define BASE_INPUT(addr) [p] "m" (*(const char *)(addr)) ++#if defined(HAVE_AS_CLWB) || defined(HAVE_AS_XSAVEOPT) ++# define INPUT BASE_INPUT ++#else ++# define INPUT(addr) "a" (addr), BASE_INPUT(addr) ++#endif ++ ++ asm volatile (CLWB_ENCODING :: INPUT(addr)); ++ ++#undef INPUT ++#undef BASE_INPUT ++#undef CLWB_ENCODING ++ } ++ ++ asm volatile ("sfence" ::: "memory"); ++} ++ + unsigned int guest_flush_tlb_flags(const struct domain *d) + { + bool shadow = paging_mode_shadow(d); +diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c +index 66f8ce71741c..4c742cd8fe81 100644 +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -3431,7 +3431,7 @@ gnttab_swap_grant_ref(XEN_GUEST_HANDLE_PARAM(gnttab_swap_grant_ref_t) uop, + return 0; + } + +-static int cache_flush(const gnttab_cache_flush_t *cflush, grant_ref_t *cur_ref) ++static int _cache_flush(const gnttab_cache_flush_t *cflush, grant_ref_t *cur_ref) + { + struct domain *d, *owner; + struct page_info *page; +@@ -3525,7 +3525,7 @@ gnttab_cache_flush(XEN_GUEST_HANDLE_PARAM(gnttab_cache_flush_t) uop, + return -EFAULT; + for ( ; ; ) + { +- int ret = cache_flush(&op, cur_ref); ++ int ret = _cache_flush(&op, cur_ref); + + if ( ret < 0 ) + return ret; +diff --git a/xen/drivers/passthrough/vtd/extern.h b/xen/drivers/passthrough/vtd/extern.h +index 01e010a10d61..401079299725 100644 +--- a/xen/drivers/passthrough/vtd/extern.h ++++ b/xen/drivers/passthrough/vtd/extern.h +@@ -76,7 +76,6 @@ int __must_check qinval_device_iotlb_sync(struct vtd_iommu *iommu, + struct pci_dev *pdev, + u16 did, u16 size, u64 addr); + +-unsigned int get_cache_line_size(void); + void flush_all_cache(void); + + uint64_t alloc_pgtable_maddr(unsigned long npages, nodeid_t node); +diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c +index 8975c1de61bc..bc377c9bcfa4 100644 +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -31,6 +31,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -206,54 +207,6 @@ static void check_cleanup_domid_map(const struct domain *d, + } + } + +-static void sync_cache(const void *addr, unsigned int size) +-{ +- static unsigned long clflush_size = 0; +- const void *end = addr + size; +- +- if ( clflush_size == 0 ) +- clflush_size = get_cache_line_size(); +- +- addr -= (unsigned long)addr & (clflush_size - 1); +- for ( ; addr < end; addr += clflush_size ) +-/* +- * The arguments to a macro must not include preprocessor directives. Doing so +- * results in undefined behavior, so we have to create some defines here in +- * order to avoid it. +- */ +-#if defined(HAVE_AS_CLWB) +-# define CLWB_ENCODING "clwb %[p]" +-#elif defined(HAVE_AS_XSAVEOPT) +-# define CLWB_ENCODING "data16 xsaveopt %[p]" /* clwb */ +-#else +-# define CLWB_ENCODING ".byte 0x66, 0x0f, 0xae, 0x30" /* clwb (%%rax) */ +-#endif +- +-#define BASE_INPUT(addr) [p] "m" (*(const char *)(addr)) +-#if defined(HAVE_AS_CLWB) || defined(HAVE_AS_XSAVEOPT) +-# define INPUT BASE_INPUT +-#else +-# define INPUT(addr) "a" (addr), BASE_INPUT(addr) +-#endif +- /* +- * Note regarding the use of NOP_DS_PREFIX: it's faster to do a clflush +- * + prefix than a clflush + nop, and hence the prefix is added instead +- * of letting the alternative framework fill the gap by appending nops. +- */ +- alternative_io_2(".byte " __stringify(NOP_DS_PREFIX) "; clflush %[p]", +- "data16 clflush %[p]", /* clflushopt */ +- X86_FEATURE_CLFLUSHOPT, +- CLWB_ENCODING, +- X86_FEATURE_CLWB, /* no outputs */, +- INPUT(addr)); +-#undef INPUT +-#undef BASE_INPUT +-#undef CLWB_ENCODING +- +- alternative_2("", "sfence", X86_FEATURE_CLFLUSHOPT, +- "sfence", X86_FEATURE_CLWB); +-} +- + /* Allocate page table, return its machine address */ + uint64_t alloc_pgtable_maddr(unsigned long npages, nodeid_t node) + { +@@ -273,7 +226,7 @@ uint64_t alloc_pgtable_maddr(unsigned long npages, nodeid_t node) + clear_page(vaddr); + + if ( (iommu_ops.init ? &iommu_ops : &vtd_ops)->sync_cache ) +- sync_cache(vaddr, PAGE_SIZE); ++ cache_writeback(vaddr, PAGE_SIZE); + unmap_domain_page(vaddr); + cur_pg++; + } +@@ -1305,7 +1258,7 @@ int __init iommu_alloc(struct acpi_drhd_unit *drhd) + iommu->nr_pt_levels = agaw_to_level(agaw); + + if ( !ecap_coherent(iommu->ecap) ) +- vtd_ops.sync_cache = sync_cache; ++ vtd_ops.sync_cache = cache_writeback; + + /* allocate domain id bitmap */ + iommu->domid_bitmap = xzalloc_array(unsigned long, BITS_TO_LONGS(nr_dom)); +diff --git a/xen/drivers/passthrough/vtd/x86/vtd.c b/xen/drivers/passthrough/vtd/x86/vtd.c +index 6681dccd6970..55f0faa521cb 100644 +--- a/xen/drivers/passthrough/vtd/x86/vtd.c ++++ b/xen/drivers/passthrough/vtd/x86/vtd.c +@@ -47,11 +47,6 @@ void unmap_vtd_domain_page(const void *va) + unmap_domain_page(va); + } + +-unsigned int get_cache_line_size(void) +-{ +- return ((cpuid_ebx(1) >> 8) & 0xff) * 8; +-} +- + void flush_all_cache() + { + wbinvd(); +diff --git a/xen/include/asm-x86/cache.h b/xen/include/asm-x86/cache.h +index 1f7173d8c72c..e4770efb22b9 100644 +--- a/xen/include/asm-x86/cache.h ++++ b/xen/include/asm-x86/cache.h +@@ -11,4 +11,11 @@ + + #define __read_mostly __section(".data.read_mostly") + ++#ifndef __ASSEMBLY__ ++ ++void cache_flush(const void *addr, unsigned int size); ++void cache_writeback(const void *addr, unsigned int size); ++ ++#endif ++ + #endif diff --git a/xsa402-4.16-4.patch b/xsa402-4.16-4.patch new file mode 100644 index 0000000..2110922 --- /dev/null +++ b/xsa402-4.16-4.patch @@ -0,0 +1,83 @@ +From: Andrew Cooper +Subject: x86/amd: Work around CLFLUSH ordering on older parts + +On pre-CLFLUSHOPT AMD CPUs, CLFLUSH is weakely ordered with everything, +including reads and writes to the address, and LFENCE/SFENCE instructions. + +This creates a multitude of problematic corner cases, laid out in the manual. +Arrange to use MFENCE on both sides of the CLFLUSH to force proper ordering. + +This is part of XSA-402. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c +index a8e37dbb1f5c..b3b9a0df5fed 100644 +--- a/xen/arch/x86/cpu/amd.c ++++ b/xen/arch/x86/cpu/amd.c +@@ -812,6 +812,14 @@ static void init_amd(struct cpuinfo_x86 *c) + if (!cpu_has_lfence_dispatch) + __set_bit(X86_FEATURE_MFENCE_RDTSC, c->x86_capability); + ++ /* ++ * On pre-CLFLUSHOPT AMD CPUs, CLFLUSH is weakly ordered with ++ * everything, including reads and writes to address, and ++ * LFENCE/SFENCE instructions. ++ */ ++ if (!cpu_has_clflushopt) ++ setup_force_cpu_cap(X86_BUG_CLFLUSH_MFENCE); ++ + switch(c->x86) + { + case 0xf ... 0x11: +diff --git a/xen/arch/x86/flushtlb.c b/xen/arch/x86/flushtlb.c +index 0c912b8669f8..dcbb4064012e 100644 +--- a/xen/arch/x86/flushtlb.c ++++ b/xen/arch/x86/flushtlb.c +@@ -259,6 +259,13 @@ unsigned int flush_area_local(const void *va, unsigned int flags) + return flags; + } + ++/* ++ * On pre-CLFLUSHOPT AMD CPUs, CLFLUSH is weakly ordered with everything, ++ * including reads and writes to address, and LFENCE/SFENCE instructions. ++ * ++ * This function only works safely after alternatives have run. Luckily, at ++ * the time of writing, we don't flush the caches that early. ++ */ + void cache_flush(const void *addr, unsigned int size) + { + /* +@@ -268,6 +275,8 @@ void cache_flush(const void *addr, unsigned int size) + unsigned int clflush_size = current_cpu_data.x86_clflush_size ?: 16; + const void *end = addr + size; + ++ alternative("", "mfence", X86_BUG_CLFLUSH_MFENCE); ++ + addr -= (unsigned long)addr & (clflush_size - 1); + for ( ; addr < end; addr += clflush_size ) + { +@@ -283,7 +292,9 @@ void cache_flush(const void *addr, unsigned int size) + [p] "m" (*(const char *)(addr))); + } + +- alternative("", "sfence", X86_FEATURE_CLFLUSHOPT); ++ alternative_2("", ++ "sfence", X86_FEATURE_CLFLUSHOPT, ++ "mfence", X86_BUG_CLFLUSH_MFENCE); + } + + void cache_writeback(const void *addr, unsigned int size) +diff --git a/xen/include/asm-x86/cpufeatures.h b/xen/include/asm-x86/cpufeatures.h +index 7413febd7ad8..ff3157d52d13 100644 +--- a/xen/include/asm-x86/cpufeatures.h ++++ b/xen/include/asm-x86/cpufeatures.h +@@ -47,6 +47,7 @@ XEN_CPUFEATURE(XEN_IBT, X86_SYNTH(27)) /* Xen uses CET Indirect Branch + + #define X86_BUG_FPU_PTRS X86_BUG( 0) /* (F)X{SAVE,RSTOR} doesn't save/restore FOP/FIP/FDP. */ + #define X86_BUG_NULL_SEG X86_BUG( 1) /* NULL-ing a selector preserves the base and limit. */ ++#define X86_BUG_CLFLUSH_MFENCE X86_BUG( 2) /* MFENCE needed to serialise CLFLUSH */ + + /* Total number of capability words, inc synth and bug words. */ + #define NCAPINTS (FSCAPINTS + X86_NR_SYNTH + X86_NR_BUG) /* N 32-bit words worth of info */ diff --git a/xsa402-4.16-5.patch b/xsa402-4.16-5.patch new file mode 100644 index 0000000..4806d25 --- /dev/null +++ b/xsa402-4.16-5.patch @@ -0,0 +1,148 @@ +From: Andrew Cooper +Subject: x86/pv: Track and flush non-coherent mappings of RAM + +There are legitimate uses of WC mappings of RAM, e.g. for DMA buffers with +devices that make non-coherent writes. The Linux sound subsystem makes +extensive use of this technique. + +For such usecases, the guest's DMA buffer is mapped and consistently used as +WC, and Xen doesn't interact with the buffer. + +However, a mischevious guest can use WC mappings to deliberately create +non-coherency between the cache and RAM, and use this to trick Xen into +validating a pagetable which isn't actually safe. + +Allocate a new PGT_non_coherent to track the non-coherency of mappings. Set +it whenever a non-coherent writeable mapping is created. If the page is used +as anything other than PGT_writable_page, force a cache flush before +validation. Also force a cache flush before the page is returned to the heap. + +This is CVE-2022-26364, part of XSA-402. + +Reported-by: Jann Horn +Signed-off-by: Andrew Cooper +Reviewed-by: George Dunlap +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index ab32d13a1a0d..bab9624fabb7 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -997,6 +997,15 @@ get_page_from_l1e( + return -EACCES; + } + ++ /* ++ * Track writeable non-coherent mappings to RAM pages, to trigger a cache ++ * flush later if the target is used as anything but a PGT_writeable page. ++ * We care about all writeable mappings, including foreign mappings. ++ */ ++ if ( !boot_cpu_has(X86_FEATURE_XEN_SELFSNOOP) && ++ (l1f & (PAGE_CACHE_ATTRS | _PAGE_RW)) == (_PAGE_WC | _PAGE_RW) ) ++ set_bit(_PGT_non_coherent, &page->u.inuse.type_info); ++ + return 0; + + could_not_pin: +@@ -2454,6 +2463,19 @@ static int cleanup_page_mappings(struct page_info *page) + } + } + ++ /* ++ * Flush the cache if there were previously non-coherent writeable ++ * mappings of this page. This forces the page to be coherent before it ++ * is freed back to the heap. ++ */ ++ if ( __test_and_clear_bit(_PGT_non_coherent, &page->u.inuse.type_info) ) ++ { ++ void *addr = __map_domain_page(page); ++ ++ cache_flush(addr, PAGE_SIZE); ++ unmap_domain_page(addr); ++ } ++ + return rc; + } + +@@ -3028,6 +3050,22 @@ static int _get_page_type(struct page_info *page, unsigned long type, + if ( unlikely(!(nx & PGT_validated)) ) + { + /* ++ * Flush the cache if there were previously non-coherent mappings of ++ * this page, and we're trying to use it as anything other than a ++ * writeable page. This forces the page to be coherent before we ++ * validate its contents for safety. ++ */ ++ if ( (nx & PGT_non_coherent) && type != PGT_writable_page ) ++ { ++ void *addr = __map_domain_page(page); ++ ++ cache_flush(addr, PAGE_SIZE); ++ unmap_domain_page(addr); ++ ++ page->u.inuse.type_info &= ~PGT_non_coherent; ++ } ++ ++ /* + * No special validation needed for writable or shared pages. Page + * tables and GDT/LDT need to have their contents audited. + * +diff --git a/xen/arch/x86/pv/grant_table.c b/xen/arch/x86/pv/grant_table.c +index 0325618c9883..81c72e61ed55 100644 +--- a/xen/arch/x86/pv/grant_table.c ++++ b/xen/arch/x86/pv/grant_table.c +@@ -109,7 +109,17 @@ int create_grant_pv_mapping(uint64_t addr, mfn_t frame, + + ol1e = *pl1e; + if ( UPDATE_ENTRY(l1, pl1e, ol1e, nl1e, gl1mfn, curr, 0) ) ++ { ++ /* ++ * We always create mappings in this path. However, our caller, ++ * map_grant_ref(), only passes potentially non-zero cache_flags for ++ * MMIO frames, so this path doesn't create non-coherent mappings of ++ * RAM frames and there's no need to calculate PGT_non_coherent. ++ */ ++ ASSERT(!cache_flags || is_iomem_page(frame)); ++ + rc = GNTST_okay; ++ } + + out_unlock: + page_unlock(page); +@@ -294,7 +304,18 @@ int replace_grant_pv_mapping(uint64_t addr, mfn_t frame, + l1e_get_flags(ol1e), addr, grant_pte_flags); + + if ( UPDATE_ENTRY(l1, pl1e, ol1e, nl1e, gl1mfn, curr, 0) ) ++ { ++ /* ++ * Generally, replace_grant_pv_mapping() is used to destroy mappings ++ * (n1le = l1e_empty()), but it can be a present mapping on the ++ * GNTABOP_unmap_and_replace path. ++ * ++ * In such cases, the PTE is fully transplanted from its old location ++ * via steal_linear_addr(), so we need not perform PGT_non_coherent ++ * checking here. ++ */ + rc = GNTST_okay; ++ } + + out_unlock: + page_unlock(page); +diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h +index 8a9a43bb0a9d..7464167ae192 100644 +--- a/xen/include/asm-x86/mm.h ++++ b/xen/include/asm-x86/mm.h +@@ -53,8 +53,12 @@ + #define _PGT_partial PG_shift(8) + #define PGT_partial PG_mask(1, 8) + ++/* Has this page been mapped writeable with a non-coherent memory type? */ ++#define _PGT_non_coherent PG_shift(9) ++#define PGT_non_coherent PG_mask(1, 9) ++ + /* Count of uses of this frame as its current type. */ +-#define PGT_count_width PG_shift(8) ++#define PGT_count_width PG_shift(9) + #define PGT_count_mask ((1UL< Date: Mon, 13 Jun 2022 17:18:01 +0200 Subject: [PATCH 038/137] Rebuilt for Python 3.11 --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 9332a75..d62b768 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.16.1 -Release: 2%{?dist} +Release: 3%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -954,6 +954,9 @@ fi %endif %changelog +* Mon Jun 13 2022 Python Maint - 4.16.1-3 +- Rebuilt for Python 3.11 + * Sat Jun 11 2022 Michael Young - 4.16.1-2 - stop building for ix86 and armv7hl due to missing build dependency - x86 pv: Race condition in typeref acquisition [XSA-401, CVE-2022-26362] From 6cac8ee872b8dd7d8432d9efed1fe1b99121a5b5 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 21 Jun 2022 23:12:54 +0100 Subject: [PATCH 039/137] x86: MMIO Stale Data vulnerabilities [XSA-404, CVE-2022-21123, CVE-2022-21125, CVE-2022-21166] --- xen.spec | 14 ++- xsa404-4.16-1.patch | 239 ++++++++++++++++++++++++++++++++++++++++++++ xsa404-4.16-2.patch | 85 ++++++++++++++++ xsa404-4.16-3.patch | 177 ++++++++++++++++++++++++++++++++ 4 files changed, 513 insertions(+), 2 deletions(-) create mode 100644 xsa404-4.16-1.patch create mode 100644 xsa404-4.16-2.patch create mode 100644 xsa404-4.16-3.patch diff --git a/xen.spec b/xen.spec index d62b768..8ae2294 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.16.1 -Release: 3%{?dist} +Release: 4%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -119,6 +119,9 @@ Patch51: xen.git-b378ee56c7e0bb5eeb35dcc55b3d29e5f50eb566.patch Patch52: xsa402-4.16-3.patch Patch53: xsa402-4.16-4.patch Patch54: xsa402-4.16-5.patch +Patch55: xsa404-4.16-1.patch +Patch56: xsa404-4.16-2.patch +Patch57: xsa404-4.16-3.patch %if %build_qemutrad @@ -339,6 +342,9 @@ manage Xen virtual machines. %patch52 -p1 %patch53 -p1 %patch54 -p1 +%patch55 -p1 +%patch56 -p1 +%patch57 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -954,8 +960,12 @@ fi %endif %changelog +* Tue Jun 21 2022 Michael Young - 4.16.1-4 +- x86: MMIO Stale Data vulnerabilities [XSA-404, CVE-2022-21123, + CVE-2022-21125, CVE-2022-21166] + * Mon Jun 13 2022 Python Maint - 4.16.1-3 -- Rebuilt for Python 3.11 +- Rebuilt for Python 3.11 (F37 build only) * Sat Jun 11 2022 Michael Young - 4.16.1-2 - stop building for ix86 and armv7hl due to missing build dependency diff --git a/xsa404-4.16-1.patch b/xsa404-4.16-1.patch new file mode 100644 index 0000000..c101279 --- /dev/null +++ b/xsa404-4.16-1.patch @@ -0,0 +1,239 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Make VERW flushing runtime conditional + +Currently, VERW flushing to mitigate MDS is boot time conditional per domain +type. However, to provide mitigations for DRPW (CVE-2022-21166), we need to +conditionally use VERW based on the trustworthiness of the guest, and the +devices passed through. + +Remove the PV/HVM alternatives and instead issue a VERW on the return-to-guest +path depending on the SCF_verw bit in cpuinfo spec_ctrl_flags. + +Introduce spec_ctrl_init_domain() and d->arch.verw to calculate the VERW +disposition at domain creation time, and context switch the SCF_verw bit. + +For now, VERW flushing is used and controlled exactly as before, but later +patches will add per-domain cases too. + +No change in behaviour. + +This is part of XSA-404. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +Reviewed-by: Roger Pau Monné + +diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc +index 1d08fb7e9aa6..d5cb09f86541 100644 +--- a/docs/misc/xen-command-line.pandoc ++++ b/docs/misc/xen-command-line.pandoc +@@ -2258,9 +2258,8 @@ in place for guests to use. + Use of a positive boolean value for either of these options is invalid. + + The booleans `pv=`, `hvm=`, `msr-sc=`, `rsb=` and `md-clear=` offer fine +-grained control over the alternative blocks used by Xen. These impact Xen's +-ability to protect itself, and Xen's ability to virtualise support for guests +-to use. ++grained control over the primitives by Xen. These impact Xen's ability to ++protect itself, and Xen's ability to virtualise support for guests to use. + + * `pv=` and `hvm=` offer control over all suboptions for PV and HVM guests + respectively. +diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c +index ef1812dc1402..1fe6644a71ae 100644 +--- a/xen/arch/x86/domain.c ++++ b/xen/arch/x86/domain.c +@@ -863,6 +863,8 @@ int arch_domain_create(struct domain *d, + + d->arch.msr_relaxed = config->arch.misc_flags & XEN_X86_MSR_RELAXED; + ++ spec_ctrl_init_domain(d); ++ + return 0; + + fail: +@@ -2017,14 +2019,15 @@ static void __context_switch(void) + void context_switch(struct vcpu *prev, struct vcpu *next) + { + unsigned int cpu = smp_processor_id(); ++ struct cpu_info *info = get_cpu_info(); + const struct domain *prevd = prev->domain, *nextd = next->domain; + unsigned int dirty_cpu = read_atomic(&next->dirty_cpu); + + ASSERT(prev != next); + ASSERT(local_irq_is_enabled()); + +- get_cpu_info()->use_pv_cr3 = false; +- get_cpu_info()->xen_cr3 = 0; ++ info->use_pv_cr3 = false; ++ info->xen_cr3 = 0; + + if ( unlikely(dirty_cpu != cpu) && dirty_cpu != VCPU_CPU_CLEAN ) + { +@@ -2088,6 +2091,11 @@ void context_switch(struct vcpu *prev, struct vcpu *next) + *last_id = next_id; + } + } ++ ++ /* Update the top-of-stack block with the VERW disposition. */ ++ info->spec_ctrl_flags &= ~SCF_verw; ++ if ( nextd->arch.verw ) ++ info->spec_ctrl_flags |= SCF_verw; + } + + sched_context_switched(prev, next); +diff --git a/xen/arch/x86/hvm/vmx/entry.S b/xen/arch/x86/hvm/vmx/entry.S +index 49651f3c435a..5f5de45a1309 100644 +--- a/xen/arch/x86/hvm/vmx/entry.S ++++ b/xen/arch/x86/hvm/vmx/entry.S +@@ -87,7 +87,7 @@ UNLIKELY_END(realmode) + + /* WARNING! `ret`, `call *`, `jmp *` not safe beyond this point. */ + /* SPEC_CTRL_EXIT_TO_VMX Req: %rsp=regs/cpuinfo Clob: */ +- ALTERNATIVE "", __stringify(verw CPUINFO_verw_sel(%rsp)), X86_FEATURE_SC_VERW_HVM ++ DO_SPEC_CTRL_COND_VERW + + mov VCPU_hvm_guest_cr2(%rbx),%rax + +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index c19464da70ce..21730aa03071 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -36,8 +36,8 @@ static bool __initdata opt_msr_sc_pv = true; + static bool __initdata opt_msr_sc_hvm = true; + static int8_t __initdata opt_rsb_pv = -1; + static bool __initdata opt_rsb_hvm = true; +-static int8_t __initdata opt_md_clear_pv = -1; +-static int8_t __initdata opt_md_clear_hvm = -1; ++static int8_t __read_mostly opt_md_clear_pv = -1; ++static int8_t __read_mostly opt_md_clear_hvm = -1; + + /* Cmdline controls for Xen's speculative settings. */ + static enum ind_thunk { +@@ -932,6 +932,13 @@ static __init void mds_calculations(uint64_t caps) + } + } + ++void spec_ctrl_init_domain(struct domain *d) ++{ ++ bool pv = is_pv_domain(d); ++ ++ d->arch.verw = pv ? opt_md_clear_pv : opt_md_clear_hvm; ++} ++ + void __init init_speculation_mitigations(void) + { + enum ind_thunk thunk = THUNK_DEFAULT; +@@ -1196,21 +1203,20 @@ void __init init_speculation_mitigations(void) + boot_cpu_has(X86_FEATURE_MD_CLEAR)); + + /* +- * Enable MDS defences as applicable. The PV blocks need using all the +- * time, and the Idle blocks need using if either PV or HVM defences are +- * used. ++ * Enable MDS defences as applicable. The Idle blocks need using if ++ * either PV or HVM defences are used. + * + * HVM is more complicated. The MD_CLEAR microcode extends L1D_FLUSH with +- * equivelent semantics to avoid needing to perform both flushes on the +- * HVM path. The HVM blocks don't need activating if our hypervisor told +- * us it was handling L1D_FLUSH, or we are using L1D_FLUSH ourselves. ++ * equivalent semantics to avoid needing to perform both flushes on the ++ * HVM path. Therefore, we don't need VERW in addition to L1D_FLUSH. ++ * ++ * After calculating the appropriate idle setting, simplify ++ * opt_md_clear_hvm to mean just "should we VERW on the way into HVM ++ * guests", so spec_ctrl_init_domain() can calculate suitable settings. + */ +- if ( opt_md_clear_pv ) +- setup_force_cpu_cap(X86_FEATURE_SC_VERW_PV); + if ( opt_md_clear_pv || opt_md_clear_hvm ) + setup_force_cpu_cap(X86_FEATURE_SC_VERW_IDLE); +- if ( opt_md_clear_hvm && !(caps & ARCH_CAPS_SKIP_L1DFL) && !opt_l1d_flush ) +- setup_force_cpu_cap(X86_FEATURE_SC_VERW_HVM); ++ opt_md_clear_hvm &= !(caps & ARCH_CAPS_SKIP_L1DFL) && !opt_l1d_flush; + + /* + * Warn the user if they are on MLPDS/MFBDS-vulnerable hardware with HT +diff --git a/xen/include/asm-x86/cpufeatures.h b/xen/include/asm-x86/cpufeatures.h +index ff3157d52d13..bd45a144ee78 100644 +--- a/xen/include/asm-x86/cpufeatures.h ++++ b/xen/include/asm-x86/cpufeatures.h +@@ -35,8 +35,7 @@ XEN_CPUFEATURE(SC_RSB_HVM, X86_SYNTH(19)) /* RSB overwrite needed for HVM + XEN_CPUFEATURE(XEN_SELFSNOOP, X86_SYNTH(20)) /* SELFSNOOP gets used by Xen itself */ + XEN_CPUFEATURE(SC_MSR_IDLE, X86_SYNTH(21)) /* (SC_MSR_PV || SC_MSR_HVM) && default_xen_spec_ctrl */ + XEN_CPUFEATURE(XEN_LBR, X86_SYNTH(22)) /* Xen uses MSR_DEBUGCTL.LBR */ +-XEN_CPUFEATURE(SC_VERW_PV, X86_SYNTH(23)) /* VERW used by Xen for PV */ +-XEN_CPUFEATURE(SC_VERW_HVM, X86_SYNTH(24)) /* VERW used by Xen for HVM */ ++/* Bits 23,24 unused. */ + XEN_CPUFEATURE(SC_VERW_IDLE, X86_SYNTH(25)) /* VERW used by Xen for idle */ + XEN_CPUFEATURE(XEN_SHSTK, X86_SYNTH(26)) /* Xen uses CET Shadow Stacks */ + XEN_CPUFEATURE(XEN_IBT, X86_SYNTH(27)) /* Xen uses CET Indirect Branch Tracking */ +diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h +index 92d54de0b9a1..2398a1d99da9 100644 +--- a/xen/include/asm-x86/domain.h ++++ b/xen/include/asm-x86/domain.h +@@ -319,6 +319,9 @@ struct arch_domain + uint32_t pci_cf8; + uint8_t cmos_idx; + ++ /* Use VERW on return-to-guest for its flushing side effect. */ ++ bool verw; ++ + union { + struct pv_domain pv; + struct hvm_domain hvm; +diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h +index f76029523610..751355f471f4 100644 +--- a/xen/include/asm-x86/spec_ctrl.h ++++ b/xen/include/asm-x86/spec_ctrl.h +@@ -24,6 +24,7 @@ + #define SCF_use_shadow (1 << 0) + #define SCF_ist_wrmsr (1 << 1) + #define SCF_ist_rsb (1 << 2) ++#define SCF_verw (1 << 3) + + #ifndef __ASSEMBLY__ + +@@ -32,6 +33,7 @@ + #include + + void init_speculation_mitigations(void); ++void spec_ctrl_init_domain(struct domain *d); + + extern bool opt_ibpb; + extern bool opt_ssbd; +diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h +index 02b3b18ce69f..5a590bac44aa 100644 +--- a/xen/include/asm-x86/spec_ctrl_asm.h ++++ b/xen/include/asm-x86/spec_ctrl_asm.h +@@ -136,6 +136,19 @@ + #endif + .endm + ++.macro DO_SPEC_CTRL_COND_VERW ++/* ++ * Requires %rsp=cpuinfo ++ * ++ * Issue a VERW for its flushing side effect, if indicated. This is a Spectre ++ * v1 gadget, but the IRET/VMEntry is serialising. ++ */ ++ testb $SCF_verw, CPUINFO_spec_ctrl_flags(%rsp) ++ jz .L\@_verw_skip ++ verw CPUINFO_verw_sel(%rsp) ++.L\@_verw_skip: ++.endm ++ + .macro DO_SPEC_CTRL_ENTRY maybexen:req + /* + * Requires %rsp=regs (also cpuinfo if !maybexen) +@@ -231,8 +244,7 @@ + #define SPEC_CTRL_EXIT_TO_PV \ + ALTERNATIVE "", \ + DO_SPEC_CTRL_EXIT_TO_GUEST, X86_FEATURE_SC_MSR_PV; \ +- ALTERNATIVE "", __stringify(verw CPUINFO_verw_sel(%rsp)), \ +- X86_FEATURE_SC_VERW_PV ++ DO_SPEC_CTRL_COND_VERW + + /* + * Use in IST interrupt/exception context. May interrupt Xen or PV context. diff --git a/xsa404-4.16-2.patch b/xsa404-4.16-2.patch new file mode 100644 index 0000000..732b26a --- /dev/null +++ b/xsa404-4.16-2.patch @@ -0,0 +1,85 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Enumeration for MMIO Stale Data controls + +The three *_NO bits indicate non-susceptibility to the SSDP, FBSDP and PSDP +data movement primitives. + +FB_CLEAR indicates that the VERW instruction has re-gained it's Fill Buffer +flushing side effect. This is only enumerated on parts where VERW had +previously lost it's flushing side effect due to the MDS/TAA vulnerabilities +being fixed in hardware. + +FB_CLEAR_CTRL is available on a subset of FB_CLEAR parts where the Fill Buffer +clearing side effect of VERW can be turned off for performance reasons. + +This is part of XSA-404. + +Signed-off-by: Andrew Cooper +Reviewed-by: Roger Pau Monné + +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 21730aa03071..d285538bde9f 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -323,7 +323,7 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + * Hardware read-only information, stating immunity to certain issues, or + * suggestions of which mitigation to use. + */ +- printk(" Hardware hints:%s%s%s%s%s%s%s%s%s%s%s\n", ++ printk(" Hardware hints:%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", + (caps & ARCH_CAPS_RDCL_NO) ? " RDCL_NO" : "", + (caps & ARCH_CAPS_IBRS_ALL) ? " IBRS_ALL" : "", + (caps & ARCH_CAPS_RSBA) ? " RSBA" : "", +@@ -332,13 +332,16 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + (caps & ARCH_CAPS_SSB_NO) ? " SSB_NO" : "", + (caps & ARCH_CAPS_MDS_NO) ? " MDS_NO" : "", + (caps & ARCH_CAPS_TAA_NO) ? " TAA_NO" : "", ++ (caps & ARCH_CAPS_SBDR_SSDP_NO) ? " SBDR_SSDP_NO" : "", ++ (caps & ARCH_CAPS_FBSDP_NO) ? " FBSDP_NO" : "", ++ (caps & ARCH_CAPS_PSDP_NO) ? " PSDP_NO" : "", + (e8b & cpufeat_mask(X86_FEATURE_IBRS_ALWAYS)) ? " IBRS_ALWAYS" : "", + (e8b & cpufeat_mask(X86_FEATURE_STIBP_ALWAYS)) ? " STIBP_ALWAYS" : "", + (e8b & cpufeat_mask(X86_FEATURE_IBRS_FAST)) ? " IBRS_FAST" : "", + (e8b & cpufeat_mask(X86_FEATURE_IBRS_SAME_MODE)) ? " IBRS_SAME_MODE" : ""); + + /* Hardware features which need driving to mitigate issues. */ +- printk(" Hardware features:%s%s%s%s%s%s%s%s%s%s\n", ++ printk(" Hardware features:%s%s%s%s%s%s%s%s%s%s%s%s\n", + (e8b & cpufeat_mask(X86_FEATURE_IBPB)) || + (_7d0 & cpufeat_mask(X86_FEATURE_IBRSB)) ? " IBPB" : "", + (e8b & cpufeat_mask(X86_FEATURE_IBRS)) || +@@ -353,7 +356,9 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + (_7d0 & cpufeat_mask(X86_FEATURE_MD_CLEAR)) ? " MD_CLEAR" : "", + (_7d0 & cpufeat_mask(X86_FEATURE_SRBDS_CTRL)) ? " SRBDS_CTRL" : "", + (e8b & cpufeat_mask(X86_FEATURE_VIRT_SSBD)) ? " VIRT_SSBD" : "", +- (caps & ARCH_CAPS_TSX_CTRL) ? " TSX_CTRL" : ""); ++ (caps & ARCH_CAPS_TSX_CTRL) ? " TSX_CTRL" : "", ++ (caps & ARCH_CAPS_FB_CLEAR) ? " FB_CLEAR" : "", ++ (caps & ARCH_CAPS_FB_CLEAR_CTRL) ? " FB_CLEAR_CTRL" : ""); + + /* Compiled-in support which pertains to mitigations. */ + if ( IS_ENABLED(CONFIG_INDIRECT_THUNK) || IS_ENABLED(CONFIG_SHADOW_PAGING) ) +diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h +index 31964b88af7a..72bc32ba04ff 100644 +--- a/xen/include/asm-x86/msr-index.h ++++ b/xen/include/asm-x86/msr-index.h +@@ -66,6 +66,11 @@ + #define ARCH_CAPS_IF_PSCHANGE_MC_NO (_AC(1, ULL) << 6) + #define ARCH_CAPS_TSX_CTRL (_AC(1, ULL) << 7) + #define ARCH_CAPS_TAA_NO (_AC(1, ULL) << 8) ++#define ARCH_CAPS_SBDR_SSDP_NO (_AC(1, ULL) << 13) ++#define ARCH_CAPS_FBSDP_NO (_AC(1, ULL) << 14) ++#define ARCH_CAPS_PSDP_NO (_AC(1, ULL) << 15) ++#define ARCH_CAPS_FB_CLEAR (_AC(1, ULL) << 17) ++#define ARCH_CAPS_FB_CLEAR_CTRL (_AC(1, ULL) << 18) + + #define MSR_FLUSH_CMD 0x0000010b + #define FLUSH_CMD_L1D (_AC(1, ULL) << 0) +@@ -83,6 +88,7 @@ + #define MCU_OPT_CTRL_RNGDS_MITG_DIS (_AC(1, ULL) << 0) + #define MCU_OPT_CTRL_RTM_ALLOW (_AC(1, ULL) << 1) + #define MCU_OPT_CTRL_RTM_LOCKED (_AC(1, ULL) << 2) ++#define MCU_OPT_CTRL_FB_CLEAR_DIS (_AC(1, ULL) << 3) + + #define MSR_RTIT_OUTPUT_BASE 0x00000560 + #define MSR_RTIT_OUTPUT_MASK 0x00000561 diff --git a/xsa404-4.16-3.patch b/xsa404-4.16-3.patch new file mode 100644 index 0000000..dab0c31 --- /dev/null +++ b/xsa404-4.16-3.patch @@ -0,0 +1,177 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Add spec-ctrl=unpriv-mmio + +Per Xen's support statement, PCI passthrough should be to trusted domains +because the overall system security depends on factors outside of Xen's +control. + +As such, Xen, in a supported configuration, is not vulnerable to DRPW/SBDR. + +However, users who have risk assessed their configuration may be happy with +the risk of DoS, but unhappy with the risk of cross-domain data leakage. Such +users should enable this option. + +On CPUs vulnerable to MDS, the existing mitigations are the best we can do to +mitigate MMIO cross-domain data leakage. + +On CPUs fixed to MDS but vulnerable MMIO stale data leakage, this option: + + * On CPUs susceptible to FBSDP, mitigates cross-domain fill buffer leakage + using FB_CLEAR. + * On CPUs susceptible to SBDR, mitigates RNG data recovery by engaging the + srb-lock, previously used to mitigate SRBDS. + +Both mitigations require microcode from IPU 2022.1, May 2022. + +This is part of XSA-404. + +Signed-off-by: Andrew Cooper +Reviewed-by: Roger Pau Monné +--- +Backporting note: For Xen 4.7 and earlier with bool_t not aliasing bool, the +ARCH_CAPS_FB_CLEAR hunk needs !! + +diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc +index d5cb09f86541..a642e43476a2 100644 +--- a/docs/misc/xen-command-line.pandoc ++++ b/docs/misc/xen-command-line.pandoc +@@ -2235,7 +2235,7 @@ By default SSBD will be mitigated at runtime (i.e `ssbd=runtime`). + ### spec-ctrl (x86) + > `= List of [ , xen=, {pv,hvm,msr-sc,rsb,md-clear}=, + > bti-thunk=retpoline|lfence|jmp, {ibrs,ibpb,ssbd,eager-fpu, +-> l1d-flush,branch-harden,srb-lock}= ]` ++> l1d-flush,branch-harden,srb-lock,unpriv-mmio}= ]` + + Controls for speculative execution sidechannel mitigations. By default, Xen + will pick the most appropriate mitigations based on compiled in support, +@@ -2314,8 +2314,16 @@ Xen will enable this mitigation. + On hardware supporting SRBDS_CTRL, the `srb-lock=` option can be used to force + or prevent Xen from protect the Special Register Buffer from leaking stale + data. By default, Xen will enable this mitigation, except on parts where MDS +-is fixed and TAA is fixed/mitigated (in which case, there is believed to be no +-way for an attacker to obtain the stale data). ++is fixed and TAA is fixed/mitigated and there are no unprivileged MMIO ++mappings (in which case, there is believed to be no way for an attacker to ++obtain stale data). ++ ++The `unpriv-mmio=` boolean indicates whether the system has (or will have) ++less than fully privileged domains granted access to MMIO devices. By ++default, this option is disabled. If enabled, Xen will use the `FB_CLEAR` ++and/or `SRBDS_CTRL` functionality available in the Intel May 2022 microcode ++release to mitigate cross-domain leakage of data via the MMIO Stale Data ++vulnerabilities. + + ### sync_console + > `= ` +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index d285538bde9f..099113ba41e6 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -67,6 +67,8 @@ static bool __initdata cpu_has_bug_msbds_only; /* => minimal HT impact. */ + static bool __initdata cpu_has_bug_mds; /* Any other M{LP,SB,FB}DS combination. */ + + static int8_t __initdata opt_srb_lock = -1; ++static bool __initdata opt_unpriv_mmio; ++static bool __read_mostly opt_fb_clear_mmio; + + static int __init parse_spec_ctrl(const char *s) + { +@@ -184,6 +186,8 @@ static int __init parse_spec_ctrl(const char *s) + opt_branch_harden = val; + else if ( (val = parse_boolean("srb-lock", s, ss)) >= 0 ) + opt_srb_lock = val; ++ else if ( (val = parse_boolean("unpriv-mmio", s, ss)) >= 0 ) ++ opt_unpriv_mmio = val; + else + rc = -EINVAL; + +@@ -392,7 +396,8 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + opt_srb_lock ? " SRB_LOCK+" : " SRB_LOCK-", + opt_ibpb ? " IBPB" : "", + opt_l1d_flush ? " L1D_FLUSH" : "", +- opt_md_clear_pv || opt_md_clear_hvm ? " VERW" : "", ++ opt_md_clear_pv || opt_md_clear_hvm || ++ opt_fb_clear_mmio ? " VERW" : "", + opt_branch_harden ? " BRANCH_HARDEN" : ""); + + /* L1TF diagnostics, printed if vulnerable or PV shadowing is in use. */ +@@ -941,7 +946,9 @@ void spec_ctrl_init_domain(struct domain *d) + { + bool pv = is_pv_domain(d); + +- d->arch.verw = pv ? opt_md_clear_pv : opt_md_clear_hvm; ++ d->arch.verw = ++ (pv ? opt_md_clear_pv : opt_md_clear_hvm) || ++ (opt_fb_clear_mmio && is_iommu_enabled(d)); + } + + void __init init_speculation_mitigations(void) +@@ -1196,6 +1203,18 @@ void __init init_speculation_mitigations(void) + mds_calculations(caps); + + /* ++ * Parts which enumerate FB_CLEAR are those which are post-MDS_NO and have ++ * reintroduced the VERW fill buffer flushing side effect because of a ++ * susceptibility to FBSDP. ++ * ++ * If unprivileged guests have (or will have) MMIO mappings, we can ++ * mitigate cross-domain leakage of fill buffer data by issuing VERW on ++ * the return-to-guest path. ++ */ ++ if ( opt_unpriv_mmio ) ++ opt_fb_clear_mmio = caps & ARCH_CAPS_FB_CLEAR; ++ ++ /* + * By default, enable PV and HVM mitigations on MDS-vulnerable hardware. + * This will only be a token effort for MLPDS/MFBDS when HT is enabled, + * but it is somewhat better than nothing. +@@ -1208,18 +1227,20 @@ void __init init_speculation_mitigations(void) + boot_cpu_has(X86_FEATURE_MD_CLEAR)); + + /* +- * Enable MDS defences as applicable. The Idle blocks need using if +- * either PV or HVM defences are used. ++ * Enable MDS/MMIO defences as applicable. The Idle blocks need using if ++ * either the PV or HVM MDS defences are used, or if we may give MMIO ++ * access to untrusted guests. + * + * HVM is more complicated. The MD_CLEAR microcode extends L1D_FLUSH with + * equivalent semantics to avoid needing to perform both flushes on the +- * HVM path. Therefore, we don't need VERW in addition to L1D_FLUSH. ++ * HVM path. Therefore, we don't need VERW in addition to L1D_FLUSH (for ++ * MDS mitigations. L1D_FLUSH is not safe for MMIO mitigations.) + * + * After calculating the appropriate idle setting, simplify + * opt_md_clear_hvm to mean just "should we VERW on the way into HVM + * guests", so spec_ctrl_init_domain() can calculate suitable settings. + */ +- if ( opt_md_clear_pv || opt_md_clear_hvm ) ++ if ( opt_md_clear_pv || opt_md_clear_hvm || opt_fb_clear_mmio ) + setup_force_cpu_cap(X86_FEATURE_SC_VERW_IDLE); + opt_md_clear_hvm &= !(caps & ARCH_CAPS_SKIP_L1DFL) && !opt_l1d_flush; + +@@ -1284,14 +1305,19 @@ void __init init_speculation_mitigations(void) + * On some SRBDS-affected hardware, it may be safe to relax srb-lock by + * default. + * +- * On parts which enumerate MDS_NO and not TAA_NO, TSX is the only known +- * way to access the Fill Buffer. If TSX isn't available (inc. SKU +- * reasons on some models), or TSX is explicitly disabled, then there is +- * no need for the extra overhead to protect RDRAND/RDSEED. ++ * All parts with SRBDS_CTRL suffer SSDP, the mechanism by which stale RNG ++ * data becomes available to other contexts. To recover the data, an ++ * attacker needs to use: ++ * - SBDS (MDS or TAA to sample the cores fill buffer) ++ * - SBDR (Architecturally retrieve stale transaction buffer contents) ++ * - DRPW (Architecturally latch stale fill buffer data) ++ * ++ * On MDS_NO parts, and with TAA_NO or TSX unavailable/disabled, and there ++ * is no unprivileged MMIO access, the RNG data doesn't need protecting. + */ + if ( cpu_has_srbds_ctrl ) + { +- if ( opt_srb_lock == -1 && ++ if ( opt_srb_lock == -1 && !opt_unpriv_mmio && + (caps & (ARCH_CAPS_MDS_NO|ARCH_CAPS_TAA_NO)) == ARCH_CAPS_MDS_NO && + (!cpu_has_hle || ((caps & ARCH_CAPS_TSX_CTRL) && rtm_disabled)) ) + opt_srb_lock = 0; From 9381fafa32e594a7a211a5c11120c395f2b9a100 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 6 Jul 2022 12:30:05 +0100 Subject: [PATCH 040/137] Linux disk/nic frontends data leaks [XSA-403, CVE-2022-26365, CVE-2022-33740, CVE-2022-33741, CVE-2022-3374] --- xen.spec | 8 ++++++- xsa403-4.16-1.patch | 56 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 xsa403-4.16-1.patch diff --git a/xen.spec b/xen.spec index 8ae2294..712a927 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.16.1 -Release: 4%{?dist} +Release: 5%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -122,6 +122,7 @@ Patch54: xsa402-4.16-5.patch Patch55: xsa404-4.16-1.patch Patch56: xsa404-4.16-2.patch Patch57: xsa404-4.16-3.patch +Patch58: xsa403-4.16-1.patch %if %build_qemutrad @@ -345,6 +346,7 @@ manage Xen virtual machines. %patch55 -p1 %patch56 -p1 %patch57 -p1 +%patch58 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -960,6 +962,10 @@ fi %endif %changelog +* Tue Jul 05 2022 Michael Young - 4.16.1-5 +- Linux disk/nic frontends data leaks [XSA-403, CVE-2022-26365, + CVE-2022-33740, CVE-2022-33741, CVE-2022-3374] + * Tue Jun 21 2022 Michael Young - 4.16.1-4 - x86: MMIO Stale Data vulnerabilities [XSA-404, CVE-2022-21123, CVE-2022-21125, CVE-2022-21166] diff --git a/xsa403-4.16-1.patch b/xsa403-4.16-1.patch new file mode 100644 index 0000000..f334848 --- /dev/null +++ b/xsa403-4.16-1.patch @@ -0,0 +1,56 @@ +From 8c6791798de4b68923e422f565581af1ee74124c Mon Sep 17 00:00:00 2001 +From: Roger Pau Monne +Date: Thu, 30 Jun 2022 14:35:35 +0200 +Subject: [PATCH] tools/libxl: env variable to signal whether disk/nic backend + is trusted +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Introduce support in libxl for fetching the default backend trusted +option for disk and nic devices. + +Users can set libxl_{disk,nic}_backend_untrusted environment variable +to notify libxl of whether the backends for disk and nic devices +should be trusted. Such information is passed into the frontend so it +can take the appropriate measures. + +This is part of XSA-403. + +Signed-off-by: Roger Pau Monné +--- + tools/libs/light/libxl_disk.c | 3 +++ + tools/libs/light/libxl_nic.c | 3 +++ + 2 files changed, 6 insertions(+) + +diff --git a/tools/libs/light/libxl_disk.c b/tools/libs/light/libxl_disk.c +index 93936d0dd0..0aaf6afce8 100644 +--- a/tools/libs/light/libxl_disk.c ++++ b/tools/libs/light/libxl_disk.c +@@ -395,6 +395,9 @@ static void device_disk_add(libxl__egc *egc, uint32_t domid, + flexarray_append(front, GCSPRINTF("%d", device->devid)); + flexarray_append(front, "device-type"); + flexarray_append(front, disk->is_cdrom ? "cdrom" : "disk"); ++ flexarray_append(front, "trusted"); ++ flexarray_append(front, getenv("libxl_disk_backend_untrusted") ? "0" ++ : "1"); + + /* + * Old PV kernel disk frontends before 2.6.26 rely on tool stack to +diff --git a/tools/libs/light/libxl_nic.c b/tools/libs/light/libxl_nic.c +index 0b9e70c9d1..34c3fe6df0 100644 +--- a/tools/libs/light/libxl_nic.c ++++ b/tools/libs/light/libxl_nic.c +@@ -255,6 +255,9 @@ static int libxl__set_xenstore_nic(libxl__gc *gc, uint32_t domid, + flexarray_append(back, "hotplug-status"); + flexarray_append(back, ""); + ++ flexarray_append(front, "trusted"); ++ flexarray_append(front, getenv("libxl_nic_backend_untrusted") ? "0" : "1"); ++ + return 0; + } + +-- +2.37.0 + From a743b1060882168e09b55e4d575d624de75dc6ab Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 7 Jul 2022 09:41:58 +0100 Subject: [PATCH 041/137] Add bug reference, fix CVE reference --- xen.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 712a927..c849eb2 100644 --- a/xen.spec +++ b/xen.spec @@ -964,7 +964,7 @@ fi %changelog * Tue Jul 05 2022 Michael Young - 4.16.1-5 - Linux disk/nic frontends data leaks [XSA-403, CVE-2022-26365, - CVE-2022-33740, CVE-2022-33741, CVE-2022-3374] + CVE-2022-33740, CVE-2022-33741, CVE-2022-33742] (#2104747) * Tue Jun 21 2022 Michael Young - 4.16.1-4 - x86: MMIO Stale Data vulnerabilities [XSA-404, CVE-2022-21123, From ab3164079ee42ece7c49aecd70ee3d5e551f96f7 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 12 Jul 2022 21:34:09 +0100 Subject: [PATCH 042/137] Retbleed - arbitrary speculative code execution with return instructions [XSA-407, CVE-2022-23816, CVE-2022-23825, CVE-2022-29900] --- ...33f4c80b7eaf9fb4e36ebba8259580857a9d.patch | 93 ++++++ ...a8176ccc4ff7dfe3c06969af9ebfab0d7b04.patch | 233 ++++++++++++++ ...b02403a9df2523527caad24f17af5060fbe7.patch | 86 +++++ ...c8bb3e5686141cef6fa1dc86ea9f37c5388a.patch | 137 ++++++++ xen.spec | 30 +- xsa407-4.16-1.patch | 153 +++++++++ xsa407-4.16-2.patch | 99 ++++++ xsa407-4.16-3.patch | 86 +++++ xsa407-4.16-4.patch | 96 ++++++ xsa407-4.16-5.patch | 285 +++++++++++++++++ xsa407-4.16-6.patch | 93 ++++++ xsa407-4.16-7.patch | 94 ++++++ xsa407-4.16-8.patch | 293 ++++++++++++++++++ 13 files changed, 1777 insertions(+), 1 deletion(-) create mode 100644 xen.git-09d533f4c80b7eaf9fb4e36ebba8259580857a9d.patch create mode 100644 xen.git-db6ca8176ccc4ff7dfe3c06969af9ebfab0d7b04.patch create mode 100644 xen.git-eec5b02403a9df2523527caad24f17af5060fbe7.patch create mode 100644 xen.git-f066c8bb3e5686141cef6fa1dc86ea9f37c5388a.patch create mode 100644 xsa407-4.16-1.patch create mode 100644 xsa407-4.16-2.patch create mode 100644 xsa407-4.16-3.patch create mode 100644 xsa407-4.16-4.patch create mode 100644 xsa407-4.16-5.patch create mode 100644 xsa407-4.16-6.patch create mode 100644 xsa407-4.16-7.patch create mode 100644 xsa407-4.16-8.patch diff --git a/xen.git-09d533f4c80b7eaf9fb4e36ebba8259580857a9d.patch b/xen.git-09d533f4c80b7eaf9fb4e36ebba8259580857a9d.patch new file mode 100644 index 0000000..1285967 --- /dev/null +++ b/xen.git-09d533f4c80b7eaf9fb4e36ebba8259580857a9d.patch @@ -0,0 +1,93 @@ +From 09d533f4c80b7eaf9fb4e36ebba8259580857a9d Mon Sep 17 00:00:00 2001 +From: Andrew Cooper +Date: Tue, 12 Jul 2022 11:12:46 +0200 +Subject: [PATCH] x86/spec-ctrl: Only adjust MSR_SPEC_CTRL for idle with legacy + IBRS +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf8 +Content-Transfer-Encoding: 8bit + +Back at the time of the original Spectre-v2 fixes, it was recommended to clear +MSR_SPEC_CTRL when going idle. This is because of the side effects on the +sibling thread caused by the microcode IBRS and STIBP implementations which +were retrofitted to existing CPUs. + +However, there are no relevant cross-thread impacts for the hardware +IBRS/STIBP implementations, so this logic should not be used on Intel CPUs +supporting eIBRS, or any AMD CPUs; doing so only adds unnecessary latency to +the idle path. + +Furthermore, there's no point playing with MSR_SPEC_CTRL in the idle paths if +SMT is disabled for other reasons. + +Fixes: 8d03080d2a33 ("x86/spec-ctrl: Cease using thunk=lfence on AMD") +Signed-off-by: Andrew Cooper +Reviewed-by: Roger Pau Monné +master commit: ffc7694e0c99eea158c32aa164b7d1e1bb1dc46b +master date: 2022-06-30 18:07:13 +0100 +--- + xen/arch/x86/spec_ctrl.c | 10 ++++++++-- + xen/include/asm-x86/cpufeatures.h | 2 +- + xen/include/asm-x86/spec_ctrl.h | 5 +++-- + 3 files changed, 12 insertions(+), 5 deletions(-) + +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 099113ba41..1ed5ceda8b 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -1150,8 +1150,14 @@ void __init init_speculation_mitigations(void) + /* (Re)init BSP state now that default_spec_ctrl_flags has been calculated. */ + init_shadow_spec_ctrl_state(); + +- /* If Xen is using any MSR_SPEC_CTRL settings, adjust the idle path. */ +- if ( default_xen_spec_ctrl ) ++ /* ++ * For microcoded IBRS only (i.e. Intel, pre eIBRS), it is recommended to ++ * clear MSR_SPEC_CTRL before going idle, to avoid impacting sibling ++ * threads. Activate this if SMT is enabled, and Xen is using a non-zero ++ * MSR_SPEC_CTRL setting. ++ */ ++ if ( boot_cpu_has(X86_FEATURE_IBRSB) && !(caps & ARCH_CAPS_IBRS_ALL) && ++ hw_smt_enabled && default_xen_spec_ctrl ) + setup_force_cpu_cap(X86_FEATURE_SC_MSR_IDLE); + + xpti_init_default(caps); +diff --git a/xen/include/asm-x86/cpufeatures.h b/xen/include/asm-x86/cpufeatures.h +index bd45a144ee..493d338a08 100644 +--- a/xen/include/asm-x86/cpufeatures.h ++++ b/xen/include/asm-x86/cpufeatures.h +@@ -33,7 +33,7 @@ XEN_CPUFEATURE(SC_MSR_HVM, X86_SYNTH(17)) /* MSR_SPEC_CTRL used by Xen fo + XEN_CPUFEATURE(SC_RSB_PV, X86_SYNTH(18)) /* RSB overwrite needed for PV */ + XEN_CPUFEATURE(SC_RSB_HVM, X86_SYNTH(19)) /* RSB overwrite needed for HVM */ + XEN_CPUFEATURE(XEN_SELFSNOOP, X86_SYNTH(20)) /* SELFSNOOP gets used by Xen itself */ +-XEN_CPUFEATURE(SC_MSR_IDLE, X86_SYNTH(21)) /* (SC_MSR_PV || SC_MSR_HVM) && default_xen_spec_ctrl */ ++XEN_CPUFEATURE(SC_MSR_IDLE, X86_SYNTH(21)) /* Clear MSR_SPEC_CTRL on idle */ + XEN_CPUFEATURE(XEN_LBR, X86_SYNTH(22)) /* Xen uses MSR_DEBUGCTL.LBR */ + /* Bits 23,24 unused. */ + XEN_CPUFEATURE(SC_VERW_IDLE, X86_SYNTH(25)) /* VERW used by Xen for idle */ +diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h +index 751355f471..7e83e0179f 100644 +--- a/xen/include/asm-x86/spec_ctrl.h ++++ b/xen/include/asm-x86/spec_ctrl.h +@@ -78,7 +78,8 @@ static always_inline void spec_ctrl_enter_idle(struct cpu_info *info) + uint32_t val = 0; + + /* +- * Branch Target Injection: ++ * It is recommended in some cases to clear MSR_SPEC_CTRL when going idle, ++ * to avoid impacting sibling threads. + * + * Latch the new shadow value, then enable shadowing, then update the MSR. + * There are no SMP issues here; only local processor ordering concerns. +@@ -114,7 +115,7 @@ static always_inline void spec_ctrl_exit_idle(struct cpu_info *info) + uint32_t val = info->xen_spec_ctrl; + + /* +- * Branch Target Injection: ++ * Restore MSR_SPEC_CTRL on exit from idle. + * + * Disable shadowing before updating the MSR. There are no SMP issues + * here; only local processor ordering concerns. +-- +2.30.2 + diff --git a/xen.git-db6ca8176ccc4ff7dfe3c06969af9ebfab0d7b04.patch b/xen.git-db6ca8176ccc4ff7dfe3c06969af9ebfab0d7b04.patch new file mode 100644 index 0000000..4d5bb71 --- /dev/null +++ b/xen.git-db6ca8176ccc4ff7dfe3c06969af9ebfab0d7b04.patch @@ -0,0 +1,233 @@ +From db6ca8176ccc4ff7dfe3c06969af9ebfab0d7b04 Mon Sep 17 00:00:00 2001 +From: Andrew Cooper +Date: Tue, 12 Jul 2022 11:13:33 +0200 +Subject: [PATCH] x86/spec-ctrl: Knobs for STIBP and PSFD, and follow hardware + STIBP hint +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf8 +Content-Transfer-Encoding: 8bit + +STIBP and PSFD are slightly weird bits, because they're both implied by other +bits in MSR_SPEC_CTRL. Add fine grain controls for them, and take the +implications into account when setting IBRS/SSBD. + +Rearrange the IBPB text/variables/logic to keep all the MSR_SPEC_CTRL bits +together, for consistency. + +However, AMD have a hardware hint CPUID bit recommending that STIBP be set +unilaterally. This is advertised on Zen3, so follow the recommendation. +Furthermore, in such cases, set STIBP behind the guest's back for now. This +has negligible overhead for the guest, but saves a WRMSR on vmentry. This is +the only default change. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +Reviewed-by: Roger Pau Monné +master commit: fef244b179c06fcdfa581f7d57fa6e578c49ff50 +master date: 2022-06-30 18:07:13 +0100 +--- + docs/misc/xen-command-line.pandoc | 21 +++++++--- + xen/arch/x86/hvm/svm/vmcb.c | 9 +++++ + xen/arch/x86/spec_ctrl.c | 65 ++++++++++++++++++++++++++----- + 3 files changed, 81 insertions(+), 14 deletions(-) + +diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc +index a642e43476..46e9c58d35 100644 +--- a/docs/misc/xen-command-line.pandoc ++++ b/docs/misc/xen-command-line.pandoc +@@ -2234,8 +2234,9 @@ By default SSBD will be mitigated at runtime (i.e `ssbd=runtime`). + + ### spec-ctrl (x86) + > `= List of [ , xen=, {pv,hvm,msr-sc,rsb,md-clear}=, +-> bti-thunk=retpoline|lfence|jmp, {ibrs,ibpb,ssbd,eager-fpu, +-> l1d-flush,branch-harden,srb-lock,unpriv-mmio}= ]` ++> bti-thunk=retpoline|lfence|jmp, {ibrs,ibpb,ssbd,psfd, ++> eager-fpu,l1d-flush,branch-harden,srb-lock, ++> unpriv-mmio}= ]` + + Controls for speculative execution sidechannel mitigations. By default, Xen + will pick the most appropriate mitigations based on compiled in support, +@@ -2285,9 +2286,10 @@ On hardware supporting IBRS (Indirect Branch Restricted Speculation), the + If Xen is not using IBRS itself, functionality is still set up so IBRS can be + virtualised for guests. + +-On hardware supporting IBPB (Indirect Branch Prediction Barrier), the `ibpb=` +-option can be used to force (the default) or prevent Xen from issuing branch +-prediction barriers on vcpu context switches. ++On hardware supporting STIBP (Single Thread Indirect Branch Predictors), the ++`stibp=` option can be used to force or prevent Xen using the feature itself. ++By default, Xen will use STIBP when IBRS is in use (IBRS implies STIBP), and ++when hardware hints recommend using it as a blanket setting. + + On hardware supporting SSBD (Speculative Store Bypass Disable), the `ssbd=` + option can be used to force or prevent Xen using the feature itself. On AMD +@@ -2295,6 +2297,15 @@ hardware, this is a global option applied at boot, and not virtualised for + guest use. On Intel hardware, the feature is virtualised for guests, + independently of Xen's choice of setting. + ++On hardware supporting PSFD (Predictive Store Forwarding Disable), the `psfd=` ++option can be used to force or prevent Xen using the feature itself. By ++default, Xen will not use PSFD. PSFD is implied by SSBD, and SSBD is off by ++default. ++ ++On hardware supporting IBPB (Indirect Branch Prediction Barrier), the `ibpb=` ++option can be used to force (the default) or prevent Xen from issuing branch ++prediction barriers on vcpu context switches. ++ + On all hardware, the `eager-fpu=` option can be used to force or prevent Xen + from using fully eager FPU context switches. This is currently implemented as + a global control. By default, Xen will choose to use fully eager context +diff --git a/xen/arch/x86/hvm/svm/vmcb.c b/xen/arch/x86/hvm/svm/vmcb.c +index 565e997155..ef7224eb5d 100644 +--- a/xen/arch/x86/hvm/svm/vmcb.c ++++ b/xen/arch/x86/hvm/svm/vmcb.c +@@ -29,6 +29,7 @@ + #include + #include + #include ++#include + + struct vmcb_struct *alloc_vmcb(void) + { +@@ -176,6 +177,14 @@ static int construct_vmcb(struct vcpu *v) + vmcb->_pause_filter_thresh = SVM_PAUSETHRESH_INIT; + } + ++ /* ++ * When default_xen_spec_ctrl simply SPEC_CTRL_STIBP, default this behind ++ * the back of the VM too. Our SMT topology isn't accurate, the overhead ++ * is neglegable, and doing this saves a WRMSR on the vmentry path. ++ */ ++ if ( default_xen_spec_ctrl == SPEC_CTRL_STIBP ) ++ v->arch.msrs->spec_ctrl.raw = SPEC_CTRL_STIBP; ++ + return 0; + } + +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 1ed5ceda8b..dfdd45c358 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -48,9 +48,13 @@ static enum ind_thunk { + THUNK_LFENCE, + THUNK_JMP, + } opt_thunk __initdata = THUNK_DEFAULT; ++ + static int8_t __initdata opt_ibrs = -1; ++int8_t __initdata opt_stibp = -1; ++bool __read_mostly opt_ssbd; ++int8_t __initdata opt_psfd = -1; ++ + bool __read_mostly opt_ibpb = true; +-bool __read_mostly opt_ssbd = false; + int8_t __read_mostly opt_eager_fpu = -1; + int8_t __read_mostly opt_l1d_flush = -1; + static bool __initdata opt_branch_harden = true; +@@ -172,12 +176,20 @@ static int __init parse_spec_ctrl(const char *s) + else + rc = -EINVAL; + } ++ ++ /* Bits in MSR_SPEC_CTRL. */ + else if ( (val = parse_boolean("ibrs", s, ss)) >= 0 ) + opt_ibrs = val; +- else if ( (val = parse_boolean("ibpb", s, ss)) >= 0 ) +- opt_ibpb = val; ++ else if ( (val = parse_boolean("stibp", s, ss)) >= 0 ) ++ opt_stibp = val; + else if ( (val = parse_boolean("ssbd", s, ss)) >= 0 ) + opt_ssbd = val; ++ else if ( (val = parse_boolean("psfd", s, ss)) >= 0 ) ++ opt_psfd = val; ++ ++ /* Misc settings. */ ++ else if ( (val = parse_boolean("ibpb", s, ss)) >= 0 ) ++ opt_ibpb = val; + else if ( (val = parse_boolean("eager-fpu", s, ss)) >= 0 ) + opt_eager_fpu = val; + else if ( (val = parse_boolean("l1d-flush", s, ss)) >= 0 ) +@@ -376,7 +388,7 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + "\n"); + + /* Settings for Xen's protection, irrespective of guests. */ +- printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s%s, Other:%s%s%s%s%s\n", ++ printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s%s%s, Other:%s%s%s%s%s\n", + thunk == THUNK_NONE ? "N/A" : + thunk == THUNK_RETPOLINE ? "RETPOLINE" : + thunk == THUNK_LFENCE ? "LFENCE" : +@@ -390,6 +402,9 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + (!boot_cpu_has(X86_FEATURE_SSBD) && + !boot_cpu_has(X86_FEATURE_AMD_SSBD)) ? "" : + (default_xen_spec_ctrl & SPEC_CTRL_SSBD) ? " SSBD+" : " SSBD-", ++ (!boot_cpu_has(X86_FEATURE_PSFD) && ++ !boot_cpu_has(X86_FEATURE_INTEL_PSFD)) ? "" : ++ (default_xen_spec_ctrl & SPEC_CTRL_PSFD) ? " PSFD+" : " PSFD-", + !(caps & ARCH_CAPS_TSX_CTRL) ? "" : + (opt_tsx & 1) ? " TSX+" : " TSX-", + !cpu_has_srbds_ctrl ? "" : +@@ -979,10 +994,7 @@ void __init init_speculation_mitigations(void) + if ( !has_spec_ctrl ) + printk(XENLOG_WARNING "?!? CET active, but no MSR_SPEC_CTRL?\n"); + else if ( opt_ibrs == -1 ) +- { + opt_ibrs = ibrs = true; +- default_xen_spec_ctrl |= SPEC_CTRL_IBRS | SPEC_CTRL_STIBP; +- } + + if ( opt_thunk == THUNK_DEFAULT || opt_thunk == THUNK_RETPOLINE ) + thunk = THUNK_JMP; +@@ -1086,14 +1098,49 @@ void __init init_speculation_mitigations(void) + setup_force_cpu_cap(X86_FEATURE_SC_MSR_HVM); + } + +- /* If we have IBRS available, see whether we should use it. */ ++ /* Figure out default_xen_spec_ctrl. */ + if ( has_spec_ctrl && ibrs ) ++ { ++ /* IBRS implies STIBP. */ ++ if ( opt_stibp == -1 ) ++ opt_stibp = 1; ++ + default_xen_spec_ctrl |= SPEC_CTRL_IBRS; ++ } ++ ++ /* ++ * Use STIBP by default if the hardware hint is set. Otherwise, leave it ++ * off as it a severe performance pentalty on pre-eIBRS Intel hardware ++ * where it was retrofitted in microcode. ++ */ ++ if ( opt_stibp == -1 ) ++ opt_stibp = !!boot_cpu_has(X86_FEATURE_STIBP_ALWAYS); ++ ++ if ( opt_stibp && (boot_cpu_has(X86_FEATURE_STIBP) || ++ boot_cpu_has(X86_FEATURE_AMD_STIBP)) ) ++ default_xen_spec_ctrl |= SPEC_CTRL_STIBP; + +- /* If we have SSBD available, see whether we should use it. */ + if ( opt_ssbd && (boot_cpu_has(X86_FEATURE_SSBD) || + boot_cpu_has(X86_FEATURE_AMD_SSBD)) ) ++ { ++ /* SSBD implies PSFD */ ++ if ( opt_psfd == -1 ) ++ opt_psfd = 1; ++ + default_xen_spec_ctrl |= SPEC_CTRL_SSBD; ++ } ++ ++ /* ++ * Don't use PSFD by default. AMD designed the predictor to ++ * auto-clear on privilege change. PSFD is implied by SSBD, which is ++ * off by default. ++ */ ++ if ( opt_psfd == -1 ) ++ opt_psfd = 0; ++ ++ if ( opt_psfd && (boot_cpu_has(X86_FEATURE_PSFD) || ++ boot_cpu_has(X86_FEATURE_INTEL_PSFD)) ) ++ default_xen_spec_ctrl |= SPEC_CTRL_PSFD; + + /* + * PV guests can create RSB entries for any linear address they control, +-- +2.30.2 + diff --git a/xen.git-eec5b02403a9df2523527caad24f17af5060fbe7.patch b/xen.git-eec5b02403a9df2523527caad24f17af5060fbe7.patch new file mode 100644 index 0000000..1394851 --- /dev/null +++ b/xen.git-eec5b02403a9df2523527caad24f17af5060fbe7.patch @@ -0,0 +1,86 @@ +From eec5b02403a9df2523527caad24f17af5060fbe7 Mon Sep 17 00:00:00 2001 +From: Andrew Cooper +Date: Tue, 12 Jul 2022 11:15:03 +0200 +Subject: [PATCH] xen/cmdline: Extend parse_boolean() to signal a name match + +This will help parsing a sub-option which has boolean and non-boolean options +available. + +First, rework 'int val' into 'bool has_neg_prefix'. This inverts it's value, +but the resulting logic is far easier to follow. + +Second, reject anything of the form 'no-$FOO=' which excludes ambiguous +constructs such as 'no-$foo=yes' which have never been valid. + +This just leaves the case where everything is otherwise fine, but parse_bool() +can't interpret the provided string. + +Signed-off-by: Andrew Cooper +Reviewed-by: Juergen Gross +Reviewed-by: Jan Beulich +master commit: 382326cac528dd1eb0d04efd5c05363c453e29f4 +master date: 2022-07-11 15:21:35 +0100 +--- + xen/common/kernel.c | 20 ++++++++++++++++---- + xen/include/xen/lib.h | 3 ++- + 2 files changed, 18 insertions(+), 5 deletions(-) + +diff --git a/xen/common/kernel.c b/xen/common/kernel.c +index e119e5401f..7ed96521f9 100644 +--- a/xen/common/kernel.c ++++ b/xen/common/kernel.c +@@ -272,9 +272,9 @@ int parse_bool(const char *s, const char *e) + int parse_boolean(const char *name, const char *s, const char *e) + { + size_t slen, nlen; +- int val = !!strncmp(s, "no-", 3); ++ bool has_neg_prefix = !strncmp(s, "no-", 3); + +- if ( !val ) ++ if ( has_neg_prefix ) + s += 3; + + slen = e ? ({ ASSERT(e >= s); e - s; }) : strlen(s); +@@ -286,11 +286,23 @@ int parse_boolean(const char *name, const char *s, const char *e) + + /* Exact, unadorned name? Result depends on the 'no-' prefix. */ + if ( slen == nlen ) +- return val; ++ return !has_neg_prefix; ++ ++ /* Inexact match with a 'no-' prefix? Not valid. */ ++ if ( has_neg_prefix ) ++ return -1; + + /* =$SOMETHING? Defer to the regular boolean parsing. */ + if ( s[nlen] == '=' ) +- return parse_bool(&s[nlen + 1], e); ++ { ++ int b = parse_bool(&s[nlen + 1], e); ++ ++ if ( b >= 0 ) ++ return b; ++ ++ /* Not a boolean, but the name matched. Signal specially. */ ++ return -2; ++ } + + /* Unrecognised. Give up. */ + return -1; +diff --git a/xen/include/xen/lib.h b/xen/include/xen/lib.h +index c6987973bf..2296044caf 100644 +--- a/xen/include/xen/lib.h ++++ b/xen/include/xen/lib.h +@@ -80,7 +80,8 @@ int parse_bool(const char *s, const char *e); + /** + * Given a specific name, parses a string of the form: + * [no-]$NAME[=...] +- * returning 0 or 1 for a recognised boolean, or -1 for an error. ++ * returning 0 or 1 for a recognised boolean. Returns -1 for general errors, ++ * and -2 for "not a boolean, but $NAME= matches". + */ + int parse_boolean(const char *name, const char *s, const char *e); + +-- +2.30.2 + diff --git a/xen.git-f066c8bb3e5686141cef6fa1dc86ea9f37c5388a.patch b/xen.git-f066c8bb3e5686141cef6fa1dc86ea9f37c5388a.patch new file mode 100644 index 0000000..56cdada --- /dev/null +++ b/xen.git-f066c8bb3e5686141cef6fa1dc86ea9f37c5388a.patch @@ -0,0 +1,137 @@ +From f066c8bb3e5686141cef6fa1dc86ea9f37c5388a Mon Sep 17 00:00:00 2001 +From: Andrew Cooper +Date: Tue, 12 Jul 2022 11:15:37 +0200 +Subject: [PATCH] x86/spec-ctrl: Add fine-grained cmdline suboptions for + primitives + +Support controling the PV/HVM suboption of msr-sc/rsb/md-clear, which +previously wasn't possible. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +master commit: 27357c394ba6e1571a89105b840ce1c6f026485c +master date: 2022-07-11 15:21:35 +0100 +--- + docs/misc/xen-command-line.pandoc | 12 ++++-- + xen/arch/x86/spec_ctrl.c | 66 ++++++++++++++++++++++++++----- + 2 files changed, 66 insertions(+), 12 deletions(-) + +diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc +index 46e9c58d35..1bbdb55129 100644 +--- a/docs/misc/xen-command-line.pandoc ++++ b/docs/misc/xen-command-line.pandoc +@@ -2233,7 +2233,8 @@ not be able to control the state of the mitigation. + By default SSBD will be mitigated at runtime (i.e `ssbd=runtime`). + + ### spec-ctrl (x86) +-> `= List of [ , xen=, {pv,hvm,msr-sc,rsb,md-clear}=, ++> `= List of [ , xen=, {pv,hvm}=, ++> {msr-sc,rsb,md-clear}=|{pv,hvm}=, + > bti-thunk=retpoline|lfence|jmp, {ibrs,ibpb,ssbd,psfd, + > eager-fpu,l1d-flush,branch-harden,srb-lock, + > unpriv-mmio}= ]` +@@ -2258,12 +2259,17 @@ in place for guests to use. + + Use of a positive boolean value for either of these options is invalid. + +-The booleans `pv=`, `hvm=`, `msr-sc=`, `rsb=` and `md-clear=` offer fine ++The `pv=`, `hvm=`, `msr-sc=`, `rsb=` and `md-clear=` options offer fine + grained control over the primitives by Xen. These impact Xen's ability to +-protect itself, and Xen's ability to virtualise support for guests to use. ++protect itself, and/or Xen's ability to virtualise support for guests to use. + + * `pv=` and `hvm=` offer control over all suboptions for PV and HVM guests + respectively. ++* Each other option can be used either as a plain boolean ++ (e.g. `spec-ctrl=rsb` to control both the PV and HVM sub-options), or with ++ `pv=` or `hvm=` subsuboptions (e.g. `spec-ctrl=rsb=no-hvm` to disable HVM ++ RSB only). ++ + * `msr-sc=` offers control over Xen's support for manipulating `MSR_SPEC_CTRL` + on entry and exit. These blocks are necessary to virtualise support for + guests and if disabled, guests will be unable to use IBRS/STIBP/SSBD/etc. +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index ae74943c10..9507e5da60 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -147,20 +147,68 @@ static int __init parse_spec_ctrl(const char *s) + opt_rsb_hvm = val; + opt_md_clear_hvm = val; + } +- else if ( (val = parse_boolean("msr-sc", s, ss)) >= 0 ) ++ else if ( (val = parse_boolean("msr-sc", s, ss)) != -1 ) + { +- opt_msr_sc_pv = val; +- opt_msr_sc_hvm = val; ++ switch ( val ) ++ { ++ case 0: ++ case 1: ++ opt_msr_sc_pv = opt_msr_sc_hvm = val; ++ break; ++ ++ case -2: ++ s += strlen("msr-sc="); ++ if ( (val = parse_boolean("pv", s, ss)) >= 0 ) ++ opt_msr_sc_pv = val; ++ else if ( (val = parse_boolean("hvm", s, ss)) >= 0 ) ++ opt_msr_sc_hvm = val; ++ else ++ default: ++ rc = -EINVAL; ++ break; ++ } + } +- else if ( (val = parse_boolean("rsb", s, ss)) >= 0 ) ++ else if ( (val = parse_boolean("rsb", s, ss)) != -1 ) + { +- opt_rsb_pv = val; +- opt_rsb_hvm = val; ++ switch ( val ) ++ { ++ case 0: ++ case 1: ++ opt_rsb_pv = opt_rsb_hvm = val; ++ break; ++ ++ case -2: ++ s += strlen("rsb="); ++ if ( (val = parse_boolean("pv", s, ss)) >= 0 ) ++ opt_rsb_pv = val; ++ else if ( (val = parse_boolean("hvm", s, ss)) >= 0 ) ++ opt_rsb_hvm = val; ++ else ++ default: ++ rc = -EINVAL; ++ break; ++ } + } +- else if ( (val = parse_boolean("md-clear", s, ss)) >= 0 ) ++ else if ( (val = parse_boolean("md-clear", s, ss)) != -1 ) + { +- opt_md_clear_pv = val; +- opt_md_clear_hvm = val; ++ switch ( val ) ++ { ++ case 0: ++ case 1: ++ opt_md_clear_pv = opt_md_clear_hvm = val; ++ break; ++ ++ case -2: ++ s += strlen("md-clear="); ++ if ( (val = parse_boolean("pv", s, ss)) >= 0 ) ++ opt_md_clear_pv = val; ++ else if ( (val = parse_boolean("hvm", s, ss)) >= 0 ) ++ opt_md_clear_hvm = val; ++ else ++ default: ++ rc = -EINVAL; ++ break; ++ } + } + + /* Xen's speculative sidechannel mitigation settings. */ +-- +2.30.2 + diff --git a/xen.spec b/xen.spec index c849eb2..02e1c71 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.16.1 -Release: 5%{?dist} +Release: 6%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -123,6 +123,18 @@ Patch55: xsa404-4.16-1.patch Patch56: xsa404-4.16-2.patch Patch57: xsa404-4.16-3.patch Patch58: xsa403-4.16-1.patch +Patch59: xen.git-09d533f4c80b7eaf9fb4e36ebba8259580857a9d.patch +Patch60: xen.git-db6ca8176ccc4ff7dfe3c06969af9ebfab0d7b04.patch +Patch61: xen.git-eec5b02403a9df2523527caad24f17af5060fbe7.patch +Patch62: xen.git-f066c8bb3e5686141cef6fa1dc86ea9f37c5388a.patch +Patch63: xsa407-4.16-1.patch +Patch64: xsa407-4.16-2.patch +Patch65: xsa407-4.16-3.patch +Patch66: xsa407-4.16-4.patch +Patch67: xsa407-4.16-5.patch +Patch68: xsa407-4.16-6.patch +Patch69: xsa407-4.16-7.patch +Patch70: xsa407-4.16-8.patch %if %build_qemutrad @@ -347,6 +359,18 @@ manage Xen virtual machines. %patch56 -p1 %patch57 -p1 %patch58 -p1 +%patch59 -p1 +%patch60 -p1 +%patch61 -p1 +%patch62 -p1 +%patch63 -p1 +%patch64 -p1 +%patch65 -p1 +%patch66 -p1 +%patch67 -p1 +%patch68 -p1 +%patch69 -p1 +%patch70 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -962,6 +986,10 @@ fi %endif %changelog +* Tue Jul 12 2022 Michael Young - 4.16.1-6 +- Retbleed - arbitrary speculative code execution with return instructions + [XSA-407, CVE-2022-23816, CVE-2022-23825, CVE-2022-29900] + * Tue Jul 05 2022 Michael Young - 4.16.1-5 - Linux disk/nic frontends data leaks [XSA-403, CVE-2022-26365, CVE-2022-33740, CVE-2022-33741, CVE-2022-33742] (#2104747) diff --git a/xsa407-4.16-1.patch b/xsa407-4.16-1.patch new file mode 100644 index 0000000..d6dc7fc --- /dev/null +++ b/xsa407-4.16-1.patch @@ -0,0 +1,153 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Rework spec_ctrl_flags context switching + +We are shortly going to need to context switch new bits in both the vcpu and +S3 paths. Introduce SCF_IST_MASK and SCF_DOM_MASK, and rework d->arch.verw +into d->arch.spec_ctrl_flags to accommodate. + +No functional change. + +This is part of XSA-407. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/acpi/power.c b/xen/arch/x86/acpi/power.c +index 5eaa77f66a28..dd397f713067 100644 +--- a/xen/arch/x86/acpi/power.c ++++ b/xen/arch/x86/acpi/power.c +@@ -248,8 +248,8 @@ static int enter_state(u32 state) + error = 0; + + ci = get_cpu_info(); +- /* Avoid NMI/#MC using MSR_SPEC_CTRL until we've reloaded microcode. */ +- ci->spec_ctrl_flags &= ~SCF_ist_wrmsr; ++ /* Avoid NMI/#MC using unsafe MSRs until we've reloaded microcode. */ ++ ci->spec_ctrl_flags &= ~SCF_IST_MASK; + + ACPI_FLUSH_CPU_CACHE(); + +@@ -292,8 +292,8 @@ static int enter_state(u32 state) + if ( !recheck_cpu_features(0) ) + panic("Missing previously available feature(s)\n"); + +- /* Re-enabled default NMI/#MC use of MSR_SPEC_CTRL. */ +- ci->spec_ctrl_flags |= (default_spec_ctrl_flags & SCF_ist_wrmsr); ++ /* Re-enabled default NMI/#MC use of MSRs now microcode is loaded. */ ++ ci->spec_ctrl_flags |= (default_spec_ctrl_flags & SCF_IST_MASK); + + if ( boot_cpu_has(X86_FEATURE_IBRSB) || boot_cpu_has(X86_FEATURE_IBRS) ) + { +diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c +index 1fe6644a71ae..82a0b73cf6ef 100644 +--- a/xen/arch/x86/domain.c ++++ b/xen/arch/x86/domain.c +@@ -2092,10 +2092,10 @@ void context_switch(struct vcpu *prev, struct vcpu *next) + } + } + +- /* Update the top-of-stack block with the VERW disposition. */ +- info->spec_ctrl_flags &= ~SCF_verw; +- if ( nextd->arch.verw ) +- info->spec_ctrl_flags |= SCF_verw; ++ /* Update the top-of-stack block with the new spec_ctrl settings. */ ++ info->spec_ctrl_flags = ++ (info->spec_ctrl_flags & ~SCF_DOM_MASK) | ++ (nextd->arch.spec_ctrl_flags & SCF_DOM_MASK); + } + + sched_context_switched(prev, next); +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 9507e5da60a9..7e646680f1c7 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -1010,9 +1010,12 @@ void spec_ctrl_init_domain(struct domain *d) + { + bool pv = is_pv_domain(d); + +- d->arch.verw = +- (pv ? opt_md_clear_pv : opt_md_clear_hvm) || +- (opt_fb_clear_mmio && is_iommu_enabled(d)); ++ bool verw = ((pv ? opt_md_clear_pv : opt_md_clear_hvm) || ++ (opt_fb_clear_mmio && is_iommu_enabled(d))); ++ ++ d->arch.spec_ctrl_flags = ++ (verw ? SCF_verw : 0) | ++ 0; + } + + void __init init_speculation_mitigations(void) +diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h +index 2398a1d99da9..e4c099262cb7 100644 +--- a/xen/include/asm-x86/domain.h ++++ b/xen/include/asm-x86/domain.h +@@ -319,8 +319,7 @@ struct arch_domain + uint32_t pci_cf8; + uint8_t cmos_idx; + +- /* Use VERW on return-to-guest for its flushing side effect. */ +- bool verw; ++ uint8_t spec_ctrl_flags; /* See SCF_DOM_MASK */ + + union { + struct pv_domain pv; +diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h +index 7e83e0179fb9..3cd72e40305f 100644 +--- a/xen/include/asm-x86/spec_ctrl.h ++++ b/xen/include/asm-x86/spec_ctrl.h +@@ -20,12 +20,40 @@ + #ifndef __X86_SPEC_CTRL_H__ + #define __X86_SPEC_CTRL_H__ + +-/* Encoding of cpuinfo.spec_ctrl_flags */ ++/* ++ * Encoding of: ++ * cpuinfo.spec_ctrl_flags ++ * default_spec_ctrl_flags ++ * domain.spec_ctrl_flags ++ * ++ * Live settings are in the top-of-stack block, because they need to be ++ * accessable when XPTI is active. Some settings are fixed from boot, some ++ * context switched per domain, and some inhibited in the S3 path. ++ */ + #define SCF_use_shadow (1 << 0) + #define SCF_ist_wrmsr (1 << 1) + #define SCF_ist_rsb (1 << 2) + #define SCF_verw (1 << 3) + ++/* ++ * The IST paths (NMI/#MC) can interrupt any arbitrary context. Some ++ * functionality requires updated microcode to work. ++ * ++ * On boot, this is easy; we load microcode before figuring out which ++ * speculative protections to apply. However, on the S3 resume path, we must ++ * be able to disable the configured mitigations until microcode is reloaded. ++ * ++ * These are the controls to inhibit on the S3 resume path until microcode has ++ * been reloaded. ++ */ ++#define SCF_IST_MASK (SCF_ist_wrmsr) ++ ++/* ++ * Some speculative protections are per-domain. These settings are merged ++ * into the top-of-stack block in the context switch path. ++ */ ++#define SCF_DOM_MASK (SCF_verw) ++ + #ifndef __ASSEMBLY__ + + #include +diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h +index 5a590bac44aa..66b00d511fc6 100644 +--- a/xen/include/asm-x86/spec_ctrl_asm.h ++++ b/xen/include/asm-x86/spec_ctrl_asm.h +@@ -248,9 +248,6 @@ + + /* + * Use in IST interrupt/exception context. May interrupt Xen or PV context. +- * Fine grain control of SCF_ist_wrmsr is needed for safety in the S3 resume +- * path to avoid using MSR_SPEC_CTRL before the microcode introducing it has +- * been reloaded. + */ + .macro SPEC_CTRL_ENTRY_FROM_INTR_IST + /* diff --git a/xsa407-4.16-2.patch b/xsa407-4.16-2.patch new file mode 100644 index 0000000..012d028 --- /dev/null +++ b/xsa407-4.16-2.patch @@ -0,0 +1,99 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Rename SCF_ist_wrmsr to SCF_ist_sc_msr + +We are about to introduce SCF_ist_ibpb, at which point SCF_ist_wrmsr becomes +ambiguous. + +No functional change. + +This is part of XSA-407. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 7e646680f1c7..89f95c083e1b 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -1115,7 +1115,7 @@ void __init init_speculation_mitigations(void) + { + if ( opt_msr_sc_pv ) + { +- default_spec_ctrl_flags |= SCF_ist_wrmsr; ++ default_spec_ctrl_flags |= SCF_ist_sc_msr; + setup_force_cpu_cap(X86_FEATURE_SC_MSR_PV); + } + +@@ -1126,7 +1126,7 @@ void __init init_speculation_mitigations(void) + * Xen's value is not restored atomically. An early NMI hitting + * the VMExit path needs to restore Xen's value for safety. + */ +- default_spec_ctrl_flags |= SCF_ist_wrmsr; ++ default_spec_ctrl_flags |= SCF_ist_sc_msr; + setup_force_cpu_cap(X86_FEATURE_SC_MSR_HVM); + } + } +@@ -1139,7 +1139,7 @@ void __init init_speculation_mitigations(void) + * on real hardware matches the availability of MSR_SPEC_CTRL in the + * first place. + * +- * No need for SCF_ist_wrmsr because Xen's value is restored ++ * No need for SCF_ist_sc_msr because Xen's value is restored + * atomically WRT NMIs in the VMExit path. + * + * TODO: Adjust cpu_has_svm_spec_ctrl to be usable earlier on boot. +diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h +index 3cd72e40305f..f8f0ac47e759 100644 +--- a/xen/include/asm-x86/spec_ctrl.h ++++ b/xen/include/asm-x86/spec_ctrl.h +@@ -31,7 +31,7 @@ + * context switched per domain, and some inhibited in the S3 path. + */ + #define SCF_use_shadow (1 << 0) +-#define SCF_ist_wrmsr (1 << 1) ++#define SCF_ist_sc_msr (1 << 1) + #define SCF_ist_rsb (1 << 2) + #define SCF_verw (1 << 3) + +@@ -46,7 +46,7 @@ + * These are the controls to inhibit on the S3 resume path until microcode has + * been reloaded. + */ +-#define SCF_IST_MASK (SCF_ist_wrmsr) ++#define SCF_IST_MASK (SCF_ist_sc_msr) + + /* + * Some speculative protections are per-domain. These settings are merged +diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h +index 66b00d511fc6..0ff1b118f882 100644 +--- a/xen/include/asm-x86/spec_ctrl_asm.h ++++ b/xen/include/asm-x86/spec_ctrl_asm.h +@@ -266,8 +266,8 @@ + + .L\@_skip_rsb: + +- test $SCF_ist_wrmsr, %al +- jz .L\@_skip_wrmsr ++ test $SCF_ist_sc_msr, %al ++ jz .L\@_skip_msr_spec_ctrl + + xor %edx, %edx + testb $3, UREGS_cs(%rsp) +@@ -290,7 +290,7 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): + * to speculate around the WRMSR. As a result, we need a dispatch + * serialising instruction in the else clause. + */ +-.L\@_skip_wrmsr: ++.L\@_skip_msr_spec_ctrl: + lfence + UNLIKELY_END(\@_serialise) + .endm +@@ -301,7 +301,7 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): + * Requires %rbx=stack_end + * Clobbers %rax, %rcx, %rdx + */ +- testb $SCF_ist_wrmsr, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%rbx) ++ testb $SCF_ist_sc_msr, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%rbx) + jz .L\@_skip + + DO_SPEC_CTRL_EXIT_TO_XEN diff --git a/xsa407-4.16-3.patch b/xsa407-4.16-3.patch new file mode 100644 index 0000000..c6f7e6b --- /dev/null +++ b/xsa407-4.16-3.patch @@ -0,0 +1,86 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Rename opt_ibpb to opt_ibpb_ctxt_switch + +We are about to introduce the use of IBPB at different points in Xen, making +opt_ibpb ambiguous. Rename it to opt_ibpb_ctxt_switch. + +No functional change. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c +index 82a0b73cf6ef..0d39981550ca 100644 +--- a/xen/arch/x86/domain.c ++++ b/xen/arch/x86/domain.c +@@ -2064,7 +2064,7 @@ void context_switch(struct vcpu *prev, struct vcpu *next) + + ctxt_switch_levelling(next); + +- if ( opt_ibpb && !is_idle_domain(nextd) ) ++ if ( opt_ibpb_ctxt_switch && !is_idle_domain(nextd) ) + { + static DEFINE_PER_CPU(unsigned int, last); + unsigned int *last_id = &this_cpu(last); +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 89f95c083e1b..f4ae36eae2d0 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -54,7 +54,7 @@ int8_t __initdata opt_stibp = -1; + bool __read_mostly opt_ssbd; + int8_t __initdata opt_psfd = -1; + +-bool __read_mostly opt_ibpb = true; ++bool __read_mostly opt_ibpb_ctxt_switch = true; + int8_t __read_mostly opt_eager_fpu = -1; + int8_t __read_mostly opt_l1d_flush = -1; + static bool __initdata opt_branch_harden = true; +@@ -117,7 +117,7 @@ static int __init parse_spec_ctrl(const char *s) + + opt_thunk = THUNK_JMP; + opt_ibrs = 0; +- opt_ibpb = false; ++ opt_ibpb_ctxt_switch = false; + opt_ssbd = false; + opt_l1d_flush = 0; + opt_branch_harden = false; +@@ -238,7 +238,7 @@ static int __init parse_spec_ctrl(const char *s) + + /* Misc settings. */ + else if ( (val = parse_boolean("ibpb", s, ss)) >= 0 ) +- opt_ibpb = val; ++ opt_ibpb_ctxt_switch = val; + else if ( (val = parse_boolean("eager-fpu", s, ss)) >= 0 ) + opt_eager_fpu = val; + else if ( (val = parse_boolean("l1d-flush", s, ss)) >= 0 ) +@@ -458,7 +458,7 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + (opt_tsx & 1) ? " TSX+" : " TSX-", + !cpu_has_srbds_ctrl ? "" : + opt_srb_lock ? " SRB_LOCK+" : " SRB_LOCK-", +- opt_ibpb ? " IBPB" : "", ++ opt_ibpb_ctxt_switch ? " IBPB-ctxt" : "", + opt_l1d_flush ? " L1D_FLUSH" : "", + opt_md_clear_pv || opt_md_clear_hvm || + opt_fb_clear_mmio ? " VERW" : "", +@@ -1240,7 +1240,7 @@ void __init init_speculation_mitigations(void) + + /* Check we have hardware IBPB support before using it... */ + if ( !boot_cpu_has(X86_FEATURE_IBRSB) && !boot_cpu_has(X86_FEATURE_IBPB) ) +- opt_ibpb = false; ++ opt_ibpb_ctxt_switch = false; + + /* Check whether Eager FPU should be enabled by default. */ + if ( opt_eager_fpu == -1 ) +diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h +index f8f0ac47e759..fb4365575620 100644 +--- a/xen/include/asm-x86/spec_ctrl.h ++++ b/xen/include/asm-x86/spec_ctrl.h +@@ -63,7 +63,7 @@ + void init_speculation_mitigations(void); + void spec_ctrl_init_domain(struct domain *d); + +-extern bool opt_ibpb; ++extern bool opt_ibpb_ctxt_switch; + extern bool opt_ssbd; + extern int8_t opt_eager_fpu; + extern int8_t opt_l1d_flush; diff --git a/xsa407-4.16-4.patch b/xsa407-4.16-4.patch new file mode 100644 index 0000000..50e5f0f --- /dev/null +++ b/xsa407-4.16-4.patch @@ -0,0 +1,96 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Rework SPEC_CTRL_ENTRY_FROM_INTR_IST + +We are shortly going to add a conditional IBPB in this path. + +Therefore, we cannot hold spec_ctrl_flags in %eax, and rely on only clobbering +it after we're done with its contents. %rbx is available for use, and the +more normal register to hold preserved information in. + +With %rax freed up, use it instead of %rdx for the RSB tmp register, and for +the adjustment to spec_ctrl_flags. + +This leaves no use of %rdx, except as 0 for the upper half of WRMSR. In +practice, %rdx is 0 from SAVE_ALL on all paths and isn't likely to change in +the foreseeable future, so update the macro entry requirements to state this +dependency. This marginal optimisation can be revisited if circumstances +change. + +No practical change. + +This is part of XSA-407. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S +index 2a86938f1f32..a1810bf4d311 100644 +--- a/xen/arch/x86/x86_64/entry.S ++++ b/xen/arch/x86/x86_64/entry.S +@@ -932,7 +932,7 @@ ENTRY(double_fault) + + GET_STACK_END(14) + +- SPEC_CTRL_ENTRY_FROM_INTR_IST /* Req: %rsp=regs, %r14=end, Clob: acd */ ++ SPEC_CTRL_ENTRY_FROM_INTR_IST /* Req: %rsp=regs, %r14=end, %rdx=0, Clob: abcd */ + /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ + + mov STACK_CPUINFO_FIELD(xen_cr3)(%r14), %rbx +@@ -968,7 +968,7 @@ handle_ist_exception: + + GET_STACK_END(14) + +- SPEC_CTRL_ENTRY_FROM_INTR_IST /* Req: %rsp=regs, %r14=end, Clob: acd */ ++ SPEC_CTRL_ENTRY_FROM_INTR_IST /* Req: %rsp=regs, %r14=end, %rdx=0, Clob: abcd */ + /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ + + mov STACK_CPUINFO_FIELD(xen_cr3)(%r14), %rcx +diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h +index 0ff1b118f882..15e24cde00d1 100644 +--- a/xen/include/asm-x86/spec_ctrl_asm.h ++++ b/xen/include/asm-x86/spec_ctrl_asm.h +@@ -251,34 +251,33 @@ + */ + .macro SPEC_CTRL_ENTRY_FROM_INTR_IST + /* +- * Requires %rsp=regs, %r14=stack_end +- * Clobbers %rax, %rcx, %rdx ++ * Requires %rsp=regs, %r14=stack_end, %rdx=0 ++ * Clobbers %rax, %rbx, %rcx, %rdx + * + * This is logical merge of DO_OVERWRITE_RSB and DO_SPEC_CTRL_ENTRY + * maybexen=1, but with conditionals rather than alternatives. + */ +- movzbl STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14), %eax ++ movzbl STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14), %ebx + +- test $SCF_ist_rsb, %al ++ test $SCF_ist_rsb, %bl + jz .L\@_skip_rsb + +- DO_OVERWRITE_RSB tmp=rdx /* Clobbers %rcx/%rdx */ ++ DO_OVERWRITE_RSB /* Clobbers %rax/%rcx */ + + .L\@_skip_rsb: + +- test $SCF_ist_sc_msr, %al ++ test $SCF_ist_sc_msr, %bl + jz .L\@_skip_msr_spec_ctrl + +- xor %edx, %edx ++ xor %eax, %eax + testb $3, UREGS_cs(%rsp) +- setnz %dl +- not %edx +- and %dl, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14) ++ setnz %al ++ not %eax ++ and %al, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14) + + /* Load Xen's intended value. */ + mov $MSR_SPEC_CTRL, %ecx + movzbl STACK_CPUINFO_FIELD(xen_spec_ctrl)(%r14), %eax +- xor %edx, %edx + wrmsr + + /* Opencoded UNLIKELY_START() with no condition. */ diff --git a/xsa407-4.16-5.patch b/xsa407-4.16-5.patch new file mode 100644 index 0000000..ef6da7c --- /dev/null +++ b/xsa407-4.16-5.patch @@ -0,0 +1,285 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Support IBPB-on-entry + +We are going to need this to mitigate Branch Type Confusion on AMD/Hygon CPUs, +but as we've talked about using it in other cases too, arrange to support it +generally. However, this is also very expensive in some cases, so we're going +to want per-domain controls. + +Introduce SCF_ist_ibpb and SCF_entry_ibpb controls, adding them to the IST and +DOM masks as appropriate. Also introduce X86_FEATURE_IBPB_ENTRY_{PV,HVM} to +to patch the code blocks. + +For SVM, the STGI is serialising enough to protect against Spectre-v1 attacks, +so no "else lfence" is necessary. VT-x will use use the MSR host load list, +so doesn't need any code in the VMExit path. + +For the IST path, we can't safely check CPL==0 to skip a flush, as we might +have hit an entry path before it's IBPB. As IST hitting Xen is rare, flush +irrespective of CPL. A later path, SCF_ist_sc_msr, provides Spectre-v1 +safety. + +For the PV paths, we know we're interrupting CPL>0, while for the INTR paths, +we can safely check CPL==0. Only flush when interrupting guest context. + +An "else lfence" is needed for safety, but we want to be able to skip it on +unaffected CPUs, so the block wants to be an alternative, which means the +lfence has to be inline rather than UNLIKELY() (the replacement block doesn't +have displacements fixed up for anything other than the first instruction). + +As with SPEC_CTRL_ENTRY_FROM_INTR_IST, %rdx is 0 on entry so rely on this to +shrink the logic marginally. Update the comments to specify this new +dependency. + +This is part of XSA-407. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/hvm/svm/entry.S b/xen/arch/x86/hvm/svm/entry.S +index 4ae55a2ef605..0ff4008060fa 100644 +--- a/xen/arch/x86/hvm/svm/entry.S ++++ b/xen/arch/x86/hvm/svm/entry.S +@@ -97,7 +97,19 @@ __UNLIKELY_END(nsvm_hap) + + GET_CURRENT(bx) + +- /* SPEC_CTRL_ENTRY_FROM_SVM Req: %rsp=regs/cpuinfo Clob: acd */ ++ /* SPEC_CTRL_ENTRY_FROM_SVM Req: %rsp=regs/cpuinfo, %rdx=0 Clob: acd */ ++ ++ .macro svm_vmexit_cond_ibpb ++ testb $SCF_entry_ibpb, CPUINFO_xen_spec_ctrl(%rsp) ++ jz .L_skip_ibpb ++ ++ mov $MSR_PRED_CMD, %ecx ++ mov $PRED_CMD_IBPB, %eax ++ wrmsr ++.L_skip_ibpb: ++ .endm ++ ALTERNATIVE "", svm_vmexit_cond_ibpb, X86_FEATURE_IBPB_ENTRY_HVM ++ + ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_HVM + + .macro svm_vmexit_spec_ctrl +@@ -114,6 +126,10 @@ __UNLIKELY_END(nsvm_hap) + ALTERNATIVE "", svm_vmexit_spec_ctrl, X86_FEATURE_SC_MSR_HVM + /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ + ++ /* ++ * STGI is executed unconditionally, and is sufficiently serialising ++ * to safely resolve any Spectre-v1 concerns in the above logic. ++ */ + stgi + GLOBAL(svm_stgi_label) + mov %rsp,%rdi +diff --git a/xen/arch/x86/hvm/vmx/vmcs.c b/xen/arch/x86/hvm/vmx/vmcs.c +index f9f9bc18cdbc..dd817cee4e69 100644 +--- a/xen/arch/x86/hvm/vmx/vmcs.c ++++ b/xen/arch/x86/hvm/vmx/vmcs.c +@@ -1345,6 +1345,10 @@ static int construct_vmcs(struct vcpu *v) + rc = vmx_add_msr(v, MSR_FLUSH_CMD, FLUSH_CMD_L1D, + VMX_MSR_GUEST_LOADONLY); + ++ if ( !rc && (d->arch.spec_ctrl_flags & SCF_entry_ibpb) ) ++ rc = vmx_add_msr(v, MSR_PRED_CMD, PRED_CMD_IBPB, ++ VMX_MSR_HOST); ++ + out: + vmx_vmcs_exit(v); + +diff --git a/xen/arch/x86/x86_64/compat/entry.S b/xen/arch/x86/x86_64/compat/entry.S +index 5fd6dbbd4513..b86d38d1c50d 100644 +--- a/xen/arch/x86/x86_64/compat/entry.S ++++ b/xen/arch/x86/x86_64/compat/entry.S +@@ -18,7 +18,7 @@ ENTRY(entry_int82) + movl $HYPERCALL_VECTOR, 4(%rsp) + SAVE_ALL compat=1 /* DPL1 gate, restricted to 32bit PV guests only. */ + +- SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, Clob: acd */ ++ SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, %rdx=0, Clob: acd */ + /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ + + CR4_PV32_RESTORE +diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S +index a1810bf4d311..fba8ae498f74 100644 +--- a/xen/arch/x86/x86_64/entry.S ++++ b/xen/arch/x86/x86_64/entry.S +@@ -260,7 +260,7 @@ ENTRY(lstar_enter) + movl $TRAP_syscall, 4(%rsp) + SAVE_ALL + +- SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, Clob: acd */ ++ SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, %rdx=0, Clob: acd */ + /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ + + GET_STACK_END(bx) +@@ -298,7 +298,7 @@ ENTRY(cstar_enter) + movl $TRAP_syscall, 4(%rsp) + SAVE_ALL + +- SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, Clob: acd */ ++ SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, %rdx=0, Clob: acd */ + /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ + + GET_STACK_END(bx) +@@ -338,7 +338,7 @@ GLOBAL(sysenter_eflags_saved) + movl $TRAP_syscall, 4(%rsp) + SAVE_ALL + +- SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, Clob: acd */ ++ SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, %rdx=0, Clob: acd */ + /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ + + GET_STACK_END(bx) +@@ -392,7 +392,7 @@ ENTRY(int80_direct_trap) + movl $0x80, 4(%rsp) + SAVE_ALL + +- SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, Clob: acd */ ++ SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, %rdx=0, Clob: acd */ + /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ + + GET_STACK_END(bx) +@@ -674,7 +674,7 @@ ENTRY(common_interrupt) + + GET_STACK_END(14) + +- SPEC_CTRL_ENTRY_FROM_INTR /* Req: %rsp=regs, %r14=end, Clob: acd */ ++ SPEC_CTRL_ENTRY_FROM_INTR /* Req: %rsp=regs, %r14=end, %rdx=0, Clob: acd */ + /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ + + mov STACK_CPUINFO_FIELD(xen_cr3)(%r14), %rcx +@@ -708,7 +708,7 @@ GLOBAL(handle_exception) + + GET_STACK_END(14) + +- SPEC_CTRL_ENTRY_FROM_INTR /* Req: %rsp=regs, %r14=end, Clob: acd */ ++ SPEC_CTRL_ENTRY_FROM_INTR /* Req: %rsp=regs, %r14=end, %rdx=0, Clob: acd */ + /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ + + mov STACK_CPUINFO_FIELD(xen_cr3)(%r14), %rcx +diff --git a/xen/include/asm-x86/cpufeatures.h b/xen/include/asm-x86/cpufeatures.h +index 493d338a085e..672c9ee22ba2 100644 +--- a/xen/include/asm-x86/cpufeatures.h ++++ b/xen/include/asm-x86/cpufeatures.h +@@ -39,6 +39,8 @@ XEN_CPUFEATURE(XEN_LBR, X86_SYNTH(22)) /* Xen uses MSR_DEBUGCTL.LBR */ + XEN_CPUFEATURE(SC_VERW_IDLE, X86_SYNTH(25)) /* VERW used by Xen for idle */ + XEN_CPUFEATURE(XEN_SHSTK, X86_SYNTH(26)) /* Xen uses CET Shadow Stacks */ + XEN_CPUFEATURE(XEN_IBT, X86_SYNTH(27)) /* Xen uses CET Indirect Branch Tracking */ ++XEN_CPUFEATURE(IBPB_ENTRY_PV, X86_SYNTH(28)) /* MSR_PRED_CMD used by Xen for PV */ ++XEN_CPUFEATURE(IBPB_ENTRY_HVM, X86_SYNTH(29)) /* MSR_PRED_CMD used by Xen for HVM */ + + /* Bug words follow the synthetic words. */ + #define X86_NR_BUG 1 +diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h +index fb4365575620..3fc599a817c4 100644 +--- a/xen/include/asm-x86/spec_ctrl.h ++++ b/xen/include/asm-x86/spec_ctrl.h +@@ -34,6 +34,8 @@ + #define SCF_ist_sc_msr (1 << 1) + #define SCF_ist_rsb (1 << 2) + #define SCF_verw (1 << 3) ++#define SCF_ist_ibpb (1 << 4) ++#define SCF_entry_ibpb (1 << 5) + + /* + * The IST paths (NMI/#MC) can interrupt any arbitrary context. Some +@@ -46,13 +48,13 @@ + * These are the controls to inhibit on the S3 resume path until microcode has + * been reloaded. + */ +-#define SCF_IST_MASK (SCF_ist_sc_msr) ++#define SCF_IST_MASK (SCF_ist_sc_msr | SCF_ist_ibpb) + + /* + * Some speculative protections are per-domain. These settings are merged + * into the top-of-stack block in the context switch path. + */ +-#define SCF_DOM_MASK (SCF_verw) ++#define SCF_DOM_MASK (SCF_verw | SCF_entry_ibpb) + + #ifndef __ASSEMBLY__ + +diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h +index 15e24cde00d1..9eb4ad9ab71d 100644 +--- a/xen/include/asm-x86/spec_ctrl_asm.h ++++ b/xen/include/asm-x86/spec_ctrl_asm.h +@@ -88,6 +88,35 @@ + * - SPEC_CTRL_EXIT_TO_{SVM,VMX} + */ + ++.macro DO_SPEC_CTRL_COND_IBPB maybexen:req ++/* ++ * Requires %rsp=regs (also cpuinfo if !maybexen) ++ * Requires %r14=stack_end (if maybexen), %rdx=0 ++ * Clobbers %rax, %rcx, %rdx ++ * ++ * Conditionally issue IBPB if SCF_entry_ibpb is active. In the maybexen ++ * case, we can safely look at UREGS_cs to skip taking the hit when ++ * interrupting Xen. ++ */ ++ .if \maybexen ++ testb $SCF_entry_ibpb, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14) ++ jz .L\@_skip ++ testb $3, UREGS_cs(%rsp) ++ .else ++ testb $SCF_entry_ibpb, CPUINFO_xen_spec_ctrl(%rsp) ++ .endif ++ jz .L\@_skip ++ ++ mov $MSR_PRED_CMD, %ecx ++ mov $PRED_CMD_IBPB, %eax ++ wrmsr ++ jmp .L\@_done ++ ++.L\@_skip: ++ lfence ++.L\@_done: ++.endm ++ + .macro DO_OVERWRITE_RSB tmp=rax + /* + * Requires nothing +@@ -225,12 +254,16 @@ + + /* Use after an entry from PV context (syscall/sysenter/int80/int82/etc). */ + #define SPEC_CTRL_ENTRY_FROM_PV \ ++ ALTERNATIVE "", __stringify(DO_SPEC_CTRL_COND_IBPB maybexen=0), \ ++ X86_FEATURE_IBPB_ENTRY_PV; \ + ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_PV; \ + ALTERNATIVE "", __stringify(DO_SPEC_CTRL_ENTRY maybexen=0), \ + X86_FEATURE_SC_MSR_PV + + /* Use in interrupt/exception context. May interrupt Xen or PV context. */ + #define SPEC_CTRL_ENTRY_FROM_INTR \ ++ ALTERNATIVE "", __stringify(DO_SPEC_CTRL_COND_IBPB maybexen=1), \ ++ X86_FEATURE_IBPB_ENTRY_PV; \ + ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_PV; \ + ALTERNATIVE "", __stringify(DO_SPEC_CTRL_ENTRY maybexen=1), \ + X86_FEATURE_SC_MSR_PV +@@ -254,11 +287,23 @@ + * Requires %rsp=regs, %r14=stack_end, %rdx=0 + * Clobbers %rax, %rbx, %rcx, %rdx + * +- * This is logical merge of DO_OVERWRITE_RSB and DO_SPEC_CTRL_ENTRY +- * maybexen=1, but with conditionals rather than alternatives. ++ * This is logical merge of: ++ * DO_SPEC_CTRL_COND_IBPB maybexen=0 ++ * DO_OVERWRITE_RSB ++ * DO_SPEC_CTRL_ENTRY maybexen=1 ++ * but with conditionals rather than alternatives. + */ + movzbl STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14), %ebx + ++ test $SCF_ist_ibpb, %bl ++ jz .L\@_skip_ibpb ++ ++ mov $MSR_PRED_CMD, %ecx ++ mov $PRED_CMD_IBPB, %eax ++ wrmsr ++ ++.L\@_skip_ibpb: ++ + test $SCF_ist_rsb, %bl + jz .L\@_skip_rsb + diff --git a/xsa407-4.16-6.patch b/xsa407-4.16-6.patch new file mode 100644 index 0000000..92290dc --- /dev/null +++ b/xsa407-4.16-6.patch @@ -0,0 +1,93 @@ +From: Andrew Cooper +Subject: x86/cpuid: Enumeration for BTC_NO + +BTC_NO indicates that hardware is not succeptable to Branch Type Confusion. + +Zen3 CPUs don't suffer BTC. + +This is part of XSA-407. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/tools/libs/light/libxl_cpuid.c b/tools/libs/light/libxl_cpuid.c +index d462f9e421ed..bf6fdee360a9 100644 +--- a/tools/libs/light/libxl_cpuid.c ++++ b/tools/libs/light/libxl_cpuid.c +@@ -288,6 +288,7 @@ int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str) + {"virt-ssbd", 0x80000008, NA, CPUID_REG_EBX, 25, 1}, + {"ssb-no", 0x80000008, NA, CPUID_REG_EBX, 26, 1}, + {"psfd", 0x80000008, NA, CPUID_REG_EBX, 28, 1}, ++ {"btc-no", 0x80000008, NA, CPUID_REG_EBX, 29, 1}, + + {"nc", 0x80000008, NA, CPUID_REG_ECX, 0, 8}, + {"apicidsize", 0x80000008, NA, CPUID_REG_ECX, 12, 4}, +diff --git a/tools/misc/xen-cpuid.c b/tools/misc/xen-cpuid.c +index bc7dcf55757a..fe22f5f5b68b 100644 +--- a/tools/misc/xen-cpuid.c ++++ b/tools/misc/xen-cpuid.c +@@ -158,7 +158,7 @@ static const char *const str_e8b[32] = + /* [22] */ [23] = "ppin", + [24] = "amd-ssbd", [25] = "virt-ssbd", + [26] = "ssb-no", +- [28] = "psfd", ++ [28] = "psfd", [29] = "btc-no", + }; + + static const char *const str_7d0[32] = +diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c +index b3b9a0df5fed..b158e3acb5c7 100644 +--- a/xen/arch/x86/cpu/amd.c ++++ b/xen/arch/x86/cpu/amd.c +@@ -847,6 +847,16 @@ static void init_amd(struct cpuinfo_x86 *c) + warning_add(text); + } + break; ++ ++ case 0x19: ++ /* ++ * Zen3 (Fam19h model < 0x10) parts are not susceptible to ++ * Branch Type Confusion, but predate the allocation of the ++ * BTC_NO bit. Fill it back in if we're not virtualised. ++ */ ++ if (!cpu_has_hypervisor && !cpu_has(c, X86_FEATURE_BTC_NO)) ++ __set_bit(X86_FEATURE_BTC_NO, c->x86_capability); ++ break; + } + + display_cacheinfo(c); +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index f4ae36eae2d0..0f101c057f3e 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -388,7 +388,7 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + * Hardware read-only information, stating immunity to certain issues, or + * suggestions of which mitigation to use. + */ +- printk(" Hardware hints:%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", ++ printk(" Hardware hints:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", + (caps & ARCH_CAPS_RDCL_NO) ? " RDCL_NO" : "", + (caps & ARCH_CAPS_IBRS_ALL) ? " IBRS_ALL" : "", + (caps & ARCH_CAPS_RSBA) ? " RSBA" : "", +@@ -403,7 +403,8 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + (e8b & cpufeat_mask(X86_FEATURE_IBRS_ALWAYS)) ? " IBRS_ALWAYS" : "", + (e8b & cpufeat_mask(X86_FEATURE_STIBP_ALWAYS)) ? " STIBP_ALWAYS" : "", + (e8b & cpufeat_mask(X86_FEATURE_IBRS_FAST)) ? " IBRS_FAST" : "", +- (e8b & cpufeat_mask(X86_FEATURE_IBRS_SAME_MODE)) ? " IBRS_SAME_MODE" : ""); ++ (e8b & cpufeat_mask(X86_FEATURE_IBRS_SAME_MODE)) ? " IBRS_SAME_MODE" : "", ++ (e8b & cpufeat_mask(X86_FEATURE_BTC_NO)) ? " BTC_NO" : ""); + + /* Hardware features which need driving to mitigate issues. */ + printk(" Hardware features:%s%s%s%s%s%s%s%s%s%s%s%s\n", +diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h +index 743b857dcd5c..e7b8167800a2 100644 +--- a/xen/include/public/arch-x86/cpufeatureset.h ++++ b/xen/include/public/arch-x86/cpufeatureset.h +@@ -266,6 +266,7 @@ XEN_CPUFEATURE(AMD_SSBD, 8*32+24) /*S MSR_SPEC_CTRL.SSBD available */ + XEN_CPUFEATURE(VIRT_SSBD, 8*32+25) /* MSR_VIRT_SPEC_CTRL.SSBD */ + XEN_CPUFEATURE(SSB_NO, 8*32+26) /*A Hardware not vulnerable to SSB */ + XEN_CPUFEATURE(PSFD, 8*32+28) /*S MSR_SPEC_CTRL.PSFD */ ++XEN_CPUFEATURE(BTC_NO, 8*32+29) /*A Hardware not vulnerable to Branch Type Confusion */ + + /* Intel-defined CPU features, CPUID level 0x00000007:0.edx, word 9 */ + XEN_CPUFEATURE(AVX512_4VNNIW, 9*32+ 2) /*A AVX512 Neural Network Instructions */ diff --git a/xsa407-4.16-7.patch b/xsa407-4.16-7.patch new file mode 100644 index 0000000..b71a0f8 --- /dev/null +++ b/xsa407-4.16-7.patch @@ -0,0 +1,94 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Enable Zen2 chickenbit + +... as instructed in the Branch Type Confusion whitepaper. + +This is part of XSA-407. + +Signed-off-by: Andrew Cooper + +diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c +index b158e3acb5c7..37ac84ddd74d 100644 +--- a/xen/arch/x86/cpu/amd.c ++++ b/xen/arch/x86/cpu/amd.c +@@ -731,6 +731,31 @@ void amd_init_ssbd(const struct cpuinfo_x86 *c) + printk_once(XENLOG_ERR "No SSBD controls available\n"); + } + ++/* ++ * On Zen2 we offer this chicken (bit) on the altar of Speculation. ++ * ++ * Refer to the AMD Branch Type Confusion whitepaper: ++ * https://XXX ++ * ++ * Setting this unnamed bit supposedly causes prediction information on ++ * non-branch instructions to be ignored. It is to be set unilaterally in ++ * newer microcode. ++ * ++ * This chickenbit is something unrelated on Zen1, and Zen1 vs Zen2 isn't a ++ * simple model number comparison, so use STIBP as a heuristic to separate the ++ * two uarches in Fam17h(AMD)/18h(Hygon). ++ */ ++void amd_init_spectral_chicken(void) ++{ ++ uint64_t val, chickenbit = 1 << 1; ++ ++ if (cpu_has_hypervisor || !boot_cpu_has(X86_FEATURE_AMD_STIBP)) ++ return; ++ ++ if (rdmsr_safe(MSR_AMD64_DE_CFG2, val) == 0 && !(val & chickenbit)) ++ wrmsr_safe(MSR_AMD64_DE_CFG2, val | chickenbit); ++} ++ + void __init detect_zen2_null_seg_behaviour(void) + { + uint64_t base; +@@ -796,6 +821,9 @@ static void init_amd(struct cpuinfo_x86 *c) + + amd_init_ssbd(c); + ++ if (c->x86 == 0x17) ++ amd_init_spectral_chicken(); ++ + /* Probe for NSCB on Zen2 CPUs when not virtualised */ + if (!cpu_has_hypervisor && !cpu_has_nscb && c == &boot_cpu_data && + c->x86 == 0x17) +diff --git a/xen/arch/x86/cpu/cpu.h b/xen/arch/x86/cpu/cpu.h +index b593bd85f04f..145bc5156a86 100644 +--- a/xen/arch/x86/cpu/cpu.h ++++ b/xen/arch/x86/cpu/cpu.h +@@ -22,4 +22,5 @@ void early_init_amd(struct cpuinfo_x86 *c); + void amd_log_freq(const struct cpuinfo_x86 *c); + void amd_init_lfence(struct cpuinfo_x86 *c); + void amd_init_ssbd(const struct cpuinfo_x86 *c); ++void amd_init_spectral_chicken(void); + void detect_zen2_null_seg_behaviour(void); +diff --git a/xen/arch/x86/cpu/hygon.c b/xen/arch/x86/cpu/hygon.c +index cdc94130dd2e..6f8d491297e8 100644 +--- a/xen/arch/x86/cpu/hygon.c ++++ b/xen/arch/x86/cpu/hygon.c +@@ -41,6 +41,12 @@ static void init_hygon(struct cpuinfo_x86 *c) + detect_zen2_null_seg_behaviour(); + + /* ++ * TODO: Check heuristic safety with Hygon first ++ if (c->x86 == 0x18) ++ amd_init_spectral_chicken(); ++ */ ++ ++ /* + * Hygon CPUs before Zen2 don't clear segment bases/limits when + * loading a NULL selector. + */ +diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h +index 72bc32ba04ff..d3735e499e0f 100644 +--- a/xen/include/asm-x86/msr-index.h ++++ b/xen/include/asm-x86/msr-index.h +@@ -361,6 +361,7 @@ + #define MSR_AMD64_DE_CFG 0xc0011029 + #define AMD64_DE_CFG_LFENCE_SERIALISE (_AC(1, ULL) << 1) + #define MSR_AMD64_EX_CFG 0xc001102c ++#define MSR_AMD64_DE_CFG2 0xc00110e3 + + #define MSR_AMD64_DR0_ADDRESS_MASK 0xc0011027 + #define MSR_AMD64_DR1_ADDRESS_MASK 0xc0011019 diff --git a/xsa407-4.16-8.patch b/xsa407-4.16-8.patch new file mode 100644 index 0000000..8d53169 --- /dev/null +++ b/xsa407-4.16-8.patch @@ -0,0 +1,293 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Mitigate Branch Type Confusion when possible + +Branch Type Confusion affects AMD/Hygon CPUs on Zen2 and earlier. To +mitigate, we require SMT safety (STIBP on Zen2, no-SMT on Zen1), and to issue +an IBPB on each entry to Xen, to flush the BTB. + +Due to performance concerns, dom0 (which is trusted in most configurations) is +excluded from protections by default. + +Therefore: + * Use STIBP by default on Zen2 too, which now means we want it on by default + on all hardware supporting STIBP. + * Break the current IBPB logic out into a new function, extending it with + IBPB-at-entry logic. + * Change the existing IBPB-at-ctxt-switch boolean to be tristate, and disable + it by default when IBPB-at-entry is providing sufficient safety. + +If all PV guests on the system are trusted, then it is recommended to boot +with `spec-ctrl=ibpb-entry=no-pv`, as this will provide an additional marginal +perf improvement. + +This is part of XSA-407 / CVE-2022-23825. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc +index 1bbdb55129cc..bd6826d0ae05 100644 +--- a/docs/misc/xen-command-line.pandoc ++++ b/docs/misc/xen-command-line.pandoc +@@ -2234,7 +2234,7 @@ By default SSBD will be mitigated at runtime (i.e `ssbd=runtime`). + + ### spec-ctrl (x86) + > `= List of [ , xen=, {pv,hvm}=, +-> {msr-sc,rsb,md-clear}=|{pv,hvm}=, ++> {msr-sc,rsb,md-clear,ibpb-entry}=|{pv,hvm}=, + > bti-thunk=retpoline|lfence|jmp, {ibrs,ibpb,ssbd,psfd, + > eager-fpu,l1d-flush,branch-harden,srb-lock, + > unpriv-mmio}= ]` +@@ -2259,9 +2259,10 @@ in place for guests to use. + + Use of a positive boolean value for either of these options is invalid. + +-The `pv=`, `hvm=`, `msr-sc=`, `rsb=` and `md-clear=` options offer fine +-grained control over the primitives by Xen. These impact Xen's ability to +-protect itself, and/or Xen's ability to virtualise support for guests to use. ++The `pv=`, `hvm=`, `msr-sc=`, `rsb=`, `md-clear=` and `ibpb-entry=` options ++offer fine grained control over the primitives by Xen. These impact Xen's ++ability to protect itself, and/or Xen's ability to virtualise support for ++guests to use. + + * `pv=` and `hvm=` offer control over all suboptions for PV and HVM guests + respectively. +@@ -2280,6 +2281,11 @@ protect itself, and/or Xen's ability to virtualise support for guests to use. + compatibility with development versions of this fix, `mds=` is also accepted + on Xen 4.12 and earlier as an alias. Consult vendor documentation in + preference to here.* ++* `ibpb-entry=` offers control over whether IBPB (Indirect Branch Prediction ++ Barrier) is used on entry to Xen. This is used by default on hardware ++ vulnerable to Branch Type Confusion, but for performance reasons, dom0 is ++ unprotected by default. If it necessary to protect dom0 too, boot with ++ `spec-ctrl=ibpb-entry`. + + If Xen was compiled with INDIRECT_THUNK support, `bti-thunk=` can be used to + select which of the thunks gets patched into the `__x86_indirect_thunk_%reg` +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 0f101c057f3e..1d9796c34d71 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -39,6 +39,10 @@ static bool __initdata opt_rsb_hvm = true; + static int8_t __read_mostly opt_md_clear_pv = -1; + static int8_t __read_mostly opt_md_clear_hvm = -1; + ++static int8_t __read_mostly opt_ibpb_entry_pv = -1; ++static int8_t __read_mostly opt_ibpb_entry_hvm = -1; ++static bool __read_mostly opt_ibpb_entry_dom0; ++ + /* Cmdline controls for Xen's speculative settings. */ + static enum ind_thunk { + THUNK_DEFAULT, /* Decide which thunk to use at boot time. */ +@@ -54,7 +58,7 @@ int8_t __initdata opt_stibp = -1; + bool __read_mostly opt_ssbd; + int8_t __initdata opt_psfd = -1; + +-bool __read_mostly opt_ibpb_ctxt_switch = true; ++int8_t __read_mostly opt_ibpb_ctxt_switch = -1; + int8_t __read_mostly opt_eager_fpu = -1; + int8_t __read_mostly opt_l1d_flush = -1; + static bool __initdata opt_branch_harden = true; +@@ -114,6 +118,9 @@ static int __init parse_spec_ctrl(const char *s) + opt_rsb_hvm = false; + opt_md_clear_pv = 0; + opt_md_clear_hvm = 0; ++ opt_ibpb_entry_pv = 0; ++ opt_ibpb_entry_hvm = 0; ++ opt_ibpb_entry_dom0 = false; + + opt_thunk = THUNK_JMP; + opt_ibrs = 0; +@@ -140,12 +147,14 @@ static int __init parse_spec_ctrl(const char *s) + opt_msr_sc_pv = val; + opt_rsb_pv = val; + opt_md_clear_pv = val; ++ opt_ibpb_entry_pv = val; + } + else if ( (val = parse_boolean("hvm", s, ss)) >= 0 ) + { + opt_msr_sc_hvm = val; + opt_rsb_hvm = val; + opt_md_clear_hvm = val; ++ opt_ibpb_entry_hvm = val; + } + else if ( (val = parse_boolean("msr-sc", s, ss)) != -1 ) + { +@@ -210,6 +219,28 @@ static int __init parse_spec_ctrl(const char *s) + break; + } + } ++ else if ( (val = parse_boolean("ibpb-entry", s, ss)) != -1 ) ++ { ++ switch ( val ) ++ { ++ case 0: ++ case 1: ++ opt_ibpb_entry_pv = opt_ibpb_entry_hvm = ++ opt_ibpb_entry_dom0 = val; ++ break; ++ ++ case -2: ++ s += strlen("ibpb-entry="); ++ if ( (val = parse_boolean("pv", s, ss)) >= 0 ) ++ opt_ibpb_entry_pv = val; ++ else if ( (val = parse_boolean("hvm", s, ss)) >= 0 ) ++ opt_ibpb_entry_hvm = val; ++ else ++ default: ++ rc = -EINVAL; ++ break; ++ } ++ } + + /* Xen's speculative sidechannel mitigation settings. */ + else if ( !strncmp(s, "bti-thunk=", 10) ) +@@ -477,27 +508,31 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + * mitigation support for guests. + */ + #ifdef CONFIG_HVM +- printk(" Support for HVM VMs:%s%s%s%s%s\n", ++ printk(" Support for HVM VMs:%s%s%s%s%s%s\n", + (boot_cpu_has(X86_FEATURE_SC_MSR_HVM) || + boot_cpu_has(X86_FEATURE_SC_RSB_HVM) || + boot_cpu_has(X86_FEATURE_MD_CLEAR) || ++ boot_cpu_has(X86_FEATURE_IBPB_ENTRY_HVM) || + opt_eager_fpu) ? "" : " None", + boot_cpu_has(X86_FEATURE_SC_MSR_HVM) ? " MSR_SPEC_CTRL" : "", + boot_cpu_has(X86_FEATURE_SC_RSB_HVM) ? " RSB" : "", + opt_eager_fpu ? " EAGER_FPU" : "", +- boot_cpu_has(X86_FEATURE_MD_CLEAR) ? " MD_CLEAR" : ""); ++ boot_cpu_has(X86_FEATURE_MD_CLEAR) ? " MD_CLEAR" : "", ++ boot_cpu_has(X86_FEATURE_IBPB_ENTRY_HVM) ? " IBPB-entry" : ""); + + #endif + #ifdef CONFIG_PV +- printk(" Support for PV VMs:%s%s%s%s%s\n", ++ printk(" Support for PV VMs:%s%s%s%s%s%s\n", + (boot_cpu_has(X86_FEATURE_SC_MSR_PV) || + boot_cpu_has(X86_FEATURE_SC_RSB_PV) || + boot_cpu_has(X86_FEATURE_MD_CLEAR) || ++ boot_cpu_has(X86_FEATURE_IBPB_ENTRY_PV) || + opt_eager_fpu) ? "" : " None", + boot_cpu_has(X86_FEATURE_SC_MSR_PV) ? " MSR_SPEC_CTRL" : "", + boot_cpu_has(X86_FEATURE_SC_RSB_PV) ? " RSB" : "", + opt_eager_fpu ? " EAGER_FPU" : "", +- boot_cpu_has(X86_FEATURE_MD_CLEAR) ? " MD_CLEAR" : ""); ++ boot_cpu_has(X86_FEATURE_MD_CLEAR) ? " MD_CLEAR" : "", ++ boot_cpu_has(X86_FEATURE_IBPB_ENTRY_PV) ? " IBPB-entry" : ""); + + printk(" XPTI (64-bit PV only): Dom0 %s, DomU %s (with%s PCID)\n", + opt_xpti_hwdom ? "enabled" : "disabled", +@@ -759,6 +794,55 @@ static bool __init should_use_eager_fpu(void) + } + } + ++static void __init ibpb_calculations(void) ++{ ++ /* Check we have hardware IBPB support before using it... */ ++ if ( !boot_cpu_has(X86_FEATURE_IBRSB) && !boot_cpu_has(X86_FEATURE_IBPB) ) ++ { ++ opt_ibpb_entry_hvm = opt_ibpb_entry_pv = opt_ibpb_ctxt_switch = 0; ++ opt_ibpb_entry_dom0 = false; ++ return; ++ } ++ ++ /* ++ * IBPB-on-entry mitigations for Branch Type Confusion. ++ * ++ * IBPB && !BTC_NO selects all AMD/Hygon hardware, not known to be safe, ++ * that we can provide some form of mitigation on. ++ */ ++ if ( opt_ibpb_entry_pv == -1 ) ++ opt_ibpb_entry_pv = (IS_ENABLED(CONFIG_PV) && ++ boot_cpu_has(X86_FEATURE_IBPB) && ++ !boot_cpu_has(X86_FEATURE_BTC_NO)); ++ if ( opt_ibpb_entry_hvm == -1 ) ++ opt_ibpb_entry_hvm = (IS_ENABLED(CONFIG_HVM) && ++ boot_cpu_has(X86_FEATURE_IBPB) && ++ !boot_cpu_has(X86_FEATURE_BTC_NO)); ++ ++ if ( opt_ibpb_entry_pv ) ++ { ++ setup_force_cpu_cap(X86_FEATURE_IBPB_ENTRY_PV); ++ ++ /* ++ * We only need to flush in IST context if we're protecting against PV ++ * guests. HVM IBPB-on-entry protections are both atomic with ++ * NMI/#MC, so can't interrupt Xen ahead of having already flushed the ++ * BTB. ++ */ ++ default_spec_ctrl_flags |= SCF_ist_ibpb; ++ } ++ if ( opt_ibpb_entry_hvm ) ++ setup_force_cpu_cap(X86_FEATURE_IBPB_ENTRY_HVM); ++ ++ /* ++ * If we're using IBPB-on-entry to protect against PV and HVM guests ++ * (ignoring dom0 if trusted), then there's no need to also issue IBPB on ++ * context switch too. ++ */ ++ if ( opt_ibpb_ctxt_switch == -1 ) ++ opt_ibpb_ctxt_switch = !(opt_ibpb_entry_hvm && opt_ibpb_entry_pv); ++} ++ + /* Calculate whether this CPU is vulnerable to L1TF. */ + static __init void l1tf_calculations(uint64_t caps) + { +@@ -1014,8 +1098,12 @@ void spec_ctrl_init_domain(struct domain *d) + bool verw = ((pv ? opt_md_clear_pv : opt_md_clear_hvm) || + (opt_fb_clear_mmio && is_iommu_enabled(d))); + ++ bool ibpb = ((pv ? opt_ibpb_entry_pv : opt_ibpb_entry_hvm) && ++ (d->domain_id != 0 || opt_ibpb_entry_dom0)); ++ + d->arch.spec_ctrl_flags = + (verw ? SCF_verw : 0) | ++ (ibpb ? SCF_entry_ibpb : 0) | + 0; + } + +@@ -1162,12 +1250,15 @@ void __init init_speculation_mitigations(void) + } + + /* +- * Use STIBP by default if the hardware hint is set. Otherwise, leave it +- * off as it a severe performance pentalty on pre-eIBRS Intel hardware +- * where it was retrofitted in microcode. ++ * Use STIBP by default on all AMD systems. Zen3 and later enumerate ++ * STIBP_ALWAYS, but STIBP is needed on Zen2 as part of the mitigations ++ * for Branch Type Confusion. ++ * ++ * Leave STIBP off by default on Intel. Pre-eIBRS systems suffer a ++ * substantial perf hit when it was implemented in microcode. + */ + if ( opt_stibp == -1 ) +- opt_stibp = !!boot_cpu_has(X86_FEATURE_STIBP_ALWAYS); ++ opt_stibp = !!boot_cpu_has(X86_FEATURE_AMD_STIBP); + + if ( opt_stibp && (boot_cpu_has(X86_FEATURE_STIBP) || + boot_cpu_has(X86_FEATURE_AMD_STIBP)) ) +@@ -1239,9 +1330,7 @@ void __init init_speculation_mitigations(void) + if ( opt_rsb_hvm ) + setup_force_cpu_cap(X86_FEATURE_SC_RSB_HVM); + +- /* Check we have hardware IBPB support before using it... */ +- if ( !boot_cpu_has(X86_FEATURE_IBRSB) && !boot_cpu_has(X86_FEATURE_IBPB) ) +- opt_ibpb_ctxt_switch = false; ++ ibpb_calculations(); + + /* Check whether Eager FPU should be enabled by default. */ + if ( opt_eager_fpu == -1 ) +diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h +index 3fc599a817c4..9403b81dc7af 100644 +--- a/xen/include/asm-x86/spec_ctrl.h ++++ b/xen/include/asm-x86/spec_ctrl.h +@@ -65,7 +65,7 @@ + void init_speculation_mitigations(void); + void spec_ctrl_init_domain(struct domain *d); + +-extern bool opt_ibpb_ctxt_switch; ++extern int8_t opt_ibpb_ctxt_switch; + extern bool opt_ssbd; + extern int8_t opt_eager_fpu; + extern int8_t opt_l1d_flush; From abd09f4c108c879bcd9d8d7315bf3df9ad3192bc Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Sat, 23 Jul 2022 12:58:40 +0000 Subject: [PATCH 043/137] Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 02e1c71..5736a89 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.16.1 -Release: 6%{?dist} +Release: 7%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -986,6 +986,9 @@ fi %endif %changelog +* Sat Jul 23 2022 Fedora Release Engineering - 4.16.1-7 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild + * Tue Jul 12 2022 Michael Young - 4.16.1-6 - Retbleed - arbitrary speculative code execution with return instructions [XSA-407, CVE-2022-23816, CVE-2022-23825, CVE-2022-29900] From 305416ce5ac8f7272d3b85723c4cd3adec938ef0 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 26 Jul 2022 19:35:01 +0100 Subject: [PATCH 044/137] insufficient TLB flush for x86 PV guests in shadow mode [XSA-408, CVE-2022-33745] --- xen.spec | 8 +++++++- xsa408.patch | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 xsa408.patch diff --git a/xen.spec b/xen.spec index 5736a89..23a7806 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.16.1 -Release: 7%{?dist} +Release: 8%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -135,6 +135,7 @@ Patch67: xsa407-4.16-5.patch Patch68: xsa407-4.16-6.patch Patch69: xsa407-4.16-7.patch Patch70: xsa407-4.16-8.patch +Patch71: xsa408.patch %if %build_qemutrad @@ -371,6 +372,7 @@ manage Xen virtual machines. %patch68 -p1 %patch69 -p1 %patch70 -p1 +%patch71 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -986,6 +988,10 @@ fi %endif %changelog +* Tue Jul 26 2022 Michael Young - 4.16.1-8 +- insufficient TLB flush for x86 PV guests in shadow mode [XSA-408, + CVE-2022-33745] + * Sat Jul 23 2022 Fedora Release Engineering - 4.16.1-7 - Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild diff --git a/xsa408.patch b/xsa408.patch new file mode 100644 index 0000000..c58193f --- /dev/null +++ b/xsa408.patch @@ -0,0 +1,36 @@ +From: Jan Beulich +Subject: x86/mm: correct TLB flush condition in _get_page_type() + +When this logic was moved, it was moved across the point where nx is +updated to hold the new type for the page. IOW originally it was +equivalent to using x (and perhaps x would better have been used), but +now it isn't anymore. Switch to using x, which then brings things in +line again with the slightly earlier comment there (now) talking about +transitions _from_ writable. + +I have to confess though that I cannot make a direct connection between +the reported observed behavior of guests leaving several pages around +with pending general references and the change here. Repeated testing, +nevertheless, confirms the reported issue is no longer there. + +This is CVE-2022-33745 / XSA-408. + +Reported-by: Charles Arnold +Fixes: 8cc5036bc385 ("x86/pv: Fix ABAC cmpxchg() race in _get_page_type()") +Signed-off-by: Jan Beulich +Reviewed-by: Andrew Cooper +--- +I'd be happy to update the description to actually connect things, as +long as someone can give some plausible explanation. + +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -3038,7 +3038,7 @@ static int _get_page_type(struct page_in + if ( unlikely(!cpumask_empty(mask)) && + /* Shadow mode: track only writable pages. */ + (!shadow_mode_enabled(d) || +- ((nx & PGT_type_mask) == PGT_writable_page)) ) ++ ((x & PGT_type_mask) == PGT_writable_page)) ) + { + perfc_incr(need_flush_tlb_flush); + /* From 744bd1519465bc36618f642f7dd12ae90f59f9af Mon Sep 17 00:00:00 2001 From: Michael Young Date: Mon, 1 Aug 2022 19:39:42 +0100 Subject: [PATCH 045/137] add bug reference --- xen.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 23a7806..5f6be71 100644 --- a/xen.spec +++ b/xen.spec @@ -990,7 +990,7 @@ fi %changelog * Tue Jul 26 2022 Michael Young - 4.16.1-8 - insufficient TLB flush for x86 PV guests in shadow mode [XSA-408, - CVE-2022-33745] + CVE-2022-33745] (#2112223) * Sat Jul 23 2022 Fedora Release Engineering - 4.16.1-7 - Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild From 136d0a1506e43791ffe0752208799f626918130f Mon Sep 17 00:00:00 2001 From: Michael Young Date: Sat, 17 Sep 2022 16:52:46 +0100 Subject: [PATCH 046/137] update to xen-4.16.2 --- .gitignore | 2 +- sources | 2 +- ...33f4c80b7eaf9fb4e36ebba8259580857a9d.patch | 93 ------ ...ee56c7e0bb5eeb35dcc55b3d29e5f50eb566.patch | 43 --- ...a8176ccc4ff7dfe3c06969af9ebfab0d7b04.patch | 233 -------------- ...b02403a9df2523527caad24f17af5060fbe7.patch | 86 ----- ...c8bb3e5686141cef6fa1dc86ea9f37c5388a.patch | 137 -------- xen.spec | 58 +--- xsa401-4.16-1.patch | 170 ---------- xsa401-4.16-2.patch | 191 ------------ xsa402-4.16-1.patch | 43 --- xsa402-4.16-2.patch | 213 ------------- xsa402-4.16-3.patch | 284 ----------------- xsa402-4.16-4.patch | 83 ----- xsa402-4.16-5.patch | 148 --------- xsa403-4.16-1.patch | 56 ---- xsa404-4.16-1.patch | 239 -------------- xsa404-4.16-2.patch | 85 ----- xsa404-4.16-3.patch | 177 ----------- xsa407-4.16-1.patch | 153 --------- xsa407-4.16-2.patch | 99 ------ xsa407-4.16-3.patch | 86 ----- xsa407-4.16-4.patch | 96 ------ xsa407-4.16-5.patch | 285 ----------------- xsa407-4.16-6.patch | 93 ------ xsa407-4.16-7.patch | 94 ------ xsa407-4.16-8.patch | 293 ------------------ xsa408.patch | 36 --- 28 files changed, 8 insertions(+), 3570 deletions(-) delete mode 100644 xen.git-09d533f4c80b7eaf9fb4e36ebba8259580857a9d.patch delete mode 100644 xen.git-b378ee56c7e0bb5eeb35dcc55b3d29e5f50eb566.patch delete mode 100644 xen.git-db6ca8176ccc4ff7dfe3c06969af9ebfab0d7b04.patch delete mode 100644 xen.git-eec5b02403a9df2523527caad24f17af5060fbe7.patch delete mode 100644 xen.git-f066c8bb3e5686141cef6fa1dc86ea9f37c5388a.patch delete mode 100644 xsa401-4.16-1.patch delete mode 100644 xsa401-4.16-2.patch delete mode 100644 xsa402-4.16-1.patch delete mode 100644 xsa402-4.16-2.patch delete mode 100644 xsa402-4.16-3.patch delete mode 100644 xsa402-4.16-4.patch delete mode 100644 xsa402-4.16-5.patch delete mode 100644 xsa403-4.16-1.patch delete mode 100644 xsa404-4.16-1.patch delete mode 100644 xsa404-4.16-2.patch delete mode 100644 xsa404-4.16-3.patch delete mode 100644 xsa407-4.16-1.patch delete mode 100644 xsa407-4.16-2.patch delete mode 100644 xsa407-4.16-3.patch delete mode 100644 xsa407-4.16-4.patch delete mode 100644 xsa407-4.16-5.patch delete mode 100644 xsa407-4.16-6.patch delete mode 100644 xsa407-4.16-7.patch delete mode 100644 xsa407-4.16-8.patch delete mode 100644 xsa408.patch diff --git a/.gitignore b/.gitignore index 8f4e85c..f41c7f1 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.16.1.tar.gz +/xen-4.16.2.tar.gz diff --git a/sources b/sources index 6a26e23..f6803eb 100644 --- a/sources +++ b/sources @@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.16.1.tar.gz) = eeabba9c263cd2425bca083e32b5ebfc6c716c00553759c144fd4b6f64a89836b260787fa25ba22c1f5c4ea65aaad7c95b8c2c1070d3377b1c43c9517aa7032a +SHA512 (xen-4.16.2.tar.gz) = b6cd036c1073798dffa167ca14c954fbdfb4c0ef99662f7c435e7e5de687d1bde8856ff6bd030d0d2e661bd17ab631551f01b2cc728cad7e70b59aaa6e692783 diff --git a/xen.git-09d533f4c80b7eaf9fb4e36ebba8259580857a9d.patch b/xen.git-09d533f4c80b7eaf9fb4e36ebba8259580857a9d.patch deleted file mode 100644 index 1285967..0000000 --- a/xen.git-09d533f4c80b7eaf9fb4e36ebba8259580857a9d.patch +++ /dev/null @@ -1,93 +0,0 @@ -From 09d533f4c80b7eaf9fb4e36ebba8259580857a9d Mon Sep 17 00:00:00 2001 -From: Andrew Cooper -Date: Tue, 12 Jul 2022 11:12:46 +0200 -Subject: [PATCH] x86/spec-ctrl: Only adjust MSR_SPEC_CTRL for idle with legacy - IBRS -MIME-Version: 1.0 -Content-Type: text/plain; charset=utf8 -Content-Transfer-Encoding: 8bit - -Back at the time of the original Spectre-v2 fixes, it was recommended to clear -MSR_SPEC_CTRL when going idle. This is because of the side effects on the -sibling thread caused by the microcode IBRS and STIBP implementations which -were retrofitted to existing CPUs. - -However, there are no relevant cross-thread impacts for the hardware -IBRS/STIBP implementations, so this logic should not be used on Intel CPUs -supporting eIBRS, or any AMD CPUs; doing so only adds unnecessary latency to -the idle path. - -Furthermore, there's no point playing with MSR_SPEC_CTRL in the idle paths if -SMT is disabled for other reasons. - -Fixes: 8d03080d2a33 ("x86/spec-ctrl: Cease using thunk=lfence on AMD") -Signed-off-by: Andrew Cooper -Reviewed-by: Roger Pau Monné -master commit: ffc7694e0c99eea158c32aa164b7d1e1bb1dc46b -master date: 2022-06-30 18:07:13 +0100 ---- - xen/arch/x86/spec_ctrl.c | 10 ++++++++-- - xen/include/asm-x86/cpufeatures.h | 2 +- - xen/include/asm-x86/spec_ctrl.h | 5 +++-- - 3 files changed, 12 insertions(+), 5 deletions(-) - -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 099113ba41..1ed5ceda8b 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -1150,8 +1150,14 @@ void __init init_speculation_mitigations(void) - /* (Re)init BSP state now that default_spec_ctrl_flags has been calculated. */ - init_shadow_spec_ctrl_state(); - -- /* If Xen is using any MSR_SPEC_CTRL settings, adjust the idle path. */ -- if ( default_xen_spec_ctrl ) -+ /* -+ * For microcoded IBRS only (i.e. Intel, pre eIBRS), it is recommended to -+ * clear MSR_SPEC_CTRL before going idle, to avoid impacting sibling -+ * threads. Activate this if SMT is enabled, and Xen is using a non-zero -+ * MSR_SPEC_CTRL setting. -+ */ -+ if ( boot_cpu_has(X86_FEATURE_IBRSB) && !(caps & ARCH_CAPS_IBRS_ALL) && -+ hw_smt_enabled && default_xen_spec_ctrl ) - setup_force_cpu_cap(X86_FEATURE_SC_MSR_IDLE); - - xpti_init_default(caps); -diff --git a/xen/include/asm-x86/cpufeatures.h b/xen/include/asm-x86/cpufeatures.h -index bd45a144ee..493d338a08 100644 ---- a/xen/include/asm-x86/cpufeatures.h -+++ b/xen/include/asm-x86/cpufeatures.h -@@ -33,7 +33,7 @@ XEN_CPUFEATURE(SC_MSR_HVM, X86_SYNTH(17)) /* MSR_SPEC_CTRL used by Xen fo - XEN_CPUFEATURE(SC_RSB_PV, X86_SYNTH(18)) /* RSB overwrite needed for PV */ - XEN_CPUFEATURE(SC_RSB_HVM, X86_SYNTH(19)) /* RSB overwrite needed for HVM */ - XEN_CPUFEATURE(XEN_SELFSNOOP, X86_SYNTH(20)) /* SELFSNOOP gets used by Xen itself */ --XEN_CPUFEATURE(SC_MSR_IDLE, X86_SYNTH(21)) /* (SC_MSR_PV || SC_MSR_HVM) && default_xen_spec_ctrl */ -+XEN_CPUFEATURE(SC_MSR_IDLE, X86_SYNTH(21)) /* Clear MSR_SPEC_CTRL on idle */ - XEN_CPUFEATURE(XEN_LBR, X86_SYNTH(22)) /* Xen uses MSR_DEBUGCTL.LBR */ - /* Bits 23,24 unused. */ - XEN_CPUFEATURE(SC_VERW_IDLE, X86_SYNTH(25)) /* VERW used by Xen for idle */ -diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h -index 751355f471..7e83e0179f 100644 ---- a/xen/include/asm-x86/spec_ctrl.h -+++ b/xen/include/asm-x86/spec_ctrl.h -@@ -78,7 +78,8 @@ static always_inline void spec_ctrl_enter_idle(struct cpu_info *info) - uint32_t val = 0; - - /* -- * Branch Target Injection: -+ * It is recommended in some cases to clear MSR_SPEC_CTRL when going idle, -+ * to avoid impacting sibling threads. - * - * Latch the new shadow value, then enable shadowing, then update the MSR. - * There are no SMP issues here; only local processor ordering concerns. -@@ -114,7 +115,7 @@ static always_inline void spec_ctrl_exit_idle(struct cpu_info *info) - uint32_t val = info->xen_spec_ctrl; - - /* -- * Branch Target Injection: -+ * Restore MSR_SPEC_CTRL on exit from idle. - * - * Disable shadowing before updating the MSR. There are no SMP issues - * here; only local processor ordering concerns. --- -2.30.2 - diff --git a/xen.git-b378ee56c7e0bb5eeb35dcc55b3d29e5f50eb566.patch b/xen.git-b378ee56c7e0bb5eeb35dcc55b3d29e5f50eb566.patch deleted file mode 100644 index 2f1b7f5..0000000 --- a/xen.git-b378ee56c7e0bb5eeb35dcc55b3d29e5f50eb566.patch +++ /dev/null @@ -1,43 +0,0 @@ -From: Jan Beulich -Date: Tue, 7 Jun 2022 11:58:16 +0000 (+0200) -Subject: VT-d: refuse to use IOMMU with reserved CAP.ND value -X-Git-Url: http://xenbits.xenproject.org/gitweb/?p=xen.git;a=commitdiff_plain;h=b378ee56c7e0bb5eeb35dcc55b3d29e5f50eb566 - -VT-d: refuse to use IOMMU with reserved CAP.ND value - -The field taking the value 7 (resulting in 18-bit DIDs when using the -calculation in cap_ndoms(), when the DID fields are only 16 bits wide) -is reserved. Instead of misbehaving in case we would encounter such an -IOMMU, refuse to use it. - -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné -Reviewed-by: Kevin Tian -master commit: a1545fbf45c689aff39ce76a6eaa609d32ef72a7 -master date: 2022-04-20 10:54:26 +0200 ---- - -diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c -index 93dd8aa643..8975c1de61 100644 ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -1279,8 +1279,11 @@ int __init iommu_alloc(struct acpi_drhd_unit *drhd) - - quirk_iommu_caps(iommu); - -+ nr_dom = cap_ndoms(iommu->cap); -+ - if ( cap_fault_reg_offset(iommu->cap) + - cap_num_fault_regs(iommu->cap) * PRIMARY_FAULT_REG_LEN >= PAGE_SIZE || -+ ((nr_dom - 1) >> 16) /* I.e. cap.nd > 6 */ || - ecap_iotlb_offset(iommu->ecap) >= PAGE_SIZE ) - { - printk(XENLOG_ERR VTDPREFIX "IOMMU: unsupported\n"); -@@ -1305,7 +1308,6 @@ int __init iommu_alloc(struct acpi_drhd_unit *drhd) - vtd_ops.sync_cache = sync_cache; - - /* allocate domain id bitmap */ -- nr_dom = cap_ndoms(iommu->cap); - iommu->domid_bitmap = xzalloc_array(unsigned long, BITS_TO_LONGS(nr_dom)); - if ( !iommu->domid_bitmap ) - return -ENOMEM; diff --git a/xen.git-db6ca8176ccc4ff7dfe3c06969af9ebfab0d7b04.patch b/xen.git-db6ca8176ccc4ff7dfe3c06969af9ebfab0d7b04.patch deleted file mode 100644 index 4d5bb71..0000000 --- a/xen.git-db6ca8176ccc4ff7dfe3c06969af9ebfab0d7b04.patch +++ /dev/null @@ -1,233 +0,0 @@ -From db6ca8176ccc4ff7dfe3c06969af9ebfab0d7b04 Mon Sep 17 00:00:00 2001 -From: Andrew Cooper -Date: Tue, 12 Jul 2022 11:13:33 +0200 -Subject: [PATCH] x86/spec-ctrl: Knobs for STIBP and PSFD, and follow hardware - STIBP hint -MIME-Version: 1.0 -Content-Type: text/plain; charset=utf8 -Content-Transfer-Encoding: 8bit - -STIBP and PSFD are slightly weird bits, because they're both implied by other -bits in MSR_SPEC_CTRL. Add fine grain controls for them, and take the -implications into account when setting IBRS/SSBD. - -Rearrange the IBPB text/variables/logic to keep all the MSR_SPEC_CTRL bits -together, for consistency. - -However, AMD have a hardware hint CPUID bit recommending that STIBP be set -unilaterally. This is advertised on Zen3, so follow the recommendation. -Furthermore, in such cases, set STIBP behind the guest's back for now. This -has negligible overhead for the guest, but saves a WRMSR on vmentry. This is -the only default change. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -Reviewed-by: Roger Pau Monné -master commit: fef244b179c06fcdfa581f7d57fa6e578c49ff50 -master date: 2022-06-30 18:07:13 +0100 ---- - docs/misc/xen-command-line.pandoc | 21 +++++++--- - xen/arch/x86/hvm/svm/vmcb.c | 9 +++++ - xen/arch/x86/spec_ctrl.c | 65 ++++++++++++++++++++++++++----- - 3 files changed, 81 insertions(+), 14 deletions(-) - -diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc -index a642e43476..46e9c58d35 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -2234,8 +2234,9 @@ By default SSBD will be mitigated at runtime (i.e `ssbd=runtime`). - - ### spec-ctrl (x86) - > `= List of [ , xen=, {pv,hvm,msr-sc,rsb,md-clear}=, --> bti-thunk=retpoline|lfence|jmp, {ibrs,ibpb,ssbd,eager-fpu, --> l1d-flush,branch-harden,srb-lock,unpriv-mmio}= ]` -+> bti-thunk=retpoline|lfence|jmp, {ibrs,ibpb,ssbd,psfd, -+> eager-fpu,l1d-flush,branch-harden,srb-lock, -+> unpriv-mmio}= ]` - - Controls for speculative execution sidechannel mitigations. By default, Xen - will pick the most appropriate mitigations based on compiled in support, -@@ -2285,9 +2286,10 @@ On hardware supporting IBRS (Indirect Branch Restricted Speculation), the - If Xen is not using IBRS itself, functionality is still set up so IBRS can be - virtualised for guests. - --On hardware supporting IBPB (Indirect Branch Prediction Barrier), the `ibpb=` --option can be used to force (the default) or prevent Xen from issuing branch --prediction barriers on vcpu context switches. -+On hardware supporting STIBP (Single Thread Indirect Branch Predictors), the -+`stibp=` option can be used to force or prevent Xen using the feature itself. -+By default, Xen will use STIBP when IBRS is in use (IBRS implies STIBP), and -+when hardware hints recommend using it as a blanket setting. - - On hardware supporting SSBD (Speculative Store Bypass Disable), the `ssbd=` - option can be used to force or prevent Xen using the feature itself. On AMD -@@ -2295,6 +2297,15 @@ hardware, this is a global option applied at boot, and not virtualised for - guest use. On Intel hardware, the feature is virtualised for guests, - independently of Xen's choice of setting. - -+On hardware supporting PSFD (Predictive Store Forwarding Disable), the `psfd=` -+option can be used to force or prevent Xen using the feature itself. By -+default, Xen will not use PSFD. PSFD is implied by SSBD, and SSBD is off by -+default. -+ -+On hardware supporting IBPB (Indirect Branch Prediction Barrier), the `ibpb=` -+option can be used to force (the default) or prevent Xen from issuing branch -+prediction barriers on vcpu context switches. -+ - On all hardware, the `eager-fpu=` option can be used to force or prevent Xen - from using fully eager FPU context switches. This is currently implemented as - a global control. By default, Xen will choose to use fully eager context -diff --git a/xen/arch/x86/hvm/svm/vmcb.c b/xen/arch/x86/hvm/svm/vmcb.c -index 565e997155..ef7224eb5d 100644 ---- a/xen/arch/x86/hvm/svm/vmcb.c -+++ b/xen/arch/x86/hvm/svm/vmcb.c -@@ -29,6 +29,7 @@ - #include - #include - #include -+#include - - struct vmcb_struct *alloc_vmcb(void) - { -@@ -176,6 +177,14 @@ static int construct_vmcb(struct vcpu *v) - vmcb->_pause_filter_thresh = SVM_PAUSETHRESH_INIT; - } - -+ /* -+ * When default_xen_spec_ctrl simply SPEC_CTRL_STIBP, default this behind -+ * the back of the VM too. Our SMT topology isn't accurate, the overhead -+ * is neglegable, and doing this saves a WRMSR on the vmentry path. -+ */ -+ if ( default_xen_spec_ctrl == SPEC_CTRL_STIBP ) -+ v->arch.msrs->spec_ctrl.raw = SPEC_CTRL_STIBP; -+ - return 0; - } - -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 1ed5ceda8b..dfdd45c358 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -48,9 +48,13 @@ static enum ind_thunk { - THUNK_LFENCE, - THUNK_JMP, - } opt_thunk __initdata = THUNK_DEFAULT; -+ - static int8_t __initdata opt_ibrs = -1; -+int8_t __initdata opt_stibp = -1; -+bool __read_mostly opt_ssbd; -+int8_t __initdata opt_psfd = -1; -+ - bool __read_mostly opt_ibpb = true; --bool __read_mostly opt_ssbd = false; - int8_t __read_mostly opt_eager_fpu = -1; - int8_t __read_mostly opt_l1d_flush = -1; - static bool __initdata opt_branch_harden = true; -@@ -172,12 +176,20 @@ static int __init parse_spec_ctrl(const char *s) - else - rc = -EINVAL; - } -+ -+ /* Bits in MSR_SPEC_CTRL. */ - else if ( (val = parse_boolean("ibrs", s, ss)) >= 0 ) - opt_ibrs = val; -- else if ( (val = parse_boolean("ibpb", s, ss)) >= 0 ) -- opt_ibpb = val; -+ else if ( (val = parse_boolean("stibp", s, ss)) >= 0 ) -+ opt_stibp = val; - else if ( (val = parse_boolean("ssbd", s, ss)) >= 0 ) - opt_ssbd = val; -+ else if ( (val = parse_boolean("psfd", s, ss)) >= 0 ) -+ opt_psfd = val; -+ -+ /* Misc settings. */ -+ else if ( (val = parse_boolean("ibpb", s, ss)) >= 0 ) -+ opt_ibpb = val; - else if ( (val = parse_boolean("eager-fpu", s, ss)) >= 0 ) - opt_eager_fpu = val; - else if ( (val = parse_boolean("l1d-flush", s, ss)) >= 0 ) -@@ -376,7 +388,7 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - "\n"); - - /* Settings for Xen's protection, irrespective of guests. */ -- printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s%s, Other:%s%s%s%s%s\n", -+ printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s%s%s, Other:%s%s%s%s%s\n", - thunk == THUNK_NONE ? "N/A" : - thunk == THUNK_RETPOLINE ? "RETPOLINE" : - thunk == THUNK_LFENCE ? "LFENCE" : -@@ -390,6 +402,9 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - (!boot_cpu_has(X86_FEATURE_SSBD) && - !boot_cpu_has(X86_FEATURE_AMD_SSBD)) ? "" : - (default_xen_spec_ctrl & SPEC_CTRL_SSBD) ? " SSBD+" : " SSBD-", -+ (!boot_cpu_has(X86_FEATURE_PSFD) && -+ !boot_cpu_has(X86_FEATURE_INTEL_PSFD)) ? "" : -+ (default_xen_spec_ctrl & SPEC_CTRL_PSFD) ? " PSFD+" : " PSFD-", - !(caps & ARCH_CAPS_TSX_CTRL) ? "" : - (opt_tsx & 1) ? " TSX+" : " TSX-", - !cpu_has_srbds_ctrl ? "" : -@@ -979,10 +994,7 @@ void __init init_speculation_mitigations(void) - if ( !has_spec_ctrl ) - printk(XENLOG_WARNING "?!? CET active, but no MSR_SPEC_CTRL?\n"); - else if ( opt_ibrs == -1 ) -- { - opt_ibrs = ibrs = true; -- default_xen_spec_ctrl |= SPEC_CTRL_IBRS | SPEC_CTRL_STIBP; -- } - - if ( opt_thunk == THUNK_DEFAULT || opt_thunk == THUNK_RETPOLINE ) - thunk = THUNK_JMP; -@@ -1086,14 +1098,49 @@ void __init init_speculation_mitigations(void) - setup_force_cpu_cap(X86_FEATURE_SC_MSR_HVM); - } - -- /* If we have IBRS available, see whether we should use it. */ -+ /* Figure out default_xen_spec_ctrl. */ - if ( has_spec_ctrl && ibrs ) -+ { -+ /* IBRS implies STIBP. */ -+ if ( opt_stibp == -1 ) -+ opt_stibp = 1; -+ - default_xen_spec_ctrl |= SPEC_CTRL_IBRS; -+ } -+ -+ /* -+ * Use STIBP by default if the hardware hint is set. Otherwise, leave it -+ * off as it a severe performance pentalty on pre-eIBRS Intel hardware -+ * where it was retrofitted in microcode. -+ */ -+ if ( opt_stibp == -1 ) -+ opt_stibp = !!boot_cpu_has(X86_FEATURE_STIBP_ALWAYS); -+ -+ if ( opt_stibp && (boot_cpu_has(X86_FEATURE_STIBP) || -+ boot_cpu_has(X86_FEATURE_AMD_STIBP)) ) -+ default_xen_spec_ctrl |= SPEC_CTRL_STIBP; - -- /* If we have SSBD available, see whether we should use it. */ - if ( opt_ssbd && (boot_cpu_has(X86_FEATURE_SSBD) || - boot_cpu_has(X86_FEATURE_AMD_SSBD)) ) -+ { -+ /* SSBD implies PSFD */ -+ if ( opt_psfd == -1 ) -+ opt_psfd = 1; -+ - default_xen_spec_ctrl |= SPEC_CTRL_SSBD; -+ } -+ -+ /* -+ * Don't use PSFD by default. AMD designed the predictor to -+ * auto-clear on privilege change. PSFD is implied by SSBD, which is -+ * off by default. -+ */ -+ if ( opt_psfd == -1 ) -+ opt_psfd = 0; -+ -+ if ( opt_psfd && (boot_cpu_has(X86_FEATURE_PSFD) || -+ boot_cpu_has(X86_FEATURE_INTEL_PSFD)) ) -+ default_xen_spec_ctrl |= SPEC_CTRL_PSFD; - - /* - * PV guests can create RSB entries for any linear address they control, --- -2.30.2 - diff --git a/xen.git-eec5b02403a9df2523527caad24f17af5060fbe7.patch b/xen.git-eec5b02403a9df2523527caad24f17af5060fbe7.patch deleted file mode 100644 index 1394851..0000000 --- a/xen.git-eec5b02403a9df2523527caad24f17af5060fbe7.patch +++ /dev/null @@ -1,86 +0,0 @@ -From eec5b02403a9df2523527caad24f17af5060fbe7 Mon Sep 17 00:00:00 2001 -From: Andrew Cooper -Date: Tue, 12 Jul 2022 11:15:03 +0200 -Subject: [PATCH] xen/cmdline: Extend parse_boolean() to signal a name match - -This will help parsing a sub-option which has boolean and non-boolean options -available. - -First, rework 'int val' into 'bool has_neg_prefix'. This inverts it's value, -but the resulting logic is far easier to follow. - -Second, reject anything of the form 'no-$FOO=' which excludes ambiguous -constructs such as 'no-$foo=yes' which have never been valid. - -This just leaves the case where everything is otherwise fine, but parse_bool() -can't interpret the provided string. - -Signed-off-by: Andrew Cooper -Reviewed-by: Juergen Gross -Reviewed-by: Jan Beulich -master commit: 382326cac528dd1eb0d04efd5c05363c453e29f4 -master date: 2022-07-11 15:21:35 +0100 ---- - xen/common/kernel.c | 20 ++++++++++++++++---- - xen/include/xen/lib.h | 3 ++- - 2 files changed, 18 insertions(+), 5 deletions(-) - -diff --git a/xen/common/kernel.c b/xen/common/kernel.c -index e119e5401f..7ed96521f9 100644 ---- a/xen/common/kernel.c -+++ b/xen/common/kernel.c -@@ -272,9 +272,9 @@ int parse_bool(const char *s, const char *e) - int parse_boolean(const char *name, const char *s, const char *e) - { - size_t slen, nlen; -- int val = !!strncmp(s, "no-", 3); -+ bool has_neg_prefix = !strncmp(s, "no-", 3); - -- if ( !val ) -+ if ( has_neg_prefix ) - s += 3; - - slen = e ? ({ ASSERT(e >= s); e - s; }) : strlen(s); -@@ -286,11 +286,23 @@ int parse_boolean(const char *name, const char *s, const char *e) - - /* Exact, unadorned name? Result depends on the 'no-' prefix. */ - if ( slen == nlen ) -- return val; -+ return !has_neg_prefix; -+ -+ /* Inexact match with a 'no-' prefix? Not valid. */ -+ if ( has_neg_prefix ) -+ return -1; - - /* =$SOMETHING? Defer to the regular boolean parsing. */ - if ( s[nlen] == '=' ) -- return parse_bool(&s[nlen + 1], e); -+ { -+ int b = parse_bool(&s[nlen + 1], e); -+ -+ if ( b >= 0 ) -+ return b; -+ -+ /* Not a boolean, but the name matched. Signal specially. */ -+ return -2; -+ } - - /* Unrecognised. Give up. */ - return -1; -diff --git a/xen/include/xen/lib.h b/xen/include/xen/lib.h -index c6987973bf..2296044caf 100644 ---- a/xen/include/xen/lib.h -+++ b/xen/include/xen/lib.h -@@ -80,7 +80,8 @@ int parse_bool(const char *s, const char *e); - /** - * Given a specific name, parses a string of the form: - * [no-]$NAME[=...] -- * returning 0 or 1 for a recognised boolean, or -1 for an error. -+ * returning 0 or 1 for a recognised boolean. Returns -1 for general errors, -+ * and -2 for "not a boolean, but $NAME= matches". - */ - int parse_boolean(const char *name, const char *s, const char *e); - --- -2.30.2 - diff --git a/xen.git-f066c8bb3e5686141cef6fa1dc86ea9f37c5388a.patch b/xen.git-f066c8bb3e5686141cef6fa1dc86ea9f37c5388a.patch deleted file mode 100644 index 56cdada..0000000 --- a/xen.git-f066c8bb3e5686141cef6fa1dc86ea9f37c5388a.patch +++ /dev/null @@ -1,137 +0,0 @@ -From f066c8bb3e5686141cef6fa1dc86ea9f37c5388a Mon Sep 17 00:00:00 2001 -From: Andrew Cooper -Date: Tue, 12 Jul 2022 11:15:37 +0200 -Subject: [PATCH] x86/spec-ctrl: Add fine-grained cmdline suboptions for - primitives - -Support controling the PV/HVM suboption of msr-sc/rsb/md-clear, which -previously wasn't possible. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -master commit: 27357c394ba6e1571a89105b840ce1c6f026485c -master date: 2022-07-11 15:21:35 +0100 ---- - docs/misc/xen-command-line.pandoc | 12 ++++-- - xen/arch/x86/spec_ctrl.c | 66 ++++++++++++++++++++++++++----- - 2 files changed, 66 insertions(+), 12 deletions(-) - -diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc -index 46e9c58d35..1bbdb55129 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -2233,7 +2233,8 @@ not be able to control the state of the mitigation. - By default SSBD will be mitigated at runtime (i.e `ssbd=runtime`). - - ### spec-ctrl (x86) --> `= List of [ , xen=, {pv,hvm,msr-sc,rsb,md-clear}=, -+> `= List of [ , xen=, {pv,hvm}=, -+> {msr-sc,rsb,md-clear}=|{pv,hvm}=, - > bti-thunk=retpoline|lfence|jmp, {ibrs,ibpb,ssbd,psfd, - > eager-fpu,l1d-flush,branch-harden,srb-lock, - > unpriv-mmio}= ]` -@@ -2258,12 +2259,17 @@ in place for guests to use. - - Use of a positive boolean value for either of these options is invalid. - --The booleans `pv=`, `hvm=`, `msr-sc=`, `rsb=` and `md-clear=` offer fine -+The `pv=`, `hvm=`, `msr-sc=`, `rsb=` and `md-clear=` options offer fine - grained control over the primitives by Xen. These impact Xen's ability to --protect itself, and Xen's ability to virtualise support for guests to use. -+protect itself, and/or Xen's ability to virtualise support for guests to use. - - * `pv=` and `hvm=` offer control over all suboptions for PV and HVM guests - respectively. -+* Each other option can be used either as a plain boolean -+ (e.g. `spec-ctrl=rsb` to control both the PV and HVM sub-options), or with -+ `pv=` or `hvm=` subsuboptions (e.g. `spec-ctrl=rsb=no-hvm` to disable HVM -+ RSB only). -+ - * `msr-sc=` offers control over Xen's support for manipulating `MSR_SPEC_CTRL` - on entry and exit. These blocks are necessary to virtualise support for - guests and if disabled, guests will be unable to use IBRS/STIBP/SSBD/etc. -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index ae74943c10..9507e5da60 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -147,20 +147,68 @@ static int __init parse_spec_ctrl(const char *s) - opt_rsb_hvm = val; - opt_md_clear_hvm = val; - } -- else if ( (val = parse_boolean("msr-sc", s, ss)) >= 0 ) -+ else if ( (val = parse_boolean("msr-sc", s, ss)) != -1 ) - { -- opt_msr_sc_pv = val; -- opt_msr_sc_hvm = val; -+ switch ( val ) -+ { -+ case 0: -+ case 1: -+ opt_msr_sc_pv = opt_msr_sc_hvm = val; -+ break; -+ -+ case -2: -+ s += strlen("msr-sc="); -+ if ( (val = parse_boolean("pv", s, ss)) >= 0 ) -+ opt_msr_sc_pv = val; -+ else if ( (val = parse_boolean("hvm", s, ss)) >= 0 ) -+ opt_msr_sc_hvm = val; -+ else -+ default: -+ rc = -EINVAL; -+ break; -+ } - } -- else if ( (val = parse_boolean("rsb", s, ss)) >= 0 ) -+ else if ( (val = parse_boolean("rsb", s, ss)) != -1 ) - { -- opt_rsb_pv = val; -- opt_rsb_hvm = val; -+ switch ( val ) -+ { -+ case 0: -+ case 1: -+ opt_rsb_pv = opt_rsb_hvm = val; -+ break; -+ -+ case -2: -+ s += strlen("rsb="); -+ if ( (val = parse_boolean("pv", s, ss)) >= 0 ) -+ opt_rsb_pv = val; -+ else if ( (val = parse_boolean("hvm", s, ss)) >= 0 ) -+ opt_rsb_hvm = val; -+ else -+ default: -+ rc = -EINVAL; -+ break; -+ } - } -- else if ( (val = parse_boolean("md-clear", s, ss)) >= 0 ) -+ else if ( (val = parse_boolean("md-clear", s, ss)) != -1 ) - { -- opt_md_clear_pv = val; -- opt_md_clear_hvm = val; -+ switch ( val ) -+ { -+ case 0: -+ case 1: -+ opt_md_clear_pv = opt_md_clear_hvm = val; -+ break; -+ -+ case -2: -+ s += strlen("md-clear="); -+ if ( (val = parse_boolean("pv", s, ss)) >= 0 ) -+ opt_md_clear_pv = val; -+ else if ( (val = parse_boolean("hvm", s, ss)) >= 0 ) -+ opt_md_clear_hvm = val; -+ else -+ default: -+ rc = -EINVAL; -+ break; -+ } - } - - /* Xen's speculative sidechannel mitigation settings. */ --- -2.30.2 - diff --git a/xen.spec b/xen.spec index 5f6be71..db5b96d 100644 --- a/xen.spec +++ b/xen.spec @@ -54,8 +54,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.16.1 -Release: 8%{?dist} +Version: 4.16.2 +Release: 1%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -111,31 +111,6 @@ Patch43: xen.gcc11.fixes.patch Patch44: xsa376.patch Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch -Patch47: xsa401-4.16-1.patch -Patch48: xsa401-4.16-2.patch -Patch49: xsa402-4.16-1.patch -Patch50: xsa402-4.16-2.patch -Patch51: xen.git-b378ee56c7e0bb5eeb35dcc55b3d29e5f50eb566.patch -Patch52: xsa402-4.16-3.patch -Patch53: xsa402-4.16-4.patch -Patch54: xsa402-4.16-5.patch -Patch55: xsa404-4.16-1.patch -Patch56: xsa404-4.16-2.patch -Patch57: xsa404-4.16-3.patch -Patch58: xsa403-4.16-1.patch -Patch59: xen.git-09d533f4c80b7eaf9fb4e36ebba8259580857a9d.patch -Patch60: xen.git-db6ca8176ccc4ff7dfe3c06969af9ebfab0d7b04.patch -Patch61: xen.git-eec5b02403a9df2523527caad24f17af5060fbe7.patch -Patch62: xen.git-f066c8bb3e5686141cef6fa1dc86ea9f37c5388a.patch -Patch63: xsa407-4.16-1.patch -Patch64: xsa407-4.16-2.patch -Patch65: xsa407-4.16-3.patch -Patch66: xsa407-4.16-4.patch -Patch67: xsa407-4.16-5.patch -Patch68: xsa407-4.16-6.patch -Patch69: xsa407-4.16-7.patch -Patch70: xsa407-4.16-8.patch -Patch71: xsa408.patch %if %build_qemutrad @@ -348,31 +323,6 @@ manage Xen virtual machines. %patch44 -p1 %patch45 -p1 %patch46 -p1 -%patch47 -p1 -%patch48 -p1 -%patch49 -p1 -%patch50 -p1 -%patch51 -p1 -%patch52 -p1 -%patch53 -p1 -%patch54 -p1 -%patch55 -p1 -%patch56 -p1 -%patch57 -p1 -%patch58 -p1 -%patch59 -p1 -%patch60 -p1 -%patch61 -p1 -%patch62 -p1 -%patch63 -p1 -%patch64 -p1 -%patch65 -p1 -%patch66 -p1 -%patch67 -p1 -%patch68 -p1 -%patch69 -p1 -%patch70 -p1 -%patch71 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -988,6 +938,10 @@ fi %endif %changelog +* Sat Sep 17 2022 Michael Young - 4.16.2-1 +- update to xen-4.16.2 + remove or adjust patches now included or superceded upstream + * Tue Jul 26 2022 Michael Young - 4.16.1-8 - insufficient TLB flush for x86 PV guests in shadow mode [XSA-408, CVE-2022-33745] (#2112223) diff --git a/xsa401-4.16-1.patch b/xsa401-4.16-1.patch deleted file mode 100644 index 5c8c506..0000000 --- a/xsa401-4.16-1.patch +++ /dev/null @@ -1,170 +0,0 @@ -From: Andrew Cooper -Subject: x86/pv: Clean up _get_page_type() - -Various fixes for clarity, ahead of making complicated changes. - - * Split the overflow check out of the if/else chain for type handling, as - it's somewhat unrelated. - * Comment the main if/else chain to explain what is going on. Adjust one - ASSERT() and state the bit layout for validate-locked and partial states. - * Correct the comment about TLB flushing, as it's backwards. The problem - case is when writeable mappings are retained to a page becoming read-only, - as it allows the guest to bypass Xen's safety checks for updates. - * Reduce the scope of 'y'. It is an artefact of the cmpxchg loop and not - valid for use by subsequent logic. Switch to using ACCESS_ONCE() to treat - all reads as explicitly volatile. The only thing preventing the validated - wait-loop being infinite is the compiler barrier hidden in cpu_relax(). - * Replace one page_get_owner(page) with the already-calculated 'd' already in - scope. - -No functional change. - -This is part of XSA-401 / CVE-2022-26362. - -Signed-off-by: Andrew Cooper -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich -Reviewed-by: George Dunlap - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 796faca64103..ddd32f88c798 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -2935,16 +2935,17 @@ static int _put_page_type(struct page_info *page, unsigned int flags, - static int _get_page_type(struct page_info *page, unsigned long type, - bool preemptible) - { -- unsigned long nx, x, y = page->u.inuse.type_info; -+ unsigned long nx, x; - int rc = 0; - - ASSERT(!(type & ~(PGT_type_mask | PGT_pae_xen_l2))); - ASSERT(!in_irq()); - -- for ( ; ; ) -+ for ( unsigned long y = ACCESS_ONCE(page->u.inuse.type_info); ; ) - { - x = y; - nx = x + 1; -+ - if ( unlikely((nx & PGT_count_mask) == 0) ) - { - gdprintk(XENLOG_WARNING, -@@ -2952,8 +2953,15 @@ static int _get_page_type(struct page_info *page, unsigned long type, - mfn_x(page_to_mfn(page))); - return -EINVAL; - } -- else if ( unlikely((x & PGT_count_mask) == 0) ) -+ -+ if ( unlikely((x & PGT_count_mask) == 0) ) - { -+ /* -+ * Typeref 0 -> 1. -+ * -+ * Type changes are permitted when the typeref is 0. If the type -+ * actually changes, the page needs re-validating. -+ */ - struct domain *d = page_get_owner(page); - - if ( d && shadow_mode_enabled(d) ) -@@ -2964,8 +2972,8 @@ static int _get_page_type(struct page_info *page, unsigned long type, - { - /* - * On type change we check to flush stale TLB entries. It is -- * vital that no other CPUs are left with mappings of a frame -- * which is about to become writeable to the guest. -+ * vital that no other CPUs are left with writeable mappings -+ * to a frame which is intending to become pgtable/segdesc. - */ - cpumask_t *mask = this_cpu(scratch_cpumask); - -@@ -2977,7 +2985,7 @@ static int _get_page_type(struct page_info *page, unsigned long type, - - if ( unlikely(!cpumask_empty(mask)) && - /* Shadow mode: track only writable pages. */ -- (!shadow_mode_enabled(page_get_owner(page)) || -+ (!shadow_mode_enabled(d) || - ((nx & PGT_type_mask) == PGT_writable_page)) ) - { - perfc_incr(need_flush_tlb_flush); -@@ -3008,7 +3016,14 @@ static int _get_page_type(struct page_info *page, unsigned long type, - } - else if ( unlikely((x & (PGT_type_mask|PGT_pae_xen_l2)) != type) ) - { -- /* Don't log failure if it could be a recursive-mapping attempt. */ -+ /* -+ * else, we're trying to take a new reference, of the wrong type. -+ * -+ * This (being able to prohibit use of the wrong type) is what the -+ * typeref system exists for, but skip printing the failure if it -+ * looks like a recursive mapping, as subsequent logic might -+ * ultimately permit the attempt. -+ */ - if ( ((x & PGT_type_mask) == PGT_l2_page_table) && - (type == PGT_l1_page_table) ) - return -EINVAL; -@@ -3027,18 +3042,46 @@ static int _get_page_type(struct page_info *page, unsigned long type, - } - else if ( unlikely(!(x & PGT_validated)) ) - { -+ /* -+ * else, the count is non-zero, and we're grabbing the right type; -+ * but the page hasn't been validated yet. -+ * -+ * The page is in one of two states (depending on PGT_partial), -+ * and should have exactly one reference. -+ */ -+ ASSERT((x & (PGT_type_mask | PGT_count_mask)) == (type | 1)); -+ - if ( !(x & PGT_partial) ) - { -- /* Someone else is updating validation of this page. Wait... */ -+ /* -+ * The page has been left in the "validate locked" state -+ * (i.e. PGT_[type] | 1) which means that a concurrent caller -+ * of _get_page_type() is in the middle of validation. -+ * -+ * Spin waiting for the concurrent user to complete (partial -+ * or fully validated), then restart our attempt to acquire a -+ * type reference. -+ */ - do { - if ( preemptible && hypercall_preempt_check() ) - return -EINTR; - cpu_relax(); -- } while ( (y = page->u.inuse.type_info) == x ); -+ } while ( (y = ACCESS_ONCE(page->u.inuse.type_info)) == x ); - continue; - } -- /* Type ref count was left at 1 when PGT_partial got set. */ -- ASSERT((x & PGT_count_mask) == 1); -+ -+ /* -+ * The page has been left in the "partial" state -+ * (i.e., PGT_[type] | PGT_partial | 1). -+ * -+ * Rather than bumping the type count, we need to try to grab the -+ * validation lock; if we succeed, we need to validate the page, -+ * then drop the general ref associated with the PGT_partial bit. -+ * -+ * We grab the validation lock by setting nx to (PGT_[type] | 1) -+ * (i.e., non-zero type count, neither PGT_validated nor -+ * PGT_partial set). -+ */ - nx = x & ~PGT_partial; - } - -@@ -3087,6 +3130,13 @@ static int _get_page_type(struct page_info *page, unsigned long type, - } - - out: -+ /* -+ * Did we drop the PGT_partial bit when acquiring the typeref? If so, -+ * drop the general reference that went along with it. -+ * -+ * N.B. validate_page() may have have re-set PGT_partial, not reflected in -+ * nx, but will have taken an extra ref when doing so. -+ */ - if ( (x & PGT_partial) && !(nx & PGT_partial) ) - put_page(page); - diff --git a/xsa401-4.16-2.patch b/xsa401-4.16-2.patch deleted file mode 100644 index be58db5..0000000 --- a/xsa401-4.16-2.patch +++ /dev/null @@ -1,191 +0,0 @@ -From: Andrew Cooper -Subject: x86/pv: Fix ABAC cmpxchg() race in _get_page_type() - -_get_page_type() suffers from a race condition where it incorrectly assumes -that because 'x' was read and a subsequent a cmpxchg() succeeds, the type -cannot have changed in-between. Consider: - -CPU A: - 1. Creates an L2e referencing pg - `-> _get_page_type(pg, PGT_l1_page_table), sees count 0, type PGT_writable_page - 2. Issues flush_tlb_mask() -CPU B: - 3. Creates a writeable mapping of pg - `-> _get_page_type(pg, PGT_writable_page), count increases to 1 - 4. Writes into new mapping, creating a TLB entry for pg - 5. Removes the writeable mapping of pg - `-> _put_page_type(pg), count goes back down to 0 -CPU A: - 7. Issues cmpxchg(), setting count 1, type PGT_l1_page_table - -CPU B now has a writeable mapping to pg, which Xen believes is a pagetable and -suitably protected (i.e. read-only). The TLB flush in step 2 must be deferred -until after the guest is prohibited from creating new writeable mappings, -which is after step 7. - -Defer all safety actions until after the cmpxchg() has successfully taken the -intended typeref, because that is what prevents concurrent users from using -the old type. - -Also remove the early validation for writeable and shared pages. This removes -race conditions where one half of a parallel mapping attempt can return -successfully before: - * The IOMMU pagetables are in sync with the new page type - * Writeable mappings to shared pages have been torn down - -This is part of XSA-401 / CVE-2022-26362. - -Reported-by: Jann Horn -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -Reviewed-by: George Dunlap - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index ddd32f88c798..1693b580b152 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -2962,56 +2962,12 @@ static int _get_page_type(struct page_info *page, unsigned long type, - * Type changes are permitted when the typeref is 0. If the type - * actually changes, the page needs re-validating. - */ -- struct domain *d = page_get_owner(page); -- -- if ( d && shadow_mode_enabled(d) ) -- shadow_prepare_page_type_change(d, page, type); - - ASSERT(!(x & PGT_pae_xen_l2)); - if ( (x & PGT_type_mask) != type ) - { -- /* -- * On type change we check to flush stale TLB entries. It is -- * vital that no other CPUs are left with writeable mappings -- * to a frame which is intending to become pgtable/segdesc. -- */ -- cpumask_t *mask = this_cpu(scratch_cpumask); -- -- BUG_ON(in_irq()); -- cpumask_copy(mask, d->dirty_cpumask); -- -- /* Don't flush if the timestamp is old enough */ -- tlbflush_filter(mask, page->tlbflush_timestamp); -- -- if ( unlikely(!cpumask_empty(mask)) && -- /* Shadow mode: track only writable pages. */ -- (!shadow_mode_enabled(d) || -- ((nx & PGT_type_mask) == PGT_writable_page)) ) -- { -- perfc_incr(need_flush_tlb_flush); -- /* -- * If page was a page table make sure the flush is -- * performed using an IPI in order to avoid changing the -- * type of a page table page under the feet of -- * spurious_page_fault(). -- */ -- flush_mask(mask, -- (x & PGT_type_mask) && -- (x & PGT_type_mask) <= PGT_root_page_table -- ? FLUSH_TLB | FLUSH_FORCE_IPI -- : FLUSH_TLB); -- } -- -- /* We lose existing type and validity. */ - nx &= ~(PGT_type_mask | PGT_validated); - nx |= type; -- -- /* -- * No special validation needed for writable pages. -- * Page tables and GDT/LDT need to be scanned for validity. -- */ -- if ( type == PGT_writable_page || type == PGT_shared_page ) -- nx |= PGT_validated; - } - } - else if ( unlikely((x & (PGT_type_mask|PGT_pae_xen_l2)) != type) ) -@@ -3092,6 +3048,56 @@ static int _get_page_type(struct page_info *page, unsigned long type, - return -EINTR; - } - -+ /* -+ * One typeref has been taken and is now globally visible. -+ * -+ * The page is either in the "validate locked" state (PGT_[type] | 1) or -+ * fully validated (PGT_[type] | PGT_validated | >0). -+ */ -+ -+ if ( unlikely((x & PGT_count_mask) == 0) ) -+ { -+ struct domain *d = page_get_owner(page); -+ -+ if ( d && shadow_mode_enabled(d) ) -+ shadow_prepare_page_type_change(d, page, type); -+ -+ if ( (x & PGT_type_mask) != type ) -+ { -+ /* -+ * On type change we check to flush stale TLB entries. It is -+ * vital that no other CPUs are left with writeable mappings -+ * to a frame which is intending to become pgtable/segdesc. -+ */ -+ cpumask_t *mask = this_cpu(scratch_cpumask); -+ -+ BUG_ON(in_irq()); -+ cpumask_copy(mask, d->dirty_cpumask); -+ -+ /* Don't flush if the timestamp is old enough */ -+ tlbflush_filter(mask, page->tlbflush_timestamp); -+ -+ if ( unlikely(!cpumask_empty(mask)) && -+ /* Shadow mode: track only writable pages. */ -+ (!shadow_mode_enabled(d) || -+ ((nx & PGT_type_mask) == PGT_writable_page)) ) -+ { -+ perfc_incr(need_flush_tlb_flush); -+ /* -+ * If page was a page table make sure the flush is -+ * performed using an IPI in order to avoid changing the -+ * type of a page table page under the feet of -+ * spurious_page_fault(). -+ */ -+ flush_mask(mask, -+ (x & PGT_type_mask) && -+ (x & PGT_type_mask) <= PGT_root_page_table -+ ? FLUSH_TLB | FLUSH_FORCE_IPI -+ : FLUSH_TLB); -+ } -+ } -+ } -+ - if ( unlikely(((x & PGT_type_mask) == PGT_writable_page) != - (type == PGT_writable_page)) ) - { -@@ -3120,13 +3126,25 @@ static int _get_page_type(struct page_info *page, unsigned long type, - - if ( unlikely(!(nx & PGT_validated)) ) - { -- if ( !(x & PGT_partial) ) -+ /* -+ * No special validation needed for writable or shared pages. Page -+ * tables and GDT/LDT need to have their contents audited. -+ * -+ * per validate_page(), non-atomic updates are fine here. -+ */ -+ if ( type == PGT_writable_page || type == PGT_shared_page ) -+ page->u.inuse.type_info |= PGT_validated; -+ else - { -- page->nr_validated_ptes = 0; -- page->partial_flags = 0; -- page->linear_pt_count = 0; -+ if ( !(x & PGT_partial) ) -+ { -+ page->nr_validated_ptes = 0; -+ page->partial_flags = 0; -+ page->linear_pt_count = 0; -+ } -+ -+ rc = validate_page(page, type, preemptible); - } -- rc = validate_page(page, type, preemptible); - } - - out: diff --git a/xsa402-4.16-1.patch b/xsa402-4.16-1.patch deleted file mode 100644 index b783383..0000000 --- a/xsa402-4.16-1.patch +++ /dev/null @@ -1,43 +0,0 @@ -From: Andrew Cooper -Subject: x86/page: Introduce _PAGE_* constants for memory types - -... rather than opencoding the PAT/PCD/PWT attributes in __PAGE_HYPERVISOR_* -constants. These are going to be needed by forthcoming logic. - -No functional change. - -This is part of XSA-402. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/include/asm-x86/page.h b/xen/include/asm-x86/page.h -index 1d080cffbe84..2e542050f65a 100644 ---- a/xen/include/asm-x86/page.h -+++ b/xen/include/asm-x86/page.h -@@ -331,6 +331,14 @@ void efi_update_l4_pgtable(unsigned int l4idx, l4_pgentry_t); - - #define PAGE_CACHE_ATTRS (_PAGE_PAT | _PAGE_PCD | _PAGE_PWT) - -+/* Memory types, encoded under Xen's choice of MSR_PAT. */ -+#define _PAGE_WB ( 0) -+#define _PAGE_WT ( _PAGE_PWT) -+#define _PAGE_UCM ( _PAGE_PCD ) -+#define _PAGE_UC ( _PAGE_PCD | _PAGE_PWT) -+#define _PAGE_WC (_PAGE_PAT ) -+#define _PAGE_WP (_PAGE_PAT | _PAGE_PWT) -+ - /* - * Debug option: Ensure that granted mappings are not implicitly unmapped. - * WARNING: This will need to be disabled to run OSes that use the spare PTE -@@ -349,8 +357,8 @@ void efi_update_l4_pgtable(unsigned int l4idx, l4_pgentry_t); - #define __PAGE_HYPERVISOR_RX (_PAGE_PRESENT | _PAGE_ACCESSED) - #define __PAGE_HYPERVISOR (__PAGE_HYPERVISOR_RX | \ - _PAGE_DIRTY | _PAGE_RW) --#define __PAGE_HYPERVISOR_UCMINUS (__PAGE_HYPERVISOR | _PAGE_PCD) --#define __PAGE_HYPERVISOR_UC (__PAGE_HYPERVISOR | _PAGE_PCD | _PAGE_PWT) -+#define __PAGE_HYPERVISOR_UCMINUS (__PAGE_HYPERVISOR | _PAGE_UCM) -+#define __PAGE_HYPERVISOR_UC (__PAGE_HYPERVISOR | _PAGE_UC) - #define __PAGE_HYPERVISOR_SHSTK (__PAGE_HYPERVISOR_RO | _PAGE_DIRTY) - - #define MAP_SMALL_PAGES _PAGE_AVAIL0 /* don't use superpages mappings */ diff --git a/xsa402-4.16-2.patch b/xsa402-4.16-2.patch deleted file mode 100644 index ebb2f5e..0000000 --- a/xsa402-4.16-2.patch +++ /dev/null @@ -1,213 +0,0 @@ -From: Andrew Cooper -Subject: x86: Don't change the cacheability of the directmap - -Changeset 55f97f49b7ce ("x86: Change cache attributes of Xen 1:1 page mappings -in response to guest mapping requests") attempted to keep the cacheability -consistent between different mappings of the same page. - -The reason wasn't described in the changelog, but it is understood to be in -regards to a concern over machine check exceptions, owing to errata when using -mixed cacheabilities. It did this primarily by updating Xen's mapping of the -page in the direct map when the guest mapped a page with reduced cacheability. - -Unfortunately, the logic didn't actually prevent mixed cacheability from -occurring: - * A guest could map a page normally, and then map the same page with - different cacheability; nothing prevented this. - * The cacheability of the directmap was always latest-takes-precedence in - terms of guest requests. - * Grant-mapped frames with lesser cacheability didn't adjust the page's - cacheattr settings. - * The map_domain_page() function still unconditionally created WB mappings, - irrespective of the page's cacheattr settings. - -Additionally, update_xen_mappings() had a bug where the alias calculation was -wrong for mfn's which were .init content, which should have been treated as -fully guest pages, not Xen pages. - -Worse yet, the logic introduced a vulnerability whereby necessary -pagetable/segdesc adjustments made by Xen in the validation logic could become -non-coherent between the cache and main memory. The CPU could subsequently -operate on the stale value in the cache, rather than the safe value in main -memory. - -The directmap contains primarily mappings of RAM. PAT/MTRR conflict -resolution is asymmetric, and generally for MTRR=WB ranges, PAT of lesser -cacheability resolves to being coherent. The special case is WC mappings, -which are non-coherent against MTRR=WB regions (except for fully-coherent -CPUs). - -Xen must not have any WC cacheability in the directmap, to prevent Xen's -actions from creating non-coherency. (Guest actions creating non-coherency is -dealt with in subsequent patches.) As all memory types for MTRR=WB ranges -inter-operate coherently, so leave Xen's directmap mappings as WB. - -Only PV guests with access to devices can use reduced-cacheability mappings to -begin with, and they're trusted not to mount DoSs against the system anyway. - -Drop PGC_cacheattr_{base,mask} entirely, and the logic to manipulate them. -Shift the later PGC_* constants up, to gain 3 extra bits in the main reference -count. Retain the check in get_page_from_l1e() for special_pages() because a -guest has no business using reduced cacheability on these. - -This reverts changeset 55f97f49b7ce6c3520c555d19caac6cf3f9a5df0 - -This is CVE-2022-26363, part of XSA-402. - -Signed-off-by: Andrew Cooper -Reviewed-by: George Dunlap - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index c6429b0f749a..ab32d13a1a0d 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -783,28 +783,6 @@ bool is_iomem_page(mfn_t mfn) - return (page_get_owner(page) == dom_io); - } - --static int update_xen_mappings(unsigned long mfn, unsigned int cacheattr) --{ -- int err = 0; -- bool alias = mfn >= PFN_DOWN(xen_phys_start) && -- mfn < PFN_UP(xen_phys_start + xen_virt_end - XEN_VIRT_START); -- unsigned long xen_va = -- XEN_VIRT_START + ((mfn - PFN_DOWN(xen_phys_start)) << PAGE_SHIFT); -- -- if ( boot_cpu_has(X86_FEATURE_XEN_SELFSNOOP) ) -- return 0; -- -- if ( unlikely(alias) && cacheattr ) -- err = map_pages_to_xen(xen_va, _mfn(mfn), 1, 0); -- if ( !err ) -- err = map_pages_to_xen((unsigned long)mfn_to_virt(mfn), _mfn(mfn), 1, -- PAGE_HYPERVISOR | cacheattr_to_pte_flags(cacheattr)); -- if ( unlikely(alias) && !cacheattr && !err ) -- err = map_pages_to_xen(xen_va, _mfn(mfn), 1, PAGE_HYPERVISOR); -- -- return err; --} -- - #ifndef NDEBUG - struct mmio_emul_range_ctxt { - const struct domain *d; -@@ -1009,47 +987,14 @@ get_page_from_l1e( - goto could_not_pin; - } - -- if ( pte_flags_to_cacheattr(l1f) != -- ((page->count_info & PGC_cacheattr_mask) >> PGC_cacheattr_base) ) -+ if ( (l1f & PAGE_CACHE_ATTRS) != _PAGE_WB && is_special_page(page) ) - { -- unsigned long x, nx, y = page->count_info; -- unsigned long cacheattr = pte_flags_to_cacheattr(l1f); -- int err; -- -- if ( is_special_page(page) ) -- { -- if ( write ) -- put_page_type(page); -- put_page(page); -- gdprintk(XENLOG_WARNING, -- "Attempt to change cache attributes of Xen heap page\n"); -- return -EACCES; -- } -- -- do { -- x = y; -- nx = (x & ~PGC_cacheattr_mask) | (cacheattr << PGC_cacheattr_base); -- } while ( (y = cmpxchg(&page->count_info, x, nx)) != x ); -- -- err = update_xen_mappings(mfn, cacheattr); -- if ( unlikely(err) ) -- { -- cacheattr = y & PGC_cacheattr_mask; -- do { -- x = y; -- nx = (x & ~PGC_cacheattr_mask) | cacheattr; -- } while ( (y = cmpxchg(&page->count_info, x, nx)) != x ); -- -- if ( write ) -- put_page_type(page); -- put_page(page); -- -- gdprintk(XENLOG_WARNING, "Error updating mappings for mfn %" PRI_mfn -- " (pfn %" PRI_pfn ", from L1 entry %" PRIpte ") for d%d\n", -- mfn, get_gpfn_from_mfn(mfn), -- l1e_get_intpte(l1e), l1e_owner->domain_id); -- return err; -- } -+ if ( write ) -+ put_page_type(page); -+ put_page(page); -+ gdprintk(XENLOG_WARNING, -+ "Attempt to change cache attributes of Xen heap page\n"); -+ return -EACCES; - } - - return 0; -@@ -2467,25 +2412,10 @@ static int mod_l4_entry(l4_pgentry_t *pl4e, - */ - static int cleanup_page_mappings(struct page_info *page) - { -- unsigned int cacheattr = -- (page->count_info & PGC_cacheattr_mask) >> PGC_cacheattr_base; - int rc = 0; - unsigned long mfn = mfn_x(page_to_mfn(page)); - - /* -- * If we've modified xen mappings as a result of guest cache -- * attributes, restore them to the "normal" state. -- */ -- if ( unlikely(cacheattr) ) -- { -- page->count_info &= ~PGC_cacheattr_mask; -- -- BUG_ON(is_special_page(page)); -- -- rc = update_xen_mappings(mfn, 0); -- } -- -- /* - * If this may be in a PV domain's IOMMU, remove it. - * - * NB that writable xenheap pages have their type set and cleared by -diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h -index cb9052749963..8a9a43bb0a9d 100644 ---- a/xen/include/asm-x86/mm.h -+++ b/xen/include/asm-x86/mm.h -@@ -69,25 +69,22 @@ - /* Set when is using a page as a page table */ - #define _PGC_page_table PG_shift(3) - #define PGC_page_table PG_mask(1, 3) -- /* 3-bit PAT/PCD/PWT cache-attribute hint. */ --#define PGC_cacheattr_base PG_shift(6) --#define PGC_cacheattr_mask PG_mask(7, 6) - /* Page is broken? */ --#define _PGC_broken PG_shift(7) --#define PGC_broken PG_mask(1, 7) -+#define _PGC_broken PG_shift(4) -+#define PGC_broken PG_mask(1, 4) - /* Mutually-exclusive page states: { inuse, offlining, offlined, free }. */ --#define PGC_state PG_mask(3, 9) --#define PGC_state_inuse PG_mask(0, 9) --#define PGC_state_offlining PG_mask(1, 9) --#define PGC_state_offlined PG_mask(2, 9) --#define PGC_state_free PG_mask(3, 9) -+#define PGC_state PG_mask(3, 6) -+#define PGC_state_inuse PG_mask(0, 6) -+#define PGC_state_offlining PG_mask(1, 6) -+#define PGC_state_offlined PG_mask(2, 6) -+#define PGC_state_free PG_mask(3, 6) - #define page_state_is(pg, st) (((pg)->count_info&PGC_state) == PGC_state_##st) - /* Page is not reference counted (see below for caveats) */ --#define _PGC_extra PG_shift(10) --#define PGC_extra PG_mask(1, 10) -+#define _PGC_extra PG_shift(7) -+#define PGC_extra PG_mask(1, 7) - - /* Count of references to this frame. */ --#define PGC_count_width PG_shift(10) -+#define PGC_count_width PG_shift(7) - #define PGC_count_mask ((1UL< -Subject: x86: Split cache_flush() out of cache_writeback() - -Subsequent changes will want a fully flushing version. - -Use the new helper rather than opencoding it in flush_area_local(). This -resolves an outstanding issue where the conditional sfence is on the wrong -side of the clflushopt loop. clflushopt is ordered with respect to older -stores, not to younger stores. - -Rename gnttab_cache_flush()'s helper to avoid colliding in name. -grant_table.c can see the prototype from cache.h so the build fails -otherwise. - -This is part of XSA-402. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -Xen 4.16 and earlier: - * Also backport half of c/s 3330013e67396 "VT-d / x86: re-arrange cache - syncing" to split cache_writeback() out of the IOMMU logic, but without the - associated hooks changes. - -diff --git a/xen/arch/x86/flushtlb.c b/xen/arch/x86/flushtlb.c -index 25798df50f54..0c912b8669f8 100644 ---- a/xen/arch/x86/flushtlb.c -+++ b/xen/arch/x86/flushtlb.c -@@ -234,7 +234,7 @@ unsigned int flush_area_local(const void *va, unsigned int flags) - if ( flags & FLUSH_CACHE ) - { - const struct cpuinfo_x86 *c = ¤t_cpu_data; -- unsigned long i, sz = 0; -+ unsigned long sz = 0; - - if ( order < (BITS_PER_LONG - PAGE_SHIFT) ) - sz = 1UL << (order + PAGE_SHIFT); -@@ -244,13 +244,7 @@ unsigned int flush_area_local(const void *va, unsigned int flags) - c->x86_clflush_size && c->x86_cache_size && sz && - ((sz >> 10) < c->x86_cache_size) ) - { -- alternative("", "sfence", X86_FEATURE_CLFLUSHOPT); -- for ( i = 0; i < sz; i += c->x86_clflush_size ) -- alternative_input(".byte " __stringify(NOP_DS_PREFIX) ";" -- " clflush %0", -- "data16 clflush %0", /* clflushopt */ -- X86_FEATURE_CLFLUSHOPT, -- "m" (((const char *)va)[i])); -+ cache_flush(va, sz); - flags &= ~FLUSH_CACHE; - } - else -@@ -265,6 +259,80 @@ unsigned int flush_area_local(const void *va, unsigned int flags) - return flags; - } - -+void cache_flush(const void *addr, unsigned int size) -+{ -+ /* -+ * This function may be called before current_cpu_data is established. -+ * Hence a fallback is needed to prevent the loop below becoming infinite. -+ */ -+ unsigned int clflush_size = current_cpu_data.x86_clflush_size ?: 16; -+ const void *end = addr + size; -+ -+ addr -= (unsigned long)addr & (clflush_size - 1); -+ for ( ; addr < end; addr += clflush_size ) -+ { -+ /* -+ * Note regarding the "ds" prefix use: it's faster to do a clflush -+ * + prefix than a clflush + nop, and hence the prefix is added instead -+ * of letting the alternative framework fill the gap by appending nops. -+ */ -+ alternative_io("ds; clflush %[p]", -+ "data16 clflush %[p]", /* clflushopt */ -+ X86_FEATURE_CLFLUSHOPT, -+ /* no outputs */, -+ [p] "m" (*(const char *)(addr))); -+ } -+ -+ alternative("", "sfence", X86_FEATURE_CLFLUSHOPT); -+} -+ -+void cache_writeback(const void *addr, unsigned int size) -+{ -+ unsigned int clflush_size; -+ const void *end = addr + size; -+ -+ /* Fall back to CLFLUSH{,OPT} when CLWB isn't available. */ -+ if ( !boot_cpu_has(X86_FEATURE_CLWB) ) -+ return cache_flush(addr, size); -+ -+ /* -+ * This function may be called before current_cpu_data is established. -+ * Hence a fallback is needed to prevent the loop below becoming infinite. -+ */ -+ clflush_size = current_cpu_data.x86_clflush_size ?: 16; -+ addr -= (unsigned long)addr & (clflush_size - 1); -+ for ( ; addr < end; addr += clflush_size ) -+ { -+/* -+ * The arguments to a macro must not include preprocessor directives. Doing so -+ * results in undefined behavior, so we have to create some defines here in -+ * order to avoid it. -+ */ -+#if defined(HAVE_AS_CLWB) -+# define CLWB_ENCODING "clwb %[p]" -+#elif defined(HAVE_AS_XSAVEOPT) -+# define CLWB_ENCODING "data16 xsaveopt %[p]" /* clwb */ -+#else -+# define CLWB_ENCODING ".byte 0x66, 0x0f, 0xae, 0x30" /* clwb (%%rax) */ -+#endif -+ -+#define BASE_INPUT(addr) [p] "m" (*(const char *)(addr)) -+#if defined(HAVE_AS_CLWB) || defined(HAVE_AS_XSAVEOPT) -+# define INPUT BASE_INPUT -+#else -+# define INPUT(addr) "a" (addr), BASE_INPUT(addr) -+#endif -+ -+ asm volatile (CLWB_ENCODING :: INPUT(addr)); -+ -+#undef INPUT -+#undef BASE_INPUT -+#undef CLWB_ENCODING -+ } -+ -+ asm volatile ("sfence" ::: "memory"); -+} -+ - unsigned int guest_flush_tlb_flags(const struct domain *d) - { - bool shadow = paging_mode_shadow(d); -diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c -index 66f8ce71741c..4c742cd8fe81 100644 ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -3431,7 +3431,7 @@ gnttab_swap_grant_ref(XEN_GUEST_HANDLE_PARAM(gnttab_swap_grant_ref_t) uop, - return 0; - } - --static int cache_flush(const gnttab_cache_flush_t *cflush, grant_ref_t *cur_ref) -+static int _cache_flush(const gnttab_cache_flush_t *cflush, grant_ref_t *cur_ref) - { - struct domain *d, *owner; - struct page_info *page; -@@ -3525,7 +3525,7 @@ gnttab_cache_flush(XEN_GUEST_HANDLE_PARAM(gnttab_cache_flush_t) uop, - return -EFAULT; - for ( ; ; ) - { -- int ret = cache_flush(&op, cur_ref); -+ int ret = _cache_flush(&op, cur_ref); - - if ( ret < 0 ) - return ret; -diff --git a/xen/drivers/passthrough/vtd/extern.h b/xen/drivers/passthrough/vtd/extern.h -index 01e010a10d61..401079299725 100644 ---- a/xen/drivers/passthrough/vtd/extern.h -+++ b/xen/drivers/passthrough/vtd/extern.h -@@ -76,7 +76,6 @@ int __must_check qinval_device_iotlb_sync(struct vtd_iommu *iommu, - struct pci_dev *pdev, - u16 did, u16 size, u64 addr); - --unsigned int get_cache_line_size(void); - void flush_all_cache(void); - - uint64_t alloc_pgtable_maddr(unsigned long npages, nodeid_t node); -diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c -index 8975c1de61bc..bc377c9bcfa4 100644 ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -31,6 +31,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -206,54 +207,6 @@ static void check_cleanup_domid_map(const struct domain *d, - } - } - --static void sync_cache(const void *addr, unsigned int size) --{ -- static unsigned long clflush_size = 0; -- const void *end = addr + size; -- -- if ( clflush_size == 0 ) -- clflush_size = get_cache_line_size(); -- -- addr -= (unsigned long)addr & (clflush_size - 1); -- for ( ; addr < end; addr += clflush_size ) --/* -- * The arguments to a macro must not include preprocessor directives. Doing so -- * results in undefined behavior, so we have to create some defines here in -- * order to avoid it. -- */ --#if defined(HAVE_AS_CLWB) --# define CLWB_ENCODING "clwb %[p]" --#elif defined(HAVE_AS_XSAVEOPT) --# define CLWB_ENCODING "data16 xsaveopt %[p]" /* clwb */ --#else --# define CLWB_ENCODING ".byte 0x66, 0x0f, 0xae, 0x30" /* clwb (%%rax) */ --#endif -- --#define BASE_INPUT(addr) [p] "m" (*(const char *)(addr)) --#if defined(HAVE_AS_CLWB) || defined(HAVE_AS_XSAVEOPT) --# define INPUT BASE_INPUT --#else --# define INPUT(addr) "a" (addr), BASE_INPUT(addr) --#endif -- /* -- * Note regarding the use of NOP_DS_PREFIX: it's faster to do a clflush -- * + prefix than a clflush + nop, and hence the prefix is added instead -- * of letting the alternative framework fill the gap by appending nops. -- */ -- alternative_io_2(".byte " __stringify(NOP_DS_PREFIX) "; clflush %[p]", -- "data16 clflush %[p]", /* clflushopt */ -- X86_FEATURE_CLFLUSHOPT, -- CLWB_ENCODING, -- X86_FEATURE_CLWB, /* no outputs */, -- INPUT(addr)); --#undef INPUT --#undef BASE_INPUT --#undef CLWB_ENCODING -- -- alternative_2("", "sfence", X86_FEATURE_CLFLUSHOPT, -- "sfence", X86_FEATURE_CLWB); --} -- - /* Allocate page table, return its machine address */ - uint64_t alloc_pgtable_maddr(unsigned long npages, nodeid_t node) - { -@@ -273,7 +226,7 @@ uint64_t alloc_pgtable_maddr(unsigned long npages, nodeid_t node) - clear_page(vaddr); - - if ( (iommu_ops.init ? &iommu_ops : &vtd_ops)->sync_cache ) -- sync_cache(vaddr, PAGE_SIZE); -+ cache_writeback(vaddr, PAGE_SIZE); - unmap_domain_page(vaddr); - cur_pg++; - } -@@ -1305,7 +1258,7 @@ int __init iommu_alloc(struct acpi_drhd_unit *drhd) - iommu->nr_pt_levels = agaw_to_level(agaw); - - if ( !ecap_coherent(iommu->ecap) ) -- vtd_ops.sync_cache = sync_cache; -+ vtd_ops.sync_cache = cache_writeback; - - /* allocate domain id bitmap */ - iommu->domid_bitmap = xzalloc_array(unsigned long, BITS_TO_LONGS(nr_dom)); -diff --git a/xen/drivers/passthrough/vtd/x86/vtd.c b/xen/drivers/passthrough/vtd/x86/vtd.c -index 6681dccd6970..55f0faa521cb 100644 ---- a/xen/drivers/passthrough/vtd/x86/vtd.c -+++ b/xen/drivers/passthrough/vtd/x86/vtd.c -@@ -47,11 +47,6 @@ void unmap_vtd_domain_page(const void *va) - unmap_domain_page(va); - } - --unsigned int get_cache_line_size(void) --{ -- return ((cpuid_ebx(1) >> 8) & 0xff) * 8; --} -- - void flush_all_cache() - { - wbinvd(); -diff --git a/xen/include/asm-x86/cache.h b/xen/include/asm-x86/cache.h -index 1f7173d8c72c..e4770efb22b9 100644 ---- a/xen/include/asm-x86/cache.h -+++ b/xen/include/asm-x86/cache.h -@@ -11,4 +11,11 @@ - - #define __read_mostly __section(".data.read_mostly") - -+#ifndef __ASSEMBLY__ -+ -+void cache_flush(const void *addr, unsigned int size); -+void cache_writeback(const void *addr, unsigned int size); -+ -+#endif -+ - #endif diff --git a/xsa402-4.16-4.patch b/xsa402-4.16-4.patch deleted file mode 100644 index 2110922..0000000 --- a/xsa402-4.16-4.patch +++ /dev/null @@ -1,83 +0,0 @@ -From: Andrew Cooper -Subject: x86/amd: Work around CLFLUSH ordering on older parts - -On pre-CLFLUSHOPT AMD CPUs, CLFLUSH is weakely ordered with everything, -including reads and writes to the address, and LFENCE/SFENCE instructions. - -This creates a multitude of problematic corner cases, laid out in the manual. -Arrange to use MFENCE on both sides of the CLFLUSH to force proper ordering. - -This is part of XSA-402. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c -index a8e37dbb1f5c..b3b9a0df5fed 100644 ---- a/xen/arch/x86/cpu/amd.c -+++ b/xen/arch/x86/cpu/amd.c -@@ -812,6 +812,14 @@ static void init_amd(struct cpuinfo_x86 *c) - if (!cpu_has_lfence_dispatch) - __set_bit(X86_FEATURE_MFENCE_RDTSC, c->x86_capability); - -+ /* -+ * On pre-CLFLUSHOPT AMD CPUs, CLFLUSH is weakly ordered with -+ * everything, including reads and writes to address, and -+ * LFENCE/SFENCE instructions. -+ */ -+ if (!cpu_has_clflushopt) -+ setup_force_cpu_cap(X86_BUG_CLFLUSH_MFENCE); -+ - switch(c->x86) - { - case 0xf ... 0x11: -diff --git a/xen/arch/x86/flushtlb.c b/xen/arch/x86/flushtlb.c -index 0c912b8669f8..dcbb4064012e 100644 ---- a/xen/arch/x86/flushtlb.c -+++ b/xen/arch/x86/flushtlb.c -@@ -259,6 +259,13 @@ unsigned int flush_area_local(const void *va, unsigned int flags) - return flags; - } - -+/* -+ * On pre-CLFLUSHOPT AMD CPUs, CLFLUSH is weakly ordered with everything, -+ * including reads and writes to address, and LFENCE/SFENCE instructions. -+ * -+ * This function only works safely after alternatives have run. Luckily, at -+ * the time of writing, we don't flush the caches that early. -+ */ - void cache_flush(const void *addr, unsigned int size) - { - /* -@@ -268,6 +275,8 @@ void cache_flush(const void *addr, unsigned int size) - unsigned int clflush_size = current_cpu_data.x86_clflush_size ?: 16; - const void *end = addr + size; - -+ alternative("", "mfence", X86_BUG_CLFLUSH_MFENCE); -+ - addr -= (unsigned long)addr & (clflush_size - 1); - for ( ; addr < end; addr += clflush_size ) - { -@@ -283,7 +292,9 @@ void cache_flush(const void *addr, unsigned int size) - [p] "m" (*(const char *)(addr))); - } - -- alternative("", "sfence", X86_FEATURE_CLFLUSHOPT); -+ alternative_2("", -+ "sfence", X86_FEATURE_CLFLUSHOPT, -+ "mfence", X86_BUG_CLFLUSH_MFENCE); - } - - void cache_writeback(const void *addr, unsigned int size) -diff --git a/xen/include/asm-x86/cpufeatures.h b/xen/include/asm-x86/cpufeatures.h -index 7413febd7ad8..ff3157d52d13 100644 ---- a/xen/include/asm-x86/cpufeatures.h -+++ b/xen/include/asm-x86/cpufeatures.h -@@ -47,6 +47,7 @@ XEN_CPUFEATURE(XEN_IBT, X86_SYNTH(27)) /* Xen uses CET Indirect Branch - - #define X86_BUG_FPU_PTRS X86_BUG( 0) /* (F)X{SAVE,RSTOR} doesn't save/restore FOP/FIP/FDP. */ - #define X86_BUG_NULL_SEG X86_BUG( 1) /* NULL-ing a selector preserves the base and limit. */ -+#define X86_BUG_CLFLUSH_MFENCE X86_BUG( 2) /* MFENCE needed to serialise CLFLUSH */ - - /* Total number of capability words, inc synth and bug words. */ - #define NCAPINTS (FSCAPINTS + X86_NR_SYNTH + X86_NR_BUG) /* N 32-bit words worth of info */ diff --git a/xsa402-4.16-5.patch b/xsa402-4.16-5.patch deleted file mode 100644 index 4806d25..0000000 --- a/xsa402-4.16-5.patch +++ /dev/null @@ -1,148 +0,0 @@ -From: Andrew Cooper -Subject: x86/pv: Track and flush non-coherent mappings of RAM - -There are legitimate uses of WC mappings of RAM, e.g. for DMA buffers with -devices that make non-coherent writes. The Linux sound subsystem makes -extensive use of this technique. - -For such usecases, the guest's DMA buffer is mapped and consistently used as -WC, and Xen doesn't interact with the buffer. - -However, a mischevious guest can use WC mappings to deliberately create -non-coherency between the cache and RAM, and use this to trick Xen into -validating a pagetable which isn't actually safe. - -Allocate a new PGT_non_coherent to track the non-coherency of mappings. Set -it whenever a non-coherent writeable mapping is created. If the page is used -as anything other than PGT_writable_page, force a cache flush before -validation. Also force a cache flush before the page is returned to the heap. - -This is CVE-2022-26364, part of XSA-402. - -Reported-by: Jann Horn -Signed-off-by: Andrew Cooper -Reviewed-by: George Dunlap -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index ab32d13a1a0d..bab9624fabb7 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -997,6 +997,15 @@ get_page_from_l1e( - return -EACCES; - } - -+ /* -+ * Track writeable non-coherent mappings to RAM pages, to trigger a cache -+ * flush later if the target is used as anything but a PGT_writeable page. -+ * We care about all writeable mappings, including foreign mappings. -+ */ -+ if ( !boot_cpu_has(X86_FEATURE_XEN_SELFSNOOP) && -+ (l1f & (PAGE_CACHE_ATTRS | _PAGE_RW)) == (_PAGE_WC | _PAGE_RW) ) -+ set_bit(_PGT_non_coherent, &page->u.inuse.type_info); -+ - return 0; - - could_not_pin: -@@ -2454,6 +2463,19 @@ static int cleanup_page_mappings(struct page_info *page) - } - } - -+ /* -+ * Flush the cache if there were previously non-coherent writeable -+ * mappings of this page. This forces the page to be coherent before it -+ * is freed back to the heap. -+ */ -+ if ( __test_and_clear_bit(_PGT_non_coherent, &page->u.inuse.type_info) ) -+ { -+ void *addr = __map_domain_page(page); -+ -+ cache_flush(addr, PAGE_SIZE); -+ unmap_domain_page(addr); -+ } -+ - return rc; - } - -@@ -3028,6 +3050,22 @@ static int _get_page_type(struct page_info *page, unsigned long type, - if ( unlikely(!(nx & PGT_validated)) ) - { - /* -+ * Flush the cache if there were previously non-coherent mappings of -+ * this page, and we're trying to use it as anything other than a -+ * writeable page. This forces the page to be coherent before we -+ * validate its contents for safety. -+ */ -+ if ( (nx & PGT_non_coherent) && type != PGT_writable_page ) -+ { -+ void *addr = __map_domain_page(page); -+ -+ cache_flush(addr, PAGE_SIZE); -+ unmap_domain_page(addr); -+ -+ page->u.inuse.type_info &= ~PGT_non_coherent; -+ } -+ -+ /* - * No special validation needed for writable or shared pages. Page - * tables and GDT/LDT need to have their contents audited. - * -diff --git a/xen/arch/x86/pv/grant_table.c b/xen/arch/x86/pv/grant_table.c -index 0325618c9883..81c72e61ed55 100644 ---- a/xen/arch/x86/pv/grant_table.c -+++ b/xen/arch/x86/pv/grant_table.c -@@ -109,7 +109,17 @@ int create_grant_pv_mapping(uint64_t addr, mfn_t frame, - - ol1e = *pl1e; - if ( UPDATE_ENTRY(l1, pl1e, ol1e, nl1e, gl1mfn, curr, 0) ) -+ { -+ /* -+ * We always create mappings in this path. However, our caller, -+ * map_grant_ref(), only passes potentially non-zero cache_flags for -+ * MMIO frames, so this path doesn't create non-coherent mappings of -+ * RAM frames and there's no need to calculate PGT_non_coherent. -+ */ -+ ASSERT(!cache_flags || is_iomem_page(frame)); -+ - rc = GNTST_okay; -+ } - - out_unlock: - page_unlock(page); -@@ -294,7 +304,18 @@ int replace_grant_pv_mapping(uint64_t addr, mfn_t frame, - l1e_get_flags(ol1e), addr, grant_pte_flags); - - if ( UPDATE_ENTRY(l1, pl1e, ol1e, nl1e, gl1mfn, curr, 0) ) -+ { -+ /* -+ * Generally, replace_grant_pv_mapping() is used to destroy mappings -+ * (n1le = l1e_empty()), but it can be a present mapping on the -+ * GNTABOP_unmap_and_replace path. -+ * -+ * In such cases, the PTE is fully transplanted from its old location -+ * via steal_linear_addr(), so we need not perform PGT_non_coherent -+ * checking here. -+ */ - rc = GNTST_okay; -+ } - - out_unlock: - page_unlock(page); -diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h -index 8a9a43bb0a9d..7464167ae192 100644 ---- a/xen/include/asm-x86/mm.h -+++ b/xen/include/asm-x86/mm.h -@@ -53,8 +53,12 @@ - #define _PGT_partial PG_shift(8) - #define PGT_partial PG_mask(1, 8) - -+/* Has this page been mapped writeable with a non-coherent memory type? */ -+#define _PGT_non_coherent PG_shift(9) -+#define PGT_non_coherent PG_mask(1, 9) -+ - /* Count of uses of this frame as its current type. */ --#define PGT_count_width PG_shift(8) -+#define PGT_count_width PG_shift(9) - #define PGT_count_mask ((1UL< -Date: Thu, 30 Jun 2022 14:35:35 +0200 -Subject: [PATCH] tools/libxl: env variable to signal whether disk/nic backend - is trusted -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Introduce support in libxl for fetching the default backend trusted -option for disk and nic devices. - -Users can set libxl_{disk,nic}_backend_untrusted environment variable -to notify libxl of whether the backends for disk and nic devices -should be trusted. Such information is passed into the frontend so it -can take the appropriate measures. - -This is part of XSA-403. - -Signed-off-by: Roger Pau Monné ---- - tools/libs/light/libxl_disk.c | 3 +++ - tools/libs/light/libxl_nic.c | 3 +++ - 2 files changed, 6 insertions(+) - -diff --git a/tools/libs/light/libxl_disk.c b/tools/libs/light/libxl_disk.c -index 93936d0dd0..0aaf6afce8 100644 ---- a/tools/libs/light/libxl_disk.c -+++ b/tools/libs/light/libxl_disk.c -@@ -395,6 +395,9 @@ static void device_disk_add(libxl__egc *egc, uint32_t domid, - flexarray_append(front, GCSPRINTF("%d", device->devid)); - flexarray_append(front, "device-type"); - flexarray_append(front, disk->is_cdrom ? "cdrom" : "disk"); -+ flexarray_append(front, "trusted"); -+ flexarray_append(front, getenv("libxl_disk_backend_untrusted") ? "0" -+ : "1"); - - /* - * Old PV kernel disk frontends before 2.6.26 rely on tool stack to -diff --git a/tools/libs/light/libxl_nic.c b/tools/libs/light/libxl_nic.c -index 0b9e70c9d1..34c3fe6df0 100644 ---- a/tools/libs/light/libxl_nic.c -+++ b/tools/libs/light/libxl_nic.c -@@ -255,6 +255,9 @@ static int libxl__set_xenstore_nic(libxl__gc *gc, uint32_t domid, - flexarray_append(back, "hotplug-status"); - flexarray_append(back, ""); - -+ flexarray_append(front, "trusted"); -+ flexarray_append(front, getenv("libxl_nic_backend_untrusted") ? "0" : "1"); -+ - return 0; - } - --- -2.37.0 - diff --git a/xsa404-4.16-1.patch b/xsa404-4.16-1.patch deleted file mode 100644 index c101279..0000000 --- a/xsa404-4.16-1.patch +++ /dev/null @@ -1,239 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Make VERW flushing runtime conditional - -Currently, VERW flushing to mitigate MDS is boot time conditional per domain -type. However, to provide mitigations for DRPW (CVE-2022-21166), we need to -conditionally use VERW based on the trustworthiness of the guest, and the -devices passed through. - -Remove the PV/HVM alternatives and instead issue a VERW on the return-to-guest -path depending on the SCF_verw bit in cpuinfo spec_ctrl_flags. - -Introduce spec_ctrl_init_domain() and d->arch.verw to calculate the VERW -disposition at domain creation time, and context switch the SCF_verw bit. - -For now, VERW flushing is used and controlled exactly as before, but later -patches will add per-domain cases too. - -No change in behaviour. - -This is part of XSA-404. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -Reviewed-by: Roger Pau Monné - -diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc -index 1d08fb7e9aa6..d5cb09f86541 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -2258,9 +2258,8 @@ in place for guests to use. - Use of a positive boolean value for either of these options is invalid. - - The booleans `pv=`, `hvm=`, `msr-sc=`, `rsb=` and `md-clear=` offer fine --grained control over the alternative blocks used by Xen. These impact Xen's --ability to protect itself, and Xen's ability to virtualise support for guests --to use. -+grained control over the primitives by Xen. These impact Xen's ability to -+protect itself, and Xen's ability to virtualise support for guests to use. - - * `pv=` and `hvm=` offer control over all suboptions for PV and HVM guests - respectively. -diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c -index ef1812dc1402..1fe6644a71ae 100644 ---- a/xen/arch/x86/domain.c -+++ b/xen/arch/x86/domain.c -@@ -863,6 +863,8 @@ int arch_domain_create(struct domain *d, - - d->arch.msr_relaxed = config->arch.misc_flags & XEN_X86_MSR_RELAXED; - -+ spec_ctrl_init_domain(d); -+ - return 0; - - fail: -@@ -2017,14 +2019,15 @@ static void __context_switch(void) - void context_switch(struct vcpu *prev, struct vcpu *next) - { - unsigned int cpu = smp_processor_id(); -+ struct cpu_info *info = get_cpu_info(); - const struct domain *prevd = prev->domain, *nextd = next->domain; - unsigned int dirty_cpu = read_atomic(&next->dirty_cpu); - - ASSERT(prev != next); - ASSERT(local_irq_is_enabled()); - -- get_cpu_info()->use_pv_cr3 = false; -- get_cpu_info()->xen_cr3 = 0; -+ info->use_pv_cr3 = false; -+ info->xen_cr3 = 0; - - if ( unlikely(dirty_cpu != cpu) && dirty_cpu != VCPU_CPU_CLEAN ) - { -@@ -2088,6 +2091,11 @@ void context_switch(struct vcpu *prev, struct vcpu *next) - *last_id = next_id; - } - } -+ -+ /* Update the top-of-stack block with the VERW disposition. */ -+ info->spec_ctrl_flags &= ~SCF_verw; -+ if ( nextd->arch.verw ) -+ info->spec_ctrl_flags |= SCF_verw; - } - - sched_context_switched(prev, next); -diff --git a/xen/arch/x86/hvm/vmx/entry.S b/xen/arch/x86/hvm/vmx/entry.S -index 49651f3c435a..5f5de45a1309 100644 ---- a/xen/arch/x86/hvm/vmx/entry.S -+++ b/xen/arch/x86/hvm/vmx/entry.S -@@ -87,7 +87,7 @@ UNLIKELY_END(realmode) - - /* WARNING! `ret`, `call *`, `jmp *` not safe beyond this point. */ - /* SPEC_CTRL_EXIT_TO_VMX Req: %rsp=regs/cpuinfo Clob: */ -- ALTERNATIVE "", __stringify(verw CPUINFO_verw_sel(%rsp)), X86_FEATURE_SC_VERW_HVM -+ DO_SPEC_CTRL_COND_VERW - - mov VCPU_hvm_guest_cr2(%rbx),%rax - -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index c19464da70ce..21730aa03071 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -36,8 +36,8 @@ static bool __initdata opt_msr_sc_pv = true; - static bool __initdata opt_msr_sc_hvm = true; - static int8_t __initdata opt_rsb_pv = -1; - static bool __initdata opt_rsb_hvm = true; --static int8_t __initdata opt_md_clear_pv = -1; --static int8_t __initdata opt_md_clear_hvm = -1; -+static int8_t __read_mostly opt_md_clear_pv = -1; -+static int8_t __read_mostly opt_md_clear_hvm = -1; - - /* Cmdline controls for Xen's speculative settings. */ - static enum ind_thunk { -@@ -932,6 +932,13 @@ static __init void mds_calculations(uint64_t caps) - } - } - -+void spec_ctrl_init_domain(struct domain *d) -+{ -+ bool pv = is_pv_domain(d); -+ -+ d->arch.verw = pv ? opt_md_clear_pv : opt_md_clear_hvm; -+} -+ - void __init init_speculation_mitigations(void) - { - enum ind_thunk thunk = THUNK_DEFAULT; -@@ -1196,21 +1203,20 @@ void __init init_speculation_mitigations(void) - boot_cpu_has(X86_FEATURE_MD_CLEAR)); - - /* -- * Enable MDS defences as applicable. The PV blocks need using all the -- * time, and the Idle blocks need using if either PV or HVM defences are -- * used. -+ * Enable MDS defences as applicable. The Idle blocks need using if -+ * either PV or HVM defences are used. - * - * HVM is more complicated. The MD_CLEAR microcode extends L1D_FLUSH with -- * equivelent semantics to avoid needing to perform both flushes on the -- * HVM path. The HVM blocks don't need activating if our hypervisor told -- * us it was handling L1D_FLUSH, or we are using L1D_FLUSH ourselves. -+ * equivalent semantics to avoid needing to perform both flushes on the -+ * HVM path. Therefore, we don't need VERW in addition to L1D_FLUSH. -+ * -+ * After calculating the appropriate idle setting, simplify -+ * opt_md_clear_hvm to mean just "should we VERW on the way into HVM -+ * guests", so spec_ctrl_init_domain() can calculate suitable settings. - */ -- if ( opt_md_clear_pv ) -- setup_force_cpu_cap(X86_FEATURE_SC_VERW_PV); - if ( opt_md_clear_pv || opt_md_clear_hvm ) - setup_force_cpu_cap(X86_FEATURE_SC_VERW_IDLE); -- if ( opt_md_clear_hvm && !(caps & ARCH_CAPS_SKIP_L1DFL) && !opt_l1d_flush ) -- setup_force_cpu_cap(X86_FEATURE_SC_VERW_HVM); -+ opt_md_clear_hvm &= !(caps & ARCH_CAPS_SKIP_L1DFL) && !opt_l1d_flush; - - /* - * Warn the user if they are on MLPDS/MFBDS-vulnerable hardware with HT -diff --git a/xen/include/asm-x86/cpufeatures.h b/xen/include/asm-x86/cpufeatures.h -index ff3157d52d13..bd45a144ee78 100644 ---- a/xen/include/asm-x86/cpufeatures.h -+++ b/xen/include/asm-x86/cpufeatures.h -@@ -35,8 +35,7 @@ XEN_CPUFEATURE(SC_RSB_HVM, X86_SYNTH(19)) /* RSB overwrite needed for HVM - XEN_CPUFEATURE(XEN_SELFSNOOP, X86_SYNTH(20)) /* SELFSNOOP gets used by Xen itself */ - XEN_CPUFEATURE(SC_MSR_IDLE, X86_SYNTH(21)) /* (SC_MSR_PV || SC_MSR_HVM) && default_xen_spec_ctrl */ - XEN_CPUFEATURE(XEN_LBR, X86_SYNTH(22)) /* Xen uses MSR_DEBUGCTL.LBR */ --XEN_CPUFEATURE(SC_VERW_PV, X86_SYNTH(23)) /* VERW used by Xen for PV */ --XEN_CPUFEATURE(SC_VERW_HVM, X86_SYNTH(24)) /* VERW used by Xen for HVM */ -+/* Bits 23,24 unused. */ - XEN_CPUFEATURE(SC_VERW_IDLE, X86_SYNTH(25)) /* VERW used by Xen for idle */ - XEN_CPUFEATURE(XEN_SHSTK, X86_SYNTH(26)) /* Xen uses CET Shadow Stacks */ - XEN_CPUFEATURE(XEN_IBT, X86_SYNTH(27)) /* Xen uses CET Indirect Branch Tracking */ -diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h -index 92d54de0b9a1..2398a1d99da9 100644 ---- a/xen/include/asm-x86/domain.h -+++ b/xen/include/asm-x86/domain.h -@@ -319,6 +319,9 @@ struct arch_domain - uint32_t pci_cf8; - uint8_t cmos_idx; - -+ /* Use VERW on return-to-guest for its flushing side effect. */ -+ bool verw; -+ - union { - struct pv_domain pv; - struct hvm_domain hvm; -diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h -index f76029523610..751355f471f4 100644 ---- a/xen/include/asm-x86/spec_ctrl.h -+++ b/xen/include/asm-x86/spec_ctrl.h -@@ -24,6 +24,7 @@ - #define SCF_use_shadow (1 << 0) - #define SCF_ist_wrmsr (1 << 1) - #define SCF_ist_rsb (1 << 2) -+#define SCF_verw (1 << 3) - - #ifndef __ASSEMBLY__ - -@@ -32,6 +33,7 @@ - #include - - void init_speculation_mitigations(void); -+void spec_ctrl_init_domain(struct domain *d); - - extern bool opt_ibpb; - extern bool opt_ssbd; -diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h -index 02b3b18ce69f..5a590bac44aa 100644 ---- a/xen/include/asm-x86/spec_ctrl_asm.h -+++ b/xen/include/asm-x86/spec_ctrl_asm.h -@@ -136,6 +136,19 @@ - #endif - .endm - -+.macro DO_SPEC_CTRL_COND_VERW -+/* -+ * Requires %rsp=cpuinfo -+ * -+ * Issue a VERW for its flushing side effect, if indicated. This is a Spectre -+ * v1 gadget, but the IRET/VMEntry is serialising. -+ */ -+ testb $SCF_verw, CPUINFO_spec_ctrl_flags(%rsp) -+ jz .L\@_verw_skip -+ verw CPUINFO_verw_sel(%rsp) -+.L\@_verw_skip: -+.endm -+ - .macro DO_SPEC_CTRL_ENTRY maybexen:req - /* - * Requires %rsp=regs (also cpuinfo if !maybexen) -@@ -231,8 +244,7 @@ - #define SPEC_CTRL_EXIT_TO_PV \ - ALTERNATIVE "", \ - DO_SPEC_CTRL_EXIT_TO_GUEST, X86_FEATURE_SC_MSR_PV; \ -- ALTERNATIVE "", __stringify(verw CPUINFO_verw_sel(%rsp)), \ -- X86_FEATURE_SC_VERW_PV -+ DO_SPEC_CTRL_COND_VERW - - /* - * Use in IST interrupt/exception context. May interrupt Xen or PV context. diff --git a/xsa404-4.16-2.patch b/xsa404-4.16-2.patch deleted file mode 100644 index 732b26a..0000000 --- a/xsa404-4.16-2.patch +++ /dev/null @@ -1,85 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Enumeration for MMIO Stale Data controls - -The three *_NO bits indicate non-susceptibility to the SSDP, FBSDP and PSDP -data movement primitives. - -FB_CLEAR indicates that the VERW instruction has re-gained it's Fill Buffer -flushing side effect. This is only enumerated on parts where VERW had -previously lost it's flushing side effect due to the MDS/TAA vulnerabilities -being fixed in hardware. - -FB_CLEAR_CTRL is available on a subset of FB_CLEAR parts where the Fill Buffer -clearing side effect of VERW can be turned off for performance reasons. - -This is part of XSA-404. - -Signed-off-by: Andrew Cooper -Reviewed-by: Roger Pau Monné - -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 21730aa03071..d285538bde9f 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -323,7 +323,7 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - * Hardware read-only information, stating immunity to certain issues, or - * suggestions of which mitigation to use. - */ -- printk(" Hardware hints:%s%s%s%s%s%s%s%s%s%s%s\n", -+ printk(" Hardware hints:%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", - (caps & ARCH_CAPS_RDCL_NO) ? " RDCL_NO" : "", - (caps & ARCH_CAPS_IBRS_ALL) ? " IBRS_ALL" : "", - (caps & ARCH_CAPS_RSBA) ? " RSBA" : "", -@@ -332,13 +332,16 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - (caps & ARCH_CAPS_SSB_NO) ? " SSB_NO" : "", - (caps & ARCH_CAPS_MDS_NO) ? " MDS_NO" : "", - (caps & ARCH_CAPS_TAA_NO) ? " TAA_NO" : "", -+ (caps & ARCH_CAPS_SBDR_SSDP_NO) ? " SBDR_SSDP_NO" : "", -+ (caps & ARCH_CAPS_FBSDP_NO) ? " FBSDP_NO" : "", -+ (caps & ARCH_CAPS_PSDP_NO) ? " PSDP_NO" : "", - (e8b & cpufeat_mask(X86_FEATURE_IBRS_ALWAYS)) ? " IBRS_ALWAYS" : "", - (e8b & cpufeat_mask(X86_FEATURE_STIBP_ALWAYS)) ? " STIBP_ALWAYS" : "", - (e8b & cpufeat_mask(X86_FEATURE_IBRS_FAST)) ? " IBRS_FAST" : "", - (e8b & cpufeat_mask(X86_FEATURE_IBRS_SAME_MODE)) ? " IBRS_SAME_MODE" : ""); - - /* Hardware features which need driving to mitigate issues. */ -- printk(" Hardware features:%s%s%s%s%s%s%s%s%s%s\n", -+ printk(" Hardware features:%s%s%s%s%s%s%s%s%s%s%s%s\n", - (e8b & cpufeat_mask(X86_FEATURE_IBPB)) || - (_7d0 & cpufeat_mask(X86_FEATURE_IBRSB)) ? " IBPB" : "", - (e8b & cpufeat_mask(X86_FEATURE_IBRS)) || -@@ -353,7 +356,9 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - (_7d0 & cpufeat_mask(X86_FEATURE_MD_CLEAR)) ? " MD_CLEAR" : "", - (_7d0 & cpufeat_mask(X86_FEATURE_SRBDS_CTRL)) ? " SRBDS_CTRL" : "", - (e8b & cpufeat_mask(X86_FEATURE_VIRT_SSBD)) ? " VIRT_SSBD" : "", -- (caps & ARCH_CAPS_TSX_CTRL) ? " TSX_CTRL" : ""); -+ (caps & ARCH_CAPS_TSX_CTRL) ? " TSX_CTRL" : "", -+ (caps & ARCH_CAPS_FB_CLEAR) ? " FB_CLEAR" : "", -+ (caps & ARCH_CAPS_FB_CLEAR_CTRL) ? " FB_CLEAR_CTRL" : ""); - - /* Compiled-in support which pertains to mitigations. */ - if ( IS_ENABLED(CONFIG_INDIRECT_THUNK) || IS_ENABLED(CONFIG_SHADOW_PAGING) ) -diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h -index 31964b88af7a..72bc32ba04ff 100644 ---- a/xen/include/asm-x86/msr-index.h -+++ b/xen/include/asm-x86/msr-index.h -@@ -66,6 +66,11 @@ - #define ARCH_CAPS_IF_PSCHANGE_MC_NO (_AC(1, ULL) << 6) - #define ARCH_CAPS_TSX_CTRL (_AC(1, ULL) << 7) - #define ARCH_CAPS_TAA_NO (_AC(1, ULL) << 8) -+#define ARCH_CAPS_SBDR_SSDP_NO (_AC(1, ULL) << 13) -+#define ARCH_CAPS_FBSDP_NO (_AC(1, ULL) << 14) -+#define ARCH_CAPS_PSDP_NO (_AC(1, ULL) << 15) -+#define ARCH_CAPS_FB_CLEAR (_AC(1, ULL) << 17) -+#define ARCH_CAPS_FB_CLEAR_CTRL (_AC(1, ULL) << 18) - - #define MSR_FLUSH_CMD 0x0000010b - #define FLUSH_CMD_L1D (_AC(1, ULL) << 0) -@@ -83,6 +88,7 @@ - #define MCU_OPT_CTRL_RNGDS_MITG_DIS (_AC(1, ULL) << 0) - #define MCU_OPT_CTRL_RTM_ALLOW (_AC(1, ULL) << 1) - #define MCU_OPT_CTRL_RTM_LOCKED (_AC(1, ULL) << 2) -+#define MCU_OPT_CTRL_FB_CLEAR_DIS (_AC(1, ULL) << 3) - - #define MSR_RTIT_OUTPUT_BASE 0x00000560 - #define MSR_RTIT_OUTPUT_MASK 0x00000561 diff --git a/xsa404-4.16-3.patch b/xsa404-4.16-3.patch deleted file mode 100644 index dab0c31..0000000 --- a/xsa404-4.16-3.patch +++ /dev/null @@ -1,177 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Add spec-ctrl=unpriv-mmio - -Per Xen's support statement, PCI passthrough should be to trusted domains -because the overall system security depends on factors outside of Xen's -control. - -As such, Xen, in a supported configuration, is not vulnerable to DRPW/SBDR. - -However, users who have risk assessed their configuration may be happy with -the risk of DoS, but unhappy with the risk of cross-domain data leakage. Such -users should enable this option. - -On CPUs vulnerable to MDS, the existing mitigations are the best we can do to -mitigate MMIO cross-domain data leakage. - -On CPUs fixed to MDS but vulnerable MMIO stale data leakage, this option: - - * On CPUs susceptible to FBSDP, mitigates cross-domain fill buffer leakage - using FB_CLEAR. - * On CPUs susceptible to SBDR, mitigates RNG data recovery by engaging the - srb-lock, previously used to mitigate SRBDS. - -Both mitigations require microcode from IPU 2022.1, May 2022. - -This is part of XSA-404. - -Signed-off-by: Andrew Cooper -Reviewed-by: Roger Pau Monné ---- -Backporting note: For Xen 4.7 and earlier with bool_t not aliasing bool, the -ARCH_CAPS_FB_CLEAR hunk needs !! - -diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc -index d5cb09f86541..a642e43476a2 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -2235,7 +2235,7 @@ By default SSBD will be mitigated at runtime (i.e `ssbd=runtime`). - ### spec-ctrl (x86) - > `= List of [ , xen=, {pv,hvm,msr-sc,rsb,md-clear}=, - > bti-thunk=retpoline|lfence|jmp, {ibrs,ibpb,ssbd,eager-fpu, --> l1d-flush,branch-harden,srb-lock}= ]` -+> l1d-flush,branch-harden,srb-lock,unpriv-mmio}= ]` - - Controls for speculative execution sidechannel mitigations. By default, Xen - will pick the most appropriate mitigations based on compiled in support, -@@ -2314,8 +2314,16 @@ Xen will enable this mitigation. - On hardware supporting SRBDS_CTRL, the `srb-lock=` option can be used to force - or prevent Xen from protect the Special Register Buffer from leaking stale - data. By default, Xen will enable this mitigation, except on parts where MDS --is fixed and TAA is fixed/mitigated (in which case, there is believed to be no --way for an attacker to obtain the stale data). -+is fixed and TAA is fixed/mitigated and there are no unprivileged MMIO -+mappings (in which case, there is believed to be no way for an attacker to -+obtain stale data). -+ -+The `unpriv-mmio=` boolean indicates whether the system has (or will have) -+less than fully privileged domains granted access to MMIO devices. By -+default, this option is disabled. If enabled, Xen will use the `FB_CLEAR` -+and/or `SRBDS_CTRL` functionality available in the Intel May 2022 microcode -+release to mitigate cross-domain leakage of data via the MMIO Stale Data -+vulnerabilities. - - ### sync_console - > `= ` -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index d285538bde9f..099113ba41e6 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -67,6 +67,8 @@ static bool __initdata cpu_has_bug_msbds_only; /* => minimal HT impact. */ - static bool __initdata cpu_has_bug_mds; /* Any other M{LP,SB,FB}DS combination. */ - - static int8_t __initdata opt_srb_lock = -1; -+static bool __initdata opt_unpriv_mmio; -+static bool __read_mostly opt_fb_clear_mmio; - - static int __init parse_spec_ctrl(const char *s) - { -@@ -184,6 +186,8 @@ static int __init parse_spec_ctrl(const char *s) - opt_branch_harden = val; - else if ( (val = parse_boolean("srb-lock", s, ss)) >= 0 ) - opt_srb_lock = val; -+ else if ( (val = parse_boolean("unpriv-mmio", s, ss)) >= 0 ) -+ opt_unpriv_mmio = val; - else - rc = -EINVAL; - -@@ -392,7 +396,8 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - opt_srb_lock ? " SRB_LOCK+" : " SRB_LOCK-", - opt_ibpb ? " IBPB" : "", - opt_l1d_flush ? " L1D_FLUSH" : "", -- opt_md_clear_pv || opt_md_clear_hvm ? " VERW" : "", -+ opt_md_clear_pv || opt_md_clear_hvm || -+ opt_fb_clear_mmio ? " VERW" : "", - opt_branch_harden ? " BRANCH_HARDEN" : ""); - - /* L1TF diagnostics, printed if vulnerable or PV shadowing is in use. */ -@@ -941,7 +946,9 @@ void spec_ctrl_init_domain(struct domain *d) - { - bool pv = is_pv_domain(d); - -- d->arch.verw = pv ? opt_md_clear_pv : opt_md_clear_hvm; -+ d->arch.verw = -+ (pv ? opt_md_clear_pv : opt_md_clear_hvm) || -+ (opt_fb_clear_mmio && is_iommu_enabled(d)); - } - - void __init init_speculation_mitigations(void) -@@ -1196,6 +1203,18 @@ void __init init_speculation_mitigations(void) - mds_calculations(caps); - - /* -+ * Parts which enumerate FB_CLEAR are those which are post-MDS_NO and have -+ * reintroduced the VERW fill buffer flushing side effect because of a -+ * susceptibility to FBSDP. -+ * -+ * If unprivileged guests have (or will have) MMIO mappings, we can -+ * mitigate cross-domain leakage of fill buffer data by issuing VERW on -+ * the return-to-guest path. -+ */ -+ if ( opt_unpriv_mmio ) -+ opt_fb_clear_mmio = caps & ARCH_CAPS_FB_CLEAR; -+ -+ /* - * By default, enable PV and HVM mitigations on MDS-vulnerable hardware. - * This will only be a token effort for MLPDS/MFBDS when HT is enabled, - * but it is somewhat better than nothing. -@@ -1208,18 +1227,20 @@ void __init init_speculation_mitigations(void) - boot_cpu_has(X86_FEATURE_MD_CLEAR)); - - /* -- * Enable MDS defences as applicable. The Idle blocks need using if -- * either PV or HVM defences are used. -+ * Enable MDS/MMIO defences as applicable. The Idle blocks need using if -+ * either the PV or HVM MDS defences are used, or if we may give MMIO -+ * access to untrusted guests. - * - * HVM is more complicated. The MD_CLEAR microcode extends L1D_FLUSH with - * equivalent semantics to avoid needing to perform both flushes on the -- * HVM path. Therefore, we don't need VERW in addition to L1D_FLUSH. -+ * HVM path. Therefore, we don't need VERW in addition to L1D_FLUSH (for -+ * MDS mitigations. L1D_FLUSH is not safe for MMIO mitigations.) - * - * After calculating the appropriate idle setting, simplify - * opt_md_clear_hvm to mean just "should we VERW on the way into HVM - * guests", so spec_ctrl_init_domain() can calculate suitable settings. - */ -- if ( opt_md_clear_pv || opt_md_clear_hvm ) -+ if ( opt_md_clear_pv || opt_md_clear_hvm || opt_fb_clear_mmio ) - setup_force_cpu_cap(X86_FEATURE_SC_VERW_IDLE); - opt_md_clear_hvm &= !(caps & ARCH_CAPS_SKIP_L1DFL) && !opt_l1d_flush; - -@@ -1284,14 +1305,19 @@ void __init init_speculation_mitigations(void) - * On some SRBDS-affected hardware, it may be safe to relax srb-lock by - * default. - * -- * On parts which enumerate MDS_NO and not TAA_NO, TSX is the only known -- * way to access the Fill Buffer. If TSX isn't available (inc. SKU -- * reasons on some models), or TSX is explicitly disabled, then there is -- * no need for the extra overhead to protect RDRAND/RDSEED. -+ * All parts with SRBDS_CTRL suffer SSDP, the mechanism by which stale RNG -+ * data becomes available to other contexts. To recover the data, an -+ * attacker needs to use: -+ * - SBDS (MDS or TAA to sample the cores fill buffer) -+ * - SBDR (Architecturally retrieve stale transaction buffer contents) -+ * - DRPW (Architecturally latch stale fill buffer data) -+ * -+ * On MDS_NO parts, and with TAA_NO or TSX unavailable/disabled, and there -+ * is no unprivileged MMIO access, the RNG data doesn't need protecting. - */ - if ( cpu_has_srbds_ctrl ) - { -- if ( opt_srb_lock == -1 && -+ if ( opt_srb_lock == -1 && !opt_unpriv_mmio && - (caps & (ARCH_CAPS_MDS_NO|ARCH_CAPS_TAA_NO)) == ARCH_CAPS_MDS_NO && - (!cpu_has_hle || ((caps & ARCH_CAPS_TSX_CTRL) && rtm_disabled)) ) - opt_srb_lock = 0; diff --git a/xsa407-4.16-1.patch b/xsa407-4.16-1.patch deleted file mode 100644 index d6dc7fc..0000000 --- a/xsa407-4.16-1.patch +++ /dev/null @@ -1,153 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Rework spec_ctrl_flags context switching - -We are shortly going to need to context switch new bits in both the vcpu and -S3 paths. Introduce SCF_IST_MASK and SCF_DOM_MASK, and rework d->arch.verw -into d->arch.spec_ctrl_flags to accommodate. - -No functional change. - -This is part of XSA-407. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/acpi/power.c b/xen/arch/x86/acpi/power.c -index 5eaa77f66a28..dd397f713067 100644 ---- a/xen/arch/x86/acpi/power.c -+++ b/xen/arch/x86/acpi/power.c -@@ -248,8 +248,8 @@ static int enter_state(u32 state) - error = 0; - - ci = get_cpu_info(); -- /* Avoid NMI/#MC using MSR_SPEC_CTRL until we've reloaded microcode. */ -- ci->spec_ctrl_flags &= ~SCF_ist_wrmsr; -+ /* Avoid NMI/#MC using unsafe MSRs until we've reloaded microcode. */ -+ ci->spec_ctrl_flags &= ~SCF_IST_MASK; - - ACPI_FLUSH_CPU_CACHE(); - -@@ -292,8 +292,8 @@ static int enter_state(u32 state) - if ( !recheck_cpu_features(0) ) - panic("Missing previously available feature(s)\n"); - -- /* Re-enabled default NMI/#MC use of MSR_SPEC_CTRL. */ -- ci->spec_ctrl_flags |= (default_spec_ctrl_flags & SCF_ist_wrmsr); -+ /* Re-enabled default NMI/#MC use of MSRs now microcode is loaded. */ -+ ci->spec_ctrl_flags |= (default_spec_ctrl_flags & SCF_IST_MASK); - - if ( boot_cpu_has(X86_FEATURE_IBRSB) || boot_cpu_has(X86_FEATURE_IBRS) ) - { -diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c -index 1fe6644a71ae..82a0b73cf6ef 100644 ---- a/xen/arch/x86/domain.c -+++ b/xen/arch/x86/domain.c -@@ -2092,10 +2092,10 @@ void context_switch(struct vcpu *prev, struct vcpu *next) - } - } - -- /* Update the top-of-stack block with the VERW disposition. */ -- info->spec_ctrl_flags &= ~SCF_verw; -- if ( nextd->arch.verw ) -- info->spec_ctrl_flags |= SCF_verw; -+ /* Update the top-of-stack block with the new spec_ctrl settings. */ -+ info->spec_ctrl_flags = -+ (info->spec_ctrl_flags & ~SCF_DOM_MASK) | -+ (nextd->arch.spec_ctrl_flags & SCF_DOM_MASK); - } - - sched_context_switched(prev, next); -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 9507e5da60a9..7e646680f1c7 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -1010,9 +1010,12 @@ void spec_ctrl_init_domain(struct domain *d) - { - bool pv = is_pv_domain(d); - -- d->arch.verw = -- (pv ? opt_md_clear_pv : opt_md_clear_hvm) || -- (opt_fb_clear_mmio && is_iommu_enabled(d)); -+ bool verw = ((pv ? opt_md_clear_pv : opt_md_clear_hvm) || -+ (opt_fb_clear_mmio && is_iommu_enabled(d))); -+ -+ d->arch.spec_ctrl_flags = -+ (verw ? SCF_verw : 0) | -+ 0; - } - - void __init init_speculation_mitigations(void) -diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h -index 2398a1d99da9..e4c099262cb7 100644 ---- a/xen/include/asm-x86/domain.h -+++ b/xen/include/asm-x86/domain.h -@@ -319,8 +319,7 @@ struct arch_domain - uint32_t pci_cf8; - uint8_t cmos_idx; - -- /* Use VERW on return-to-guest for its flushing side effect. */ -- bool verw; -+ uint8_t spec_ctrl_flags; /* See SCF_DOM_MASK */ - - union { - struct pv_domain pv; -diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h -index 7e83e0179fb9..3cd72e40305f 100644 ---- a/xen/include/asm-x86/spec_ctrl.h -+++ b/xen/include/asm-x86/spec_ctrl.h -@@ -20,12 +20,40 @@ - #ifndef __X86_SPEC_CTRL_H__ - #define __X86_SPEC_CTRL_H__ - --/* Encoding of cpuinfo.spec_ctrl_flags */ -+/* -+ * Encoding of: -+ * cpuinfo.spec_ctrl_flags -+ * default_spec_ctrl_flags -+ * domain.spec_ctrl_flags -+ * -+ * Live settings are in the top-of-stack block, because they need to be -+ * accessable when XPTI is active. Some settings are fixed from boot, some -+ * context switched per domain, and some inhibited in the S3 path. -+ */ - #define SCF_use_shadow (1 << 0) - #define SCF_ist_wrmsr (1 << 1) - #define SCF_ist_rsb (1 << 2) - #define SCF_verw (1 << 3) - -+/* -+ * The IST paths (NMI/#MC) can interrupt any arbitrary context. Some -+ * functionality requires updated microcode to work. -+ * -+ * On boot, this is easy; we load microcode before figuring out which -+ * speculative protections to apply. However, on the S3 resume path, we must -+ * be able to disable the configured mitigations until microcode is reloaded. -+ * -+ * These are the controls to inhibit on the S3 resume path until microcode has -+ * been reloaded. -+ */ -+#define SCF_IST_MASK (SCF_ist_wrmsr) -+ -+/* -+ * Some speculative protections are per-domain. These settings are merged -+ * into the top-of-stack block in the context switch path. -+ */ -+#define SCF_DOM_MASK (SCF_verw) -+ - #ifndef __ASSEMBLY__ - - #include -diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h -index 5a590bac44aa..66b00d511fc6 100644 ---- a/xen/include/asm-x86/spec_ctrl_asm.h -+++ b/xen/include/asm-x86/spec_ctrl_asm.h -@@ -248,9 +248,6 @@ - - /* - * Use in IST interrupt/exception context. May interrupt Xen or PV context. -- * Fine grain control of SCF_ist_wrmsr is needed for safety in the S3 resume -- * path to avoid using MSR_SPEC_CTRL before the microcode introducing it has -- * been reloaded. - */ - .macro SPEC_CTRL_ENTRY_FROM_INTR_IST - /* diff --git a/xsa407-4.16-2.patch b/xsa407-4.16-2.patch deleted file mode 100644 index 012d028..0000000 --- a/xsa407-4.16-2.patch +++ /dev/null @@ -1,99 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Rename SCF_ist_wrmsr to SCF_ist_sc_msr - -We are about to introduce SCF_ist_ibpb, at which point SCF_ist_wrmsr becomes -ambiguous. - -No functional change. - -This is part of XSA-407. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 7e646680f1c7..89f95c083e1b 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -1115,7 +1115,7 @@ void __init init_speculation_mitigations(void) - { - if ( opt_msr_sc_pv ) - { -- default_spec_ctrl_flags |= SCF_ist_wrmsr; -+ default_spec_ctrl_flags |= SCF_ist_sc_msr; - setup_force_cpu_cap(X86_FEATURE_SC_MSR_PV); - } - -@@ -1126,7 +1126,7 @@ void __init init_speculation_mitigations(void) - * Xen's value is not restored atomically. An early NMI hitting - * the VMExit path needs to restore Xen's value for safety. - */ -- default_spec_ctrl_flags |= SCF_ist_wrmsr; -+ default_spec_ctrl_flags |= SCF_ist_sc_msr; - setup_force_cpu_cap(X86_FEATURE_SC_MSR_HVM); - } - } -@@ -1139,7 +1139,7 @@ void __init init_speculation_mitigations(void) - * on real hardware matches the availability of MSR_SPEC_CTRL in the - * first place. - * -- * No need for SCF_ist_wrmsr because Xen's value is restored -+ * No need for SCF_ist_sc_msr because Xen's value is restored - * atomically WRT NMIs in the VMExit path. - * - * TODO: Adjust cpu_has_svm_spec_ctrl to be usable earlier on boot. -diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h -index 3cd72e40305f..f8f0ac47e759 100644 ---- a/xen/include/asm-x86/spec_ctrl.h -+++ b/xen/include/asm-x86/spec_ctrl.h -@@ -31,7 +31,7 @@ - * context switched per domain, and some inhibited in the S3 path. - */ - #define SCF_use_shadow (1 << 0) --#define SCF_ist_wrmsr (1 << 1) -+#define SCF_ist_sc_msr (1 << 1) - #define SCF_ist_rsb (1 << 2) - #define SCF_verw (1 << 3) - -@@ -46,7 +46,7 @@ - * These are the controls to inhibit on the S3 resume path until microcode has - * been reloaded. - */ --#define SCF_IST_MASK (SCF_ist_wrmsr) -+#define SCF_IST_MASK (SCF_ist_sc_msr) - - /* - * Some speculative protections are per-domain. These settings are merged -diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h -index 66b00d511fc6..0ff1b118f882 100644 ---- a/xen/include/asm-x86/spec_ctrl_asm.h -+++ b/xen/include/asm-x86/spec_ctrl_asm.h -@@ -266,8 +266,8 @@ - - .L\@_skip_rsb: - -- test $SCF_ist_wrmsr, %al -- jz .L\@_skip_wrmsr -+ test $SCF_ist_sc_msr, %al -+ jz .L\@_skip_msr_spec_ctrl - - xor %edx, %edx - testb $3, UREGS_cs(%rsp) -@@ -290,7 +290,7 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): - * to speculate around the WRMSR. As a result, we need a dispatch - * serialising instruction in the else clause. - */ --.L\@_skip_wrmsr: -+.L\@_skip_msr_spec_ctrl: - lfence - UNLIKELY_END(\@_serialise) - .endm -@@ -301,7 +301,7 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): - * Requires %rbx=stack_end - * Clobbers %rax, %rcx, %rdx - */ -- testb $SCF_ist_wrmsr, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%rbx) -+ testb $SCF_ist_sc_msr, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%rbx) - jz .L\@_skip - - DO_SPEC_CTRL_EXIT_TO_XEN diff --git a/xsa407-4.16-3.patch b/xsa407-4.16-3.patch deleted file mode 100644 index c6f7e6b..0000000 --- a/xsa407-4.16-3.patch +++ /dev/null @@ -1,86 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Rename opt_ibpb to opt_ibpb_ctxt_switch - -We are about to introduce the use of IBPB at different points in Xen, making -opt_ibpb ambiguous. Rename it to opt_ibpb_ctxt_switch. - -No functional change. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c -index 82a0b73cf6ef..0d39981550ca 100644 ---- a/xen/arch/x86/domain.c -+++ b/xen/arch/x86/domain.c -@@ -2064,7 +2064,7 @@ void context_switch(struct vcpu *prev, struct vcpu *next) - - ctxt_switch_levelling(next); - -- if ( opt_ibpb && !is_idle_domain(nextd) ) -+ if ( opt_ibpb_ctxt_switch && !is_idle_domain(nextd) ) - { - static DEFINE_PER_CPU(unsigned int, last); - unsigned int *last_id = &this_cpu(last); -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 89f95c083e1b..f4ae36eae2d0 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -54,7 +54,7 @@ int8_t __initdata opt_stibp = -1; - bool __read_mostly opt_ssbd; - int8_t __initdata opt_psfd = -1; - --bool __read_mostly opt_ibpb = true; -+bool __read_mostly opt_ibpb_ctxt_switch = true; - int8_t __read_mostly opt_eager_fpu = -1; - int8_t __read_mostly opt_l1d_flush = -1; - static bool __initdata opt_branch_harden = true; -@@ -117,7 +117,7 @@ static int __init parse_spec_ctrl(const char *s) - - opt_thunk = THUNK_JMP; - opt_ibrs = 0; -- opt_ibpb = false; -+ opt_ibpb_ctxt_switch = false; - opt_ssbd = false; - opt_l1d_flush = 0; - opt_branch_harden = false; -@@ -238,7 +238,7 @@ static int __init parse_spec_ctrl(const char *s) - - /* Misc settings. */ - else if ( (val = parse_boolean("ibpb", s, ss)) >= 0 ) -- opt_ibpb = val; -+ opt_ibpb_ctxt_switch = val; - else if ( (val = parse_boolean("eager-fpu", s, ss)) >= 0 ) - opt_eager_fpu = val; - else if ( (val = parse_boolean("l1d-flush", s, ss)) >= 0 ) -@@ -458,7 +458,7 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - (opt_tsx & 1) ? " TSX+" : " TSX-", - !cpu_has_srbds_ctrl ? "" : - opt_srb_lock ? " SRB_LOCK+" : " SRB_LOCK-", -- opt_ibpb ? " IBPB" : "", -+ opt_ibpb_ctxt_switch ? " IBPB-ctxt" : "", - opt_l1d_flush ? " L1D_FLUSH" : "", - opt_md_clear_pv || opt_md_clear_hvm || - opt_fb_clear_mmio ? " VERW" : "", -@@ -1240,7 +1240,7 @@ void __init init_speculation_mitigations(void) - - /* Check we have hardware IBPB support before using it... */ - if ( !boot_cpu_has(X86_FEATURE_IBRSB) && !boot_cpu_has(X86_FEATURE_IBPB) ) -- opt_ibpb = false; -+ opt_ibpb_ctxt_switch = false; - - /* Check whether Eager FPU should be enabled by default. */ - if ( opt_eager_fpu == -1 ) -diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h -index f8f0ac47e759..fb4365575620 100644 ---- a/xen/include/asm-x86/spec_ctrl.h -+++ b/xen/include/asm-x86/spec_ctrl.h -@@ -63,7 +63,7 @@ - void init_speculation_mitigations(void); - void spec_ctrl_init_domain(struct domain *d); - --extern bool opt_ibpb; -+extern bool opt_ibpb_ctxt_switch; - extern bool opt_ssbd; - extern int8_t opt_eager_fpu; - extern int8_t opt_l1d_flush; diff --git a/xsa407-4.16-4.patch b/xsa407-4.16-4.patch deleted file mode 100644 index 50e5f0f..0000000 --- a/xsa407-4.16-4.patch +++ /dev/null @@ -1,96 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Rework SPEC_CTRL_ENTRY_FROM_INTR_IST - -We are shortly going to add a conditional IBPB in this path. - -Therefore, we cannot hold spec_ctrl_flags in %eax, and rely on only clobbering -it after we're done with its contents. %rbx is available for use, and the -more normal register to hold preserved information in. - -With %rax freed up, use it instead of %rdx for the RSB tmp register, and for -the adjustment to spec_ctrl_flags. - -This leaves no use of %rdx, except as 0 for the upper half of WRMSR. In -practice, %rdx is 0 from SAVE_ALL on all paths and isn't likely to change in -the foreseeable future, so update the macro entry requirements to state this -dependency. This marginal optimisation can be revisited if circumstances -change. - -No practical change. - -This is part of XSA-407. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S -index 2a86938f1f32..a1810bf4d311 100644 ---- a/xen/arch/x86/x86_64/entry.S -+++ b/xen/arch/x86/x86_64/entry.S -@@ -932,7 +932,7 @@ ENTRY(double_fault) - - GET_STACK_END(14) - -- SPEC_CTRL_ENTRY_FROM_INTR_IST /* Req: %rsp=regs, %r14=end, Clob: acd */ -+ SPEC_CTRL_ENTRY_FROM_INTR_IST /* Req: %rsp=regs, %r14=end, %rdx=0, Clob: abcd */ - /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ - - mov STACK_CPUINFO_FIELD(xen_cr3)(%r14), %rbx -@@ -968,7 +968,7 @@ handle_ist_exception: - - GET_STACK_END(14) - -- SPEC_CTRL_ENTRY_FROM_INTR_IST /* Req: %rsp=regs, %r14=end, Clob: acd */ -+ SPEC_CTRL_ENTRY_FROM_INTR_IST /* Req: %rsp=regs, %r14=end, %rdx=0, Clob: abcd */ - /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ - - mov STACK_CPUINFO_FIELD(xen_cr3)(%r14), %rcx -diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h -index 0ff1b118f882..15e24cde00d1 100644 ---- a/xen/include/asm-x86/spec_ctrl_asm.h -+++ b/xen/include/asm-x86/spec_ctrl_asm.h -@@ -251,34 +251,33 @@ - */ - .macro SPEC_CTRL_ENTRY_FROM_INTR_IST - /* -- * Requires %rsp=regs, %r14=stack_end -- * Clobbers %rax, %rcx, %rdx -+ * Requires %rsp=regs, %r14=stack_end, %rdx=0 -+ * Clobbers %rax, %rbx, %rcx, %rdx - * - * This is logical merge of DO_OVERWRITE_RSB and DO_SPEC_CTRL_ENTRY - * maybexen=1, but with conditionals rather than alternatives. - */ -- movzbl STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14), %eax -+ movzbl STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14), %ebx - -- test $SCF_ist_rsb, %al -+ test $SCF_ist_rsb, %bl - jz .L\@_skip_rsb - -- DO_OVERWRITE_RSB tmp=rdx /* Clobbers %rcx/%rdx */ -+ DO_OVERWRITE_RSB /* Clobbers %rax/%rcx */ - - .L\@_skip_rsb: - -- test $SCF_ist_sc_msr, %al -+ test $SCF_ist_sc_msr, %bl - jz .L\@_skip_msr_spec_ctrl - -- xor %edx, %edx -+ xor %eax, %eax - testb $3, UREGS_cs(%rsp) -- setnz %dl -- not %edx -- and %dl, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14) -+ setnz %al -+ not %eax -+ and %al, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14) - - /* Load Xen's intended value. */ - mov $MSR_SPEC_CTRL, %ecx - movzbl STACK_CPUINFO_FIELD(xen_spec_ctrl)(%r14), %eax -- xor %edx, %edx - wrmsr - - /* Opencoded UNLIKELY_START() with no condition. */ diff --git a/xsa407-4.16-5.patch b/xsa407-4.16-5.patch deleted file mode 100644 index ef6da7c..0000000 --- a/xsa407-4.16-5.patch +++ /dev/null @@ -1,285 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Support IBPB-on-entry - -We are going to need this to mitigate Branch Type Confusion on AMD/Hygon CPUs, -but as we've talked about using it in other cases too, arrange to support it -generally. However, this is also very expensive in some cases, so we're going -to want per-domain controls. - -Introduce SCF_ist_ibpb and SCF_entry_ibpb controls, adding them to the IST and -DOM masks as appropriate. Also introduce X86_FEATURE_IBPB_ENTRY_{PV,HVM} to -to patch the code blocks. - -For SVM, the STGI is serialising enough to protect against Spectre-v1 attacks, -so no "else lfence" is necessary. VT-x will use use the MSR host load list, -so doesn't need any code in the VMExit path. - -For the IST path, we can't safely check CPL==0 to skip a flush, as we might -have hit an entry path before it's IBPB. As IST hitting Xen is rare, flush -irrespective of CPL. A later path, SCF_ist_sc_msr, provides Spectre-v1 -safety. - -For the PV paths, we know we're interrupting CPL>0, while for the INTR paths, -we can safely check CPL==0. Only flush when interrupting guest context. - -An "else lfence" is needed for safety, but we want to be able to skip it on -unaffected CPUs, so the block wants to be an alternative, which means the -lfence has to be inline rather than UNLIKELY() (the replacement block doesn't -have displacements fixed up for anything other than the first instruction). - -As with SPEC_CTRL_ENTRY_FROM_INTR_IST, %rdx is 0 on entry so rely on this to -shrink the logic marginally. Update the comments to specify this new -dependency. - -This is part of XSA-407. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/hvm/svm/entry.S b/xen/arch/x86/hvm/svm/entry.S -index 4ae55a2ef605..0ff4008060fa 100644 ---- a/xen/arch/x86/hvm/svm/entry.S -+++ b/xen/arch/x86/hvm/svm/entry.S -@@ -97,7 +97,19 @@ __UNLIKELY_END(nsvm_hap) - - GET_CURRENT(bx) - -- /* SPEC_CTRL_ENTRY_FROM_SVM Req: %rsp=regs/cpuinfo Clob: acd */ -+ /* SPEC_CTRL_ENTRY_FROM_SVM Req: %rsp=regs/cpuinfo, %rdx=0 Clob: acd */ -+ -+ .macro svm_vmexit_cond_ibpb -+ testb $SCF_entry_ibpb, CPUINFO_xen_spec_ctrl(%rsp) -+ jz .L_skip_ibpb -+ -+ mov $MSR_PRED_CMD, %ecx -+ mov $PRED_CMD_IBPB, %eax -+ wrmsr -+.L_skip_ibpb: -+ .endm -+ ALTERNATIVE "", svm_vmexit_cond_ibpb, X86_FEATURE_IBPB_ENTRY_HVM -+ - ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_HVM - - .macro svm_vmexit_spec_ctrl -@@ -114,6 +126,10 @@ __UNLIKELY_END(nsvm_hap) - ALTERNATIVE "", svm_vmexit_spec_ctrl, X86_FEATURE_SC_MSR_HVM - /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ - -+ /* -+ * STGI is executed unconditionally, and is sufficiently serialising -+ * to safely resolve any Spectre-v1 concerns in the above logic. -+ */ - stgi - GLOBAL(svm_stgi_label) - mov %rsp,%rdi -diff --git a/xen/arch/x86/hvm/vmx/vmcs.c b/xen/arch/x86/hvm/vmx/vmcs.c -index f9f9bc18cdbc..dd817cee4e69 100644 ---- a/xen/arch/x86/hvm/vmx/vmcs.c -+++ b/xen/arch/x86/hvm/vmx/vmcs.c -@@ -1345,6 +1345,10 @@ static int construct_vmcs(struct vcpu *v) - rc = vmx_add_msr(v, MSR_FLUSH_CMD, FLUSH_CMD_L1D, - VMX_MSR_GUEST_LOADONLY); - -+ if ( !rc && (d->arch.spec_ctrl_flags & SCF_entry_ibpb) ) -+ rc = vmx_add_msr(v, MSR_PRED_CMD, PRED_CMD_IBPB, -+ VMX_MSR_HOST); -+ - out: - vmx_vmcs_exit(v); - -diff --git a/xen/arch/x86/x86_64/compat/entry.S b/xen/arch/x86/x86_64/compat/entry.S -index 5fd6dbbd4513..b86d38d1c50d 100644 ---- a/xen/arch/x86/x86_64/compat/entry.S -+++ b/xen/arch/x86/x86_64/compat/entry.S -@@ -18,7 +18,7 @@ ENTRY(entry_int82) - movl $HYPERCALL_VECTOR, 4(%rsp) - SAVE_ALL compat=1 /* DPL1 gate, restricted to 32bit PV guests only. */ - -- SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, Clob: acd */ -+ SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, %rdx=0, Clob: acd */ - /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ - - CR4_PV32_RESTORE -diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S -index a1810bf4d311..fba8ae498f74 100644 ---- a/xen/arch/x86/x86_64/entry.S -+++ b/xen/arch/x86/x86_64/entry.S -@@ -260,7 +260,7 @@ ENTRY(lstar_enter) - movl $TRAP_syscall, 4(%rsp) - SAVE_ALL - -- SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, Clob: acd */ -+ SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, %rdx=0, Clob: acd */ - /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ - - GET_STACK_END(bx) -@@ -298,7 +298,7 @@ ENTRY(cstar_enter) - movl $TRAP_syscall, 4(%rsp) - SAVE_ALL - -- SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, Clob: acd */ -+ SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, %rdx=0, Clob: acd */ - /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ - - GET_STACK_END(bx) -@@ -338,7 +338,7 @@ GLOBAL(sysenter_eflags_saved) - movl $TRAP_syscall, 4(%rsp) - SAVE_ALL - -- SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, Clob: acd */ -+ SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, %rdx=0, Clob: acd */ - /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ - - GET_STACK_END(bx) -@@ -392,7 +392,7 @@ ENTRY(int80_direct_trap) - movl $0x80, 4(%rsp) - SAVE_ALL - -- SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, Clob: acd */ -+ SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, %rdx=0, Clob: acd */ - /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ - - GET_STACK_END(bx) -@@ -674,7 +674,7 @@ ENTRY(common_interrupt) - - GET_STACK_END(14) - -- SPEC_CTRL_ENTRY_FROM_INTR /* Req: %rsp=regs, %r14=end, Clob: acd */ -+ SPEC_CTRL_ENTRY_FROM_INTR /* Req: %rsp=regs, %r14=end, %rdx=0, Clob: acd */ - /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ - - mov STACK_CPUINFO_FIELD(xen_cr3)(%r14), %rcx -@@ -708,7 +708,7 @@ GLOBAL(handle_exception) - - GET_STACK_END(14) - -- SPEC_CTRL_ENTRY_FROM_INTR /* Req: %rsp=regs, %r14=end, Clob: acd */ -+ SPEC_CTRL_ENTRY_FROM_INTR /* Req: %rsp=regs, %r14=end, %rdx=0, Clob: acd */ - /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ - - mov STACK_CPUINFO_FIELD(xen_cr3)(%r14), %rcx -diff --git a/xen/include/asm-x86/cpufeatures.h b/xen/include/asm-x86/cpufeatures.h -index 493d338a085e..672c9ee22ba2 100644 ---- a/xen/include/asm-x86/cpufeatures.h -+++ b/xen/include/asm-x86/cpufeatures.h -@@ -39,6 +39,8 @@ XEN_CPUFEATURE(XEN_LBR, X86_SYNTH(22)) /* Xen uses MSR_DEBUGCTL.LBR */ - XEN_CPUFEATURE(SC_VERW_IDLE, X86_SYNTH(25)) /* VERW used by Xen for idle */ - XEN_CPUFEATURE(XEN_SHSTK, X86_SYNTH(26)) /* Xen uses CET Shadow Stacks */ - XEN_CPUFEATURE(XEN_IBT, X86_SYNTH(27)) /* Xen uses CET Indirect Branch Tracking */ -+XEN_CPUFEATURE(IBPB_ENTRY_PV, X86_SYNTH(28)) /* MSR_PRED_CMD used by Xen for PV */ -+XEN_CPUFEATURE(IBPB_ENTRY_HVM, X86_SYNTH(29)) /* MSR_PRED_CMD used by Xen for HVM */ - - /* Bug words follow the synthetic words. */ - #define X86_NR_BUG 1 -diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h -index fb4365575620..3fc599a817c4 100644 ---- a/xen/include/asm-x86/spec_ctrl.h -+++ b/xen/include/asm-x86/spec_ctrl.h -@@ -34,6 +34,8 @@ - #define SCF_ist_sc_msr (1 << 1) - #define SCF_ist_rsb (1 << 2) - #define SCF_verw (1 << 3) -+#define SCF_ist_ibpb (1 << 4) -+#define SCF_entry_ibpb (1 << 5) - - /* - * The IST paths (NMI/#MC) can interrupt any arbitrary context. Some -@@ -46,13 +48,13 @@ - * These are the controls to inhibit on the S3 resume path until microcode has - * been reloaded. - */ --#define SCF_IST_MASK (SCF_ist_sc_msr) -+#define SCF_IST_MASK (SCF_ist_sc_msr | SCF_ist_ibpb) - - /* - * Some speculative protections are per-domain. These settings are merged - * into the top-of-stack block in the context switch path. - */ --#define SCF_DOM_MASK (SCF_verw) -+#define SCF_DOM_MASK (SCF_verw | SCF_entry_ibpb) - - #ifndef __ASSEMBLY__ - -diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h -index 15e24cde00d1..9eb4ad9ab71d 100644 ---- a/xen/include/asm-x86/spec_ctrl_asm.h -+++ b/xen/include/asm-x86/spec_ctrl_asm.h -@@ -88,6 +88,35 @@ - * - SPEC_CTRL_EXIT_TO_{SVM,VMX} - */ - -+.macro DO_SPEC_CTRL_COND_IBPB maybexen:req -+/* -+ * Requires %rsp=regs (also cpuinfo if !maybexen) -+ * Requires %r14=stack_end (if maybexen), %rdx=0 -+ * Clobbers %rax, %rcx, %rdx -+ * -+ * Conditionally issue IBPB if SCF_entry_ibpb is active. In the maybexen -+ * case, we can safely look at UREGS_cs to skip taking the hit when -+ * interrupting Xen. -+ */ -+ .if \maybexen -+ testb $SCF_entry_ibpb, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14) -+ jz .L\@_skip -+ testb $3, UREGS_cs(%rsp) -+ .else -+ testb $SCF_entry_ibpb, CPUINFO_xen_spec_ctrl(%rsp) -+ .endif -+ jz .L\@_skip -+ -+ mov $MSR_PRED_CMD, %ecx -+ mov $PRED_CMD_IBPB, %eax -+ wrmsr -+ jmp .L\@_done -+ -+.L\@_skip: -+ lfence -+.L\@_done: -+.endm -+ - .macro DO_OVERWRITE_RSB tmp=rax - /* - * Requires nothing -@@ -225,12 +254,16 @@ - - /* Use after an entry from PV context (syscall/sysenter/int80/int82/etc). */ - #define SPEC_CTRL_ENTRY_FROM_PV \ -+ ALTERNATIVE "", __stringify(DO_SPEC_CTRL_COND_IBPB maybexen=0), \ -+ X86_FEATURE_IBPB_ENTRY_PV; \ - ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_PV; \ - ALTERNATIVE "", __stringify(DO_SPEC_CTRL_ENTRY maybexen=0), \ - X86_FEATURE_SC_MSR_PV - - /* Use in interrupt/exception context. May interrupt Xen or PV context. */ - #define SPEC_CTRL_ENTRY_FROM_INTR \ -+ ALTERNATIVE "", __stringify(DO_SPEC_CTRL_COND_IBPB maybexen=1), \ -+ X86_FEATURE_IBPB_ENTRY_PV; \ - ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_PV; \ - ALTERNATIVE "", __stringify(DO_SPEC_CTRL_ENTRY maybexen=1), \ - X86_FEATURE_SC_MSR_PV -@@ -254,11 +287,23 @@ - * Requires %rsp=regs, %r14=stack_end, %rdx=0 - * Clobbers %rax, %rbx, %rcx, %rdx - * -- * This is logical merge of DO_OVERWRITE_RSB and DO_SPEC_CTRL_ENTRY -- * maybexen=1, but with conditionals rather than alternatives. -+ * This is logical merge of: -+ * DO_SPEC_CTRL_COND_IBPB maybexen=0 -+ * DO_OVERWRITE_RSB -+ * DO_SPEC_CTRL_ENTRY maybexen=1 -+ * but with conditionals rather than alternatives. - */ - movzbl STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14), %ebx - -+ test $SCF_ist_ibpb, %bl -+ jz .L\@_skip_ibpb -+ -+ mov $MSR_PRED_CMD, %ecx -+ mov $PRED_CMD_IBPB, %eax -+ wrmsr -+ -+.L\@_skip_ibpb: -+ - test $SCF_ist_rsb, %bl - jz .L\@_skip_rsb - diff --git a/xsa407-4.16-6.patch b/xsa407-4.16-6.patch deleted file mode 100644 index 92290dc..0000000 --- a/xsa407-4.16-6.patch +++ /dev/null @@ -1,93 +0,0 @@ -From: Andrew Cooper -Subject: x86/cpuid: Enumeration for BTC_NO - -BTC_NO indicates that hardware is not succeptable to Branch Type Confusion. - -Zen3 CPUs don't suffer BTC. - -This is part of XSA-407. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/tools/libs/light/libxl_cpuid.c b/tools/libs/light/libxl_cpuid.c -index d462f9e421ed..bf6fdee360a9 100644 ---- a/tools/libs/light/libxl_cpuid.c -+++ b/tools/libs/light/libxl_cpuid.c -@@ -288,6 +288,7 @@ int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str) - {"virt-ssbd", 0x80000008, NA, CPUID_REG_EBX, 25, 1}, - {"ssb-no", 0x80000008, NA, CPUID_REG_EBX, 26, 1}, - {"psfd", 0x80000008, NA, CPUID_REG_EBX, 28, 1}, -+ {"btc-no", 0x80000008, NA, CPUID_REG_EBX, 29, 1}, - - {"nc", 0x80000008, NA, CPUID_REG_ECX, 0, 8}, - {"apicidsize", 0x80000008, NA, CPUID_REG_ECX, 12, 4}, -diff --git a/tools/misc/xen-cpuid.c b/tools/misc/xen-cpuid.c -index bc7dcf55757a..fe22f5f5b68b 100644 ---- a/tools/misc/xen-cpuid.c -+++ b/tools/misc/xen-cpuid.c -@@ -158,7 +158,7 @@ static const char *const str_e8b[32] = - /* [22] */ [23] = "ppin", - [24] = "amd-ssbd", [25] = "virt-ssbd", - [26] = "ssb-no", -- [28] = "psfd", -+ [28] = "psfd", [29] = "btc-no", - }; - - static const char *const str_7d0[32] = -diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c -index b3b9a0df5fed..b158e3acb5c7 100644 ---- a/xen/arch/x86/cpu/amd.c -+++ b/xen/arch/x86/cpu/amd.c -@@ -847,6 +847,16 @@ static void init_amd(struct cpuinfo_x86 *c) - warning_add(text); - } - break; -+ -+ case 0x19: -+ /* -+ * Zen3 (Fam19h model < 0x10) parts are not susceptible to -+ * Branch Type Confusion, but predate the allocation of the -+ * BTC_NO bit. Fill it back in if we're not virtualised. -+ */ -+ if (!cpu_has_hypervisor && !cpu_has(c, X86_FEATURE_BTC_NO)) -+ __set_bit(X86_FEATURE_BTC_NO, c->x86_capability); -+ break; - } - - display_cacheinfo(c); -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index f4ae36eae2d0..0f101c057f3e 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -388,7 +388,7 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - * Hardware read-only information, stating immunity to certain issues, or - * suggestions of which mitigation to use. - */ -- printk(" Hardware hints:%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", -+ printk(" Hardware hints:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", - (caps & ARCH_CAPS_RDCL_NO) ? " RDCL_NO" : "", - (caps & ARCH_CAPS_IBRS_ALL) ? " IBRS_ALL" : "", - (caps & ARCH_CAPS_RSBA) ? " RSBA" : "", -@@ -403,7 +403,8 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - (e8b & cpufeat_mask(X86_FEATURE_IBRS_ALWAYS)) ? " IBRS_ALWAYS" : "", - (e8b & cpufeat_mask(X86_FEATURE_STIBP_ALWAYS)) ? " STIBP_ALWAYS" : "", - (e8b & cpufeat_mask(X86_FEATURE_IBRS_FAST)) ? " IBRS_FAST" : "", -- (e8b & cpufeat_mask(X86_FEATURE_IBRS_SAME_MODE)) ? " IBRS_SAME_MODE" : ""); -+ (e8b & cpufeat_mask(X86_FEATURE_IBRS_SAME_MODE)) ? " IBRS_SAME_MODE" : "", -+ (e8b & cpufeat_mask(X86_FEATURE_BTC_NO)) ? " BTC_NO" : ""); - - /* Hardware features which need driving to mitigate issues. */ - printk(" Hardware features:%s%s%s%s%s%s%s%s%s%s%s%s\n", -diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h -index 743b857dcd5c..e7b8167800a2 100644 ---- a/xen/include/public/arch-x86/cpufeatureset.h -+++ b/xen/include/public/arch-x86/cpufeatureset.h -@@ -266,6 +266,7 @@ XEN_CPUFEATURE(AMD_SSBD, 8*32+24) /*S MSR_SPEC_CTRL.SSBD available */ - XEN_CPUFEATURE(VIRT_SSBD, 8*32+25) /* MSR_VIRT_SPEC_CTRL.SSBD */ - XEN_CPUFEATURE(SSB_NO, 8*32+26) /*A Hardware not vulnerable to SSB */ - XEN_CPUFEATURE(PSFD, 8*32+28) /*S MSR_SPEC_CTRL.PSFD */ -+XEN_CPUFEATURE(BTC_NO, 8*32+29) /*A Hardware not vulnerable to Branch Type Confusion */ - - /* Intel-defined CPU features, CPUID level 0x00000007:0.edx, word 9 */ - XEN_CPUFEATURE(AVX512_4VNNIW, 9*32+ 2) /*A AVX512 Neural Network Instructions */ diff --git a/xsa407-4.16-7.patch b/xsa407-4.16-7.patch deleted file mode 100644 index b71a0f8..0000000 --- a/xsa407-4.16-7.patch +++ /dev/null @@ -1,94 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Enable Zen2 chickenbit - -... as instructed in the Branch Type Confusion whitepaper. - -This is part of XSA-407. - -Signed-off-by: Andrew Cooper - -diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c -index b158e3acb5c7..37ac84ddd74d 100644 ---- a/xen/arch/x86/cpu/amd.c -+++ b/xen/arch/x86/cpu/amd.c -@@ -731,6 +731,31 @@ void amd_init_ssbd(const struct cpuinfo_x86 *c) - printk_once(XENLOG_ERR "No SSBD controls available\n"); - } - -+/* -+ * On Zen2 we offer this chicken (bit) on the altar of Speculation. -+ * -+ * Refer to the AMD Branch Type Confusion whitepaper: -+ * https://XXX -+ * -+ * Setting this unnamed bit supposedly causes prediction information on -+ * non-branch instructions to be ignored. It is to be set unilaterally in -+ * newer microcode. -+ * -+ * This chickenbit is something unrelated on Zen1, and Zen1 vs Zen2 isn't a -+ * simple model number comparison, so use STIBP as a heuristic to separate the -+ * two uarches in Fam17h(AMD)/18h(Hygon). -+ */ -+void amd_init_spectral_chicken(void) -+{ -+ uint64_t val, chickenbit = 1 << 1; -+ -+ if (cpu_has_hypervisor || !boot_cpu_has(X86_FEATURE_AMD_STIBP)) -+ return; -+ -+ if (rdmsr_safe(MSR_AMD64_DE_CFG2, val) == 0 && !(val & chickenbit)) -+ wrmsr_safe(MSR_AMD64_DE_CFG2, val | chickenbit); -+} -+ - void __init detect_zen2_null_seg_behaviour(void) - { - uint64_t base; -@@ -796,6 +821,9 @@ static void init_amd(struct cpuinfo_x86 *c) - - amd_init_ssbd(c); - -+ if (c->x86 == 0x17) -+ amd_init_spectral_chicken(); -+ - /* Probe for NSCB on Zen2 CPUs when not virtualised */ - if (!cpu_has_hypervisor && !cpu_has_nscb && c == &boot_cpu_data && - c->x86 == 0x17) -diff --git a/xen/arch/x86/cpu/cpu.h b/xen/arch/x86/cpu/cpu.h -index b593bd85f04f..145bc5156a86 100644 ---- a/xen/arch/x86/cpu/cpu.h -+++ b/xen/arch/x86/cpu/cpu.h -@@ -22,4 +22,5 @@ void early_init_amd(struct cpuinfo_x86 *c); - void amd_log_freq(const struct cpuinfo_x86 *c); - void amd_init_lfence(struct cpuinfo_x86 *c); - void amd_init_ssbd(const struct cpuinfo_x86 *c); -+void amd_init_spectral_chicken(void); - void detect_zen2_null_seg_behaviour(void); -diff --git a/xen/arch/x86/cpu/hygon.c b/xen/arch/x86/cpu/hygon.c -index cdc94130dd2e..6f8d491297e8 100644 ---- a/xen/arch/x86/cpu/hygon.c -+++ b/xen/arch/x86/cpu/hygon.c -@@ -41,6 +41,12 @@ static void init_hygon(struct cpuinfo_x86 *c) - detect_zen2_null_seg_behaviour(); - - /* -+ * TODO: Check heuristic safety with Hygon first -+ if (c->x86 == 0x18) -+ amd_init_spectral_chicken(); -+ */ -+ -+ /* - * Hygon CPUs before Zen2 don't clear segment bases/limits when - * loading a NULL selector. - */ -diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h -index 72bc32ba04ff..d3735e499e0f 100644 ---- a/xen/include/asm-x86/msr-index.h -+++ b/xen/include/asm-x86/msr-index.h -@@ -361,6 +361,7 @@ - #define MSR_AMD64_DE_CFG 0xc0011029 - #define AMD64_DE_CFG_LFENCE_SERIALISE (_AC(1, ULL) << 1) - #define MSR_AMD64_EX_CFG 0xc001102c -+#define MSR_AMD64_DE_CFG2 0xc00110e3 - - #define MSR_AMD64_DR0_ADDRESS_MASK 0xc0011027 - #define MSR_AMD64_DR1_ADDRESS_MASK 0xc0011019 diff --git a/xsa407-4.16-8.patch b/xsa407-4.16-8.patch deleted file mode 100644 index 8d53169..0000000 --- a/xsa407-4.16-8.patch +++ /dev/null @@ -1,293 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Mitigate Branch Type Confusion when possible - -Branch Type Confusion affects AMD/Hygon CPUs on Zen2 and earlier. To -mitigate, we require SMT safety (STIBP on Zen2, no-SMT on Zen1), and to issue -an IBPB on each entry to Xen, to flush the BTB. - -Due to performance concerns, dom0 (which is trusted in most configurations) is -excluded from protections by default. - -Therefore: - * Use STIBP by default on Zen2 too, which now means we want it on by default - on all hardware supporting STIBP. - * Break the current IBPB logic out into a new function, extending it with - IBPB-at-entry logic. - * Change the existing IBPB-at-ctxt-switch boolean to be tristate, and disable - it by default when IBPB-at-entry is providing sufficient safety. - -If all PV guests on the system are trusted, then it is recommended to boot -with `spec-ctrl=ibpb-entry=no-pv`, as this will provide an additional marginal -perf improvement. - -This is part of XSA-407 / CVE-2022-23825. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc -index 1bbdb55129cc..bd6826d0ae05 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -2234,7 +2234,7 @@ By default SSBD will be mitigated at runtime (i.e `ssbd=runtime`). - - ### spec-ctrl (x86) - > `= List of [ , xen=, {pv,hvm}=, --> {msr-sc,rsb,md-clear}=|{pv,hvm}=, -+> {msr-sc,rsb,md-clear,ibpb-entry}=|{pv,hvm}=, - > bti-thunk=retpoline|lfence|jmp, {ibrs,ibpb,ssbd,psfd, - > eager-fpu,l1d-flush,branch-harden,srb-lock, - > unpriv-mmio}= ]` -@@ -2259,9 +2259,10 @@ in place for guests to use. - - Use of a positive boolean value for either of these options is invalid. - --The `pv=`, `hvm=`, `msr-sc=`, `rsb=` and `md-clear=` options offer fine --grained control over the primitives by Xen. These impact Xen's ability to --protect itself, and/or Xen's ability to virtualise support for guests to use. -+The `pv=`, `hvm=`, `msr-sc=`, `rsb=`, `md-clear=` and `ibpb-entry=` options -+offer fine grained control over the primitives by Xen. These impact Xen's -+ability to protect itself, and/or Xen's ability to virtualise support for -+guests to use. - - * `pv=` and `hvm=` offer control over all suboptions for PV and HVM guests - respectively. -@@ -2280,6 +2281,11 @@ protect itself, and/or Xen's ability to virtualise support for guests to use. - compatibility with development versions of this fix, `mds=` is also accepted - on Xen 4.12 and earlier as an alias. Consult vendor documentation in - preference to here.* -+* `ibpb-entry=` offers control over whether IBPB (Indirect Branch Prediction -+ Barrier) is used on entry to Xen. This is used by default on hardware -+ vulnerable to Branch Type Confusion, but for performance reasons, dom0 is -+ unprotected by default. If it necessary to protect dom0 too, boot with -+ `spec-ctrl=ibpb-entry`. - - If Xen was compiled with INDIRECT_THUNK support, `bti-thunk=` can be used to - select which of the thunks gets patched into the `__x86_indirect_thunk_%reg` -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 0f101c057f3e..1d9796c34d71 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -39,6 +39,10 @@ static bool __initdata opt_rsb_hvm = true; - static int8_t __read_mostly opt_md_clear_pv = -1; - static int8_t __read_mostly opt_md_clear_hvm = -1; - -+static int8_t __read_mostly opt_ibpb_entry_pv = -1; -+static int8_t __read_mostly opt_ibpb_entry_hvm = -1; -+static bool __read_mostly opt_ibpb_entry_dom0; -+ - /* Cmdline controls for Xen's speculative settings. */ - static enum ind_thunk { - THUNK_DEFAULT, /* Decide which thunk to use at boot time. */ -@@ -54,7 +58,7 @@ int8_t __initdata opt_stibp = -1; - bool __read_mostly opt_ssbd; - int8_t __initdata opt_psfd = -1; - --bool __read_mostly opt_ibpb_ctxt_switch = true; -+int8_t __read_mostly opt_ibpb_ctxt_switch = -1; - int8_t __read_mostly opt_eager_fpu = -1; - int8_t __read_mostly opt_l1d_flush = -1; - static bool __initdata opt_branch_harden = true; -@@ -114,6 +118,9 @@ static int __init parse_spec_ctrl(const char *s) - opt_rsb_hvm = false; - opt_md_clear_pv = 0; - opt_md_clear_hvm = 0; -+ opt_ibpb_entry_pv = 0; -+ opt_ibpb_entry_hvm = 0; -+ opt_ibpb_entry_dom0 = false; - - opt_thunk = THUNK_JMP; - opt_ibrs = 0; -@@ -140,12 +147,14 @@ static int __init parse_spec_ctrl(const char *s) - opt_msr_sc_pv = val; - opt_rsb_pv = val; - opt_md_clear_pv = val; -+ opt_ibpb_entry_pv = val; - } - else if ( (val = parse_boolean("hvm", s, ss)) >= 0 ) - { - opt_msr_sc_hvm = val; - opt_rsb_hvm = val; - opt_md_clear_hvm = val; -+ opt_ibpb_entry_hvm = val; - } - else if ( (val = parse_boolean("msr-sc", s, ss)) != -1 ) - { -@@ -210,6 +219,28 @@ static int __init parse_spec_ctrl(const char *s) - break; - } - } -+ else if ( (val = parse_boolean("ibpb-entry", s, ss)) != -1 ) -+ { -+ switch ( val ) -+ { -+ case 0: -+ case 1: -+ opt_ibpb_entry_pv = opt_ibpb_entry_hvm = -+ opt_ibpb_entry_dom0 = val; -+ break; -+ -+ case -2: -+ s += strlen("ibpb-entry="); -+ if ( (val = parse_boolean("pv", s, ss)) >= 0 ) -+ opt_ibpb_entry_pv = val; -+ else if ( (val = parse_boolean("hvm", s, ss)) >= 0 ) -+ opt_ibpb_entry_hvm = val; -+ else -+ default: -+ rc = -EINVAL; -+ break; -+ } -+ } - - /* Xen's speculative sidechannel mitigation settings. */ - else if ( !strncmp(s, "bti-thunk=", 10) ) -@@ -477,27 +508,31 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - * mitigation support for guests. - */ - #ifdef CONFIG_HVM -- printk(" Support for HVM VMs:%s%s%s%s%s\n", -+ printk(" Support for HVM VMs:%s%s%s%s%s%s\n", - (boot_cpu_has(X86_FEATURE_SC_MSR_HVM) || - boot_cpu_has(X86_FEATURE_SC_RSB_HVM) || - boot_cpu_has(X86_FEATURE_MD_CLEAR) || -+ boot_cpu_has(X86_FEATURE_IBPB_ENTRY_HVM) || - opt_eager_fpu) ? "" : " None", - boot_cpu_has(X86_FEATURE_SC_MSR_HVM) ? " MSR_SPEC_CTRL" : "", - boot_cpu_has(X86_FEATURE_SC_RSB_HVM) ? " RSB" : "", - opt_eager_fpu ? " EAGER_FPU" : "", -- boot_cpu_has(X86_FEATURE_MD_CLEAR) ? " MD_CLEAR" : ""); -+ boot_cpu_has(X86_FEATURE_MD_CLEAR) ? " MD_CLEAR" : "", -+ boot_cpu_has(X86_FEATURE_IBPB_ENTRY_HVM) ? " IBPB-entry" : ""); - - #endif - #ifdef CONFIG_PV -- printk(" Support for PV VMs:%s%s%s%s%s\n", -+ printk(" Support for PV VMs:%s%s%s%s%s%s\n", - (boot_cpu_has(X86_FEATURE_SC_MSR_PV) || - boot_cpu_has(X86_FEATURE_SC_RSB_PV) || - boot_cpu_has(X86_FEATURE_MD_CLEAR) || -+ boot_cpu_has(X86_FEATURE_IBPB_ENTRY_PV) || - opt_eager_fpu) ? "" : " None", - boot_cpu_has(X86_FEATURE_SC_MSR_PV) ? " MSR_SPEC_CTRL" : "", - boot_cpu_has(X86_FEATURE_SC_RSB_PV) ? " RSB" : "", - opt_eager_fpu ? " EAGER_FPU" : "", -- boot_cpu_has(X86_FEATURE_MD_CLEAR) ? " MD_CLEAR" : ""); -+ boot_cpu_has(X86_FEATURE_MD_CLEAR) ? " MD_CLEAR" : "", -+ boot_cpu_has(X86_FEATURE_IBPB_ENTRY_PV) ? " IBPB-entry" : ""); - - printk(" XPTI (64-bit PV only): Dom0 %s, DomU %s (with%s PCID)\n", - opt_xpti_hwdom ? "enabled" : "disabled", -@@ -759,6 +794,55 @@ static bool __init should_use_eager_fpu(void) - } - } - -+static void __init ibpb_calculations(void) -+{ -+ /* Check we have hardware IBPB support before using it... */ -+ if ( !boot_cpu_has(X86_FEATURE_IBRSB) && !boot_cpu_has(X86_FEATURE_IBPB) ) -+ { -+ opt_ibpb_entry_hvm = opt_ibpb_entry_pv = opt_ibpb_ctxt_switch = 0; -+ opt_ibpb_entry_dom0 = false; -+ return; -+ } -+ -+ /* -+ * IBPB-on-entry mitigations for Branch Type Confusion. -+ * -+ * IBPB && !BTC_NO selects all AMD/Hygon hardware, not known to be safe, -+ * that we can provide some form of mitigation on. -+ */ -+ if ( opt_ibpb_entry_pv == -1 ) -+ opt_ibpb_entry_pv = (IS_ENABLED(CONFIG_PV) && -+ boot_cpu_has(X86_FEATURE_IBPB) && -+ !boot_cpu_has(X86_FEATURE_BTC_NO)); -+ if ( opt_ibpb_entry_hvm == -1 ) -+ opt_ibpb_entry_hvm = (IS_ENABLED(CONFIG_HVM) && -+ boot_cpu_has(X86_FEATURE_IBPB) && -+ !boot_cpu_has(X86_FEATURE_BTC_NO)); -+ -+ if ( opt_ibpb_entry_pv ) -+ { -+ setup_force_cpu_cap(X86_FEATURE_IBPB_ENTRY_PV); -+ -+ /* -+ * We only need to flush in IST context if we're protecting against PV -+ * guests. HVM IBPB-on-entry protections are both atomic with -+ * NMI/#MC, so can't interrupt Xen ahead of having already flushed the -+ * BTB. -+ */ -+ default_spec_ctrl_flags |= SCF_ist_ibpb; -+ } -+ if ( opt_ibpb_entry_hvm ) -+ setup_force_cpu_cap(X86_FEATURE_IBPB_ENTRY_HVM); -+ -+ /* -+ * If we're using IBPB-on-entry to protect against PV and HVM guests -+ * (ignoring dom0 if trusted), then there's no need to also issue IBPB on -+ * context switch too. -+ */ -+ if ( opt_ibpb_ctxt_switch == -1 ) -+ opt_ibpb_ctxt_switch = !(opt_ibpb_entry_hvm && opt_ibpb_entry_pv); -+} -+ - /* Calculate whether this CPU is vulnerable to L1TF. */ - static __init void l1tf_calculations(uint64_t caps) - { -@@ -1014,8 +1098,12 @@ void spec_ctrl_init_domain(struct domain *d) - bool verw = ((pv ? opt_md_clear_pv : opt_md_clear_hvm) || - (opt_fb_clear_mmio && is_iommu_enabled(d))); - -+ bool ibpb = ((pv ? opt_ibpb_entry_pv : opt_ibpb_entry_hvm) && -+ (d->domain_id != 0 || opt_ibpb_entry_dom0)); -+ - d->arch.spec_ctrl_flags = - (verw ? SCF_verw : 0) | -+ (ibpb ? SCF_entry_ibpb : 0) | - 0; - } - -@@ -1162,12 +1250,15 @@ void __init init_speculation_mitigations(void) - } - - /* -- * Use STIBP by default if the hardware hint is set. Otherwise, leave it -- * off as it a severe performance pentalty on pre-eIBRS Intel hardware -- * where it was retrofitted in microcode. -+ * Use STIBP by default on all AMD systems. Zen3 and later enumerate -+ * STIBP_ALWAYS, but STIBP is needed on Zen2 as part of the mitigations -+ * for Branch Type Confusion. -+ * -+ * Leave STIBP off by default on Intel. Pre-eIBRS systems suffer a -+ * substantial perf hit when it was implemented in microcode. - */ - if ( opt_stibp == -1 ) -- opt_stibp = !!boot_cpu_has(X86_FEATURE_STIBP_ALWAYS); -+ opt_stibp = !!boot_cpu_has(X86_FEATURE_AMD_STIBP); - - if ( opt_stibp && (boot_cpu_has(X86_FEATURE_STIBP) || - boot_cpu_has(X86_FEATURE_AMD_STIBP)) ) -@@ -1239,9 +1330,7 @@ void __init init_speculation_mitigations(void) - if ( opt_rsb_hvm ) - setup_force_cpu_cap(X86_FEATURE_SC_RSB_HVM); - -- /* Check we have hardware IBPB support before using it... */ -- if ( !boot_cpu_has(X86_FEATURE_IBRSB) && !boot_cpu_has(X86_FEATURE_IBPB) ) -- opt_ibpb_ctxt_switch = false; -+ ibpb_calculations(); - - /* Check whether Eager FPU should be enabled by default. */ - if ( opt_eager_fpu == -1 ) -diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h -index 3fc599a817c4..9403b81dc7af 100644 ---- a/xen/include/asm-x86/spec_ctrl.h -+++ b/xen/include/asm-x86/spec_ctrl.h -@@ -65,7 +65,7 @@ - void init_speculation_mitigations(void); - void spec_ctrl_init_domain(struct domain *d); - --extern bool opt_ibpb_ctxt_switch; -+extern int8_t opt_ibpb_ctxt_switch; - extern bool opt_ssbd; - extern int8_t opt_eager_fpu; - extern int8_t opt_l1d_flush; diff --git a/xsa408.patch b/xsa408.patch deleted file mode 100644 index c58193f..0000000 --- a/xsa408.patch +++ /dev/null @@ -1,36 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: correct TLB flush condition in _get_page_type() - -When this logic was moved, it was moved across the point where nx is -updated to hold the new type for the page. IOW originally it was -equivalent to using x (and perhaps x would better have been used), but -now it isn't anymore. Switch to using x, which then brings things in -line again with the slightly earlier comment there (now) talking about -transitions _from_ writable. - -I have to confess though that I cannot make a direct connection between -the reported observed behavior of guests leaving several pages around -with pending general references and the change here. Repeated testing, -nevertheless, confirms the reported issue is no longer there. - -This is CVE-2022-33745 / XSA-408. - -Reported-by: Charles Arnold -Fixes: 8cc5036bc385 ("x86/pv: Fix ABAC cmpxchg() race in _get_page_type()") -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper ---- -I'd be happy to update the description to actually connect things, as -long as someone can give some plausible explanation. - ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -3038,7 +3038,7 @@ static int _get_page_type(struct page_in - if ( unlikely(!cpumask_empty(mask)) && - /* Shadow mode: track only writable pages. */ - (!shadow_mode_enabled(d) || -- ((nx & PGT_type_mask) == PGT_writable_page)) ) -+ ((x & PGT_type_mask) == PGT_writable_page)) ) - { - perfc_incr(need_flush_tlb_flush); - /* From ebc4dec9e2226e2dfdf4871fe32728fadf7cd450 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Fri, 14 Oct 2022 20:50:36 +0100 Subject: [PATCH 047/137] 3 security fixes Arm: unbounded memory consumption for 2nd-level page tables [XSA-409, CVE-2022-33747] P2M pool freeing may take excessively long [XSA-410, CVE-2022-33746] lock order inversion in transitive grant copy handling [XSA-411, CVE-2022-33748] --- xen.spec | 39 ++- ...-arch-specific-default-paging-memory.patch | 147 +++++++++ ...struct-the-P2M-pages-pool-for-guests.patch | 187 ++++++++++++ ...plement-XEN_DOMCTL_shadow_op-for-Arm.patch | 106 +++++++ ...-and-free-P2M-pages-from-the-P2M-poo.patch | 287 ++++++++++++++++++ xsa410-4.16-01.patch | 59 ++++ xsa410-4.16-02.patch | 165 ++++++++++ xsa410-4.16-03.patch | 113 +++++++ xsa410-4.16-04.patch | 62 ++++ xsa410-4.16-05.patch | 60 ++++ xsa410-4.16-06.patch | 255 ++++++++++++++++ xsa410-4.16-07.patch | 82 +++++ xsa410-4.16-08.patch | 96 ++++++ xsa410-4.16-09.patch | 159 ++++++++++ xsa410-4.16-10.patch | 171 +++++++++++ xsa411.patch | 55 ++++ 16 files changed, 2042 insertions(+), 1 deletion(-) create mode 100644 xsa409-4.13-0001-libxl-docs-Use-arch-specific-default-paging-memory.patch create mode 100644 xsa409-4.13-0002-xen-arm-Construct-the-P2M-pages-pool-for-guests.patch create mode 100644 xsa409-4.13-0003-xen-arm-libxl-Implement-XEN_DOMCTL_shadow_op-for-Arm.patch create mode 100644 xsa409-4.13-0004-xen-arm-Allocate-and-free-P2M-pages-from-the-P2M-poo.patch create mode 100644 xsa410-4.16-01.patch create mode 100644 xsa410-4.16-02.patch create mode 100644 xsa410-4.16-03.patch create mode 100644 xsa410-4.16-04.patch create mode 100644 xsa410-4.16-05.patch create mode 100644 xsa410-4.16-06.patch create mode 100644 xsa410-4.16-07.patch create mode 100644 xsa410-4.16-08.patch create mode 100644 xsa410-4.16-09.patch create mode 100644 xsa410-4.16-10.patch create mode 100644 xsa411.patch diff --git a/xen.spec b/xen.spec index db5b96d..15400e1 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.16.2 -Release: 1%{?dist} +Release: 2%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -111,6 +111,21 @@ Patch43: xen.gcc11.fixes.patch Patch44: xsa376.patch Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch +Patch47: xsa410-4.16-01.patch +Patch48: xsa410-4.16-02.patch +Patch49: xsa410-4.16-03.patch +Patch50: xsa410-4.16-04.patch +Patch51: xsa410-4.16-05.patch +Patch52: xsa410-4.16-06.patch +Patch53: xsa410-4.16-07.patch +Patch54: xsa410-4.16-08.patch +Patch55: xsa410-4.16-09.patch +Patch56: xsa410-4.16-10.patch +Patch57: xsa409-4.13-0001-libxl-docs-Use-arch-specific-default-paging-memory.patch +Patch58: xsa409-4.13-0002-xen-arm-Construct-the-P2M-pages-pool-for-guests.patch +Patch59: xsa409-4.13-0003-xen-arm-libxl-Implement-XEN_DOMCTL_shadow_op-for-Arm.patch +Patch60: xsa409-4.13-0004-xen-arm-Allocate-and-free-P2M-pages-from-the-P2M-poo.patch +Patch61: xsa411.patch %if %build_qemutrad @@ -323,6 +338,21 @@ manage Xen virtual machines. %patch44 -p1 %patch45 -p1 %patch46 -p1 +%patch47 -p1 +%patch48 -p1 +%patch49 -p1 +%patch50 -p1 +%patch51 -p1 +%patch52 -p1 +%patch53 -p1 +%patch54 -p1 +%patch55 -p1 +%patch56 -p1 +%patch57 -p1 +%patch58 -p1 +%patch59 -p1 +%patch60 -p1 +%patch61 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -938,6 +968,13 @@ fi %endif %changelog +* Fri Oct 14 2022 Michael Young - 4.16.2-2 +- Arm: unbounded memory consumption for 2nd-level page tables [XSA-409, + CVE-2022-33747] +- P2M pool freeing may take excessively long [XSA-410, CVE-2022-33746] +- lock order inversion in transitive grant copy handling [XSA-411, + CVE-2022-33748] + * Sat Sep 17 2022 Michael Young - 4.16.2-1 - update to xen-4.16.2 remove or adjust patches now included or superceded upstream diff --git a/xsa409-4.13-0001-libxl-docs-Use-arch-specific-default-paging-memory.patch b/xsa409-4.13-0001-libxl-docs-Use-arch-specific-default-paging-memory.patch new file mode 100644 index 0000000..9aca0a7 --- /dev/null +++ b/xsa409-4.13-0001-libxl-docs-Use-arch-specific-default-paging-memory.patch @@ -0,0 +1,147 @@ +From bd4a7db4001364fd03a80a2e73b81c46aaa44e9c Mon Sep 17 00:00:00 2001 +From: Henry Wang +Date: Mon, 22 Aug 2022 01:35:09 +0000 +Subject: [PATCH 1/4] libxl, docs: Use arch-specific default paging memory + +The default paging memory (descibed in `shadow_memory` entry in xl +config) in libxl is used to determine the memory pool size for xl +guests. Currently this size is only used for x86, and contains a part +of RAM to shadow the resident processes. Since on Arm there is no +shadow mode guests, so the part of RAM to shadow the resident processes +is not necessary. Therefore, this commit splits the function +`libxl_get_required_shadow_memory()` to arch specific helpers and +renamed the helper to `libxl__arch_get_required_paging_memory()`. + +On x86, this helper calls the original value from +`libxl_get_required_shadow_memory()` so no functional change intended. + +On Arm, this helper returns 1MB per vcpu plus 4KB per MiB of RAM +for the P2M map and additional 512KB. + +Also update the xl.cfg documentation to add Arm documentation +according to code changes and correct the comment style following Xen +coding style. + +This is part of CVE-2022-33747 / XSA-409. + +Suggested-by: Julien Grall +Signed-off-by: Henry Wang +Reviewed-by: Anthony PERARD +--- + docs/man/xl.cfg.5.pod.in | 5 +++++ + tools/libs/light/libxl_arch.h | 4 ++++ + tools/libs/light/libxl_arm.c | 14 ++++++++++++++ + tools/libs/light/libxl_utils.c | 9 ++------- + tools/libs/light/libxl_x86.c | 13 +++++++++++++ + 5 files changed, 38 insertions(+), 7 deletions(-) + +diff --git a/docs/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in +index b98d1613987e..eda1e77ebd06 100644 +--- a/docs/man/xl.cfg.5.pod.in ++++ b/docs/man/xl.cfg.5.pod.in +@@ -1768,6 +1768,11 @@ are not using hardware assisted paging (i.e. you are using shadow + mode) and your guest workload consists of a very large number of + similar processes then increasing this value may improve performance. + ++On Arm, this field is used to determine the size of the guest P2M pages ++pool, and the default value is 1MB per vCPU plus 4KB per MB of RAM for ++the P2M map and additional 512KB for extended regions. Users should ++adjust this value if bigger P2M pool size is needed. ++ + =back + + =head3 Processor and Platform Features +diff --git a/tools/libs/light/libxl_arch.h b/tools/libs/light/libxl_arch.h +index 1522ecb97f72..5a060c2c3033 100644 +--- a/tools/libs/light/libxl_arch.h ++++ b/tools/libs/light/libxl_arch.h +@@ -90,6 +90,10 @@ void libxl__arch_update_domain_config(libxl__gc *gc, + libxl_domain_config *dst, + const libxl_domain_config *src); + ++_hidden ++unsigned long libxl__arch_get_required_paging_memory(unsigned long maxmem_kb, ++ unsigned int smp_cpus); ++ + #if defined(__i386__) || defined(__x86_64__) + + #define LAPIC_BASE_ADDRESS 0xfee00000 +diff --git a/tools/libs/light/libxl_arm.c b/tools/libs/light/libxl_arm.c +index eef1de093914..73a95e83af24 100644 +--- a/tools/libs/light/libxl_arm.c ++++ b/tools/libs/light/libxl_arm.c +@@ -154,6 +154,20 @@ out: + return rc; + } + ++unsigned long libxl__arch_get_required_paging_memory(unsigned long maxmem_kb, ++ unsigned int smp_cpus) ++{ ++ /* ++ * 256 pages (1MB) per vcpu, ++ * plus 1 page per MiB of RAM for the P2M map, ++ * plus 1 page per MiB of extended region. This default value is 128 MiB ++ * which should be enough for domains that are not running backend. ++ * This is higher than the minimum that Xen would allocate if no value ++ * were given (but the Xen minimum is for safety, not performance). ++ */ ++ return 4 * (256 * smp_cpus + maxmem_kb / 1024 + 128); ++} ++ + static struct arch_info { + const char *guest_type; + const char *timer_compat; +diff --git a/tools/libs/light/libxl_utils.c b/tools/libs/light/libxl_utils.c +index 4699c4a0a36f..e276c0ee9cc3 100644 +--- a/tools/libs/light/libxl_utils.c ++++ b/tools/libs/light/libxl_utils.c +@@ -18,6 +18,7 @@ + #include + + #include "libxl_internal.h" ++#include "libxl_arch.h" + #include "_paths.h" + + #ifndef LIBXL_HAVE_NONCONST_LIBXL_BASENAME_RETURN_VALUE +@@ -39,13 +40,7 @@ char *libxl_basename(const char *name) + + unsigned long libxl_get_required_shadow_memory(unsigned long maxmem_kb, unsigned int smp_cpus) + { +- /* 256 pages (1MB) per vcpu, +- plus 1 page per MiB of RAM for the P2M map, +- plus 1 page per MiB of RAM to shadow the resident processes. +- This is higher than the minimum that Xen would allocate if no value +- were given (but the Xen minimum is for safety, not performance). +- */ +- return 4 * (256 * smp_cpus + 2 * (maxmem_kb / 1024)); ++ return libxl__arch_get_required_paging_memory(maxmem_kb, smp_cpus); + } + + char *libxl_domid_to_name(libxl_ctx *ctx, uint32_t domid) +diff --git a/tools/libs/light/libxl_x86.c b/tools/libs/light/libxl_x86.c +index 1feadebb1852..51362893cf98 100644 +--- a/tools/libs/light/libxl_x86.c ++++ b/tools/libs/light/libxl_x86.c +@@ -882,6 +882,19 @@ void libxl__arch_update_domain_config(libxl__gc *gc, + libxl_defbool_val(src->b_info.arch_x86.msr_relaxed)); + } + ++unsigned long libxl__arch_get_required_paging_memory(unsigned long maxmem_kb, ++ unsigned int smp_cpus) ++{ ++ /* ++ * 256 pages (1MB) per vcpu, ++ * plus 1 page per MiB of RAM for the P2M map, ++ * plus 1 page per MiB of RAM to shadow the resident processes. ++ * This is higher than the minimum that Xen would allocate if no value ++ * were given (but the Xen minimum is for safety, not performance). ++ */ ++ return 4 * (256 * smp_cpus + 2 * (maxmem_kb / 1024)); ++} ++ + /* + * Local variables: + * mode: C +-- +2.37.1 + diff --git a/xsa409-4.13-0002-xen-arm-Construct-the-P2M-pages-pool-for-guests.patch b/xsa409-4.13-0002-xen-arm-Construct-the-P2M-pages-pool-for-guests.patch new file mode 100644 index 0000000..8c03657 --- /dev/null +++ b/xsa409-4.13-0002-xen-arm-Construct-the-P2M-pages-pool-for-guests.patch @@ -0,0 +1,187 @@ +From 419a4bbc20cf7c5d7d9dedae59fb8049922e6a2c Mon Sep 17 00:00:00 2001 +From: Henry Wang +Date: Mon, 6 Jun 2022 06:17:28 +0000 +Subject: [PATCH 2/4] xen/arm: Construct the P2M pages pool for guests + +This commit constructs the p2m pages pool for guests from the +data structure and helper perspective. + +This is implemented by: + +- Adding a `struct paging_domain` which contains a freelist, a +counter variable and a spinlock to `struct arch_domain` to +indicate the free p2m pages and the number of p2m total pages in +the p2m pages pool. + +- Adding a helper `p2m_get_allocation` to get the p2m pool size. + +- Adding a helper `p2m_set_allocation` to set the p2m pages pool +size. This helper should be called before allocating memory for +a guest. + +- Adding a helper `p2m_teardown_allocation` to free the p2m pages +pool. This helper should be called during the xl domain destory. + +This is part of CVE-2022-33747 / XSA-409. + +Signed-off-by: Henry Wang +Reviewed-by: Stefano Stabellini +--- + xen/arch/arm/p2m.c | 88 ++++++++++++++++++++++++++++++++++++ + xen/include/asm-arm/domain.h | 10 ++++ + xen/include/asm-arm/p2m.h | 4 ++ + 3 files changed, 102 insertions(+) + +diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c +index 3bcd1e897e88..79f3d37f5230 100644 +--- a/xen/arch/arm/p2m.c ++++ b/xen/arch/arm/p2m.c +@@ -50,6 +50,92 @@ static uint64_t generate_vttbr(uint16_t vmid, mfn_t root_mfn) + return (mfn_to_maddr(root_mfn) | ((uint64_t)vmid << 48)); + } + ++/* Return the size of the pool, rounded up to the nearest MB */ ++unsigned int p2m_get_allocation(struct domain *d) ++{ ++ unsigned long nr_pages = ACCESS_ONCE(d->arch.paging.p2m_total_pages); ++ ++ return ROUNDUP(nr_pages, 1 << (20 - PAGE_SHIFT)) >> (20 - PAGE_SHIFT); ++} ++ ++/* ++ * Set the pool of pages to the required number of pages. ++ * Returns 0 for success, non-zero for failure. ++ * Call with d->arch.paging.lock held. ++ */ ++int p2m_set_allocation(struct domain *d, unsigned long pages, bool *preempted) ++{ ++ struct page_info *pg; ++ ++ ASSERT(spin_is_locked(&d->arch.paging.lock)); ++ ++ for ( ; ; ) ++ { ++ if ( d->arch.paging.p2m_total_pages < pages ) ++ { ++ /* Need to allocate more memory from domheap */ ++ pg = alloc_domheap_page(NULL, 0); ++ if ( pg == NULL ) ++ { ++ printk(XENLOG_ERR "Failed to allocate P2M pages.\n"); ++ return -ENOMEM; ++ } ++ ACCESS_ONCE(d->arch.paging.p2m_total_pages) = ++ d->arch.paging.p2m_total_pages + 1; ++ page_list_add_tail(pg, &d->arch.paging.p2m_freelist); ++ } ++ else if ( d->arch.paging.p2m_total_pages > pages ) ++ { ++ /* Need to return memory to domheap */ ++ pg = page_list_remove_head(&d->arch.paging.p2m_freelist); ++ if( pg ) ++ { ++ ACCESS_ONCE(d->arch.paging.p2m_total_pages) = ++ d->arch.paging.p2m_total_pages - 1; ++ free_domheap_page(pg); ++ } ++ else ++ { ++ printk(XENLOG_ERR ++ "Failed to free P2M pages, P2M freelist is empty.\n"); ++ return -ENOMEM; ++ } ++ } ++ else ++ break; ++ ++ /* Check to see if we need to yield and try again */ ++ if ( preempted && general_preempt_check() ) ++ { ++ *preempted = true; ++ return -ERESTART; ++ } ++ } ++ ++ return 0; ++} ++ ++int p2m_teardown_allocation(struct domain *d) ++{ ++ int ret = 0; ++ bool preempted = false; ++ ++ spin_lock(&d->arch.paging.lock); ++ if ( d->arch.paging.p2m_total_pages != 0 ) ++ { ++ ret = p2m_set_allocation(d, 0, &preempted); ++ if ( preempted ) ++ { ++ spin_unlock(&d->arch.paging.lock); ++ return -ERESTART; ++ } ++ ASSERT(d->arch.paging.p2m_total_pages == 0); ++ } ++ spin_unlock(&d->arch.paging.lock); ++ ++ return ret; ++} ++ + /* Unlock the flush and do a P2M TLB flush if necessary */ + void p2m_write_unlock(struct p2m_domain *p2m) + { +@@ -1599,7 +1685,9 @@ int p2m_init(struct domain *d) + unsigned int cpu; + + rwlock_init(&p2m->lock); ++ spin_lock_init(&d->arch.paging.lock); + INIT_PAGE_LIST_HEAD(&p2m->pages); ++ INIT_PAGE_LIST_HEAD(&d->arch.paging.p2m_freelist); + + p2m->vmid = INVALID_VMID; + +diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h +index 9b3647587a04..c90daa65afa7 100644 +--- a/xen/include/asm-arm/domain.h ++++ b/xen/include/asm-arm/domain.h +@@ -40,6 +40,14 @@ struct vtimer { + uint64_t cval; + }; + ++struct paging_domain { ++ spinlock_t lock; ++ /* Free P2M pages from the pre-allocated P2M pool */ ++ struct page_list_head p2m_freelist; ++ /* Number of pages from the pre-allocated P2M pool */ ++ unsigned long p2m_total_pages; ++}; ++ + struct arch_domain + { + #ifdef CONFIG_ARM_64 +@@ -51,6 +59,8 @@ struct arch_domain + + struct hvm_domain hvm; + ++ struct paging_domain paging; ++ + struct vmmio vmmio; + + /* Continuable domain_relinquish_resources(). */ +diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h +index b3ba83283e11..c9598740bd02 100644 +--- a/xen/include/asm-arm/p2m.h ++++ b/xen/include/asm-arm/p2m.h +@@ -218,6 +218,10 @@ void p2m_restore_state(struct vcpu *n); + /* Print debugging/statistial info about a domain's p2m */ + void p2m_dump_info(struct domain *d); + ++unsigned int p2m_get_allocation(struct domain *d); ++int p2m_set_allocation(struct domain *d, unsigned long pages, bool *preempted); ++int p2m_teardown_allocation(struct domain *d); ++ + static inline void p2m_write_lock(struct p2m_domain *p2m) + { + write_lock(&p2m->lock); +-- +2.37.1 + diff --git a/xsa409-4.13-0003-xen-arm-libxl-Implement-XEN_DOMCTL_shadow_op-for-Arm.patch b/xsa409-4.13-0003-xen-arm-libxl-Implement-XEN_DOMCTL_shadow_op-for-Arm.patch new file mode 100644 index 0000000..bb638e1 --- /dev/null +++ b/xsa409-4.13-0003-xen-arm-libxl-Implement-XEN_DOMCTL_shadow_op-for-Arm.patch @@ -0,0 +1,106 @@ +From 332a9979d4dd0b047aa16db201c50fcedbd56743 Mon Sep 17 00:00:00 2001 +From: Henry Wang +Date: Mon, 6 Jun 2022 06:17:29 +0000 +Subject: [PATCH 3/4] xen/arm, libxl: Implement XEN_DOMCTL_shadow_op for Arm + +This commit implements the `XEN_DOMCTL_shadow_op` support in Xen +for Arm. The p2m pages pool size for xl guests is supposed to be +determined by `XEN_DOMCTL_shadow_op`. Hence, this commit: + +- Introduces a function `p2m_domctl` and implements the subops +`XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION` and +`XEN_DOMCTL_SHADOW_OP_GET_ALLOCATION` of `XEN_DOMCTL_shadow_op`. + +- Adds the `XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION` support in libxl. + +Therefore enabling the setting of shadow memory pool size +when creating a guest from xl and getting shadow memory pool size +from Xen. + +Note that the `XEN_DOMCTL_shadow_op` added in this commit is only +a dummy op, and the functionality of setting/getting p2m memory pool +size for xl guests will be added in following commits. + +This is part of CVE-2022-33747 / XSA-409. + +Signed-off-by: Henry Wang +Reviewed-by: Stefano Stabellini +--- + tools/libs/light/libxl_arm.c | 12 ++++++++++++ + xen/arch/arm/domctl.c | 32 ++++++++++++++++++++++++++++++++ + 2 files changed, 44 insertions(+) + +diff --git a/tools/libs/light/libxl_arm.c b/tools/libs/light/libxl_arm.c +index 73a95e83af24..22a0c561bbc6 100644 +--- a/tools/libs/light/libxl_arm.c ++++ b/tools/libs/light/libxl_arm.c +@@ -131,6 +131,18 @@ int libxl__arch_domain_create(libxl__gc *gc, + libxl__domain_build_state *state, + uint32_t domid) + { ++ libxl_ctx *ctx = libxl__gc_owner(gc); ++ unsigned int shadow_mb = DIV_ROUNDUP(d_config->b_info.shadow_memkb, 1024); ++ ++ int r = xc_shadow_control(ctx->xch, domid, ++ XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION, ++ &shadow_mb, 0); ++ if (r) { ++ LOGED(ERROR, domid, ++ "Failed to set %u MiB shadow allocation", shadow_mb); ++ return ERROR_FAIL; ++ } ++ + return 0; + } + +diff --git a/xen/arch/arm/domctl.c b/xen/arch/arm/domctl.c +index 1baf25c3d98b..9bf72e693019 100644 +--- a/xen/arch/arm/domctl.c ++++ b/xen/arch/arm/domctl.c +@@ -47,11 +47,43 @@ static int handle_vuart_init(struct domain *d, + return rc; + } + ++static long p2m_domctl(struct domain *d, struct xen_domctl_shadow_op *sc, ++ XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) ++{ ++ if ( unlikely(d == current->domain) ) ++ { ++ printk(XENLOG_ERR "Tried to do a p2m domctl op on itself.\n"); ++ return -EINVAL; ++ } ++ ++ if ( unlikely(d->is_dying) ) ++ { ++ printk(XENLOG_ERR "Tried to do a p2m domctl op on dying domain %u\n", ++ d->domain_id); ++ return -EINVAL; ++ } ++ ++ switch ( sc->op ) ++ { ++ case XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION: ++ return 0; ++ case XEN_DOMCTL_SHADOW_OP_GET_ALLOCATION: ++ return 0; ++ default: ++ { ++ printk(XENLOG_ERR "Bad p2m domctl op %u\n", sc->op); ++ return -EINVAL; ++ } ++ } ++} ++ + long arch_do_domctl(struct xen_domctl *domctl, struct domain *d, + XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) + { + switch ( domctl->cmd ) + { ++ case XEN_DOMCTL_shadow_op: ++ return p2m_domctl(d, &domctl->u.shadow_op, u_domctl); + case XEN_DOMCTL_cacheflush: + { + gfn_t s = _gfn(domctl->u.cacheflush.start_pfn); +-- +2.37.1 + diff --git a/xsa409-4.13-0004-xen-arm-Allocate-and-free-P2M-pages-from-the-P2M-poo.patch b/xsa409-4.13-0004-xen-arm-Allocate-and-free-P2M-pages-from-the-P2M-poo.patch new file mode 100644 index 0000000..5366c8f --- /dev/null +++ b/xsa409-4.13-0004-xen-arm-Allocate-and-free-P2M-pages-from-the-P2M-poo.patch @@ -0,0 +1,287 @@ +From 39664d9ee041f96e9c7ee131ed8ef72a4d19c9f8 Mon Sep 17 00:00:00 2001 +From: Henry Wang +Date: Mon, 6 Jun 2022 06:17:30 +0000 +Subject: [PATCH 4/4] xen/arm: Allocate and free P2M pages from the P2M pool + +This commit sets/tearsdown of p2m pages pool for non-privileged Arm +guests by calling `p2m_set_allocation` and `p2m_teardown_allocation`. + +- For dom0, P2M pages should come from heap directly instead of p2m +pool, so that the kernel may take advantage of the extended regions. + +- For xl guests, the setting of the p2m pool is called in +`XEN_DOMCTL_shadow_op` and the p2m pool is destroyed in +`domain_relinquish_resources`. Note that domctl->u.shadow_op.mb is +updated with the new size when setting the p2m pool. + +- For dom0less domUs, the setting of the p2m pool is called before +allocating memory during domain creation. Users can specify the p2m +pool size by `xen,domain-p2m-mem-mb` dts property. + +To actually allocate/free pages from the p2m pool, this commit adds +two helper functions namely `p2m_alloc_page` and `p2m_free_page` to +`struct p2m_domain`. By replacing the `alloc_domheap_page` and +`free_domheap_page` with these two helper functions, p2m pages can +be added/removed from the list of p2m pool rather than from the heap. + +Since page from `p2m_alloc_page` is cleaned, take the opportunity +to remove the redundant `clean_page` in `p2m_create_table`. + +This is part of CVE-2022-33747 / XSA-409. + +Signed-off-by: Henry Wang +Reviewed-by: Stefano Stabellini +--- + docs/misc/arm/device-tree/booting.txt | 8 ++++ + xen/arch/arm/domain.c | 6 +++ + xen/arch/arm/domain_build.c | 29 ++++++++++++++ + xen/arch/arm/domctl.c | 23 ++++++++++- + xen/arch/arm/p2m.c | 57 +++++++++++++++++++++++++-- + 5 files changed, 118 insertions(+), 5 deletions(-) + +diff --git a/docs/misc/arm/device-tree/booting.txt b/docs/misc/arm/device-tree/booting.txt +index 71895663a4de..d92ccc56ffe0 100644 +--- a/docs/misc/arm/device-tree/booting.txt ++++ b/docs/misc/arm/device-tree/booting.txt +@@ -182,6 +182,14 @@ with the following properties: + Both #address-cells and #size-cells need to be specified because + both sub-nodes (described shortly) have reg properties. + ++- xen,domain-p2m-mem-mb ++ ++ Optional. A 32-bit integer specifying the amount of megabytes of RAM ++ used for the domain P2M pool. This is in-sync with the shadow_memory ++ option in xl.cfg. Leaving this field empty in device tree will lead to ++ the default size of domain P2M pool, i.e. 1MB per guest vCPU plus 4KB ++ per MB of guest RAM plus 512KB for guest extended regions. ++ + Under the "xen,domain" compatible node, one or more sub-nodes are present + for the DomU kernel and ramdisk. + +diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c +index 2694c39127c5..a818f33a1afa 100644 +--- a/xen/arch/arm/domain.c ++++ b/xen/arch/arm/domain.c +@@ -997,6 +997,7 @@ enum { + PROG_page, + PROG_mapping, + PROG_p2m, ++ PROG_p2m_pool, + PROG_done, + }; + +@@ -1062,6 +1063,11 @@ int domain_relinquish_resources(struct domain *d) + if ( ret ) + return ret; + ++ PROGRESS(p2m_pool): ++ ret = p2m_teardown_allocation(d); ++ if( ret ) ++ return ret; ++ + PROGRESS(done): + break; + +diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c +index d02bacbcd1ed..8aec3755ca5d 100644 +--- a/xen/arch/arm/domain_build.c ++++ b/xen/arch/arm/domain_build.c +@@ -2833,6 +2833,21 @@ static void __init find_gnttab_region(struct domain *d, + kinfo->gnttab_start, kinfo->gnttab_start + kinfo->gnttab_size); + } + ++static unsigned long __init domain_p2m_pages(unsigned long maxmem_kb, ++ unsigned int smp_cpus) ++{ ++ /* ++ * Keep in sync with libxl__get_required_paging_memory(). ++ * 256 pages (1MB) per vcpu, plus 1 page per MiB of RAM for the P2M map, ++ * plus 128 pages to cover extended regions. ++ */ ++ unsigned long memkb = 4 * (256 * smp_cpus + (maxmem_kb / 1024) + 128); ++ ++ BUILD_BUG_ON(PAGE_SIZE != SZ_4K); ++ ++ return DIV_ROUND_UP(memkb, 1024) << (20 - PAGE_SHIFT); ++} ++ + static int __init construct_domain(struct domain *d, struct kernel_info *kinfo) + { + unsigned int i; +@@ -2924,6 +2939,8 @@ static int __init construct_domU(struct domain *d, + struct kernel_info kinfo = {}; + int rc; + u64 mem; ++ u32 p2m_mem_mb; ++ unsigned long p2m_pages; + + rc = dt_property_read_u64(node, "memory", &mem); + if ( !rc ) +@@ -2933,6 +2950,18 @@ static int __init construct_domU(struct domain *d, + } + kinfo.unassigned_mem = (paddr_t)mem * SZ_1K; + ++ rc = dt_property_read_u32(node, "xen,domain-p2m-mem-mb", &p2m_mem_mb); ++ /* If xen,domain-p2m-mem-mb is not specified, use the default value. */ ++ p2m_pages = rc ? ++ p2m_mem_mb << (20 - PAGE_SHIFT) : ++ domain_p2m_pages(mem, d->max_vcpus); ++ ++ spin_lock(&d->arch.paging.lock); ++ rc = p2m_set_allocation(d, p2m_pages, NULL); ++ spin_unlock(&d->arch.paging.lock); ++ if ( rc != 0 ) ++ return rc; ++ + printk("*** LOADING DOMU cpus=%u memory=%"PRIx64"KB ***\n", d->max_vcpus, mem); + + kinfo.vpl011 = dt_property_read_bool(node, "vpl011"); +diff --git a/xen/arch/arm/domctl.c b/xen/arch/arm/domctl.c +index 9bf72e693019..c8fdeb124084 100644 +--- a/xen/arch/arm/domctl.c ++++ b/xen/arch/arm/domctl.c +@@ -50,6 +50,9 @@ static int handle_vuart_init(struct domain *d, + static long p2m_domctl(struct domain *d, struct xen_domctl_shadow_op *sc, + XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) + { ++ long rc; ++ bool preempted = false; ++ + if ( unlikely(d == current->domain) ) + { + printk(XENLOG_ERR "Tried to do a p2m domctl op on itself.\n"); +@@ -66,9 +69,27 @@ static long p2m_domctl(struct domain *d, struct xen_domctl_shadow_op *sc, + switch ( sc->op ) + { + case XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION: +- return 0; ++ { ++ /* Allow and handle preemption */ ++ spin_lock(&d->arch.paging.lock); ++ rc = p2m_set_allocation(d, sc->mb << (20 - PAGE_SHIFT), &preempted); ++ spin_unlock(&d->arch.paging.lock); ++ ++ if ( preempted ) ++ /* Not finished. Set up to re-run the call. */ ++ rc = hypercall_create_continuation(__HYPERVISOR_domctl, "h", ++ u_domctl); ++ else ++ /* Finished. Return the new allocation. */ ++ sc->mb = p2m_get_allocation(d); ++ ++ return rc; ++ } + case XEN_DOMCTL_SHADOW_OP_GET_ALLOCATION: ++ { ++ sc->mb = p2m_get_allocation(d); + return 0; ++ } + default: + { + printk(XENLOG_ERR "Bad p2m domctl op %u\n", sc->op); +diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c +index 79f3d37f5230..1bf9cbeb53cf 100644 +--- a/xen/arch/arm/p2m.c ++++ b/xen/arch/arm/p2m.c +@@ -50,6 +50,54 @@ static uint64_t generate_vttbr(uint16_t vmid, mfn_t root_mfn) + return (mfn_to_maddr(root_mfn) | ((uint64_t)vmid << 48)); + } + ++static struct page_info *p2m_alloc_page(struct domain *d) ++{ ++ struct page_info *pg; ++ ++ spin_lock(&d->arch.paging.lock); ++ /* ++ * For hardware domain, there should be no limit in the number of pages that ++ * can be allocated, so that the kernel may take advantage of the extended ++ * regions. Hence, allocate p2m pages for hardware domains from heap. ++ */ ++ if ( is_hardware_domain(d) ) ++ { ++ pg = alloc_domheap_page(NULL, 0); ++ if ( pg == NULL ) ++ { ++ printk(XENLOG_G_ERR "Failed to allocate P2M pages for hwdom.\n"); ++ spin_unlock(&d->arch.paging.lock); ++ return NULL; ++ } ++ } ++ else ++ { ++ pg = page_list_remove_head(&d->arch.paging.p2m_freelist); ++ if ( unlikely(!pg) ) ++ { ++ spin_unlock(&d->arch.paging.lock); ++ return NULL; ++ } ++ d->arch.paging.p2m_total_pages--; ++ } ++ spin_unlock(&d->arch.paging.lock); ++ ++ return pg; ++} ++ ++static void p2m_free_page(struct domain *d, struct page_info *pg) ++{ ++ spin_lock(&d->arch.paging.lock); ++ if ( is_hardware_domain(d) ) ++ free_domheap_page(pg); ++ else ++ { ++ d->arch.paging.p2m_total_pages++; ++ page_list_add_tail(pg, &d->arch.paging.p2m_freelist); ++ } ++ spin_unlock(&d->arch.paging.lock); ++} ++ + /* Return the size of the pool, rounded up to the nearest MB */ + unsigned int p2m_get_allocation(struct domain *d) + { +@@ -751,7 +799,7 @@ static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry) + + ASSERT(!p2m_is_valid(*entry)); + +- page = alloc_domheap_page(NULL, 0); ++ page = p2m_alloc_page(p2m->domain); + if ( page == NULL ) + return -ENOMEM; + +@@ -878,7 +926,7 @@ static void p2m_free_entry(struct p2m_domain *p2m, + pg = mfn_to_page(mfn); + + page_list_del(pg, &p2m->pages); +- free_domheap_page(pg); ++ p2m_free_page(p2m->domain, pg); + } + + static bool p2m_split_superpage(struct p2m_domain *p2m, lpae_t *entry, +@@ -902,7 +950,7 @@ static bool p2m_split_superpage(struct p2m_domain *p2m, lpae_t *entry, + ASSERT(level < target); + ASSERT(p2m_is_superpage(*entry, level)); + +- page = alloc_domheap_page(NULL, 0); ++ page = p2m_alloc_page(p2m->domain); + if ( !page ) + return false; + +@@ -1641,7 +1689,7 @@ int p2m_teardown(struct domain *d) + + while ( (pg = page_list_remove_head(&p2m->pages)) ) + { +- free_domheap_page(pg); ++ p2m_free_page(p2m->domain, pg); + count++; + /* Arbitrarily preempt every 512 iterations */ + if ( !(count % 512) && hypercall_preempt_check() ) +@@ -1665,6 +1713,7 @@ void p2m_final_teardown(struct domain *d) + return; + + ASSERT(page_list_empty(&p2m->pages)); ++ ASSERT(page_list_empty(&d->arch.paging.p2m_freelist)); + + if ( p2m->root ) + free_domheap_pages(p2m->root, P2M_ROOT_ORDER); +-- +2.37.1 + diff --git a/xsa410-4.16-01.patch b/xsa410-4.16-01.patch new file mode 100644 index 0000000..8f352ee --- /dev/null +++ b/xsa410-4.16-01.patch @@ -0,0 +1,59 @@ +From 4b4359122a414cc15156e13e3805988b71ff9da0 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 6 Jun 2022 06:17:25 +0000 +Subject: [PATCH 1/2] xen/arm: p2m: Prevent adding mapping when domain is dying + +During the domain destroy process, the domain will still be accessible +until it is fully destroyed. So does the P2M because we don't bail +out early if is_dying is non-zero. If a domain has permission to +modify the other domain's P2M (i.e. dom0, or a stubdomain), then +foreign mapping can be added past relinquish_p2m_mapping(). + +Therefore, we need to prevent mapping to be added when the domain +is dying. This commit prevents such adding of mapping by adding the +d->is_dying check to p2m_set_entry(). Also this commit enhances the +check in relinquish_p2m_mapping() to make sure that no mappings can +be added in the P2M after the P2M lock is released. + +This is part of CVE-2022-33746 / XSA-410. + +Signed-off-by: Julien Grall +Signed-off-by: Henry Wang +Tested-by: Henry Wang +Reviewed-by: Stefano Stabellini +--- + xen/arch/arm/p2m.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c +index fb71fa4c1c90..cbeff90f4371 100644 +--- a/xen/arch/arm/p2m.c ++++ b/xen/arch/arm/p2m.c +@@ -1093,6 +1093,15 @@ int p2m_set_entry(struct p2m_domain *p2m, + { + int rc = 0; + ++ /* ++ * Any reference taken by the P2M mappings (e.g. foreign mapping) will ++ * be dropped in relinquish_p2m_mapping(). As the P2M will still ++ * be accessible after, we need to prevent mapping to be added when the ++ * domain is dying. ++ */ ++ if ( unlikely(p2m->domain->is_dying) ) ++ return -ENOMEM; ++ + while ( nr ) + { + unsigned long mask; +@@ -1610,6 +1619,8 @@ int relinquish_p2m_mapping(struct domain *d) + unsigned int order; + gfn_t start, end; + ++ BUG_ON(!d->is_dying); ++ /* No mappings can be added in the P2M after the P2M lock is released. */ + p2m_write_lock(p2m); + + start = p2m->lowest_mapped_gfn; +-- +2.37.1 + diff --git a/xsa410-4.16-02.patch b/xsa410-4.16-02.patch new file mode 100644 index 0000000..7599a47 --- /dev/null +++ b/xsa410-4.16-02.patch @@ -0,0 +1,165 @@ +From 0d5846490348fa09a0d0915d7c795685a016ce10 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 6 Jun 2022 06:17:26 +0000 +Subject: [PATCH 2/2] xen/arm: p2m: Handle preemption when freeing intermediate + page tables + +At the moment the P2M page tables will be freed when the domain structure +is freed without any preemption. As the P2M is quite large, iterating +through this may take more time than it is reasonable without intermediate +preemption (to run softirqs and perhaps scheduler). + +Split p2m_teardown() in two parts: one preemptible and called when +relinquishing the resources, the other one non-preemptible and called +when freeing the domain structure. + +As we are now freeing the P2M pages early, we also need to prevent +further allocation if someone call p2m_set_entry() past p2m_teardown() +(I wasn't able to prove this will never happen). This is done by +the checking domain->is_dying from previous patch in p2m_set_entry(). + +Similarly, we want to make sure that no-one can accessed the free +pages. Therefore the root is cleared before freeing pages. + +This is part of CVE-2022-33746 / XSA-410. + +Signed-off-by: Julien Grall +Signed-off-by: Henry Wang +Tested-by: Henry Wang +Reviewed-by: Stefano Stabellini +--- + xen/arch/arm/domain.c | 10 +++++++-- + xen/arch/arm/p2m.c | 47 ++++++++++++++++++++++++++++++++++++--- + xen/include/asm-arm/p2m.h | 13 +++++++++-- + 3 files changed, 63 insertions(+), 7 deletions(-) + +diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c +index 96e1b235501d..2694c39127c5 100644 +--- a/xen/arch/arm/domain.c ++++ b/xen/arch/arm/domain.c +@@ -789,10 +789,10 @@ fail: + void arch_domain_destroy(struct domain *d) + { + /* IOMMU page table is shared with P2M, always call +- * iommu_domain_destroy() before p2m_teardown(). ++ * iommu_domain_destroy() before p2m_final_teardown(). + */ + iommu_domain_destroy(d); +- p2m_teardown(d); ++ p2m_final_teardown(d); + domain_vgic_free(d); + domain_vuart_free(d); + free_xenheap_page(d->shared_info); +@@ -996,6 +996,7 @@ enum { + PROG_xen, + PROG_page, + PROG_mapping, ++ PROG_p2m, + PROG_done, + }; + +@@ -1056,6 +1057,11 @@ int domain_relinquish_resources(struct domain *d) + if ( ret ) + return ret; + ++ PROGRESS(p2m): ++ ret = p2m_teardown(d); ++ if ( ret ) ++ return ret; ++ + PROGRESS(done): + break; + +diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c +index cbeff90f4371..3bcd1e897e88 100644 +--- a/xen/arch/arm/p2m.c ++++ b/xen/arch/arm/p2m.c +@@ -1527,17 +1527,58 @@ static void p2m_free_vmid(struct domain *d) + spin_unlock(&vmid_alloc_lock); + } + +-void p2m_teardown(struct domain *d) ++int p2m_teardown(struct domain *d) + { + struct p2m_domain *p2m = p2m_get_hostp2m(d); ++ unsigned long count = 0; + struct page_info *pg; ++ unsigned int i; ++ int rc = 0; ++ ++ p2m_write_lock(p2m); ++ ++ /* ++ * We are about to free the intermediate page-tables, so clear the ++ * root to prevent any walk to use them. ++ */ ++ for ( i = 0; i < P2M_ROOT_PAGES; i++ ) ++ clear_and_clean_page(p2m->root + i); ++ ++ /* ++ * The domain will not be scheduled anymore, so in theory we should ++ * not need to flush the TLBs. Do it for safety purpose. ++ * ++ * Note that all the devices have already been de-assigned. So we don't ++ * need to flush the IOMMU TLB here. ++ */ ++ p2m_force_tlb_flush_sync(p2m); ++ ++ while ( (pg = page_list_remove_head(&p2m->pages)) ) ++ { ++ free_domheap_page(pg); ++ count++; ++ /* Arbitrarily preempt every 512 iterations */ ++ if ( !(count % 512) && hypercall_preempt_check() ) ++ { ++ rc = -ERESTART; ++ break; ++ } ++ } ++ ++ p2m_write_unlock(p2m); ++ ++ return rc; ++} ++ ++void p2m_final_teardown(struct domain *d) ++{ ++ struct p2m_domain *p2m = p2m_get_hostp2m(d); + + /* p2m not actually initialized */ + if ( !p2m->domain ) + return; + +- while ( (pg = page_list_remove_head(&p2m->pages)) ) +- free_domheap_page(pg); ++ ASSERT(page_list_empty(&p2m->pages)); + + if ( p2m->root ) + free_domheap_pages(p2m->root, P2M_ROOT_ORDER); +diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h +index 8f11d9c97b5d..b3ba83283e11 100644 +--- a/xen/include/asm-arm/p2m.h ++++ b/xen/include/asm-arm/p2m.h +@@ -192,8 +192,17 @@ void setup_virt_paging(void); + /* Init the datastructures for later use by the p2m code */ + int p2m_init(struct domain *d); + +-/* Return all the p2m resources to Xen. */ +-void p2m_teardown(struct domain *d); ++/* ++ * The P2M resources are freed in two parts: ++ * - p2m_teardown() will be called when relinquish the resources. It ++ * will free large resources (e.g. intermediate page-tables) that ++ * requires preemption. ++ * - p2m_final_teardown() will be called when domain struct is been ++ * freed. This *cannot* be preempted and therefore one small ++ * resources should be freed here. ++ */ ++int p2m_teardown(struct domain *d); ++void p2m_final_teardown(struct domain *d); + + /* + * Remove mapping refcount on each mapping page in the p2m +-- +2.37.1 + diff --git a/xsa410-4.16-03.patch b/xsa410-4.16-03.patch new file mode 100644 index 0000000..7411d90 --- /dev/null +++ b/xsa410-4.16-03.patch @@ -0,0 +1,113 @@ +From: Roger Pau Monné +Subject: x86/p2m: add option to skip root pagetable removal in p2m_teardown() + +Add a new parameter to p2m_teardown() in order to select whether the +root page table should also be freed. Note that all users are +adjusted to pass the parameter to remove the root page tables, so +behavior is not modified. + +No functional change intended. + +This is part of CVE-2022-33746 / XSA-410. + +Suggested-by: Julien Grall +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +Acked-by: Tim Deegan + +--- a/xen/include/asm-x86/p2m.h ++++ b/xen/include/asm-x86/p2m.h +@@ -574,7 +574,7 @@ int p2m_init(struct domain *d); + int p2m_alloc_table(struct p2m_domain *p2m); + + /* Return all the p2m resources to Xen. */ +-void p2m_teardown(struct p2m_domain *p2m); ++void p2m_teardown(struct p2m_domain *p2m, bool remove_root); + void p2m_final_teardown(struct domain *d); + + /* Add a page to a domain's p2m table */ +--- a/xen/arch/x86/mm/hap/hap.c ++++ b/xen/arch/x86/mm/hap/hap.c +@@ -541,18 +541,18 @@ void hap_final_teardown(struct domain *d + } + + for ( i = 0; i < MAX_ALTP2M; i++ ) +- p2m_teardown(d->arch.altp2m_p2m[i]); ++ p2m_teardown(d->arch.altp2m_p2m[i], true); + } + + /* Destroy nestedp2m's first */ + for (i = 0; i < MAX_NESTEDP2M; i++) { +- p2m_teardown(d->arch.nested_p2m[i]); ++ p2m_teardown(d->arch.nested_p2m[i], true); + } + + if ( d->arch.paging.hap.total_pages != 0 ) + hap_teardown(d, NULL); + +- p2m_teardown(p2m_get_hostp2m(d)); ++ p2m_teardown(p2m_get_hostp2m(d), true); + /* Free any memory that the p2m teardown released */ + paging_lock(d); + hap_set_allocation(d, 0, NULL); +--- a/xen/arch/x86/mm/p2m.c ++++ b/xen/arch/x86/mm/p2m.c +@@ -749,11 +749,11 @@ int p2m_alloc_table(struct p2m_domain *p + * hvm fixme: when adding support for pvh non-hardware domains, this path must + * cleanup any foreign p2m types (release refcnts on them). + */ +-void p2m_teardown(struct p2m_domain *p2m) ++void p2m_teardown(struct p2m_domain *p2m, bool remove_root) + /* Return all the p2m pages to Xen. + * We know we don't have any extra mappings to these pages */ + { +- struct page_info *pg; ++ struct page_info *pg, *root_pg = NULL; + struct domain *d; + + if (p2m == NULL) +@@ -763,10 +763,22 @@ void p2m_teardown(struct p2m_domain *p2m + + p2m_lock(p2m); + ASSERT(atomic_read(&d->shr_pages) == 0); +- p2m->phys_table = pagetable_null(); ++ ++ if ( remove_root ) ++ p2m->phys_table = pagetable_null(); ++ else if ( !pagetable_is_null(p2m->phys_table) ) ++ { ++ root_pg = pagetable_get_page(p2m->phys_table); ++ clear_domain_page(pagetable_get_mfn(p2m->phys_table)); ++ } + + while ( (pg = page_list_remove_head(&p2m->pages)) ) +- d->arch.paging.free_page(d, pg); ++ if ( pg != root_pg ) ++ d->arch.paging.free_page(d, pg); ++ ++ if ( root_pg ) ++ page_list_add(root_pg, &p2m->pages); ++ + p2m_unlock(p2m); + } + +--- a/xen/arch/x86/mm/shadow/common.c ++++ b/xen/arch/x86/mm/shadow/common.c +@@ -2701,7 +2701,7 @@ int shadow_enable(struct domain *d, u32 + paging_unlock(d); + out_unlocked: + if ( rv != 0 && !pagetable_is_null(p2m_get_pagetable(p2m)) ) +- p2m_teardown(p2m); ++ p2m_teardown(p2m, true); + if ( rv != 0 && pg != NULL ) + { + pg->count_info &= ~PGC_count_mask; +@@ -2866,7 +2866,7 @@ void shadow_final_teardown(struct domain + shadow_teardown(d, NULL); + + /* It is now safe to pull down the p2m map. */ +- p2m_teardown(p2m_get_hostp2m(d)); ++ p2m_teardown(p2m_get_hostp2m(d), true); + /* Free any shadow memory that the p2m teardown released */ + paging_lock(d); + shadow_set_allocation(d, 0, NULL); diff --git a/xsa410-4.16-04.patch b/xsa410-4.16-04.patch new file mode 100644 index 0000000..2170b8e --- /dev/null +++ b/xsa410-4.16-04.patch @@ -0,0 +1,62 @@ +From: Jan Beulich +Subject: x86/HAP: adjust monitor table related error handling + +hap_make_monitor_table() will return INVALID_MFN if it encounters an +error condition, but hap_update_paging_modes() wasn’t handling this +value, resulting in an inappropriate value being stored in +monitor_table. This would subsequently misguide at least +hap_vcpu_teardown(). Avoid this by bailing early. + +Further, when a domain has/was already crashed or (perhaps less +important as there's no such path known to lead here) is already dying, +avoid calling domain_crash() on it again - that's at best confusing. + +This is part of CVE-2022-33746 / XSA-410. + +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné + +--- a/xen/arch/x86/mm/hap/hap.c ++++ b/xen/arch/x86/mm/hap/hap.c +@@ -39,6 +39,7 @@ + #include + #include + #include ++#include + + #include "private.h" + +@@ -405,8 +406,13 @@ static mfn_t hap_make_monitor_table(stru + return m4mfn; + + oom: +- printk(XENLOG_G_ERR "out of memory building monitor pagetable\n"); +- domain_crash(d); ++ if ( !d->is_dying && ++ (!d->is_shutting_down || d->shutdown_code != SHUTDOWN_crash) ) ++ { ++ printk(XENLOG_G_ERR "%pd: out of memory building monitor pagetable\n", ++ d); ++ domain_crash(d); ++ } + return INVALID_MFN; + } + +@@ -766,6 +772,9 @@ static void hap_update_paging_modes(stru + if ( pagetable_is_null(v->arch.hvm.monitor_table) ) + { + mfn_t mmfn = hap_make_monitor_table(v); ++ ++ if ( mfn_eq(mmfn, INVALID_MFN) ) ++ goto unlock; + v->arch.hvm.monitor_table = pagetable_from_mfn(mmfn); + make_cr3(v, mmfn); + hvm_update_host_cr3(v); +@@ -774,6 +783,7 @@ static void hap_update_paging_modes(stru + /* CR3 is effectively updated by a mode change. Flush ASIDs, etc. */ + hap_update_cr3(v, 0, false); + ++ unlock: + paging_unlock(d); + put_gfn(d, cr3_gfn); + } diff --git a/xsa410-4.16-05.patch b/xsa410-4.16-05.patch new file mode 100644 index 0000000..dc626c7 --- /dev/null +++ b/xsa410-4.16-05.patch @@ -0,0 +1,60 @@ +From: Jan Beulich +Subject: x86/shadow: tolerate failure of sh_set_toplevel_shadow() + +Subsequently sh_set_toplevel_shadow() will be adjusted to install a +blank entry in case prealloc fails. There are, in fact, pre-existing +error paths which would put in place a blank entry. The 4- and 2-level +code in sh_update_cr3(), however, assume the top level entry to be +valid. + +Hence bail from the function in the unlikely event that it's not. Note +that 3-level logic works differently: In particular a guest is free to +supply a PDPTR pointing at 4 non-present (or otherwise deemed invalid) +entries. The guest will crash, but we already cope with that. + +Really mfn_valid() is likely wrong to use in sh_set_toplevel_shadow(), +and it should instead be !mfn_eq(gmfn, INVALID_MFN). Avoid such a change +in security context, but add a respective assertion. + +This is part of CVE-2022-33746 / XSA-410. + +Signed-off-by: Jan Beulich +Acked-by: Tim Deegan +Reviewed-by: Andrew Cooper + +--- a/xen/arch/x86/mm/shadow/common.c ++++ b/xen/arch/x86/mm/shadow/common.c +@@ -2516,6 +2516,7 @@ void sh_set_toplevel_shadow(struct vcpu + /* Now figure out the new contents: is this a valid guest MFN? */ + if ( !mfn_valid(gmfn) ) + { ++ ASSERT(mfn_eq(gmfn, INVALID_MFN)); + new_entry = pagetable_null(); + goto install_new_entry; + } +--- a/xen/arch/x86/mm/shadow/multi.c ++++ b/xen/arch/x86/mm/shadow/multi.c +@@ -3312,6 +3312,11 @@ sh_update_cr3(struct vcpu *v, int do_loc + if ( sh_remove_write_access(d, gmfn, 4, 0) != 0 ) + guest_flush_tlb_mask(d, d->dirty_cpumask); + sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l4_shadow, sh_make_shadow); ++ if ( unlikely(pagetable_is_null(v->arch.paging.shadow.shadow_table[0])) ) ++ { ++ ASSERT(d->is_dying || d->is_shutting_down); ++ return; ++ } + if ( !shadow_mode_external(d) && !is_pv_32bit_domain(d) ) + { + mfn_t smfn = pagetable_get_mfn(v->arch.paging.shadow.shadow_table[0]); +@@ -3370,6 +3375,11 @@ sh_update_cr3(struct vcpu *v, int do_loc + if ( sh_remove_write_access(d, gmfn, 2, 0) != 0 ) + guest_flush_tlb_mask(d, d->dirty_cpumask); + sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l2_shadow, sh_make_shadow); ++ if ( unlikely(pagetable_is_null(v->arch.paging.shadow.shadow_table[0])) ) ++ { ++ ASSERT(d->is_dying || d->is_shutting_down); ++ return; ++ } + #else + #error This should never happen + #endif diff --git a/xsa410-4.16-06.patch b/xsa410-4.16-06.patch new file mode 100644 index 0000000..8c89eb3 --- /dev/null +++ b/xsa410-4.16-06.patch @@ -0,0 +1,255 @@ +From: Roger Pau Monné +Subject: x86/shadow: tolerate failure in shadow_prealloc() + +Prevent _shadow_prealloc() from calling BUG() when unable to fulfill +the pre-allocation and instead return true/false. Modify +shadow_prealloc() to crash the domain on allocation failure (if the +domain is not already dying), as shadow cannot operate normally after +that. Modify callers to also gracefully handle {_,}shadow_prealloc() +failing to fulfill the request. + +Note this in turn requires adjusting the callers of +sh_make_monitor_table() also to handle it returning INVALID_MFN. +sh_update_paging_modes() is also modified to add additional error +paths in case of allocation failure, some of those will return with +null monitor page tables (and the domain likely crashed). This is no +different that current error paths, but the newly introduced ones are +more likely to trigger. + +The now added failure points in sh_update_paging_modes() also require +that on some error return paths the previous structures are cleared, +and thus monitor table is null. + +While there adjust the 'type' parameter type of shadow_prealloc() to +unsigned int rather than u32. + +This is part of CVE-2022-33746 / XSA-410. + +Signed-off-by: Roger Pau Monné +Signed-off-by: Jan Beulich +Acked-by: Tim Deegan + +--- a/xen/arch/x86/mm/shadow/common.c ++++ b/xen/arch/x86/mm/shadow/common.c +@@ -36,6 +36,7 @@ + #include + #include + #include ++#include + #include "private.h" + + DEFINE_PER_CPU(uint32_t,trace_shadow_path_flags); +@@ -928,14 +929,15 @@ static inline void trace_shadow_prealloc + + /* Make sure there are at least count order-sized pages + * available in the shadow page pool. */ +-static void _shadow_prealloc(struct domain *d, unsigned int pages) ++static bool __must_check _shadow_prealloc(struct domain *d, unsigned int pages) + { + struct vcpu *v; + struct page_info *sp, *t; + mfn_t smfn; + int i; + +- if ( d->arch.paging.shadow.free_pages >= pages ) return; ++ if ( d->arch.paging.shadow.free_pages >= pages ) ++ return true; + + /* Shouldn't have enabled shadows if we've no vcpus. */ + ASSERT(d->vcpu && d->vcpu[0]); +@@ -951,7 +953,8 @@ static void _shadow_prealloc(struct doma + sh_unpin(d, smfn); + + /* See if that freed up enough space */ +- if ( d->arch.paging.shadow.free_pages >= pages ) return; ++ if ( d->arch.paging.shadow.free_pages >= pages ) ++ return true; + } + + /* Stage two: all shadow pages are in use in hierarchies that are +@@ -974,7 +977,7 @@ static void _shadow_prealloc(struct doma + if ( d->arch.paging.shadow.free_pages >= pages ) + { + guest_flush_tlb_mask(d, d->dirty_cpumask); +- return; ++ return true; + } + } + } +@@ -987,7 +990,12 @@ static void _shadow_prealloc(struct doma + d->arch.paging.shadow.total_pages, + d->arch.paging.shadow.free_pages, + d->arch.paging.shadow.p2m_pages); +- BUG(); ++ ++ ASSERT(d->is_dying); ++ ++ guest_flush_tlb_mask(d, d->dirty_cpumask); ++ ++ return false; + } + + /* Make sure there are at least count pages of the order according to +@@ -995,9 +1003,19 @@ static void _shadow_prealloc(struct doma + * This must be called before any calls to shadow_alloc(). Since this + * will free existing shadows to make room, it must be called early enough + * to avoid freeing shadows that the caller is currently working on. */ +-void shadow_prealloc(struct domain *d, u32 type, unsigned int count) ++bool shadow_prealloc(struct domain *d, unsigned int type, unsigned int count) + { +- return _shadow_prealloc(d, shadow_size(type) * count); ++ bool ret = _shadow_prealloc(d, shadow_size(type) * count); ++ ++ if ( !ret && !d->is_dying && ++ (!d->is_shutting_down || d->shutdown_code != SHUTDOWN_crash) ) ++ /* ++ * Failing to allocate memory required for shadow usage can only result in ++ * a domain crash, do it here rather that relying on every caller to do it. ++ */ ++ domain_crash(d); ++ ++ return ret; + } + + /* Deliberately free all the memory we can: this will tear down all of +@@ -1218,7 +1236,7 @@ void shadow_free(struct domain *d, mfn_t + static struct page_info * + shadow_alloc_p2m_page(struct domain *d) + { +- struct page_info *pg; ++ struct page_info *pg = NULL; + + /* This is called both from the p2m code (which never holds the + * paging lock) and the log-dirty code (which always does). */ +@@ -1236,16 +1254,18 @@ shadow_alloc_p2m_page(struct domain *d) + d->arch.paging.shadow.p2m_pages, + shadow_min_acceptable_pages(d)); + } +- paging_unlock(d); +- return NULL; ++ goto out; + } + +- shadow_prealloc(d, SH_type_p2m_table, 1); ++ if ( !shadow_prealloc(d, SH_type_p2m_table, 1) ) ++ goto out; ++ + pg = mfn_to_page(shadow_alloc(d, SH_type_p2m_table, 0)); + d->arch.paging.shadow.p2m_pages++; + d->arch.paging.shadow.total_pages--; + ASSERT(!page_get_owner(pg) && !(pg->count_info & PGC_count_mask)); + ++ out: + paging_unlock(d); + + return pg; +@@ -1336,7 +1356,9 @@ int shadow_set_allocation(struct domain + else if ( d->arch.paging.shadow.total_pages > pages ) + { + /* Need to return memory to domheap */ +- _shadow_prealloc(d, 1); ++ if ( !_shadow_prealloc(d, 1) ) ++ return -ENOMEM; ++ + sp = page_list_remove_head(&d->arch.paging.shadow.freelist); + ASSERT(sp); + /* +@@ -2334,12 +2356,13 @@ static void sh_update_paging_modes(struc + if ( mfn_eq(v->arch.paging.shadow.oos_snapshot[0], INVALID_MFN) ) + { + int i; ++ ++ if ( !shadow_prealloc(d, SH_type_oos_snapshot, SHADOW_OOS_PAGES) ) ++ return; ++ + for(i = 0; i < SHADOW_OOS_PAGES; i++) +- { +- shadow_prealloc(d, SH_type_oos_snapshot, 1); + v->arch.paging.shadow.oos_snapshot[i] = + shadow_alloc(d, SH_type_oos_snapshot, 0); +- } + } + #endif /* OOS */ + +@@ -2403,6 +2426,9 @@ static void sh_update_paging_modes(struc + mfn_t mmfn = sh_make_monitor_table( + v, v->arch.paging.mode->shadow.shadow_levels); + ++ if ( mfn_eq(mmfn, INVALID_MFN) ) ++ return; ++ + v->arch.hvm.monitor_table = pagetable_from_mfn(mmfn); + make_cr3(v, mmfn); + hvm_update_host_cr3(v); +@@ -2441,6 +2467,12 @@ static void sh_update_paging_modes(struc + v->arch.hvm.monitor_table = pagetable_null(); + new_mfn = sh_make_monitor_table( + v, v->arch.paging.mode->shadow.shadow_levels); ++ if ( mfn_eq(new_mfn, INVALID_MFN) ) ++ { ++ sh_destroy_monitor_table(v, old_mfn, ++ old_mode->shadow.shadow_levels); ++ return; ++ } + v->arch.hvm.monitor_table = pagetable_from_mfn(new_mfn); + SHADOW_PRINTK("new monitor table %"PRI_mfn "\n", + mfn_x(new_mfn)); +@@ -2526,7 +2558,12 @@ void sh_set_toplevel_shadow(struct vcpu + if ( !mfn_valid(smfn) ) + { + /* Make sure there's enough free shadow memory. */ +- shadow_prealloc(d, root_type, 1); ++ if ( !shadow_prealloc(d, root_type, 1) ) ++ { ++ new_entry = pagetable_null(); ++ goto install_new_entry; ++ } ++ + /* Shadow the page. */ + smfn = make_shadow(v, gmfn, root_type); + } +--- a/xen/arch/x86/mm/shadow/hvm.c ++++ b/xen/arch/x86/mm/shadow/hvm.c +@@ -700,7 +700,9 @@ mfn_t sh_make_monitor_table(const struct + ASSERT(!pagetable_get_pfn(v->arch.hvm.monitor_table)); + + /* Guarantee we can get the memory we need */ +- shadow_prealloc(d, SH_type_monitor_table, CONFIG_PAGING_LEVELS); ++ if ( !shadow_prealloc(d, SH_type_monitor_table, CONFIG_PAGING_LEVELS) ) ++ return INVALID_MFN; ++ + m4mfn = shadow_alloc(d, SH_type_monitor_table, 0); + mfn_to_page(m4mfn)->shadow_flags = 4; + +--- a/xen/arch/x86/mm/shadow/multi.c ++++ b/xen/arch/x86/mm/shadow/multi.c +@@ -2440,9 +2440,14 @@ static int sh_page_fault(struct vcpu *v, + * Preallocate shadow pages *before* removing writable accesses + * otherwhise an OOS L1 might be demoted and promoted again with + * writable mappings. */ +- shadow_prealloc(d, +- SH_type_l1_shadow, +- GUEST_PAGING_LEVELS < 4 ? 1 : GUEST_PAGING_LEVELS - 1); ++ if ( !shadow_prealloc(d, SH_type_l1_shadow, ++ GUEST_PAGING_LEVELS < 4 ++ ? 1 : GUEST_PAGING_LEVELS - 1) ) ++ { ++ paging_unlock(d); ++ put_gfn(d, gfn_x(gfn)); ++ return 0; ++ } + + rc = gw_remove_write_accesses(v, va, &gw); + +--- a/xen/arch/x86/mm/shadow/private.h ++++ b/xen/arch/x86/mm/shadow/private.h +@@ -383,7 +383,8 @@ void shadow_promote(struct domain *d, mf + void shadow_demote(struct domain *d, mfn_t gmfn, u32 type); + + /* Shadow page allocation functions */ +-void shadow_prealloc(struct domain *d, u32 shadow_type, unsigned int count); ++bool __must_check shadow_prealloc(struct domain *d, unsigned int shadow_type, ++ unsigned int count); + mfn_t shadow_alloc(struct domain *d, + u32 shadow_type, + unsigned long backpointer); diff --git a/xsa410-4.16-07.patch b/xsa410-4.16-07.patch new file mode 100644 index 0000000..9eea91c --- /dev/null +++ b/xsa410-4.16-07.patch @@ -0,0 +1,82 @@ +From: Roger Pau Monné +Subject: x86/p2m: refuse new allocations for dying domains + +This will in particular prevent any attempts to add entries to the p2m, +once - in a subsequent change - non-root entries have been removed. + +This is part of CVE-2022-33746 / XSA-410. + +Signed-off-by: Roger Pau Monné +Signed-off-by: Jan Beulich +Acked-by: Tim Deegan + +--- a/xen/arch/x86/mm/hap/hap.c ++++ b/xen/arch/x86/mm/hap/hap.c +@@ -245,6 +245,9 @@ static struct page_info *hap_alloc(struc + + ASSERT(paging_locked_by_me(d)); + ++ if ( unlikely(d->is_dying) ) ++ return NULL; ++ + pg = page_list_remove_head(&d->arch.paging.hap.freelist); + if ( unlikely(!pg) ) + return NULL; +@@ -281,7 +284,7 @@ static struct page_info *hap_alloc_p2m_p + d->arch.paging.hap.p2m_pages++; + ASSERT(!page_get_owner(pg) && !(pg->count_info & PGC_count_mask)); + } +- else if ( !d->arch.paging.p2m_alloc_failed ) ++ else if ( !d->arch.paging.p2m_alloc_failed && !d->is_dying ) + { + d->arch.paging.p2m_alloc_failed = 1; + dprintk(XENLOG_ERR, "d%i failed to allocate from HAP pool\n", +--- a/xen/arch/x86/mm/shadow/common.c ++++ b/xen/arch/x86/mm/shadow/common.c +@@ -939,6 +939,10 @@ static bool __must_check _shadow_preallo + if ( d->arch.paging.shadow.free_pages >= pages ) + return true; + ++ if ( unlikely(d->is_dying) ) ++ /* No reclaim when the domain is dying, teardown will take care of it. */ ++ return false; ++ + /* Shouldn't have enabled shadows if we've no vcpus. */ + ASSERT(d->vcpu && d->vcpu[0]); + +@@ -991,7 +995,7 @@ static bool __must_check _shadow_preallo + d->arch.paging.shadow.free_pages, + d->arch.paging.shadow.p2m_pages); + +- ASSERT(d->is_dying); ++ ASSERT_UNREACHABLE(); + + guest_flush_tlb_mask(d, d->dirty_cpumask); + +@@ -1005,10 +1009,13 @@ static bool __must_check _shadow_preallo + * to avoid freeing shadows that the caller is currently working on. */ + bool shadow_prealloc(struct domain *d, unsigned int type, unsigned int count) + { +- bool ret = _shadow_prealloc(d, shadow_size(type) * count); ++ bool ret; ++ ++ if ( unlikely(d->is_dying) ) ++ return false; + +- if ( !ret && !d->is_dying && +- (!d->is_shutting_down || d->shutdown_code != SHUTDOWN_crash) ) ++ ret = _shadow_prealloc(d, shadow_size(type) * count); ++ if ( !ret && (!d->is_shutting_down || d->shutdown_code != SHUTDOWN_crash) ) + /* + * Failing to allocate memory required for shadow usage can only result in + * a domain crash, do it here rather that relying on every caller to do it. +@@ -1238,6 +1245,9 @@ shadow_alloc_p2m_page(struct domain *d) + { + struct page_info *pg = NULL; + ++ if ( unlikely(d->is_dying) ) ++ return NULL; ++ + /* This is called both from the p2m code (which never holds the + * paging lock) and the log-dirty code (which always does). */ + paging_lock_recursive(d); diff --git a/xsa410-4.16-08.patch b/xsa410-4.16-08.patch new file mode 100644 index 0000000..92cdb49 --- /dev/null +++ b/xsa410-4.16-08.patch @@ -0,0 +1,96 @@ +From: Roger Pau Monné +Subject: x86/p2m: truly free paging pool memory for dying domains + +Modify {hap,shadow}_free to free the page immediately if the domain is +dying, so that pages don't accumulate in the pool when +{shadow,hap}_final_teardown() get called. This is to limit the amount of +work which needs to be done there (in a non-preemptable manner). + +Note the call to shadow_free() in shadow_free_p2m_page() is moved after +increasing total_pages, so that the decrease done in shadow_free() in +case the domain is dying doesn't underflow the counter, even if just for +a short interval. + +This is part of CVE-2022-33746 / XSA-410. + +Signed-off-by: Roger Pau Monné +Signed-off-by: Jan Beulich +Acked-by: Tim Deegan + +--- a/xen/arch/x86/mm/hap/hap.c ++++ b/xen/arch/x86/mm/hap/hap.c +@@ -265,6 +265,18 @@ static void hap_free(struct domain *d, m + + ASSERT(paging_locked_by_me(d)); + ++ /* ++ * For dying domains, actually free the memory here. This way less work is ++ * left to hap_final_teardown(), which cannot easily have preemption checks ++ * added. ++ */ ++ if ( unlikely(d->is_dying) ) ++ { ++ free_domheap_page(pg); ++ d->arch.paging.hap.total_pages--; ++ return; ++ } ++ + d->arch.paging.hap.free_pages++; + page_list_add_tail(pg, &d->arch.paging.hap.freelist); + } +--- a/xen/arch/x86/mm/shadow/common.c ++++ b/xen/arch/x86/mm/shadow/common.c +@@ -1187,6 +1187,7 @@ mfn_t shadow_alloc(struct domain *d, + void shadow_free(struct domain *d, mfn_t smfn) + { + struct page_info *next = NULL, *sp = mfn_to_page(smfn); ++ bool dying = ACCESS_ONCE(d->is_dying); + struct page_list_head *pin_list; + unsigned int pages; + u32 shadow_type; +@@ -1229,11 +1230,32 @@ void shadow_free(struct domain *d, mfn_t + * just before the allocator hands the page out again. */ + page_set_tlbflush_timestamp(sp); + perfc_decr(shadow_alloc_count); +- page_list_add_tail(sp, &d->arch.paging.shadow.freelist); ++ ++ /* ++ * For dying domains, actually free the memory here. This way less ++ * work is left to shadow_final_teardown(), which cannot easily have ++ * preemption checks added. ++ */ ++ if ( unlikely(dying) ) ++ { ++ /* ++ * The backpointer field (sh.back) used by shadow code aliases the ++ * domain owner field, unconditionally clear it here to avoid ++ * free_domheap_page() attempting to parse it. ++ */ ++ page_set_owner(sp, NULL); ++ free_domheap_page(sp); ++ } ++ else ++ page_list_add_tail(sp, &d->arch.paging.shadow.freelist); ++ + sp = next; + } + +- d->arch.paging.shadow.free_pages += pages; ++ if ( unlikely(dying) ) ++ d->arch.paging.shadow.total_pages -= pages; ++ else ++ d->arch.paging.shadow.free_pages += pages; + } + + /* Divert a page from the pool to be used by the p2m mapping. +@@ -1303,9 +1325,9 @@ shadow_free_p2m_page(struct domain *d, s + * paging lock) and the log-dirty code (which always does). */ + paging_lock_recursive(d); + +- shadow_free(d, page_to_mfn(pg)); + d->arch.paging.shadow.p2m_pages--; + d->arch.paging.shadow.total_pages++; ++ shadow_free(d, page_to_mfn(pg)); + + paging_unlock(d); + } diff --git a/xsa410-4.16-09.patch b/xsa410-4.16-09.patch new file mode 100644 index 0000000..60259e1 --- /dev/null +++ b/xsa410-4.16-09.patch @@ -0,0 +1,159 @@ +From: Roger Pau Monné +Subject: x86/p2m: free the paging memory pool preemptively + +The paging memory pool is currently freed in two different places: +from {shadow,hap}_teardown() via domain_relinquish_resources() and +from {shadow,hap}_final_teardown() via complete_domain_destroy(). +While the former does handle preemption, the later doesn't. + +Attempt to move as much p2m related freeing as possible to happen +before the call to {shadow,hap}_teardown(), so that most memory can be +freed in a preemptive way. In order to avoid causing issues to +existing callers leave the root p2m page tables set and free them in +{hap,shadow}_final_teardown(). Also modify {hap,shadow}_free to free +the page immediately if the domain is dying, so that pages don't +accumulate in the pool when {shadow,hap}_final_teardown() get called. + +Move altp2m_vcpu_disable_ve() to be done in hap_teardown(), as that's +the place where altp2m_active gets disabled now. + +This is part of CVE-2022-33746 / XSA-410. + +Signed-off-by: Roger Pau Monné +Signed-off-by: Jan Beulich +Acked-by: Tim Deegan + +--- a/xen/arch/x86/domain.c ++++ b/xen/arch/x86/domain.c +@@ -38,7 +38,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -2381,12 +2380,6 @@ int domain_relinquish_resources(struct d + vpmu_destroy(v); + } + +- if ( altp2m_active(d) ) +- { +- for_each_vcpu ( d, v ) +- altp2m_vcpu_disable_ve(v); +- } +- + if ( is_pv_domain(d) ) + { + for_each_vcpu ( d, v ) +--- a/xen/arch/x86/mm/hap/hap.c ++++ b/xen/arch/x86/mm/hap/hap.c +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -546,24 +547,8 @@ void hap_final_teardown(struct domain *d + unsigned int i; + + if ( hvm_altp2m_supported() ) +- { +- d->arch.altp2m_active = 0; +- +- if ( d->arch.altp2m_eptp ) +- { +- free_xenheap_page(d->arch.altp2m_eptp); +- d->arch.altp2m_eptp = NULL; +- } +- +- if ( d->arch.altp2m_visible_eptp ) +- { +- free_xenheap_page(d->arch.altp2m_visible_eptp); +- d->arch.altp2m_visible_eptp = NULL; +- } +- + for ( i = 0; i < MAX_ALTP2M; i++ ) + p2m_teardown(d->arch.altp2m_p2m[i], true); +- } + + /* Destroy nestedp2m's first */ + for (i = 0; i < MAX_NESTEDP2M; i++) { +@@ -578,6 +563,8 @@ void hap_final_teardown(struct domain *d + paging_lock(d); + hap_set_allocation(d, 0, NULL); + ASSERT(d->arch.paging.hap.p2m_pages == 0); ++ ASSERT(d->arch.paging.hap.free_pages == 0); ++ ASSERT(d->arch.paging.hap.total_pages == 0); + paging_unlock(d); + } + +@@ -603,6 +590,7 @@ void hap_vcpu_teardown(struct vcpu *v) + void hap_teardown(struct domain *d, bool *preempted) + { + struct vcpu *v; ++ unsigned int i; + + ASSERT(d->is_dying); + ASSERT(d != current->domain); +@@ -611,6 +599,28 @@ void hap_teardown(struct domain *d, bool + for_each_vcpu ( d, v ) + hap_vcpu_teardown(v); + ++ /* Leave the root pt in case we get further attempts to modify the p2m. */ ++ if ( hvm_altp2m_supported() ) ++ { ++ if ( altp2m_active(d) ) ++ for_each_vcpu ( d, v ) ++ altp2m_vcpu_disable_ve(v); ++ ++ d->arch.altp2m_active = 0; ++ ++ FREE_XENHEAP_PAGE(d->arch.altp2m_eptp); ++ FREE_XENHEAP_PAGE(d->arch.altp2m_visible_eptp); ++ ++ for ( i = 0; i < MAX_ALTP2M; i++ ) ++ p2m_teardown(d->arch.altp2m_p2m[i], false); ++ } ++ ++ /* Destroy nestedp2m's after altp2m. */ ++ for ( i = 0; i < MAX_NESTEDP2M; i++ ) ++ p2m_teardown(d->arch.nested_p2m[i], false); ++ ++ p2m_teardown(p2m_get_hostp2m(d), false); ++ + paging_lock(d); /* Keep various asserts happy */ + + if ( d->arch.paging.hap.total_pages != 0 ) +--- a/xen/arch/x86/mm/shadow/common.c ++++ b/xen/arch/x86/mm/shadow/common.c +@@ -2824,8 +2824,17 @@ void shadow_teardown(struct domain *d, b + for_each_vcpu ( d, v ) + shadow_vcpu_teardown(v); + ++ p2m_teardown(p2m_get_hostp2m(d), false); ++ + paging_lock(d); + ++ /* ++ * Reclaim all shadow memory so that shadow_set_allocation() doesn't find ++ * in-use pages, as _shadow_prealloc() will no longer try to reclaim pages ++ * because the domain is dying. ++ */ ++ shadow_blow_tables(d); ++ + #if (SHADOW_OPTIMIZATIONS & (SHOPT_VIRTUAL_TLB|SHOPT_OUT_OF_SYNC)) + /* Free the virtual-TLB array attached to each vcpu */ + for_each_vcpu(d, v) +@@ -2946,6 +2955,9 @@ void shadow_final_teardown(struct domain + d->arch.paging.shadow.total_pages, + d->arch.paging.shadow.free_pages, + d->arch.paging.shadow.p2m_pages); ++ ASSERT(!d->arch.paging.shadow.total_pages); ++ ASSERT(!d->arch.paging.shadow.free_pages); ++ ASSERT(!d->arch.paging.shadow.p2m_pages); + paging_unlock(d); + } + diff --git a/xsa410-4.16-10.patch b/xsa410-4.16-10.patch new file mode 100644 index 0000000..258b7ff --- /dev/null +++ b/xsa410-4.16-10.patch @@ -0,0 +1,171 @@ +From: Julien Grall +Subject: xen/x86: p2m: Add preemption in p2m_teardown() + +The list p2m->pages contain all the pages used by the P2M. On large +instance this can be quite large and the time spent to call +d->arch.paging.free_page() will take more than 1ms for a 80GB guest +on a Xen running in nested environment on a c5.metal. + +By extrapolation, it would take > 100ms for a 8TB guest (what we +current security support). So add some preemption in p2m_teardown() +and propagate to the callers. Note there are 3 places where +the preemption is not enabled: + - hap_final_teardown()/shadow_final_teardown(): We are + preventing update the P2M once the domain is dying (so + no more pages could be allocated) and most of the P2M pages + will be freed in preemptive manneer when relinquishing the + resources. So this is fine to disable preemption. + - shadow_enable(): This is fine because it will undo the allocation + that may have been made by p2m_alloc_table() (so only the root + page table). + +The preemption is arbitrarily checked every 1024 iterations. + +Note that with the current approach, Xen doesn't keep track on whether +the alt/nested P2Ms have been cleared. So there are some redundant work. +However, this is not expected to incurr too much overhead (the P2M lock +shouldn't be contended during teardown). So this is optimization is +left outside of the security event. + +This is part of CVE-2022-33746 / XSA-410. + +Signed-off-by: Julien Grall +Signed-off-by: Jan Beulich + +--- a/xen/include/asm-x86/p2m.h ++++ b/xen/include/asm-x86/p2m.h +@@ -574,7 +574,7 @@ int p2m_init(struct domain *d); + int p2m_alloc_table(struct p2m_domain *p2m); + + /* Return all the p2m resources to Xen. */ +-void p2m_teardown(struct p2m_domain *p2m, bool remove_root); ++void p2m_teardown(struct p2m_domain *p2m, bool remove_root, bool *preempted); + void p2m_final_teardown(struct domain *d); + + /* Add a page to a domain's p2m table */ +--- a/xen/arch/x86/mm/hap/hap.c ++++ b/xen/arch/x86/mm/hap/hap.c +@@ -548,17 +548,17 @@ void hap_final_teardown(struct domain *d + + if ( hvm_altp2m_supported() ) + for ( i = 0; i < MAX_ALTP2M; i++ ) +- p2m_teardown(d->arch.altp2m_p2m[i], true); ++ p2m_teardown(d->arch.altp2m_p2m[i], true, NULL); + + /* Destroy nestedp2m's first */ + for (i = 0; i < MAX_NESTEDP2M; i++) { +- p2m_teardown(d->arch.nested_p2m[i], true); ++ p2m_teardown(d->arch.nested_p2m[i], true, NULL); + } + + if ( d->arch.paging.hap.total_pages != 0 ) + hap_teardown(d, NULL); + +- p2m_teardown(p2m_get_hostp2m(d), true); ++ p2m_teardown(p2m_get_hostp2m(d), true, NULL); + /* Free any memory that the p2m teardown released */ + paging_lock(d); + hap_set_allocation(d, 0, NULL); +@@ -612,14 +612,24 @@ void hap_teardown(struct domain *d, bool + FREE_XENHEAP_PAGE(d->arch.altp2m_visible_eptp); + + for ( i = 0; i < MAX_ALTP2M; i++ ) +- p2m_teardown(d->arch.altp2m_p2m[i], false); ++ { ++ p2m_teardown(d->arch.altp2m_p2m[i], false, preempted); ++ if ( preempted && *preempted ) ++ return; ++ } + } + + /* Destroy nestedp2m's after altp2m. */ + for ( i = 0; i < MAX_NESTEDP2M; i++ ) +- p2m_teardown(d->arch.nested_p2m[i], false); ++ { ++ p2m_teardown(d->arch.nested_p2m[i], false, preempted); ++ if ( preempted && *preempted ) ++ return; ++ } + +- p2m_teardown(p2m_get_hostp2m(d), false); ++ p2m_teardown(p2m_get_hostp2m(d), false, preempted); ++ if ( preempted && *preempted ) ++ return; + + paging_lock(d); /* Keep various asserts happy */ + +--- a/xen/arch/x86/mm/p2m.c ++++ b/xen/arch/x86/mm/p2m.c +@@ -749,12 +749,13 @@ int p2m_alloc_table(struct p2m_domain *p + * hvm fixme: when adding support for pvh non-hardware domains, this path must + * cleanup any foreign p2m types (release refcnts on them). + */ +-void p2m_teardown(struct p2m_domain *p2m, bool remove_root) ++void p2m_teardown(struct p2m_domain *p2m, bool remove_root, bool *preempted) + /* Return all the p2m pages to Xen. + * We know we don't have any extra mappings to these pages */ + { + struct page_info *pg, *root_pg = NULL; + struct domain *d; ++ unsigned int i = 0; + + if (p2m == NULL) + return; +@@ -773,8 +774,19 @@ void p2m_teardown(struct p2m_domain *p2m + } + + while ( (pg = page_list_remove_head(&p2m->pages)) ) +- if ( pg != root_pg ) +- d->arch.paging.free_page(d, pg); ++ { ++ if ( pg == root_pg ) ++ continue; ++ ++ d->arch.paging.free_page(d, pg); ++ ++ /* Arbitrarily check preemption every 1024 iterations */ ++ if ( preempted && !(++i % 1024) && general_preempt_check() ) ++ { ++ *preempted = true; ++ break; ++ } ++ } + + if ( root_pg ) + page_list_add(root_pg, &p2m->pages); +--- a/xen/arch/x86/mm/shadow/common.c ++++ b/xen/arch/x86/mm/shadow/common.c +@@ -2770,8 +2770,12 @@ int shadow_enable(struct domain *d, u32 + out_locked: + paging_unlock(d); + out_unlocked: ++ /* ++ * This is fine to ignore the preemption here because only the root ++ * will be allocated by p2m_alloc_table(). ++ */ + if ( rv != 0 && !pagetable_is_null(p2m_get_pagetable(p2m)) ) +- p2m_teardown(p2m, true); ++ p2m_teardown(p2m, true, NULL); + if ( rv != 0 && pg != NULL ) + { + pg->count_info &= ~PGC_count_mask; +@@ -2824,7 +2828,9 @@ void shadow_teardown(struct domain *d, b + for_each_vcpu ( d, v ) + shadow_vcpu_teardown(v); + +- p2m_teardown(p2m_get_hostp2m(d), false); ++ p2m_teardown(p2m_get_hostp2m(d), false, preempted); ++ if ( preempted && *preempted ) ++ return; + + paging_lock(d); + +@@ -2945,7 +2951,7 @@ void shadow_final_teardown(struct domain + shadow_teardown(d, NULL); + + /* It is now safe to pull down the p2m map. */ +- p2m_teardown(p2m_get_hostp2m(d), true); ++ p2m_teardown(p2m_get_hostp2m(d), true, NULL); + /* Free any shadow memory that the p2m teardown released */ + paging_lock(d); + shadow_set_allocation(d, 0, NULL); diff --git a/xsa411.patch b/xsa411.patch new file mode 100644 index 0000000..50dcae4 --- /dev/null +++ b/xsa411.patch @@ -0,0 +1,55 @@ +From: Jan Beulich +Subject: gnttab: correct locking on transitive grant copy error path + +While the comment next to the lock dropping in preparation of +recursively calling acquire_grant_for_copy() mistakenly talks about the +rd == td case (excluded a few lines further up), the same concerns apply +to the calling of release_grant_for_copy() on a subsequent error path. + +This is CVE-2022-33748 / XSA-411. + +Fixes: ad48fb963dbf ("gnttab: fix transitive grant handling") +Signed-off-by: Jan Beulich +--- +v2: Extend code comment. + +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -2622,9 +2622,8 @@ acquire_grant_for_copy( + trans_domid); + + /* +- * acquire_grant_for_copy() could take the lock on the +- * remote table (if rd == td), so we have to drop the lock +- * here and reacquire. ++ * acquire_grant_for_copy() will take the lock on the remote table, ++ * so we have to drop the lock here and reacquire. + */ + active_entry_release(act); + grant_read_unlock(rgt); +@@ -2661,11 +2660,25 @@ acquire_grant_for_copy( + act->trans_gref != trans_gref || + !act->is_sub_page)) ) + { ++ /* ++ * Like above for acquire_grant_for_copy() we need to drop and then ++ * re-acquire the locks here to prevent lock order inversion issues. ++ * Unlike for acquire_grant_for_copy() we don't need to re-check ++ * anything, as release_grant_for_copy() doesn't depend on the grant ++ * table entry: It only updates internal state and the status flags. ++ */ ++ active_entry_release(act); ++ grant_read_unlock(rgt); ++ + release_grant_for_copy(td, trans_gref, readonly); + rcu_unlock_domain(td); ++ ++ grant_read_lock(rgt); ++ act = active_entry_acquire(rgt, gref); + reduce_status_for_pin(rd, act, status, readonly); + active_entry_release(act); + grant_read_unlock(rgt); ++ + put_page(*page); + *page = NULL; + return ERESTART; From ca586624b19c9efd11de7494b3ab523fcc60bd09 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 18 Oct 2022 19:15:03 +0100 Subject: [PATCH 048/137] add bug references --- xen.spec | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/xen.spec b/xen.spec index 15400e1..99e4234 100644 --- a/xen.spec +++ b/xen.spec @@ -970,10 +970,11 @@ fi %changelog * Fri Oct 14 2022 Michael Young - 4.16.2-2 - Arm: unbounded memory consumption for 2nd-level page tables [XSA-409, - CVE-2022-33747] + CVE-2022-33747] (#2135268) - P2M pool freeing may take excessively long [XSA-410, CVE-2022-33746] + (#2135641) - lock order inversion in transitive grant copy handling [XSA-411, - CVE-2022-33748] + CVE-2022-33748] (#2135263) * Sat Sep 17 2022 Michael Young - 4.16.2-1 - update to xen-4.16.2 From 97374c0faa57a5afe170cfe760cf4c82368a983a Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 1 Nov 2022 17:09:30 +0000 Subject: [PATCH 049/137] several security updates x86: unintended memory sharing between guests [XSA-412, CVE-2022-42327] Xenstore: Guests can crash xenstored [XSA-414, CVE-2022-42309] Xenstore: Guests can create orphaned Xenstore nodes [XSA-415, CVE-2022-42310] Xenstore: guests can let run xenstored out of memory [XSA-326, CVE-2022-42311, CVE-2022-42312, CVE-2022-42313, CVE-2022-42314, CVE-2022-42315, CVE-2022-42316, CVE-2022-42317, CVE-2022-42318] Xenstore: Guests can cause Xenstore to not free temporary memory [XSA-416, CVE-2022-42319] Xenstore: Guests can get access to Xenstore nodes of deleted domains [XSA-417, CVE-2022-42320] Xenstore: Guests can crash xenstored via exhausting the stack [XSA-418, CVE-2022-42321] Xenstore: Cooperating guests can create arbitrary numbers of nodes [XSA-419, CVE-2022-42322, CVE-2022-42323] Oxenstored 32->31 bit integer truncation issues [XSA-420, CVE-2022-42324] Xenstore: Guests can create arbitrary number of nodes via transactions [XSA-421, CVE-2022-42325, CVE-2022-42326] --- xen.spec | 108 +++- xsa326-4.16-oxenstored-01.patch | 55 ++ xsa326-4.16-oxenstored-02.patch | 94 ++++ xsa326-4.16-oxenstored-03.patch | 119 +++++ xsa326-4.16-oxenstored-04.patch | 85 ++++ xsa326-4.16-oxenstored-05.patch | 214 ++++++++ xsa326-4.16-oxenstored-06.patch | 127 +++++ xsa326-4.16-oxenstored-07.patch | 872 ++++++++++++++++++++++++++++++++ xsa326-4.16-oxenstored-08.patch | 49 ++ xsa326-4.16-xenstored-01.patch | 205 ++++++++ xsa326-4.16-xenstored-02.patch | 108 ++++ xsa326-4.16-xenstored-03.patch | 192 +++++++ xsa326-4.16-xenstored-04.patch | 302 +++++++++++ xsa326-4.16-xenstored-05.patch | 443 ++++++++++++++++ xsa326-4.16-xenstored-06.patch | 85 ++++ xsa326-4.16-xenstored-07.patch | 53 ++ xsa326-4.16-xenstored-08.patch | 326 ++++++++++++ xsa326-4.16-xenstored-09.patch | 244 +++++++++ xsa326-4.16-xenstored-10.patch | 88 ++++ xsa326-4.16-xenstored-11.patch | 280 ++++++++++ xsa326-4.16-xenstored-12.patch | 76 +++ xsa326-4.16-xenstored-13.patch | 89 ++++ xsa326-4.16-xenstored-14.patch | 334 ++++++++++++ xsa326-4.16-xenstored-15.patch | 54 ++ xsa326-4.16-xenstored-16.patch | 238 +++++++++ xsa412-4.16.patch | 245 +++++++++ xsa414.patch | 112 ++++ xsa415.patch | 134 +++++ xsa416-4.16.patch | 704 ++++++++++++++++++++++++++ xsa417.patch | 135 +++++ xsa418-4.16-01.patch | 119 +++++ xsa418-4.16-02.patch | 103 ++++ xsa418-4.16-03.patch | 243 +++++++++ xsa418-4.16-04.patch | 108 ++++ xsa418-4.16-05.patch | 164 ++++++ xsa418-4.16-06.patch | 174 +++++++ xsa418-4.16-07.patch | 163 ++++++ xsa419-oxenstored.patch | 85 ++++ xsa419-xenstored-01.patch | 289 +++++++++++ xsa419-xenstored-02.patch | 93 ++++ xsa419-xenstored-03.patch | 44 ++ xsa420.patch | 68 +++ xsa421-01.patch | 40 ++ xsa421-02.patch | 401 +++++++++++++++ 44 files changed, 8263 insertions(+), 1 deletion(-) create mode 100644 xsa326-4.16-oxenstored-01.patch create mode 100644 xsa326-4.16-oxenstored-02.patch create mode 100644 xsa326-4.16-oxenstored-03.patch create mode 100644 xsa326-4.16-oxenstored-04.patch create mode 100644 xsa326-4.16-oxenstored-05.patch create mode 100644 xsa326-4.16-oxenstored-06.patch create mode 100644 xsa326-4.16-oxenstored-07.patch create mode 100644 xsa326-4.16-oxenstored-08.patch create mode 100644 xsa326-4.16-xenstored-01.patch create mode 100644 xsa326-4.16-xenstored-02.patch create mode 100644 xsa326-4.16-xenstored-03.patch create mode 100644 xsa326-4.16-xenstored-04.patch create mode 100644 xsa326-4.16-xenstored-05.patch create mode 100644 xsa326-4.16-xenstored-06.patch create mode 100644 xsa326-4.16-xenstored-07.patch create mode 100644 xsa326-4.16-xenstored-08.patch create mode 100644 xsa326-4.16-xenstored-09.patch create mode 100644 xsa326-4.16-xenstored-10.patch create mode 100644 xsa326-4.16-xenstored-11.patch create mode 100644 xsa326-4.16-xenstored-12.patch create mode 100644 xsa326-4.16-xenstored-13.patch create mode 100644 xsa326-4.16-xenstored-14.patch create mode 100644 xsa326-4.16-xenstored-15.patch create mode 100644 xsa326-4.16-xenstored-16.patch create mode 100644 xsa412-4.16.patch create mode 100644 xsa414.patch create mode 100644 xsa415.patch create mode 100644 xsa416-4.16.patch create mode 100644 xsa417.patch create mode 100644 xsa418-4.16-01.patch create mode 100644 xsa418-4.16-02.patch create mode 100644 xsa418-4.16-03.patch create mode 100644 xsa418-4.16-04.patch create mode 100644 xsa418-4.16-05.patch create mode 100644 xsa418-4.16-06.patch create mode 100644 xsa418-4.16-07.patch create mode 100644 xsa419-oxenstored.patch create mode 100644 xsa419-xenstored-01.patch create mode 100644 xsa419-xenstored-02.patch create mode 100644 xsa419-xenstored-03.patch create mode 100644 xsa420.patch create mode 100644 xsa421-01.patch create mode 100644 xsa421-02.patch diff --git a/xen.spec b/xen.spec index 99e4234..db387f5 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.16.2 -Release: 2%{?dist} +Release: 3%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -126,6 +126,49 @@ Patch58: xsa409-4.13-0002-xen-arm-Construct-the-P2M-pages-pool-for-guests.patch Patch59: xsa409-4.13-0003-xen-arm-libxl-Implement-XEN_DOMCTL_shadow_op-for-Arm.patch Patch60: xsa409-4.13-0004-xen-arm-Allocate-and-free-P2M-pages-from-the-P2M-poo.patch Patch61: xsa411.patch +Patch62: xsa412-4.16.patch +Patch63: xsa414.patch +Patch64: xsa415.patch +Patch65: xsa326-4.16-oxenstored-01.patch +Patch66: xsa326-4.16-oxenstored-02.patch +Patch67: xsa326-4.16-oxenstored-03.patch +Patch68: xsa326-4.16-oxenstored-04.patch +Patch69: xsa326-4.16-oxenstored-05.patch +Patch70: xsa326-4.16-oxenstored-06.patch +Patch71: xsa326-4.16-oxenstored-07.patch +Patch72: xsa326-4.16-oxenstored-08.patch +Patch73: xsa326-4.16-xenstored-01.patch +Patch74: xsa326-4.16-xenstored-02.patch +Patch75: xsa326-4.16-xenstored-03.patch +Patch76: xsa326-4.16-xenstored-04.patch +Patch77: xsa326-4.16-xenstored-05.patch +Patch78: xsa326-4.16-xenstored-06.patch +Patch79: xsa326-4.16-xenstored-07.patch +Patch80: xsa326-4.16-xenstored-08.patch +Patch81: xsa326-4.16-xenstored-09.patch +Patch82: xsa326-4.16-xenstored-10.patch +Patch83: xsa326-4.16-xenstored-11.patch +Patch84: xsa326-4.16-xenstored-12.patch +Patch85: xsa326-4.16-xenstored-13.patch +Patch86: xsa326-4.16-xenstored-14.patch +Patch87: xsa326-4.16-xenstored-15.patch +Patch88: xsa326-4.16-xenstored-16.patch +Patch89: xsa416-4.16.patch +Patch90: xsa417.patch +Patch91: xsa418-4.16-01.patch +Patch92: xsa418-4.16-02.patch +Patch93: xsa418-4.16-03.patch +Patch94: xsa418-4.16-04.patch +Patch95: xsa418-4.16-05.patch +Patch96: xsa418-4.16-06.patch +Patch97: xsa418-4.16-07.patch +Patch98: xsa419-oxenstored.patch +Patch99: xsa419-xenstored-01.patch +Patch100: xsa419-xenstored-02.patch +Patch101: xsa419-xenstored-03.patch +Patch102: xsa420.patch +Patch103: xsa421-01.patch +Patch104: xsa421-02.patch %if %build_qemutrad @@ -353,6 +396,49 @@ manage Xen virtual machines. %patch59 -p1 %patch60 -p1 %patch61 -p1 +%patch62 -p1 +%patch63 -p1 +%patch64 -p1 +%patch65 -p1 +%patch66 -p1 +%patch67 -p1 +%patch68 -p1 +%patch69 -p1 +%patch70 -p1 +%patch71 -p1 +%patch72 -p1 +%patch73 -p1 +%patch74 -p1 +%patch75 -p1 +%patch76 -p1 +%patch77 -p1 +%patch78 -p1 +%patch79 -p1 +%patch80 -p1 +%patch81 -p1 +%patch82 -p1 +%patch83 -p1 +%patch84 -p1 +%patch85 -p1 +%patch86 -p1 +%patch87 -p1 +%patch88 -p1 +%patch89 -p1 +%patch90 -p1 +%patch91 -p1 +%patch92 -p1 +%patch93 -p1 +%patch94 -p1 +%patch95 -p1 +%patch96 -p1 +%patch97 -p1 +%patch98 -p1 +%patch99 -p1 +%patch100 -p1 +%patch101 -p1 +%patch102 -p1 +%patch103 -p1 +%patch104 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -968,6 +1054,26 @@ fi %endif %changelog +* Tue Nov 01 2022 Michael Young - 4.16.2-3 +- x86: unintended memory sharing between guests [XSA-412, CVE-2022-42327] +- Xenstore: Guests can crash xenstored [XSA-414, CVE-2022-42309] +- Xenstore: Guests can create orphaned Xenstore nodes [XSA-415, + CVE-2022-42310] +- Xenstore: guests can let run xenstored out of memory [XSA-326, + CVE-2022-42311, CVE-2022-42312, CVE-2022-42313, CVE-2022-42314, + CVE-2022-42315, CVE-2022-42316, CVE-2022-42317, CVE-2022-42318] +- Xenstore: Guests can cause Xenstore to not free temporary memory + [XSA-416, CVE-2022-42319] +- Xenstore: Guests can get access to Xenstore nodes of deleted domains + [XSA-417, CVE-2022-42320] +- Xenstore: Guests can crash xenstored via exhausting the stack + [XSA-418, CVE-2022-42321] +- Xenstore: Cooperating guests can create arbitrary numbers of nodes + [XSA-419, CVE-2022-42322, CVE-2022-42323] +- Oxenstored 32->31 bit integer truncation issues [XSA-420, CVE-2022-42324] +- Xenstore: Guests can create arbitrary number of nodes via transactions + [XSA-421, CVE-2022-42325, CVE-2022-42326] + * Fri Oct 14 2022 Michael Young - 4.16.2-2 - Arm: unbounded memory consumption for 2nd-level page tables [XSA-409, CVE-2022-33747] (#2135268) diff --git a/xsa326-4.16-oxenstored-01.patch b/xsa326-4.16-oxenstored-01.patch new file mode 100644 index 0000000..848a5d0 --- /dev/null +++ b/xsa326-4.16-oxenstored-01.patch @@ -0,0 +1,55 @@ +From 8d6bb4ac40619877130533b11655829101b31d04 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Date: Wed, 12 Oct 2022 19:13:01 +0100 +Subject: tools/ocaml/xenstored: Synchronise defaults with oxenstore.conf.in +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +We currently have 2 different set of defaults in upstream Xen git tree: +* defined in the source code, only used if there is no config file +* defined in the oxenstored.conf.in upstream Xen + +An oxenstored.conf file is not mandatory, and if missing, maxrequests in +particular has an unsafe default. + +Resync the defaults from oxenstored.conf.in into the source code. + +This is part of XSA-326 / CVE-2022-42316. + +Reported-by: Julien Grall +Signed-off-by: Edwin Török +Acked-by: Christian Lindig + +diff --git a/tools/ocaml/xenstored/define.ml b/tools/ocaml/xenstored/define.ml +index ebe18b8e312c..6b06f808595b 100644 +--- a/tools/ocaml/xenstored/define.ml ++++ b/tools/ocaml/xenstored/define.ml +@@ -21,9 +21,9 @@ let xs_daemon_socket = Paths.xen_run_stored ^ "/socket" + + let default_config_dir = Paths.xen_config_dir + +-let maxwatch = ref (50) +-let maxtransaction = ref (20) +-let maxrequests = ref (-1) (* maximum requests per transaction *) ++let maxwatch = ref (100) ++let maxtransaction = ref (10) ++let maxrequests = ref (1024) (* maximum requests per transaction *) + + let conflict_burst_limit = ref 5.0 + let conflict_max_history_seconds = ref 0.05 +diff --git a/tools/ocaml/xenstored/quota.ml b/tools/ocaml/xenstored/quota.ml +index abcac912805a..6e3d6401ae89 100644 +--- a/tools/ocaml/xenstored/quota.ml ++++ b/tools/ocaml/xenstored/quota.ml +@@ -20,8 +20,8 @@ exception Transaction_opened + + let warn fmt = Logging.warn "quota" fmt + let activate = ref true +-let maxent = ref (10000) +-let maxsize = ref (4096) ++let maxent = ref (1000) ++let maxsize = ref (2048) + + type t = { + maxent: int; (* max entities per domU *) diff --git a/xsa326-4.16-oxenstored-02.patch b/xsa326-4.16-oxenstored-02.patch new file mode 100644 index 0000000..7680efb --- /dev/null +++ b/xsa326-4.16-oxenstored-02.patch @@ -0,0 +1,94 @@ +From 78d5af44ab13bb18c87b6ad75e505bd374379cb3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Date: Thu, 28 Jul 2022 17:08:15 +0100 +Subject: tools/ocaml/xenstored: Check for maxrequests before performing + operations +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Previously we'd perform the operation, record the updated tree in the +transaction record, then try to insert a watchop path and the reply packet. + +If we exceeded max requests we would've returned EQUOTA, but still: +* have performed the operation on the transaction's tree +* have recorded the watchop, making this queue effectively unbounded + +It is better if we check whether we'd have room to store the operation before +performing the transaction, and raise EQUOTA there. Then the transaction +record won't grow. + +This is part of XSA-326 / CVE-2022-42317. + +Reported-by: Julien Grall +Signed-off-by: Edwin Török +Acked-by: Christian Lindig + +diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml +index 27790d4a5c41..dd58e6979cf9 100644 +--- a/tools/ocaml/xenstored/process.ml ++++ b/tools/ocaml/xenstored/process.ml +@@ -389,6 +389,7 @@ let input_handle_error ~cons ~doms ~fct ~con ~t ~req = + let reply_error e = + Packet.Error e in + try ++ Transaction.check_quota_exn ~perm:(Connection.get_perm con) t; + fct con t doms cons req.Packet.data + with + | Define.Invalid_path -> reply_error "EINVAL" +@@ -681,9 +682,10 @@ let process_packet ~store ~cons ~doms ~con ~req = + in + + let response = try ++ Transaction.check_quota_exn ~perm:(Connection.get_perm con) t; + if tid <> Transaction.none then + (* Remember the request and response for this operation in case we need to replay the transaction *) +- Transaction.add_operation ~perm:(Connection.get_perm con) t req response; ++ Transaction.add_operation t req response; + response + with Quota.Limit_reached -> + Packet.Error "EQUOTA" +diff --git a/tools/ocaml/xenstored/transaction.ml b/tools/ocaml/xenstored/transaction.ml +index 17b1bdf2eaf9..294143e2335b 100644 +--- a/tools/ocaml/xenstored/transaction.ml ++++ b/tools/ocaml/xenstored/transaction.ml +@@ -85,6 +85,7 @@ type t = { + oldroot: Store.Node.t; + mutable paths: (Xenbus.Xb.Op.operation * Store.Path.t) list; + mutable operations: (Packet.request * Packet.response) list; ++ mutable quota_reached: bool; + mutable read_lowpath: Store.Path.t option; + mutable write_lowpath: Store.Path.t option; + } +@@ -127,6 +128,7 @@ let make ?(internal=false) id store = + oldroot = Store.get_root store; + paths = []; + operations = []; ++ quota_reached = false; + read_lowpath = None; + write_lowpath = None; + } in +@@ -143,13 +145,19 @@ let get_root t = Store.get_root t.store + + let is_read_only t = t.paths = [] + let add_wop t ty path = t.paths <- (ty, path) :: t.paths +-let add_operation ~perm t request response = ++let get_operations t = List.rev t.operations ++ ++let check_quota_exn ~perm t = + if !Define.maxrequests >= 0 + && not (Perms.Connection.is_dom0 perm) +- && List.length t.operations >= !Define.maxrequests +- then raise Quota.Limit_reached; ++ && (t.quota_reached || List.length t.operations >= !Define.maxrequests) ++ then begin ++ t.quota_reached <- true; ++ raise Quota.Limit_reached; ++ end ++ ++let add_operation t request response = + t.operations <- (request, response) :: t.operations +-let get_operations t = List.rev t.operations + let set_read_lowpath t path = t.read_lowpath <- get_lowest path t.read_lowpath + let set_write_lowpath t path = t.write_lowpath <- get_lowest path t.write_lowpath + diff --git a/xsa326-4.16-oxenstored-03.patch b/xsa326-4.16-oxenstored-03.patch new file mode 100644 index 0000000..0b42411 --- /dev/null +++ b/xsa326-4.16-oxenstored-03.patch @@ -0,0 +1,119 @@ +From 600c45e49c2060e077c06ab19078da89aa8e2e08 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Date: Wed, 12 Oct 2022 19:13:07 +0100 +Subject: tools/ocaml: GC parameter tuning +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +By default the OCaml garbage collector would return memory to the OS only +after unused memory is 5x live memory. Tweak this to 120% instead, which +would match the major GC speed. + +This is part of XSA-326. + +Reported-by: Julien Grall +Signed-off-by: Edwin Török +Acked-by: Christian Lindig + +diff --git a/tools/ocaml/xenstored/define.ml b/tools/ocaml/xenstored/define.ml +index 6b06f808595b..ba63a8147e09 100644 +--- a/tools/ocaml/xenstored/define.ml ++++ b/tools/ocaml/xenstored/define.ml +@@ -25,6 +25,7 @@ let maxwatch = ref (100) + let maxtransaction = ref (10) + let maxrequests = ref (1024) (* maximum requests per transaction *) + ++let gc_max_overhead = ref 120 (* 120% see comment in xenstored.ml *) + let conflict_burst_limit = ref 5.0 + let conflict_max_history_seconds = ref 0.05 + let conflict_rate_limit_is_aggregate = ref true +diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml +index d44ae673c42a..3b57ad016dfb 100644 +--- a/tools/ocaml/xenstored/xenstored.ml ++++ b/tools/ocaml/xenstored/xenstored.ml +@@ -104,6 +104,7 @@ let parse_config filename = + ("quota-maxsize", Config.Set_int Quota.maxsize); + ("quota-maxrequests", Config.Set_int Define.maxrequests); + ("quota-path-max", Config.Set_int Define.path_max); ++ ("gc-max-overhead", Config.Set_int Define.gc_max_overhead); + ("test-eagain", Config.Set_bool Transaction.test_eagain); + ("persistent", Config.Set_bool Disk.enable); + ("xenstored-log-file", Config.String Logging.set_xenstored_log_destination); +@@ -265,6 +266,67 @@ let to_file store cons fds file = + (fun () -> close_out channel) + end + ++(* ++ By default OCaml's GC only returns memory to the OS when it exceeds a ++ configurable 'max overhead' setting. ++ The default is 500%, that is 5/6th of the OCaml heap needs to be free ++ and only 1/6th live for a compaction to be triggerred that would ++ release memory back to the OS. ++ If the limit is not hit then the OCaml process can reuse that memory ++ for its own purposes, but other processes won't be able to use it. ++ ++ There is also a 'space overhead' setting that controls how much work ++ each major GC slice does, and by default aims at having no more than ++ 80% or 120% (depending on version) garbage values compared to live ++ values. ++ This doesn't have as much relevance to memory returned to the OS as ++ long as space_overhead <= max_overhead, because compaction is only ++ triggerred at the end of major GC cycles. ++ ++ The defaults are too large once the program starts using ~100MiB of ++ memory, at which point ~500MiB would be unavailable to other processes ++ (which would be fine if this was the main process in this VM, but it is ++ not). ++ ++ Max overhead can also be set to 0, however this is for testing purposes ++ only (setting it lower than 'space overhead' wouldn't help because the ++ major GC wouldn't run fast enough, and compaction does have a ++ performance cost: we can only compact contiguous regions, so memory has ++ to be moved around). ++ ++ Max overhead controls how often the heap is compacted, which is useful ++ if there are burst of activity followed by long periods of idle state, ++ or if a domain quits, etc. Compaction returns memory to the OS. ++ ++ wasted = live * space_overhead / 100 ++ ++ For globally overriding the GC settings one can use OCAMLRUNPARAM, ++ however we provide a config file override to be consistent with other ++ oxenstored settings. ++ ++ One might want to dynamically adjust the overhead setting based on used ++ memory, i.e. to use a fixed upper bound in bytes, not percentage. However ++ measurements show that such adjustments increase GC overhead massively, ++ while still not guaranteeing that memory is returned any more quickly ++ than with a percentage based setting. ++ ++ The allocation policy could also be tweaked, e.g. first fit would reduce ++ fragmentation and thus memory usage, but the documentation warns that it ++ can be sensibly slower, and indeed one of our own testcases can trigger ++ such a corner case where it is multiple times slower, so it is best to keep ++ the default allocation policy (next-fit/best-fit depending on version). ++ ++ There are other tweaks that can be attempted in the future, e.g. setting ++ 'ulimit -v' to 75% of RAM, however getting the kernel to actually return ++ NULL from allocations is difficult even with that setting, and without a ++ NULL the emergency GC won't be triggerred. ++ Perhaps cgroup limits could help, but for now tweak the safest only. ++*) ++ ++let tweak_gc () = ++ Gc.set { (Gc.get ()) with Gc.max_overhead = !Define.gc_max_overhead } ++ ++ + let _ = + let cf = do_argv in + let pidfile = +@@ -274,6 +336,8 @@ let _ = + default_pidfile + in + ++ tweak_gc (); ++ + (try + Unixext.mkdir_rec (Filename.dirname pidfile) 0o755 + with _ -> diff --git a/xsa326-4.16-oxenstored-04.patch b/xsa326-4.16-oxenstored-04.patch new file mode 100644 index 0000000..0f2534b --- /dev/null +++ b/xsa326-4.16-oxenstored-04.patch @@ -0,0 +1,85 @@ +From fd6d9cd3d20e496bdbf3e0a07354f65de0bcf4ae Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Date: Fri, 29 Jul 2022 18:53:29 +0100 +Subject: tools/ocaml/libs/xb: hide type of Xb.t +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Hiding the type will make it easier to change the implementation +in the future without breaking code that relies on it. + +No functional change. + +Signed-off-by: Edwin Török +Acked-by: Christian Lindig +(cherry picked from commit 7ade30a1451734d041363c750a65d322e25b47ba) + +Reported-by: Julien Grall +diff --git a/tools/ocaml/libs/xb/xb.ml b/tools/ocaml/libs/xb/xb.ml +index 104d319d7747..8404ddd8a682 100644 +--- a/tools/ocaml/libs/xb/xb.ml ++++ b/tools/ocaml/libs/xb/xb.ml +@@ -196,6 +196,9 @@ let peek_output con = Queue.peek con.pkt_out + let input_len con = Queue.length con.pkt_in + let has_in_packet con = Queue.length con.pkt_in > 0 + let get_in_packet con = Queue.pop con.pkt_in ++let has_partial_input con = match con.partial_in with ++ | HaveHdr _ -> true ++ | NoHdr (n, _) -> n < Partial.header_size () + let has_more_input con = + match con.backend with + | Fd _ -> false +diff --git a/tools/ocaml/libs/xb/xb.mli b/tools/ocaml/libs/xb/xb.mli +index 3a00da6cddc1..794e35bb343e 100644 +--- a/tools/ocaml/libs/xb/xb.mli ++++ b/tools/ocaml/libs/xb/xb.mli +@@ -66,13 +66,7 @@ type backend_mmap = { + type backend_fd = { fd : Unix.file_descr; } + type backend = Fd of backend_fd | Xenmmap of backend_mmap + type partial_buf = HaveHdr of Partial.pkt | NoHdr of int * bytes +-type t = { +- backend : backend; +- pkt_in : Packet.t Queue.t; +- pkt_out : Packet.t Queue.t; +- mutable partial_in : partial_buf; +- mutable partial_out : string; +-} ++type t + val init_partial_in : unit -> partial_buf + val reconnect : t -> unit + val queue : t -> Packet.t -> unit +@@ -97,6 +91,7 @@ val has_output : t -> bool + val peek_output : t -> Packet.t + val input_len : t -> int + val has_in_packet : t -> bool ++val has_partial_input : t -> bool + val get_in_packet : t -> Packet.t + val has_more_input : t -> bool + val is_selectable : t -> bool +diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/connection.ml +index 65f99ea6f28a..38b47363a173 100644 +--- a/tools/ocaml/xenstored/connection.ml ++++ b/tools/ocaml/xenstored/connection.ml +@@ -125,9 +125,7 @@ let get_perm con = + let set_target con target_domid = + con.perm <- Perms.Connection.set_target (get_perm con) ~perms:[Perms.READ; Perms.WRITE] target_domid + +-let is_backend_mmap con = match con.xb.Xenbus.Xb.backend with +- | Xenbus.Xb.Xenmmap _ -> true +- | _ -> false ++let is_backend_mmap con = Xenbus.Xb.is_mmap con.xb + + let send_reply con tid rid ty data = + if (String.length data) > xenstore_payload_max && (is_backend_mmap con) then +@@ -280,9 +278,7 @@ let get_transaction con tid = + + let do_input con = Xenbus.Xb.input con.xb + let has_input con = Xenbus.Xb.has_in_packet con.xb +-let has_partial_input con = match con.xb.Xenbus.Xb.partial_in with +- | HaveHdr _ -> true +- | NoHdr (n, _) -> n < Xenbus.Partial.header_size () ++let has_partial_input con = Xenbus.Xb.has_partial_input con.xb + let pop_in con = Xenbus.Xb.get_in_packet con.xb + let has_more_input con = Xenbus.Xb.has_more_input con.xb + diff --git a/xsa326-4.16-oxenstored-05.patch b/xsa326-4.16-oxenstored-05.patch new file mode 100644 index 0000000..8b253a9 --- /dev/null +++ b/xsa326-4.16-oxenstored-05.patch @@ -0,0 +1,214 @@ +From f13fe5903361953e4ccf8602b9c8df7e64568d55 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Date: Wed, 12 Oct 2022 19:13:02 +0100 +Subject: tools/ocaml: Change Xb.input to return Packet.t option +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The queue here would only ever hold at most one element. This will simplify +follow-up patches. + +This is part of XSA-326. + +Reported-by: Julien Grall +Signed-off-by: Edwin Török +Acked-by: Christian Lindig + +diff --git a/tools/ocaml/libs/xb/xb.ml b/tools/ocaml/libs/xb/xb.ml +index 8404ddd8a682..165fd4a1edf4 100644 +--- a/tools/ocaml/libs/xb/xb.ml ++++ b/tools/ocaml/libs/xb/xb.ml +@@ -45,7 +45,6 @@ type partial_buf = HaveHdr of Partial.pkt | NoHdr of int * bytes + type t = + { + backend: backend; +- pkt_in: Packet.t Queue.t; + pkt_out: Packet.t Queue.t; + mutable partial_in: partial_buf; + mutable partial_out: string; +@@ -62,7 +61,6 @@ let reconnect t = match t.backend with + Xs_ring.close backend.mmap; + backend.eventchn_notify (); + (* Clear our old connection state *) +- Queue.clear t.pkt_in; + Queue.clear t.pkt_out; + t.partial_in <- init_partial_in (); + t.partial_out <- "" +@@ -124,7 +122,6 @@ let output con = + + (* NB: can throw Reconnect *) + let input con = +- let newpacket = ref false in + let to_read = + match con.partial_in with + | HaveHdr partial_pkt -> Partial.to_complete partial_pkt +@@ -143,21 +140,19 @@ let input con = + if Partial.to_complete partial_pkt = 0 then ( + let pkt = Packet.of_partialpkt partial_pkt in + con.partial_in <- init_partial_in (); +- Queue.push pkt con.pkt_in; +- newpacket := true +- ) ++ Some pkt ++ ) else None + | NoHdr (i, buf) -> + (* we complete the partial header *) + if sz > 0 then + Bytes.blit b 0 buf (Partial.header_size () - i) sz; + con.partial_in <- if sz = i then +- HaveHdr (Partial.of_string (Bytes.to_string buf)) else NoHdr (i - sz, buf) +- ); +- !newpacket ++ HaveHdr (Partial.of_string (Bytes.to_string buf)) else NoHdr (i - sz, buf); ++ None ++ ) + + let newcon backend = { + backend = backend; +- pkt_in = Queue.create (); + pkt_out = Queue.create (); + partial_in = init_partial_in (); + partial_out = ""; +@@ -193,9 +188,6 @@ let has_output con = has_new_output con || has_old_output con + + let peek_output con = Queue.peek con.pkt_out + +-let input_len con = Queue.length con.pkt_in +-let has_in_packet con = Queue.length con.pkt_in > 0 +-let get_in_packet con = Queue.pop con.pkt_in + let has_partial_input con = match con.partial_in with + | HaveHdr _ -> true + | NoHdr (n, _) -> n < Partial.header_size () +diff --git a/tools/ocaml/libs/xb/xb.mli b/tools/ocaml/libs/xb/xb.mli +index 794e35bb343e..91c682162cea 100644 +--- a/tools/ocaml/libs/xb/xb.mli ++++ b/tools/ocaml/libs/xb/xb.mli +@@ -77,7 +77,7 @@ val write_fd : backend_fd -> 'a -> string -> int -> int + val write_mmap : backend_mmap -> 'a -> string -> int -> int + val write : t -> string -> int -> int + val output : t -> bool +-val input : t -> bool ++val input : t -> Packet.t option + val newcon : backend -> t + val open_fd : Unix.file_descr -> t + val open_mmap : Xenmmap.mmap_interface -> (unit -> unit) -> t +@@ -89,10 +89,7 @@ val has_new_output : t -> bool + val has_old_output : t -> bool + val has_output : t -> bool + val peek_output : t -> Packet.t +-val input_len : t -> int +-val has_in_packet : t -> bool + val has_partial_input : t -> bool +-val get_in_packet : t -> Packet.t + val has_more_input : t -> bool + val is_selectable : t -> bool + val get_fd : t -> Unix.file_descr +diff --git a/tools/ocaml/libs/xs/xsraw.ml b/tools/ocaml/libs/xs/xsraw.ml +index d982fb24dbb1..451f8b38dbcc 100644 +--- a/tools/ocaml/libs/xs/xsraw.ml ++++ b/tools/ocaml/libs/xs/xsraw.ml +@@ -94,26 +94,18 @@ let pkt_send con = + done + + (* receive one packet - can sleep *) +-let pkt_recv con = +- let workdone = ref false in +- while not !workdone +- do +- workdone := Xb.input con.xb +- done; +- Xb.get_in_packet con.xb ++let rec pkt_recv con = ++ match Xb.input con.xb with ++ | Some packet -> packet ++ | None -> pkt_recv con + + let pkt_recv_timeout con timeout = + let fd = Xb.get_fd con.xb in + let r, _, _ = Unix.select [ fd ] [] [] timeout in + if r = [] then + true, None +- else ( +- let workdone = Xb.input con.xb in +- if workdone then +- false, (Some (Xb.get_in_packet con.xb)) +- else +- false, None +- ) ++ else ++ false, Xb.input con.xb + + let queue_watchevent con data = + let ls = split_string ~limit:2 '\000' data in +diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/connection.ml +index 38b47363a173..cc20e047d2b9 100644 +--- a/tools/ocaml/xenstored/connection.ml ++++ b/tools/ocaml/xenstored/connection.ml +@@ -277,9 +277,7 @@ let get_transaction con tid = + Hashtbl.find con.transactions tid + + let do_input con = Xenbus.Xb.input con.xb +-let has_input con = Xenbus.Xb.has_in_packet con.xb + let has_partial_input con = Xenbus.Xb.has_partial_input con.xb +-let pop_in con = Xenbus.Xb.get_in_packet con.xb + let has_more_input con = Xenbus.Xb.has_more_input con.xb + + let has_output con = Xenbus.Xb.has_output con.xb +@@ -307,7 +305,7 @@ let is_bad con = match con.dom with None -> false | Some dom -> Domain.is_bad_do + Restrictions below can be relaxed once xenstored learns to dump more + of its live state in a safe way *) + let has_extra_connection_data con = +- let has_in = has_input con || has_partial_input con in ++ let has_in = has_partial_input con in + let has_out = has_output con in + let has_socket = con.dom = None in + let has_nondefault_perms = make_perm con.dom <> con.perm in +diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml +index 6a3435c265d3..2d67456a2aa0 100644 +--- a/tools/ocaml/xenstored/process.ml ++++ b/tools/ocaml/xenstored/process.ml +@@ -195,10 +195,9 @@ let parse_live_update args = + | _ when Unix.gettimeofday () < t.deadline -> false + | l -> + warn "timeout reached: have to wait, migrate or shutdown %d domains:" (List.length l); +- let msgs = List.rev_map (fun con -> Printf.sprintf "%s: %d tx, in: %b, out: %b, perm: %s" ++ let msgs = List.rev_map (fun con -> Printf.sprintf "%s: %d tx, out: %b, perm: %s" + (Connection.get_domstr con) + (Connection.number_of_transactions con) +- (Connection.has_input con) + (Connection.has_output con) + (Connection.get_perm con |> Perms.Connection.to_string) + ) l in +@@ -705,16 +704,17 @@ let do_input store cons doms con = + info "%s requests a reconnect" (Connection.get_domstr con); + History.reconnect con; + info "%s reconnection complete" (Connection.get_domstr con); +- false ++ None + | Failure exp -> + error "caught exception %s" exp; + error "got a bad client %s" (sprintf "%-8s" (Connection.get_domstr con)); + Connection.mark_as_bad con; +- false ++ None + in + +- if newpacket then ( +- let packet = Connection.pop_in con in ++ match newpacket with ++ | None -> () ++ | Some packet -> + let tid, rid, ty, data = Xenbus.Xb.Packet.unpack packet in + let req = {Packet.tid=tid; Packet.rid=rid; Packet.ty=ty; Packet.data=data} in + +@@ -724,8 +724,7 @@ let do_input store cons doms con = + (Xenbus.Xb.Op.to_string ty) (sanitize_data data); *) + process_packet ~store ~cons ~doms ~con ~req; + write_access_log ~ty ~tid ~con:(Connection.get_domstr con) ~data; +- Connection.incr_ops con; +- ) ++ Connection.incr_ops con + + let do_output _store _cons _doms con = + if Connection.has_output con then ( diff --git a/xsa326-4.16-oxenstored-06.patch b/xsa326-4.16-oxenstored-06.patch new file mode 100644 index 0000000..c8ebc34 --- /dev/null +++ b/xsa326-4.16-oxenstored-06.patch @@ -0,0 +1,127 @@ +From 2440a8b69a118fe14e73eb6cab4a050922866f1a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Date: Wed, 12 Oct 2022 19:13:03 +0100 +Subject: tools/ocaml/xb: Add BoundedQueue +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Ensures we cannot store more than [capacity] elements in a [Queue]. Replacing +all Queue with this module will then ensure at compile time that all Queues +are correctly bound checked. + +Each element in the queue has a class with its own limits. This, in a +subsequent change, will ensure that command responses can proceed during a +flood of watch events. + +No functional change. + +This is part of XSA-326. + +Reported-by: Julien Grall +Signed-off-by: Edwin Török +Acked-by: Christian Lindig + +diff --git a/tools/ocaml/libs/xb/xb.ml b/tools/ocaml/libs/xb/xb.ml +index 165fd4a1edf4..4197a3888a68 100644 +--- a/tools/ocaml/libs/xb/xb.ml ++++ b/tools/ocaml/libs/xb/xb.ml +@@ -17,6 +17,98 @@ + module Op = struct include Op end + module Packet = struct include Packet end + ++module BoundedQueue : sig ++ type ('a, 'b) t ++ ++ (** [create ~capacity ~classify ~limit] creates a queue with maximum [capacity] elements. ++ This is burst capacity, each element is further classified according to [classify], ++ and each class can have its own [limit]. ++ [capacity] is enforced as an overall limit. ++ The [limit] can be dynamic, and can be smaller than the number of elements already queued of that class, ++ in which case those elements are considered to use "burst capacity". ++ *) ++ val create: capacity:int -> classify:('a -> 'b) -> limit:('b -> int) -> ('a, 'b) t ++ ++ (** [clear q] discards all elements from [q] *) ++ val clear: ('a, 'b) t -> unit ++ ++ (** [can_push q] when [length q < capacity]. *) ++ val can_push: ('a, 'b) t -> 'b -> bool ++ ++ (** [push e q] adds [e] at the end of queue [q] if [can_push q], or returns [None]. *) ++ val push: 'a -> ('a, 'b) t -> unit option ++ ++ (** [pop q] removes and returns first element in [q], or raises [Queue.Empty]. *) ++ val pop: ('a, 'b) t -> 'a ++ ++ (** [peek q] returns the first element in [q], or raises [Queue.Empty]. *) ++ val peek : ('a, 'b) t -> 'a ++ ++ (** [length q] returns the current number of elements in [q] *) ++ val length: ('a, 'b) t -> int ++ ++ (** [debug string_of_class q] prints queue usage statistics in an unspecified internal format. *) ++ val debug: ('b -> string) -> (_, 'b) t -> string ++end = struct ++ type ('a, 'b) t = ++ { q: 'a Queue.t ++ ; capacity: int ++ ; classify: 'a -> 'b ++ ; limit: 'b -> int ++ ; class_count: ('b, int) Hashtbl.t ++ } ++ ++ let create ~capacity ~classify ~limit = ++ { capacity; q = Queue.create (); classify; limit; class_count = Hashtbl.create 3 } ++ ++ let get_count t classification = try Hashtbl.find t.class_count classification with Not_found -> 0 ++ ++ let can_push_internal t classification class_count = ++ Queue.length t.q < t.capacity && class_count < t.limit classification ++ ++ let ok = Some () ++ ++ let push e t = ++ let classification = t.classify e in ++ let class_count = get_count t classification in ++ if can_push_internal t classification class_count then begin ++ Queue.push e t.q; ++ Hashtbl.replace t.class_count classification (class_count + 1); ++ ok ++ end ++ else ++ None ++ ++ let can_push t classification = ++ can_push_internal t classification @@ get_count t classification ++ ++ let clear t = ++ Queue.clear t.q; ++ Hashtbl.reset t.class_count ++ ++ let pop t = ++ let e = Queue.pop t.q in ++ let classification = t.classify e in ++ let () = match get_count t classification - 1 with ++ | 0 -> Hashtbl.remove t.class_count classification (* reduces memusage *) ++ | n -> Hashtbl.replace t.class_count classification n ++ in ++ e ++ ++ let peek t = Queue.peek t.q ++ let length t = Queue.length t.q ++ ++ let debug string_of_class t = ++ let b = Buffer.create 128 in ++ Printf.bprintf b "BoundedQueue capacity: %d, used: {" t.capacity; ++ Hashtbl.iter (fun packet_class count -> ++ Printf.bprintf b " %s: %d" (string_of_class packet_class) count ++ ) t.class_count; ++ Printf.bprintf b "}"; ++ Buffer.contents b ++end ++ ++ + exception End_of_file + exception Eagain + exception Noent diff --git a/xsa326-4.16-oxenstored-07.patch b/xsa326-4.16-oxenstored-07.patch new file mode 100644 index 0000000..ef02467 --- /dev/null +++ b/xsa326-4.16-oxenstored-07.patch @@ -0,0 +1,872 @@ +From bc0f05e6f3a3c93c853ceffd1f6d2022dc30fb77 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Date: Wed, 12 Oct 2022 19:13:04 +0100 +Subject: tools/ocaml: Limit maximum in-flight requests / outstanding replies +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Introduce a limit on the number of outstanding reply packets in the xenbus +queue. This limits the number of in-flight requests: when the output queue is +full we'll stop processing inputs until the output queue has room again. + +To avoid a busy loop on the Unix socket we only add it to the watched input +file descriptor set if we'd be able to call `input` on it. Even though Dom0 +is trusted and exempt from quotas a flood of events might cause a backlog +where events are produced faster than daemons in Dom0 can consume them, which +could lead to an unbounded queue size and OOM. + +Therefore the xenbus queue limit must apply to all connections, Dom0 is not +exempt from it, although if everything works correctly it will eventually +catch up. + +This prevents a malicious guest from sending more commands while it has +outstanding watch events or command replies in its input ring. However if it +can cause the generation of watch events by other means (e.g. by Dom0, or +another cooperative guest) and stop reading its own ring then watch events +would've queued up without limit. + +The xenstore protocol doesn't have a back-pressure mechanism, and doesn't +allow dropping watch events. In fact, dropping watch events is known to break +some pieces of normal functionality. This leaves little choice to safely +implement the xenstore protocol without exposing the xenstore daemon to +out-of-memory attacks. + +Implement the fix as pipes with bounded buffers: +* Use a bounded buffer for watch events +* The watch structure will have a bounded receiving pipe of watch events +* The source will have an "overflow" pipe of pending watch events it couldn't + deliver + +Items are queued up on one end and are sent as far along the pipe as possible: + + source domain -> watch -> xenbus of target -> xenstore ring/socket of target + +If the pipe is "full" at any point then back-pressure is applied and we prevent +more items from being queued up. For the source domain this means that we'll +stop accepting new commands as long as its pipe buffer is not empty. + +Before we try to enqueue an item we first check whether it is possible to send +it further down the pipe, by attempting to recursively flush the pipes. This +ensures that we retain the order of events as much as possible. + +We might break causality of watch events if the target domain's queue is full +and we need to start using the watch's queue. This is a breaking change in +the xenstore protocol, but only for domains which are not processing their +incoming ring as expected. + +When a watch is deleted its entire pending queue is dropped (no code is needed +for that, because it is part of the 'watch' type). + +There is a cache of watches that have pending events that we attempt to flush +at every cycle if possible. + +Introduce 3 limits here: +* quota-maxwatchevents on watch event destination: when this is hit the + source will not be allowed to queue up more watch events. +* quota-maxoustanding which is the number of responses not read from the ring: + once exceeded, no more inputs are processed until all outstanding replies + are consumed by the client. +* overflow queue on the watch event source: all watches that cannot be stored + on destination are queued up here, a single command can trigger multiple + watches (e.g. due to recursion). + +The overflow queue currently doesn't have an upper bound, it is difficult to +accurately calculate one as it depends on whether you are Dom0 and how many +watches each path has registered and how many watch events you can trigger +with a single command (e.g. a commit). However these events were already +using memory, this just moves them elsewhere, and as long as we correctly +block a domain it shouldn't result in unbounded memory usage. + +Note that Dom0 is not excluded from these checks, it is important that Dom0 is +especially not excluded when it is the source, since there are many ways in +which a guest could trigger Dom0 to send it watch events. + +This should protect against malicious frontends as long as the backend follows +the PV xenstore protocol and only exposes paths needed by the frontend, and +changes those paths at most once as a reaction to guest events, or protocol +state. + +The queue limits are per watch, and per domain-pair, so even if one +communication channel would be "blocked", others would keep working, and the +domain itself won't get blocked as long as it doesn't overflow the queue of +watch events. + +Similarly a malicious backend could cause the frontend to get blocked, but +this watch queue protects the frontend as well as long as it follows the PV +protocol. (Although note that protection against malicious backends is only a +best effort at the moment) + +This is part of XSA-326 / CVE-2022-42318. + +Reported-by: Julien Grall +Signed-off-by: Edwin Török +Acked-by: Christian Lindig + +diff --git a/tools/ocaml/libs/xb/xb.ml b/tools/ocaml/libs/xb/xb.ml +index 4197a3888a68..b292ed7a874d 100644 +--- a/tools/ocaml/libs/xb/xb.ml ++++ b/tools/ocaml/libs/xb/xb.ml +@@ -134,14 +134,44 @@ type backend = Fd of backend_fd | Xenmmap of backend_mmap + + type partial_buf = HaveHdr of Partial.pkt | NoHdr of int * bytes + ++(* ++ separate capacity reservation for replies and watch events: ++ this allows a domain to keep working even when under a constant flood of ++ watch events ++*) ++type capacity = { maxoutstanding: int; maxwatchevents: int } ++ ++module Queue = BoundedQueue ++ ++type packet_class = ++ | CommandReply ++ | Watchevent ++ ++let string_of_packet_class = function ++ | CommandReply -> "command_reply" ++ | Watchevent -> "watch_event" ++ + type t = + { + backend: backend; +- pkt_out: Packet.t Queue.t; ++ pkt_out: (Packet.t, packet_class) Queue.t; + mutable partial_in: partial_buf; + mutable partial_out: string; ++ capacity: capacity + } + ++let to_read con = ++ match con.partial_in with ++ | HaveHdr partial_pkt -> Partial.to_complete partial_pkt ++ | NoHdr (i, _) -> i ++ ++let debug t = ++ Printf.sprintf "XenBus state: partial_in: %d needed, partial_out: %d bytes, pkt_out: %d packets, %s" ++ (to_read t) ++ (String.length t.partial_out) ++ (Queue.length t.pkt_out) ++ (BoundedQueue.debug string_of_packet_class t.pkt_out) ++ + let init_partial_in () = NoHdr + (Partial.header_size (), Bytes.make (Partial.header_size()) '\000') + +@@ -199,7 +229,8 @@ let output con = + let s = if String.length con.partial_out > 0 then + con.partial_out + else if Queue.length con.pkt_out > 0 then +- Packet.to_string (Queue.pop con.pkt_out) ++ let pkt = Queue.pop con.pkt_out in ++ Packet.to_string pkt + else + "" in + (* send data from s, and save the unsent data to partial_out *) +@@ -212,12 +243,15 @@ let output con = + (* after sending one packet, partial is empty *) + con.partial_out = "" + ++(* we can only process an input packet if we're guaranteed to have room ++ to store the response packet *) ++let can_input con = Queue.can_push con.pkt_out CommandReply ++ + (* NB: can throw Reconnect *) + let input con = +- let to_read = +- match con.partial_in with +- | HaveHdr partial_pkt -> Partial.to_complete partial_pkt +- | NoHdr (i, _) -> i in ++ if not (can_input con) then None ++ else ++ let to_read = to_read con in + + (* try to get more data from input stream *) + let b = Bytes.make to_read '\000' in +@@ -243,11 +277,22 @@ let input con = + None + ) + +-let newcon backend = { ++let classify t = ++ match t.Packet.ty with ++ | Op.Watchevent -> Watchevent ++ | _ -> CommandReply ++ ++let newcon ~capacity backend = ++ let limit = function ++ | CommandReply -> capacity.maxoutstanding ++ | Watchevent -> capacity.maxwatchevents ++ in ++ { + backend = backend; +- pkt_out = Queue.create (); ++ pkt_out = Queue.create ~capacity:(capacity.maxoutstanding + capacity.maxwatchevents) ~classify ~limit; + partial_in = init_partial_in (); + partial_out = ""; ++ capacity = capacity; + } + + let open_fd fd = newcon (Fd { fd = fd; }) +diff --git a/tools/ocaml/libs/xb/xb.mli b/tools/ocaml/libs/xb/xb.mli +index 91c682162cea..71b2754ca788 100644 +--- a/tools/ocaml/libs/xb/xb.mli ++++ b/tools/ocaml/libs/xb/xb.mli +@@ -66,10 +66,11 @@ type backend_mmap = { + type backend_fd = { fd : Unix.file_descr; } + type backend = Fd of backend_fd | Xenmmap of backend_mmap + type partial_buf = HaveHdr of Partial.pkt | NoHdr of int * bytes ++type capacity = { maxoutstanding: int; maxwatchevents: int } + type t + val init_partial_in : unit -> partial_buf + val reconnect : t -> unit +-val queue : t -> Packet.t -> unit ++val queue : t -> Packet.t -> unit option + val read_fd : backend_fd -> 'a -> bytes -> int -> int + val read_mmap : backend_mmap -> 'a -> bytes -> int -> int + val read : t -> bytes -> int -> int +@@ -78,13 +79,14 @@ val write_mmap : backend_mmap -> 'a -> string -> int -> int + val write : t -> string -> int -> int + val output : t -> bool + val input : t -> Packet.t option +-val newcon : backend -> t +-val open_fd : Unix.file_descr -> t +-val open_mmap : Xenmmap.mmap_interface -> (unit -> unit) -> t ++val newcon : capacity:capacity -> backend -> t ++val open_fd : Unix.file_descr -> capacity:capacity -> t ++val open_mmap : Xenmmap.mmap_interface -> (unit -> unit) -> capacity:capacity -> t + val close : t -> unit + val is_fd : t -> bool + val is_mmap : t -> bool + val output_len : t -> int ++val can_input: t -> bool + val has_new_output : t -> bool + val has_old_output : t -> bool + val has_output : t -> bool +@@ -93,3 +95,4 @@ val has_partial_input : t -> bool + val has_more_input : t -> bool + val is_selectable : t -> bool + val get_fd : t -> Unix.file_descr ++val debug: t -> string +diff --git a/tools/ocaml/libs/xs/queueop.ml b/tools/ocaml/libs/xs/queueop.ml +index 9ff5bbd529ce..4e532cdaeacb 100644 +--- a/tools/ocaml/libs/xs/queueop.ml ++++ b/tools/ocaml/libs/xs/queueop.ml +@@ -16,9 +16,10 @@ + open Xenbus + + let data_concat ls = (String.concat "\000" ls) ^ "\000" ++let queue con pkt = let r = Xb.queue con pkt in assert (r <> None) + let queue_path ty (tid: int) (path: string) con = + let data = data_concat [ path; ] in +- Xb.queue con (Xb.Packet.create tid 0 ty data) ++ queue con (Xb.Packet.create tid 0 ty data) + + (* operations *) + let directory tid path con = queue_path Xb.Op.Directory tid path con +@@ -27,48 +28,48 @@ let read tid path con = queue_path Xb.Op.Read tid path con + let getperms tid path con = queue_path Xb.Op.Getperms tid path con + + let debug commands con = +- Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Debug (data_concat commands)) ++ queue con (Xb.Packet.create 0 0 Xb.Op.Debug (data_concat commands)) + + let watch path data con = + let data = data_concat [ path; data; ] in +- Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Watch data) ++ queue con (Xb.Packet.create 0 0 Xb.Op.Watch data) + + let unwatch path data con = + let data = data_concat [ path; data; ] in +- Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Unwatch data) ++ queue con (Xb.Packet.create 0 0 Xb.Op.Unwatch data) + + let transaction_start con = +- Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Transaction_start (data_concat [])) ++ queue con (Xb.Packet.create 0 0 Xb.Op.Transaction_start (data_concat [])) + + let transaction_end tid commit con = + let data = data_concat [ (if commit then "T" else "F"); ] in +- Xb.queue con (Xb.Packet.create tid 0 Xb.Op.Transaction_end data) ++ queue con (Xb.Packet.create tid 0 Xb.Op.Transaction_end data) + + let introduce domid mfn port con = + let data = data_concat [ Printf.sprintf "%u" domid; + Printf.sprintf "%nu" mfn; + string_of_int port; ] in +- Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Introduce data) ++ queue con (Xb.Packet.create 0 0 Xb.Op.Introduce data) + + let release domid con = + let data = data_concat [ Printf.sprintf "%u" domid; ] in +- Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Release data) ++ queue con (Xb.Packet.create 0 0 Xb.Op.Release data) + + let resume domid con = + let data = data_concat [ Printf.sprintf "%u" domid; ] in +- Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Resume data) ++ queue con (Xb.Packet.create 0 0 Xb.Op.Resume data) + + let getdomainpath domid con = + let data = data_concat [ Printf.sprintf "%u" domid; ] in +- Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Getdomainpath data) ++ queue con (Xb.Packet.create 0 0 Xb.Op.Getdomainpath data) + + let write tid path value con = + let data = path ^ "\000" ^ value (* no NULL at the end *) in +- Xb.queue con (Xb.Packet.create tid 0 Xb.Op.Write data) ++ queue con (Xb.Packet.create tid 0 Xb.Op.Write data) + + let mkdir tid path con = queue_path Xb.Op.Mkdir tid path con + let rm tid path con = queue_path Xb.Op.Rm tid path con + + let setperms tid path perms con = + let data = data_concat [ path; perms ] in +- Xb.queue con (Xb.Packet.create tid 0 Xb.Op.Setperms data) ++ queue con (Xb.Packet.create tid 0 Xb.Op.Setperms data) +diff --git a/tools/ocaml/libs/xs/xsraw.ml b/tools/ocaml/libs/xs/xsraw.ml +index 451f8b38dbcc..cbd17280600c 100644 +--- a/tools/ocaml/libs/xs/xsraw.ml ++++ b/tools/ocaml/libs/xs/xsraw.ml +@@ -36,8 +36,10 @@ type con = { + let close con = + Xb.close con.xb + ++let capacity = { Xb.maxoutstanding = 1; maxwatchevents = 0; } ++ + let open_fd fd = { +- xb = Xb.open_fd fd; ++ xb = Xb.open_fd ~capacity fd; + watchevents = Queue.create (); + } + +diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/connection.ml +index cc20e047d2b9..9624a5f9da2c 100644 +--- a/tools/ocaml/xenstored/connection.ml ++++ b/tools/ocaml/xenstored/connection.ml +@@ -20,12 +20,84 @@ open Stdext + + let xenstore_payload_max = 4096 (* xen/include/public/io/xs_wire.h *) + ++type 'a bounded_sender = 'a -> unit option ++(** a bounded sender accepts an ['a] item and returns: ++ None - if there is no room to accept the item ++ Some () - if it has successfully accepted/sent the item ++ *) ++ ++module BoundedPipe : sig ++ type 'a t ++ ++ (** [create ~capacity ~destination] creates a bounded pipe with a ++ local buffer holding at most [capacity] items. Once the buffer is ++ full it will not accept further items. items from the pipe are ++ flushed into [destination] as long as it accepts items. The ++ destination could be another pipe. ++ *) ++ val create: capacity:int -> destination:'a bounded_sender -> 'a t ++ ++ (** [is_empty t] returns whether the local buffer of [t] is empty. *) ++ val is_empty : _ t -> bool ++ ++ (** [length t] the number of items in the internal buffer *) ++ val length: _ t -> int ++ ++ (** [flush_pipe t] sends as many items from the local buffer as possible, ++ which could be none. *) ++ val flush_pipe: _ t -> unit ++ ++ (** [push t item] tries to [flush_pipe] and then push [item] ++ into the pipe if its [capacity] allows. ++ Returns [None] if there is no more room ++ *) ++ val push : 'a t -> 'a bounded_sender ++end = struct ++ (* items are enqueued in [q], and then flushed to [connect_to] *) ++ type 'a t = ++ { q: 'a Queue.t ++ ; destination: 'a bounded_sender ++ ; capacity: int ++ } ++ ++ let create ~capacity ~destination = ++ { q = Queue.create (); capacity; destination } ++ ++ let rec flush_pipe t = ++ if not Queue.(is_empty t.q) then ++ let item = Queue.peek t.q in ++ match t.destination item with ++ | None -> () (* no room *) ++ | Some () -> ++ (* successfully sent item to next stage *) ++ let _ = Queue.pop t.q in ++ (* continue trying to send more items *) ++ flush_pipe t ++ ++ let push t item = ++ (* first try to flush as many items from this pipe as possible to make room, ++ it is important to do this first to preserve the order of the items ++ *) ++ flush_pipe t; ++ if Queue.length t.q < t.capacity then begin ++ (* enqueue, instead of sending directly. ++ this ensures that [out] sees the items in the same order as we receive them ++ *) ++ Queue.push item t.q; ++ Some (flush_pipe t) ++ end else None ++ ++ let is_empty t = Queue.is_empty t.q ++ let length t = Queue.length t.q ++end ++ + type watch = { + con: t; + token: string; + path: string; + base: string; + is_relative: bool; ++ pending_watchevents: Xenbus.Xb.Packet.t BoundedPipe.t; + } + + and t = { +@@ -38,8 +110,36 @@ and t = { + anonid: int; + mutable stat_nb_ops: int; + mutable perm: Perms.Connection.t; ++ pending_source_watchevents: (watch * Xenbus.Xb.Packet.t) BoundedPipe.t + } + ++module Watch = struct ++ module T = struct ++ type t = watch ++ ++ let compare w1 w2 = ++ (* cannot compare watches from different connections *) ++ assert (w1.con == w2.con); ++ match String.compare w1.token w2.token with ++ | 0 -> String.compare w1.path w2.path ++ | n -> n ++ end ++ module Set = Set.Make(T) ++ ++ let flush_events t = ++ BoundedPipe.flush_pipe t.pending_watchevents; ++ not (BoundedPipe.is_empty t.pending_watchevents) ++ ++ let pending_watchevents t = ++ BoundedPipe.length t.pending_watchevents ++end ++ ++let source_flush_watchevents t = ++ BoundedPipe.flush_pipe t.pending_source_watchevents ++ ++let source_pending_watchevents t = ++ BoundedPipe.length t.pending_source_watchevents ++ + let mark_as_bad con = + match con.dom with + |None -> () +@@ -67,7 +167,8 @@ let watch_create ~con ~path ~token = { + token = token; + path = path; + base = get_path con; +- is_relative = path.[0] <> '/' && path.[0] <> '@' ++ is_relative = path.[0] <> '/' && path.[0] <> '@'; ++ pending_watchevents = BoundedPipe.create ~capacity:!Define.maxwatchevents ~destination:(Xenbus.Xb.queue con.xb) + } + + let get_con w = w.con +@@ -93,6 +194,9 @@ let make_perm dom = + Perms.Connection.create ~perms:[Perms.READ; Perms.WRITE] domid + + let create xbcon dom = ++ let destination (watch, pkt) = ++ BoundedPipe.push watch.pending_watchevents pkt ++ in + let id = + match dom with + | None -> let old = !anon_id_next in incr anon_id_next; old +@@ -109,6 +213,16 @@ let create xbcon dom = + anonid = id; + stat_nb_ops = 0; + perm = make_perm dom; ++ ++ (* the actual capacity will be lower, this is used as an overflow ++ buffer: anything that doesn't fit elsewhere gets put here, only ++ limited by the amount of watches that you can generate with a ++ single xenstore command (which is finite, although possibly very ++ large in theory for Dom0). Once the pipe here has any contents the ++ domain is blocked from sending more commands until it is empty ++ again though. ++ *) ++ pending_source_watchevents = BoundedPipe.create ~capacity:Sys.max_array_length ~destination + } + in + Logging.new_connection ~tid:Transaction.none ~con:(get_domstr con); +@@ -127,11 +241,17 @@ let set_target con target_domid = + + let is_backend_mmap con = Xenbus.Xb.is_mmap con.xb + +-let send_reply con tid rid ty data = ++let packet_of con tid rid ty data = + if (String.length data) > xenstore_payload_max && (is_backend_mmap con) then +- Xenbus.Xb.queue con.xb (Xenbus.Xb.Packet.create tid rid Xenbus.Xb.Op.Error "E2BIG\000") ++ Xenbus.Xb.Packet.create tid rid Xenbus.Xb.Op.Error "E2BIG\000" + else +- Xenbus.Xb.queue con.xb (Xenbus.Xb.Packet.create tid rid ty data) ++ Xenbus.Xb.Packet.create tid rid ty data ++ ++let send_reply con tid rid ty data = ++ let result = Xenbus.Xb.queue con.xb (packet_of con tid rid ty data) in ++ (* should never happen: we only process an input packet when there is room for an output packet *) ++ (* and the limit for replies is different from the limit for watch events *) ++ assert (result <> None) + + let send_error con tid rid err = send_reply con tid rid Xenbus.Xb.Op.Error (err ^ "\000") + let send_ack con tid rid ty = send_reply con tid rid ty "OK\000" +@@ -181,11 +301,11 @@ let del_watch con path token = + apath, w + + let del_watches con = +- Hashtbl.clear con.watches; ++ Hashtbl.reset con.watches; + con.nb_watches <- 0 + + let del_transactions con = +- Hashtbl.clear con.transactions ++ Hashtbl.reset con.transactions + + let list_watches con = + let ll = Hashtbl.fold +@@ -208,21 +328,29 @@ let lookup_watch_perm path = function + let lookup_watch_perms oldroot root path = + lookup_watch_perm path oldroot @ lookup_watch_perm path (Some root) + +-let fire_single_watch_unchecked watch = ++let fire_single_watch_unchecked source watch = + let data = Utils.join_by_null [watch.path; watch.token; ""] in +- send_reply watch.con Transaction.none 0 Xenbus.Xb.Op.Watchevent data ++ let pkt = packet_of watch.con Transaction.none 0 Xenbus.Xb.Op.Watchevent data in ++ ++ match BoundedPipe.push source.pending_source_watchevents (watch, pkt) with ++ | Some () -> () (* packet queued *) ++ | None -> ++ (* a well behaved Dom0 shouldn't be able to trigger this, ++ if it happens it is likely a Dom0 bug causing runaway memory usage ++ *) ++ failwith "watch event overflow, cannot happen" + +-let fire_single_watch (oldroot, root) watch = ++let fire_single_watch source (oldroot, root) watch = + let abspath = get_watch_path watch.con watch.path |> Store.Path.of_string in + let perms = lookup_watch_perms oldroot root abspath in + if Perms.can_fire_watch watch.con.perm perms then +- fire_single_watch_unchecked watch ++ fire_single_watch_unchecked source watch + else + let perms = perms |> List.map (Perms.Node.to_string ~sep:" ") |> String.concat ", " in + let con = get_domstr watch.con in + Logging.watch_not_fired ~con perms (Store.Path.to_string abspath) + +-let fire_watch roots watch path = ++let fire_watch source roots watch path = + let new_path = + if watch.is_relative && path.[0] = '/' + then begin +@@ -232,7 +360,7 @@ let fire_watch roots watch path = + end else + path + in +- fire_single_watch roots { watch with path = new_path } ++ fire_single_watch source roots { watch with path = new_path } + + (* Search for a valid unused transaction id. *) + let rec valid_transaction_id con proposed_id = +@@ -280,6 +408,7 @@ let do_input con = Xenbus.Xb.input con.xb + let has_partial_input con = Xenbus.Xb.has_partial_input con.xb + let has_more_input con = Xenbus.Xb.has_more_input con.xb + ++let can_input con = Xenbus.Xb.can_input con.xb && BoundedPipe.is_empty con.pending_source_watchevents + let has_output con = Xenbus.Xb.has_output con.xb + let has_old_output con = Xenbus.Xb.has_old_output con.xb + let has_new_output con = Xenbus.Xb.has_new_output con.xb +@@ -323,7 +452,7 @@ let prevents_live_update con = not (is_bad con) + && (has_extra_connection_data con || has_transaction_data con) + + let has_more_work con = +- has_more_input con || not (has_old_output con) && has_new_output con ++ (has_more_input con && can_input con) || not (has_old_output con) && has_new_output con + + let incr_ops con = con.stat_nb_ops <- con.stat_nb_ops + 1 + +diff --git a/tools/ocaml/xenstored/connections.ml b/tools/ocaml/xenstored/connections.ml +index 3c7429fe7f61..7d68c583b43a 100644 +--- a/tools/ocaml/xenstored/connections.ml ++++ b/tools/ocaml/xenstored/connections.ml +@@ -22,22 +22,30 @@ type t = { + domains: (int, Connection.t) Hashtbl.t; + ports: (Xeneventchn.t, Connection.t) Hashtbl.t; + mutable watches: Connection.watch list Trie.t; ++ mutable has_pending_watchevents: Connection.Watch.Set.t + } + + let create () = { + anonymous = Hashtbl.create 37; + domains = Hashtbl.create 37; + ports = Hashtbl.create 37; +- watches = Trie.create () ++ watches = Trie.create (); ++ has_pending_watchevents = Connection.Watch.Set.empty; + } + ++let get_capacity () = ++ (* not multiplied by maxwatch on purpose: 2nd queue in watch itself! *) ++ { Xenbus.Xb.maxoutstanding = !Define.maxoutstanding; maxwatchevents = !Define.maxwatchevents } ++ + let add_anonymous cons fd = +- let xbcon = Xenbus.Xb.open_fd fd in ++ let capacity = get_capacity () in ++ let xbcon = Xenbus.Xb.open_fd fd ~capacity in + let con = Connection.create xbcon None in + Hashtbl.add cons.anonymous (Xenbus.Xb.get_fd xbcon) con + + let add_domain cons dom = +- let xbcon = Xenbus.Xb.open_mmap (Domain.get_interface dom) (fun () -> Domain.notify dom) in ++ let capacity = get_capacity () in ++ let xbcon = Xenbus.Xb.open_mmap ~capacity (Domain.get_interface dom) (fun () -> Domain.notify dom) in + let con = Connection.create xbcon (Some dom) in + Hashtbl.add cons.domains (Domain.get_id dom) con; + match Domain.get_port dom with +@@ -48,7 +56,9 @@ let select ?(only_if = (fun _ -> true)) cons = + Hashtbl.fold (fun _ con (ins, outs) -> + if (only_if con) then ( + let fd = Connection.get_fd con in +- (fd :: ins, if Connection.has_output con then fd :: outs else outs) ++ let in_fds = if Connection.can_input con then fd :: ins else ins in ++ let out_fds = if Connection.has_output con then fd :: outs else outs in ++ in_fds, out_fds + ) else (ins, outs) + ) + cons.anonymous ([], []) +@@ -67,10 +77,17 @@ let del_watches_of_con con watches = + | [] -> None + | ws -> Some ws + ++let del_watches cons con = ++ Connection.del_watches con; ++ cons.watches <- Trie.map (del_watches_of_con con) cons.watches; ++ cons.has_pending_watchevents <- ++ cons.has_pending_watchevents |> Connection.Watch.Set.filter @@ fun w -> ++ Connection.get_con w != con ++ + let del_anonymous cons con = + try + Hashtbl.remove cons.anonymous (Connection.get_fd con); +- cons.watches <- Trie.map (del_watches_of_con con) cons.watches; ++ del_watches cons con; + Connection.close con + with exn -> + debug "del anonymous %s" (Printexc.to_string exn) +@@ -85,7 +102,7 @@ let del_domain cons id = + | Some p -> Hashtbl.remove cons.ports p + | None -> ()) + | None -> ()); +- cons.watches <- Trie.map (del_watches_of_con con) cons.watches; ++ del_watches cons con; + Connection.close con + with exn -> + debug "del domain %u: %s" id (Printexc.to_string exn) +@@ -136,31 +153,33 @@ let del_watch cons con path token = + cons.watches <- Trie.set cons.watches key watches; + watch + +-let del_watches cons con = +- Connection.del_watches con; +- cons.watches <- Trie.map (del_watches_of_con con) cons.watches +- + (* path is absolute *) +-let fire_watches ?oldroot root cons path recurse = ++let fire_watches ?oldroot source root cons path recurse = + let key = key_of_path path in + let path = Store.Path.to_string path in + let roots = oldroot, root in + let fire_watch _ = function + | None -> () +- | Some watches -> List.iter (fun w -> Connection.fire_watch roots w path) watches ++ | Some watches -> List.iter (fun w -> Connection.fire_watch source roots w path) watches + in + let fire_rec _x = function + | None -> () + | Some watches -> +- List.iter (Connection.fire_single_watch roots) watches ++ List.iter (Connection.fire_single_watch source roots) watches + in + Trie.iter_path fire_watch cons.watches key; + if recurse then + Trie.iter fire_rec (Trie.sub cons.watches key) + ++let send_watchevents cons con = ++ cons.has_pending_watchevents <- ++ cons.has_pending_watchevents |> Connection.Watch.Set.filter Connection.Watch.flush_events; ++ Connection.source_flush_watchevents con ++ + let fire_spec_watches root cons specpath = ++ let source = find_domain cons 0 in + iter cons (fun con -> +- List.iter (Connection.fire_single_watch (None, root)) (Connection.get_watches con specpath)) ++ List.iter (Connection.fire_single_watch source (None, root)) (Connection.get_watches con specpath)) + + let set_target cons domain target_domain = + let con = find_domain cons domain in +@@ -197,6 +216,16 @@ let debug cons = + let domains = Hashtbl.fold (fun _ con accu -> Connection.debug con :: accu) cons.domains [] in + String.concat "" (domains @ anonymous) + ++let debug_watchevents cons con = ++ (* == (physical equality) ++ has to be used here because w.con.xb.backend might contain a [unit->unit] value causing regular ++ comparison to fail due to having a 'functional value' which cannot be compared. ++ *) ++ let s = cons.has_pending_watchevents |> Connection.Watch.Set.filter (fun w -> w.con == con) in ++ let pending = s |> Connection.Watch.Set.elements ++ |> List.map (fun w -> Connection.Watch.pending_watchevents w) |> List.fold_left (+) 0 in ++ Printf.sprintf "Watches with pending events: %d, pending events total: %d" (Connection.Watch.Set.cardinal s) pending ++ + let filter ~f cons = + let fold _ v acc = if f v then v :: acc else acc in + [] +diff --git a/tools/ocaml/xenstored/define.ml b/tools/ocaml/xenstored/define.ml +index ba63a8147e09..327b6d795ec7 100644 +--- a/tools/ocaml/xenstored/define.ml ++++ b/tools/ocaml/xenstored/define.ml +@@ -24,6 +24,13 @@ let default_config_dir = Paths.xen_config_dir + let maxwatch = ref (100) + let maxtransaction = ref (10) + let maxrequests = ref (1024) (* maximum requests per transaction *) ++let maxoutstanding = ref (1024) (* maximum outstanding requests, i.e. in-flight requests / domain *) ++let maxwatchevents = ref (1024) ++(* ++ maximum outstanding watch events per watch, ++ recommended >= maxoutstanding to avoid blocking backend transactions due to ++ malicious frontends ++ *) + + let gc_max_overhead = ref 120 (* 120% see comment in xenstored.ml *) + let conflict_burst_limit = ref 5.0 +diff --git a/tools/ocaml/xenstored/oxenstored.conf.in b/tools/ocaml/xenstored/oxenstored.conf.in +index 4ae48e42d47d..9d034e744b4b 100644 +--- a/tools/ocaml/xenstored/oxenstored.conf.in ++++ b/tools/ocaml/xenstored/oxenstored.conf.in +@@ -62,6 +62,8 @@ quota-maxwatch = 100 + quota-transaction = 10 + quota-maxrequests = 1024 + quota-path-max = 1024 ++quota-maxoutstanding = 1024 ++quota-maxwatchevents = 1024 + + # Activate filed base backend + persistent = false +diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml +index 2d67456a2aa0..6dcedfda86e4 100644 +--- a/tools/ocaml/xenstored/process.ml ++++ b/tools/ocaml/xenstored/process.ml +@@ -57,7 +57,7 @@ let split_one_path data con = + | path :: "" :: [] -> Store.Path.create path (Connection.get_path con) + | _ -> raise Invalid_Cmd_Args + +-let process_watch t cons = ++let process_watch source t cons = + let oldroot = t.Transaction.oldroot in + let newroot = Store.get_root t.store in + let ops = Transaction.get_paths t |> List.rev in +@@ -67,8 +67,9 @@ let process_watch t cons = + | Xenbus.Xb.Op.Rm -> true, None, oldroot + | Xenbus.Xb.Op.Setperms -> false, Some oldroot, newroot + | _ -> raise (Failure "huh ?") in +- Connections.fire_watches ?oldroot root cons (snd op) recurse in +- List.iter (fun op -> do_op_watch op cons) ops ++ Connections.fire_watches ?oldroot source root cons (snd op) recurse in ++ List.iter (fun op -> do_op_watch op cons) ops; ++ Connections.send_watchevents cons source + + let create_implicit_path t perm path = + let dirname = Store.Path.get_parent path in +@@ -234,6 +235,20 @@ let do_debug con t _domains cons data = + | "watches" :: _ -> + let watches = Connections.debug cons in + Some (watches ^ "\000") ++ | "xenbus" :: domid :: _ -> ++ let domid = int_of_string domid in ++ let con = Connections.find_domain cons domid in ++ let s = Printf.sprintf "xenbus: %s; overflow queue length: %d, can_input: %b, has_more_input: %b, has_old_output: %b, has_new_output: %b, has_more_work: %b. pending: %s" ++ (Xenbus.Xb.debug con.xb) ++ (Connection.source_pending_watchevents con) ++ (Connection.can_input con) ++ (Connection.has_more_input con) ++ (Connection.has_old_output con) ++ (Connection.has_new_output con) ++ (Connection.has_more_work con) ++ (Connections.debug_watchevents cons con) ++ in ++ Some s + | "mfn" :: domid :: _ -> + let domid = int_of_string domid in + let con = Connections.find_domain cons domid in +@@ -342,7 +357,7 @@ let reply_ack fct con t doms cons data = + fct con t doms cons data; + Packet.Ack (fun () -> + if Transaction.get_id t = Transaction.none then +- process_watch t cons ++ process_watch con t cons + ) + + let reply_data fct con t doms cons data = +@@ -501,7 +516,7 @@ let do_watch con t _domains cons data = + Packet.Ack (fun () -> + (* xenstore.txt says this watch is fired immediately, + implying even if path doesn't exist or is unreadable *) +- Connection.fire_single_watch_unchecked watch) ++ Connection.fire_single_watch_unchecked con watch) + + let do_unwatch con _t _domains cons data = + let (node, token) = +@@ -532,7 +547,7 @@ let do_transaction_end con t domains cons data = + if not success then + raise Transaction_again; + if commit then begin +- process_watch t cons; ++ process_watch con t cons; + match t.Transaction.ty with + | Transaction.No -> + () (* no need to record anything *) +@@ -699,7 +714,8 @@ let process_packet ~store ~cons ~doms ~con ~req = + let do_input store cons doms con = + let newpacket = + try +- Connection.do_input con ++ if Connection.can_input con then Connection.do_input con ++ else None + with Xenbus.Xb.Reconnect -> + info "%s requests a reconnect" (Connection.get_domstr con); + History.reconnect con; +@@ -727,6 +743,7 @@ let do_input store cons doms con = + Connection.incr_ops con + + let do_output _store _cons _doms con = ++ Connection.source_flush_watchevents con; + if Connection.has_output con then ( + if Connection.has_new_output con then ( + let packet = Connection.peek_output con in +diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml +index 3b57ad016dfb..c799e20f1145 100644 +--- a/tools/ocaml/xenstored/xenstored.ml ++++ b/tools/ocaml/xenstored/xenstored.ml +@@ -103,6 +103,8 @@ let parse_config filename = + ("quota-maxentity", Config.Set_int Quota.maxent); + ("quota-maxsize", Config.Set_int Quota.maxsize); + ("quota-maxrequests", Config.Set_int Define.maxrequests); ++ ("quota-maxoutstanding", Config.Set_int Define.maxoutstanding); ++ ("quota-maxwatchevents", Config.Set_int Define.maxwatchevents); + ("quota-path-max", Config.Set_int Define.path_max); + ("gc-max-overhead", Config.Set_int Define.gc_max_overhead); + ("test-eagain", Config.Set_bool Transaction.test_eagain); diff --git a/xsa326-4.16-oxenstored-08.patch b/xsa326-4.16-oxenstored-08.patch new file mode 100644 index 0000000..8a47200 --- /dev/null +++ b/xsa326-4.16-oxenstored-08.patch @@ -0,0 +1,49 @@ +From 09aa10649f75a262028e9a9b7d859ef7efb23d54 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Thu, 29 Sep 2022 13:07:35 +0200 +Subject: SUPPORT.md: clarify support of untrusted driver domains with + oxenstored + +Add a support statement for the scope of support regarding different +Xenstore variants. Especially oxenstored does not (yet) have security +support of untrusted driver domains, as those might drive oxenstored +out of memory by creating lots of watch events for the guests they are +servicing. + +Add a statement regarding Live Update support of oxenstored. + +This is part of XSA-326. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Acked-by: George Dunlap +Acked-by: Julien Grall +Reviewed-by: Christian Lindig + +diff --git a/SUPPORT.md b/SUPPORT.md +index 85726102eab8..7d0cb34c8f6f 100644 +--- a/SUPPORT.md ++++ b/SUPPORT.md +@@ -179,13 +179,18 @@ Support for running qemu-xen device model in a linux stubdomain. + + Status: Tech Preview + +-## Liveupdate of C xenstored daemon ++## Xenstore + +- Status: Tech Preview ++### C xenstored daemon + +-## Liveupdate of OCaml xenstored daemon ++ Status: Supported ++ Status, Liveupdate: Tech Preview + +- Status: Tech Preview ++### OCaml xenstored daemon ++ ++ Status: Supported ++ Status, untrusted driver domains: Supported, not security supported ++ Status, Liveupdate: Not functional + + ## Toolstack/3rd party + diff --git a/xsa326-4.16-xenstored-01.patch b/xsa326-4.16-xenstored-01.patch new file mode 100644 index 0000000..b4a4b0e --- /dev/null +++ b/xsa326-4.16-xenstored-01.patch @@ -0,0 +1,205 @@ +From 5192f13a41661b1c1b9e0889d57c0f5b41925c39 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:07 +0200 +Subject: tools/xenstore: split up send_reply() + +Today send_reply() is used for both, normal request replies and watch +events. + +Split it up into send_reply() and send_event(). This will be used to +add some event specific handling. + +add_event() can be merged into send_event(), removing the need for an +intermediate memory allocation. + +This is part of XSA-326. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index e9c9695fd16e..249ad5ec6fb1 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -767,49 +767,32 @@ static void send_error(struct connection *conn, int error) + void send_reply(struct connection *conn, enum xsd_sockmsg_type type, + const void *data, unsigned int len) + { +- struct buffered_data *bdata; ++ struct buffered_data *bdata = conn->in; ++ ++ assert(type != XS_WATCH_EVENT); + + if ( len > XENSTORE_PAYLOAD_MAX ) { + send_error(conn, E2BIG); + return; + } + +- /* Replies reuse the request buffer, events need a new one. */ +- if (type != XS_WATCH_EVENT) { +- bdata = conn->in; +- /* Drop asynchronous responses, e.g. errors for watch events. */ +- if (!bdata) +- return; +- bdata->inhdr = true; +- bdata->used = 0; +- conn->in = NULL; +- } else { +- /* Message is a child of the connection for auto-cleanup. */ +- bdata = new_buffer(conn); ++ if (!bdata) ++ return; ++ bdata->inhdr = true; ++ bdata->used = 0; + +- /* +- * Allocation failure here is unfortunate: we have no way to +- * tell anybody about it. +- */ +- if (!bdata) +- return; +- } + if (len <= DEFAULT_BUFFER_SIZE) + bdata->buffer = bdata->default_buffer; +- else ++ else { + bdata->buffer = talloc_array(bdata, char, len); +- if (!bdata->buffer) { +- if (type == XS_WATCH_EVENT) { +- /* Same as above: no way to tell someone. */ +- talloc_free(bdata); ++ if (!bdata->buffer) { ++ send_error(conn, ENOMEM); + return; + } +- /* re-establish request buffer for sending ENOMEM. */ +- conn->in = bdata; +- send_error(conn, ENOMEM); +- return; + } + ++ conn->in = NULL; ++ + /* Update relevant header fields and fill in the message body. */ + bdata->hdr.msg.type = type; + bdata->hdr.msg.len = len; +@@ -817,8 +800,39 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, + + /* Queue for later transmission. */ + list_add_tail(&bdata->list, &conn->out_list); ++} + +- return; ++/* ++ * Send a watch event. ++ * As this is not directly related to the current command, errors can't be ++ * reported. ++ */ ++void send_event(struct connection *conn, const char *path, const char *token) ++{ ++ struct buffered_data *bdata; ++ unsigned int len; ++ ++ len = strlen(path) + 1 + strlen(token) + 1; ++ /* Don't try to send over-long events. */ ++ if (len > XENSTORE_PAYLOAD_MAX) ++ return; ++ ++ bdata = new_buffer(conn); ++ if (!bdata) ++ return; ++ ++ bdata->buffer = talloc_array(bdata, char, len); ++ if (!bdata->buffer) { ++ talloc_free(bdata); ++ return; ++ } ++ strcpy(bdata->buffer, path); ++ strcpy(bdata->buffer + strlen(path) + 1, token); ++ bdata->hdr.msg.type = XS_WATCH_EVENT; ++ bdata->hdr.msg.len = len; ++ ++ /* Queue for later transmission. */ ++ list_add_tail(&bdata->list, &conn->out_list); + } + + /* Some routines (write, mkdir, etc) just need a non-error return */ +diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h +index 0004fa848c83..9af9af4390bd 100644 +--- a/tools/xenstore/xenstored_core.h ++++ b/tools/xenstore/xenstored_core.h +@@ -187,6 +187,7 @@ unsigned int get_string(const struct buffered_data *data, unsigned int offset); + + void send_reply(struct connection *conn, enum xsd_sockmsg_type type, + const void *data, unsigned int len); ++void send_event(struct connection *conn, const char *path, const char *token); + + /* Some routines (write, mkdir, etc) just need a non-error return */ + void send_ack(struct connection *conn, enum xsd_sockmsg_type type); +diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c +index aca0a71bada1..99a2c266b28a 100644 +--- a/tools/xenstore/xenstored_watch.c ++++ b/tools/xenstore/xenstored_watch.c +@@ -86,35 +86,6 @@ static const char *get_watch_path(const struct watch *watch, const char *name) + } + + /* +- * Send a watch event. +- * Temporary memory allocations are done with ctx. +- */ +-static void add_event(struct connection *conn, +- const void *ctx, +- struct watch *watch, +- const char *name) +-{ +- /* Data to send (node\0token\0). */ +- unsigned int len; +- char *data; +- +- name = get_watch_path(watch, name); +- +- len = strlen(name) + 1 + strlen(watch->token) + 1; +- /* Don't try to send over-long events. */ +- if (len > XENSTORE_PAYLOAD_MAX) +- return; +- +- data = talloc_array(ctx, char, len); +- if (!data) +- return; +- strcpy(data, name); +- strcpy(data + strlen(name) + 1, watch->token); +- send_reply(conn, XS_WATCH_EVENT, data, len); +- talloc_free(data); +-} +- +-/* + * Check permissions of a specific watch to fire: + * Either the node itself or its parent have to be readable by the connection + * the watch has been setup for. In case a watch event is created due to +@@ -190,10 +161,14 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, + list_for_each_entry(watch, &i->watches, list) { + if (exact) { + if (streq(name, watch->node)) +- add_event(i, ctx, watch, name); ++ send_event(i, ++ get_watch_path(watch, name), ++ watch->token); + } else { + if (is_child(name, watch->node)) +- add_event(i, ctx, watch, name); ++ send_event(i, ++ get_watch_path(watch, name), ++ watch->token); + } + } + } +@@ -292,7 +267,7 @@ int do_watch(struct connection *conn, struct buffered_data *in) + send_ack(conn, XS_WATCH); + + /* We fire once up front: simplifies clients and restart. */ +- add_event(conn, in, watch, watch->node); ++ send_event(conn, get_watch_path(watch, watch->node), watch->token); + + return 0; + } diff --git a/xsa326-4.16-xenstored-02.patch b/xsa326-4.16-xenstored-02.patch new file mode 100644 index 0000000..540ab03 --- /dev/null +++ b/xsa326-4.16-xenstored-02.patch @@ -0,0 +1,108 @@ +From 0a4c86f8a8febd85610496470123adfc4fbc1c5d Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:07 +0200 +Subject: tools/xenstore: add helpers to free struct buffered_data + +Add two helpers for freeing struct buffered_data: free_buffered_data() +for freeing one instance and conn_free_buffered_data() for freeing all +instances for a connection. + +This is avoiding duplicated code and will help later when more actions +are needed when freeing a struct buffered_data. + +This is part of XSA-326. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 249ad5ec6fb1..527a1ebdeded 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -211,6 +211,21 @@ void reopen_log(void) + } + } + ++static void free_buffered_data(struct buffered_data *out, ++ struct connection *conn) ++{ ++ list_del(&out->list); ++ talloc_free(out); ++} ++ ++void conn_free_buffered_data(struct connection *conn) ++{ ++ struct buffered_data *out; ++ ++ while ((out = list_top(&conn->out_list, struct buffered_data, list))) ++ free_buffered_data(out, conn); ++} ++ + static bool write_messages(struct connection *conn) + { + int ret; +@@ -254,8 +269,7 @@ static bool write_messages(struct connection *conn) + + trace_io(conn, out, 1); + +- list_del(&out->list); +- talloc_free(out); ++ free_buffered_data(out, conn); + + return true; + } +@@ -1506,18 +1520,12 @@ static struct { + */ + void ignore_connection(struct connection *conn) + { +- struct buffered_data *out, *tmp; +- + trace("CONN %p ignored\n", conn); + + conn->is_ignored = true; + conn_delete_all_watches(conn); + conn_delete_all_transactions(conn); +- +- list_for_each_entry_safe(out, tmp, &conn->out_list, list) { +- list_del(&out->list); +- talloc_free(out); +- } ++ conn_free_buffered_data(conn); + + talloc_free(conn->in); + conn->in = NULL; +diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h +index 9af9af4390bd..e7ee87825c3b 100644 +--- a/tools/xenstore/xenstored_core.h ++++ b/tools/xenstore/xenstored_core.h +@@ -276,6 +276,8 @@ int remember_string(struct hashtable *hash, const char *str); + + void set_tdb_key(const char *name, TDB_DATA *key); + ++void conn_free_buffered_data(struct connection *conn); ++ + const char *dump_state_global(FILE *fp); + const char *dump_state_buffered_data(FILE *fp, const struct connection *c, + struct xs_state_connection *sc); +diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c +index d03c7d93a9e7..93c4c1edcdd1 100644 +--- a/tools/xenstore/xenstored_domain.c ++++ b/tools/xenstore/xenstored_domain.c +@@ -411,15 +411,10 @@ static struct domain *find_domain_by_domid(unsigned int domid) + static void domain_conn_reset(struct domain *domain) + { + struct connection *conn = domain->conn; +- struct buffered_data *out; + + conn_delete_all_watches(conn); + conn_delete_all_transactions(conn); +- +- while ((out = list_top(&conn->out_list, struct buffered_data, list))) { +- list_del(&out->list); +- talloc_free(out); +- } ++ conn_free_buffered_data(conn); + + talloc_free(conn->in); + diff --git a/xsa326-4.16-xenstored-03.patch b/xsa326-4.16-xenstored-03.patch new file mode 100644 index 0000000..a3a0d81 --- /dev/null +++ b/xsa326-4.16-xenstored-03.patch @@ -0,0 +1,192 @@ +From a6c4198242bf69bea1825492b7665b559023390c Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:07 +0200 +Subject: tools/xenstore: reduce number of watch events + +When removing a watched node outside of a transaction, two watch events +are being produced instead of just a single one. + +When finalizing a transaction watch events can be generated for each +node which is being modified, even if outside a transaction such +modifications might not have resulted in a watch event. + +This happens e.g.: + +- for nodes which are only modified due to added/removed child entries +- for nodes being removed or created implicitly (e.g. creation of a/b/c + is implicitly creating a/b, resulting in watch events for a, a/b and + a/b/c instead of a/b/c only) + +Avoid these additional watch events, in order to reduce the needed +memory inside Xenstore for queueing them. + +This is being achieved by adding event flags to struct accessed_node +specifying whether an event should be triggered, and whether it should +be an exact match of the modified path. Both flags can be set from +fire_watches() instead of implying them only. + +This is part of XSA-326. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 527a1ebdeded..bf2243873901 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -1295,7 +1295,7 @@ static void delete_child(struct connection *conn, + } + + static int delete_node(struct connection *conn, const void *ctx, +- struct node *parent, struct node *node) ++ struct node *parent, struct node *node, bool watch_exact) + { + char *name; + +@@ -1307,7 +1307,7 @@ static int delete_node(struct connection *conn, const void *ctx, + node->children); + child = name ? read_node(conn, node, name) : NULL; + if (child) { +- if (delete_node(conn, ctx, node, child)) ++ if (delete_node(conn, ctx, node, child, true)) + return errno; + } else { + trace("delete_node: Error deleting child '%s/%s'!\n", +@@ -1319,7 +1319,12 @@ static int delete_node(struct connection *conn, const void *ctx, + talloc_free(name); + } + +- fire_watches(conn, ctx, node->name, node, true, NULL); ++ /* ++ * Fire the watches now, when we can still see the node permissions. ++ * This fine as we are single threaded and the next possible read will ++ * be handled only after the node has been really removed. ++ */ ++ fire_watches(conn, ctx, node->name, node, watch_exact, NULL); + delete_node_single(conn, node); + delete_child(conn, parent, basename(node->name)); + talloc_free(node); +@@ -1345,13 +1350,7 @@ static int _rm(struct connection *conn, const void *ctx, struct node *node, + return (errno == ENOMEM) ? ENOMEM : EINVAL; + node->parent = parent; + +- /* +- * Fire the watches now, when we can still see the node permissions. +- * This fine as we are single threaded and the next possible read will +- * be handled only after the node has been really removed. +- */ +- fire_watches(conn, ctx, name, node, false, NULL); +- return delete_node(conn, ctx, parent, node); ++ return delete_node(conn, ctx, parent, node, false); + } + + +diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c +index faf6c930e42a..54432907fc76 100644 +--- a/tools/xenstore/xenstored_transaction.c ++++ b/tools/xenstore/xenstored_transaction.c +@@ -130,6 +130,10 @@ struct accessed_node + + /* Transaction node in data base? */ + bool ta_node; ++ ++ /* Watch event flags. */ ++ bool fire_watch; ++ bool watch_exact; + }; + + struct changed_domain +@@ -324,6 +328,29 @@ int access_node(struct connection *conn, struct node *node, + } + + /* ++ * A watch event should be fired for a node modified inside a transaction. ++ * Set the corresponding information. A non-exact event is replacing an exact ++ * one, but not the other way round. ++ */ ++void queue_watches(struct connection *conn, const char *name, bool watch_exact) ++{ ++ struct accessed_node *i; ++ ++ i = find_accessed_node(conn->transaction, name); ++ if (!i) { ++ conn->transaction->fail = true; ++ return; ++ } ++ ++ if (!i->fire_watch) { ++ i->fire_watch = true; ++ i->watch_exact = watch_exact; ++ } else if (!watch_exact) { ++ i->watch_exact = false; ++ } ++} ++ ++/* + * Finalize transaction: + * Walk through accessed nodes and check generation against global data. + * If all entries match, read the transaction entries and write them without +@@ -377,15 +404,15 @@ static int finalize_transaction(struct connection *conn, + ret = tdb_store(tdb_ctx, key, data, + TDB_REPLACE); + talloc_free(data.dptr); +- if (ret) +- goto err; +- fire_watches(conn, trans, i->node, NULL, false, +- i->perms.p ? &i->perms : NULL); + } else { +- fire_watches(conn, trans, i->node, NULL, false, ++ ret = tdb_delete(tdb_ctx, key); ++ } ++ if (ret) ++ goto err; ++ if (i->fire_watch) { ++ fire_watches(conn, trans, i->node, NULL, ++ i->watch_exact, + i->perms.p ? &i->perms : NULL); +- if (tdb_delete(tdb_ctx, key)) +- goto err; + } + } + +diff --git a/tools/xenstore/xenstored_transaction.h b/tools/xenstore/xenstored_transaction.h +index 14062730e3c9..0093cac807e3 100644 +--- a/tools/xenstore/xenstored_transaction.h ++++ b/tools/xenstore/xenstored_transaction.h +@@ -42,6 +42,9 @@ void transaction_entry_dec(struct transaction *trans, unsigned int domid); + int access_node(struct connection *conn, struct node *node, + enum node_access_type type, TDB_DATA *key); + ++/* Queue watches for a modified node. */ ++void queue_watches(struct connection *conn, const char *name, bool watch_exact); ++ + /* Prepend the transaction to name if appropriate. */ + int transaction_prepend(struct connection *conn, const char *name, + TDB_DATA *key); +diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c +index 99a2c266b28a..205d9d8ea116 100644 +--- a/tools/xenstore/xenstored_watch.c ++++ b/tools/xenstore/xenstored_watch.c +@@ -29,6 +29,7 @@ + #include "xenstore_lib.h" + #include "utils.h" + #include "xenstored_domain.h" ++#include "xenstored_transaction.h" + + extern int quota_nb_watch_per_domain; + +@@ -143,9 +144,11 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, + struct connection *i; + struct watch *watch; + +- /* During transactions, don't fire watches. */ +- if (conn && conn->transaction) ++ /* During transactions, don't fire watches, but queue them. */ ++ if (conn && conn->transaction) { ++ queue_watches(conn, name, exact); + return; ++ } + + /* Create an event for each watch. */ + list_for_each_entry(i, &connections, list) { diff --git a/xsa326-4.16-xenstored-04.patch b/xsa326-4.16-xenstored-04.patch new file mode 100644 index 0000000..facbba4 --- /dev/null +++ b/xsa326-4.16-xenstored-04.patch @@ -0,0 +1,302 @@ +From 2feed737530592688382c655680982e10951c1ec Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:07 +0200 +Subject: tools/xenstore: let unread watch events time out + +A future modification will limit the number of outstanding requests +for a domain, where "outstanding" means that the response of the +request or any resulting watch event hasn't been consumed yet. + +In order to avoid a malicious guest being capable to block other guests +by not reading watch events, add a timeout for watch events. In case a +watch event hasn't been consumed after this timeout, it is being +deleted. Set the default timeout to 20 seconds (a random value being +not too high). + +In order to support to specify other timeout values in future, use a +generic command line option for that purpose: + +--timeout|-w watch-event= + +This is part of XSA-326 / CVE-2022-42311. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index bf2243873901..45244c021cd3 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -108,6 +108,8 @@ int quota_max_transaction = 10; + int quota_nb_perms_per_node = 5; + int quota_max_path_len = XENSTORE_REL_PATH_MAX; + ++unsigned int timeout_watch_event_msec = 20000; ++ + void trace(const char *fmt, ...) + { + va_list arglist; +@@ -211,19 +213,92 @@ void reopen_log(void) + } + } + ++static uint64_t get_now_msec(void) ++{ ++ struct timespec now_ts; ++ ++ if (clock_gettime(CLOCK_MONOTONIC, &now_ts)) ++ barf_perror("Could not find time (clock_gettime failed)"); ++ ++ return now_ts.tv_sec * 1000 + now_ts.tv_nsec / 1000000; ++} ++ + static void free_buffered_data(struct buffered_data *out, + struct connection *conn) + { ++ struct buffered_data *req; ++ + list_del(&out->list); ++ ++ /* ++ * Update conn->timeout_msec with the next found timeout value in the ++ * queued pending requests. ++ */ ++ if (out->timeout_msec) { ++ conn->timeout_msec = 0; ++ list_for_each_entry(req, &conn->out_list, list) { ++ if (req->timeout_msec) { ++ conn->timeout_msec = req->timeout_msec; ++ break; ++ } ++ } ++ } ++ + talloc_free(out); + } + ++static void check_event_timeout(struct connection *conn, uint64_t msecs, ++ int *ptimeout) ++{ ++ uint64_t delta; ++ struct buffered_data *out, *tmp; ++ ++ if (!conn->timeout_msec) ++ return; ++ ++ delta = conn->timeout_msec - msecs; ++ if (conn->timeout_msec <= msecs) { ++ delta = 0; ++ list_for_each_entry_safe(out, tmp, &conn->out_list, list) { ++ /* ++ * Only look at buffers with timeout and no data ++ * already written to the ring. ++ */ ++ if (out->timeout_msec && out->inhdr && !out->used) { ++ if (out->timeout_msec > msecs) { ++ conn->timeout_msec = out->timeout_msec; ++ delta = conn->timeout_msec - msecs; ++ break; ++ } ++ ++ /* ++ * Free out without updating conn->timeout_msec, ++ * as the update is done in this loop already. ++ */ ++ out->timeout_msec = 0; ++ trace("watch event path %s for domain %u timed out\n", ++ out->buffer, conn->id); ++ free_buffered_data(out, conn); ++ } ++ } ++ if (!delta) { ++ conn->timeout_msec = 0; ++ return; ++ } ++ } ++ ++ if (*ptimeout == -1 || *ptimeout > delta) ++ *ptimeout = delta; ++} ++ + void conn_free_buffered_data(struct connection *conn) + { + struct buffered_data *out; + + while ((out = list_top(&conn->out_list, struct buffered_data, list))) + free_buffered_data(out, conn); ++ ++ conn->timeout_msec = 0; + } + + static bool write_messages(struct connection *conn) +@@ -411,6 +486,7 @@ static void initialize_fds(int *p_sock_pollfd_idx, int *ptimeout) + { + struct connection *conn; + struct wrl_timestampt now; ++ uint64_t msecs; + + if (fds) + memset(fds, 0, sizeof(struct pollfd) * current_array_size); +@@ -431,10 +507,12 @@ static void initialize_fds(int *p_sock_pollfd_idx, int *ptimeout) + + wrl_gettime_now(&now); + wrl_log_periodic(now); ++ msecs = get_now_msec(); + + list_for_each_entry(conn, &connections, list) { + if (conn->domain) { + wrl_check_timeout(conn->domain, now, ptimeout); ++ check_event_timeout(conn, msecs, ptimeout); + if (conn_can_read(conn) || + (conn_can_write(conn) && + !list_empty(&conn->out_list))) +@@ -794,6 +872,7 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, + return; + bdata->inhdr = true; + bdata->used = 0; ++ bdata->timeout_msec = 0; + + if (len <= DEFAULT_BUFFER_SIZE) + bdata->buffer = bdata->default_buffer; +@@ -845,6 +924,12 @@ void send_event(struct connection *conn, const char *path, const char *token) + bdata->hdr.msg.type = XS_WATCH_EVENT; + bdata->hdr.msg.len = len; + ++ if (timeout_watch_event_msec && domain_is_unprivileged(conn)) { ++ bdata->timeout_msec = get_now_msec() + timeout_watch_event_msec; ++ if (!conn->timeout_msec) ++ conn->timeout_msec = bdata->timeout_msec; ++ } ++ + /* Queue for later transmission. */ + list_add_tail(&bdata->list, &conn->out_list); + } +@@ -2201,6 +2286,9 @@ static void usage(void) + " -t, --transaction limit the number of transaction allowed per domain,\n" + " -A, --perm-nb limit the number of permissions per node,\n" + " -M, --path-max limit the allowed Xenstore node path length,\n" ++" -w, --timeout = set the timeout in seconds for ,\n" ++" allowed timeout candidates are:\n" ++" watch-event: time a watch-event is kept pending\n" + " -R, --no-recovery to request that no recovery should be attempted when\n" + " the store is corrupted (debug only),\n" + " -I, --internal-db store database in memory, not on disk\n" +@@ -2223,6 +2311,7 @@ static struct option options[] = { + { "transaction", 1, NULL, 't' }, + { "perm-nb", 1, NULL, 'A' }, + { "path-max", 1, NULL, 'M' }, ++ { "timeout", 1, NULL, 'w' }, + { "no-recovery", 0, NULL, 'R' }, + { "internal-db", 0, NULL, 'I' }, + { "verbose", 0, NULL, 'V' }, +@@ -2236,6 +2325,39 @@ int dom0_domid = 0; + int dom0_event = 0; + int priv_domid = 0; + ++static int get_optval_int(const char *arg) ++{ ++ char *end; ++ long val; ++ ++ val = strtol(arg, &end, 10); ++ if (!*arg || *end || val < 0 || val > INT_MAX) ++ barf("invalid parameter value \"%s\"\n", arg); ++ ++ return val; ++} ++ ++static bool what_matches(const char *arg, const char *what) ++{ ++ unsigned int what_len = strlen(what); ++ ++ return !strncmp(arg, what, what_len) && arg[what_len] == '='; ++} ++ ++static void set_timeout(const char *arg) ++{ ++ const char *eq = strchr(arg, '='); ++ int val; ++ ++ if (!eq) ++ barf("quotas must be specified via =\n"); ++ val = get_optval_int(eq + 1); ++ if (what_matches(arg, "watch-event")) ++ timeout_watch_event_msec = val * 1000; ++ else ++ barf("unknown timeout \"%s\"\n", arg); ++} ++ + int main(int argc, char *argv[]) + { + int opt; +@@ -2250,7 +2372,7 @@ int main(int argc, char *argv[]) + orig_argc = argc; + orig_argv = argv; + +- while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:M:T:RVW:U", options, ++ while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:M:T:RVW:w:U", options, + NULL)) != -1) { + switch (opt) { + case 'D': +@@ -2300,6 +2422,9 @@ int main(int argc, char *argv[]) + quota_max_path_len = min(XENSTORE_REL_PATH_MAX, + quota_max_path_len); + break; ++ case 'w': ++ set_timeout(optarg); ++ break; + case 'e': + dom0_event = strtol(optarg, NULL, 10); + break; +@@ -2741,6 +2866,12 @@ static void add_buffered_data(struct buffered_data *bdata, + barf("error restoring buffered data"); + + memcpy(bdata->buffer, data, len); ++ if (bdata->hdr.msg.type == XS_WATCH_EVENT && timeout_watch_event_msec && ++ domain_is_unprivileged(conn)) { ++ bdata->timeout_msec = get_now_msec() + timeout_watch_event_msec; ++ if (!conn->timeout_msec) ++ conn->timeout_msec = bdata->timeout_msec; ++ } + + /* Queue for later transmission. */ + list_add_tail(&bdata->list, &conn->out_list); +diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h +index e7ee87825c3b..8a81fc693f01 100644 +--- a/tools/xenstore/xenstored_core.h ++++ b/tools/xenstore/xenstored_core.h +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + #include + + #include "xenstore_lib.h" +@@ -67,6 +68,8 @@ struct buffered_data + char raw[sizeof(struct xsd_sockmsg)]; + } hdr; + ++ uint64_t timeout_msec; ++ + /* The actual data. */ + char *buffer; + char default_buffer[DEFAULT_BUFFER_SIZE]; +@@ -118,6 +121,7 @@ struct connection + + /* Buffered output data */ + struct list_head out_list; ++ uint64_t timeout_msec; + + /* Transaction context for current request (NULL if none). */ + struct transaction *transaction; +@@ -244,6 +248,8 @@ extern int dom0_event; + extern int priv_domid; + extern int quota_nb_entry_per_domain; + ++extern unsigned int timeout_watch_event_msec; ++ + /* Map the kernel's xenstore page. */ + void *xenbus_map(void); + void unmap_xenbus(void *interface); diff --git a/xsa326-4.16-xenstored-05.patch b/xsa326-4.16-xenstored-05.patch new file mode 100644 index 0000000..77f9c25 --- /dev/null +++ b/xsa326-4.16-xenstored-05.patch @@ -0,0 +1,443 @@ +From 2eee122a45eb4a218596b103ce7f0759a824cf2e Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:08 +0200 +Subject: tools/xenstore: limit outstanding requests + +Add another quota for limiting the number of outstanding requests of a +guest. As the way to specify quotas on the command line is becoming +rather nasty, switch to a new scheme using [--quota|-Q] = +allowing to add more quotas in future easily. + +Set the default value to 20 (basically a random value not seeming to +be too high or too low). + +A request is said to be outstanding if any message generated by this +request (the direct response plus potential watch events) is not yet +completely stored into a ring buffer. The initial watch event sent as +a result of registering a watch is an exception. + +Note that across a live update the relation to buffered watch events +for other domains is lost. + +Use talloc_zero() for allocating the domain structure in order to have +all per-domain quota zeroed initially. + +This is part of XSA-326 / CVE-2022-42312. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Acked-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 45244c021cd3..488d540f3a32 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -107,6 +107,7 @@ int quota_max_entry_size = 2048; /* 2K */ + int quota_max_transaction = 10; + int quota_nb_perms_per_node = 5; + int quota_max_path_len = XENSTORE_REL_PATH_MAX; ++int quota_req_outstanding = 20; + + unsigned int timeout_watch_event_msec = 20000; + +@@ -223,12 +224,24 @@ static uint64_t get_now_msec(void) + return now_ts.tv_sec * 1000 + now_ts.tv_nsec / 1000000; + } + ++/* ++ * Remove a struct buffered_data from the list of outgoing data. ++ * A struct buffered_data related to a request having caused watch events to be ++ * sent is kept until all those events have been written out. ++ * Each watch event is referencing the related request via pend.req, while the ++ * number of watch events caused by a request is kept in pend.ref.event_cnt ++ * (those two cases are mutually exclusive, so the two fields can share memory ++ * via a union). ++ * The struct buffered_data is freed only if no related watch event is ++ * referencing it. The related return data can be freed right away. ++ */ + static void free_buffered_data(struct buffered_data *out, + struct connection *conn) + { + struct buffered_data *req; + + list_del(&out->list); ++ out->on_out_list = false; + + /* + * Update conn->timeout_msec with the next found timeout value in the +@@ -244,6 +257,30 @@ static void free_buffered_data(struct buffered_data *out, + } + } + ++ if (out->hdr.msg.type == XS_WATCH_EVENT) { ++ req = out->pend.req; ++ if (req) { ++ req->pend.ref.event_cnt--; ++ if (!req->pend.ref.event_cnt && !req->on_out_list) { ++ if (req->on_ref_list) { ++ domain_outstanding_domid_dec( ++ req->pend.ref.domid); ++ list_del(&req->list); ++ } ++ talloc_free(req); ++ } ++ } ++ } else if (out->pend.ref.event_cnt) { ++ /* Hang out off from conn. */ ++ talloc_steal(NULL, out); ++ if (out->buffer != out->default_buffer) ++ talloc_free(out->buffer); ++ list_add(&out->list, &conn->ref_list); ++ out->on_ref_list = true; ++ return; ++ } else ++ domain_outstanding_dec(conn); ++ + talloc_free(out); + } + +@@ -405,6 +442,7 @@ int delay_request(struct connection *conn, struct buffered_data *in, + static int destroy_conn(void *_conn) + { + struct connection *conn = _conn; ++ struct buffered_data *req; + + /* Flush outgoing if possible, but don't block. */ + if (!conn->domain) { +@@ -418,6 +456,11 @@ static int destroy_conn(void *_conn) + break; + close(conn->fd); + } ++ ++ conn_free_buffered_data(conn); ++ list_for_each_entry(req, &conn->ref_list, list) ++ req->on_ref_list = false; ++ + if (conn->target) + talloc_unlink(conn, conn->target); + list_del(&conn->list); +@@ -893,6 +936,8 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, + + /* Queue for later transmission. */ + list_add_tail(&bdata->list, &conn->out_list); ++ bdata->on_out_list = true; ++ domain_outstanding_inc(conn); + } + + /* +@@ -900,7 +945,8 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, + * As this is not directly related to the current command, errors can't be + * reported. + */ +-void send_event(struct connection *conn, const char *path, const char *token) ++void send_event(struct buffered_data *req, struct connection *conn, ++ const char *path, const char *token) + { + struct buffered_data *bdata; + unsigned int len; +@@ -930,8 +976,13 @@ void send_event(struct connection *conn, const char *path, const char *token) + conn->timeout_msec = bdata->timeout_msec; + } + ++ bdata->pend.req = req; ++ if (req) ++ req->pend.ref.event_cnt++; ++ + /* Queue for later transmission. */ + list_add_tail(&bdata->list, &conn->out_list); ++ bdata->on_out_list = true; + } + + /* Some routines (write, mkdir, etc) just need a non-error return */ +@@ -1740,6 +1791,7 @@ static void handle_input(struct connection *conn) + return; + } + in = conn->in; ++ in->pend.ref.domid = conn->id; + + /* Not finished header yet? */ + if (in->inhdr) { +@@ -1808,6 +1860,7 @@ struct connection *new_connection(const struct interface_funcs *funcs) + new->is_stalled = false; + new->transaction_started = 0; + INIT_LIST_HEAD(&new->out_list); ++ INIT_LIST_HEAD(&new->ref_list); + INIT_LIST_HEAD(&new->watches); + INIT_LIST_HEAD(&new->transaction_list); + INIT_LIST_HEAD(&new->delayed); +@@ -2286,6 +2339,9 @@ static void usage(void) + " -t, --transaction limit the number of transaction allowed per domain,\n" + " -A, --perm-nb limit the number of permissions per node,\n" + " -M, --path-max limit the allowed Xenstore node path length,\n" ++" -Q, --quota = set the quota to the value , allowed\n" ++" quotas are:\n" ++" outstanding: number of outstanding requests\n" + " -w, --timeout = set the timeout in seconds for ,\n" + " allowed timeout candidates are:\n" + " watch-event: time a watch-event is kept pending\n" +@@ -2311,6 +2367,7 @@ static struct option options[] = { + { "transaction", 1, NULL, 't' }, + { "perm-nb", 1, NULL, 'A' }, + { "path-max", 1, NULL, 'M' }, ++ { "quota", 1, NULL, 'Q' }, + { "timeout", 1, NULL, 'w' }, + { "no-recovery", 0, NULL, 'R' }, + { "internal-db", 0, NULL, 'I' }, +@@ -2358,6 +2415,20 @@ static void set_timeout(const char *arg) + barf("unknown timeout \"%s\"\n", arg); + } + ++static void set_quota(const char *arg) ++{ ++ const char *eq = strchr(arg, '='); ++ int val; ++ ++ if (!eq) ++ barf("quotas must be specified via =\n"); ++ val = get_optval_int(eq + 1); ++ if (what_matches(arg, "outstanding")) ++ quota_req_outstanding = val; ++ else ++ barf("unknown quota \"%s\"\n", arg); ++} ++ + int main(int argc, char *argv[]) + { + int opt; +@@ -2372,8 +2443,8 @@ int main(int argc, char *argv[]) + orig_argc = argc; + orig_argv = argv; + +- while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:M:T:RVW:w:U", options, +- NULL)) != -1) { ++ while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:M:Q:T:RVW:w:U", ++ options, NULL)) != -1) { + switch (opt) { + case 'D': + no_domain_init = true; +@@ -2422,6 +2493,9 @@ int main(int argc, char *argv[]) + quota_max_path_len = min(XENSTORE_REL_PATH_MAX, + quota_max_path_len); + break; ++ case 'Q': ++ set_quota(optarg); ++ break; + case 'w': + set_timeout(optarg); + break; +@@ -2875,6 +2949,14 @@ static void add_buffered_data(struct buffered_data *bdata, + + /* Queue for later transmission. */ + list_add_tail(&bdata->list, &conn->out_list); ++ bdata->on_out_list = true; ++ /* ++ * Watch events are never "outstanding", but the request causing them ++ * are instead kept "outstanding" until all watch events caused by that ++ * request have been delivered. ++ */ ++ if (bdata->hdr.msg.type != XS_WATCH_EVENT) ++ domain_outstanding_inc(conn); + } + + void read_state_buffered_data(const void *ctx, struct connection *conn, +diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h +index 8a81fc693f01..db09f463a657 100644 +--- a/tools/xenstore/xenstored_core.h ++++ b/tools/xenstore/xenstored_core.h +@@ -56,6 +56,8 @@ struct xs_state_connection; + struct buffered_data + { + struct list_head list; ++ bool on_out_list; ++ bool on_ref_list; + + /* Are we still doing the header? */ + bool inhdr; +@@ -63,6 +65,17 @@ struct buffered_data + /* How far are we? */ + unsigned int used; + ++ /* Outstanding request accounting. */ ++ union { ++ /* ref is being used for requests. */ ++ struct { ++ unsigned int event_cnt; /* # of outstanding events. */ ++ unsigned int domid; /* domid of request. */ ++ } ref; ++ /* req is being used for watch events. */ ++ struct buffered_data *req; /* request causing event. */ ++ } pend; ++ + union { + struct xsd_sockmsg msg; + char raw[sizeof(struct xsd_sockmsg)]; +@@ -123,6 +136,9 @@ struct connection + struct list_head out_list; + uint64_t timeout_msec; + ++ /* Referenced requests no longer pending. */ ++ struct list_head ref_list; ++ + /* Transaction context for current request (NULL if none). */ + struct transaction *transaction; + +@@ -191,7 +207,8 @@ unsigned int get_string(const struct buffered_data *data, unsigned int offset); + + void send_reply(struct connection *conn, enum xsd_sockmsg_type type, + const void *data, unsigned int len); +-void send_event(struct connection *conn, const char *path, const char *token); ++void send_event(struct buffered_data *req, struct connection *conn, ++ const char *path, const char *token); + + /* Some routines (write, mkdir, etc) just need a non-error return */ + void send_ack(struct connection *conn, enum xsd_sockmsg_type type); +@@ -247,6 +264,7 @@ extern int dom0_domid; + extern int dom0_event; + extern int priv_domid; + extern int quota_nb_entry_per_domain; ++extern int quota_req_outstanding; + + extern unsigned int timeout_watch_event_msec; + +diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c +index 93c4c1edcdd1..850085a92c76 100644 +--- a/tools/xenstore/xenstored_domain.c ++++ b/tools/xenstore/xenstored_domain.c +@@ -78,6 +78,9 @@ struct domain + /* number of watch for this domain */ + int nbwatch; + ++ /* Number of outstanding requests. */ ++ int nboutstanding; ++ + /* write rate limit */ + wrl_creditt wrl_credit; /* [ -wrl_config_writecost, +_dburst ] */ + struct wrl_timestampt wrl_timestamp; +@@ -183,8 +186,12 @@ static bool domain_can_read(struct connection *conn) + { + struct xenstore_domain_interface *intf = conn->domain->interface; + +- if (domain_is_unprivileged(conn) && conn->domain->wrl_credit < 0) +- return false; ++ if (domain_is_unprivileged(conn)) { ++ if (conn->domain->wrl_credit < 0) ++ return false; ++ if (conn->domain->nboutstanding >= quota_req_outstanding) ++ return false; ++ } + + return (intf->req_cons != intf->req_prod); + } +@@ -331,7 +338,7 @@ static struct domain *alloc_domain(const void *context, unsigned int domid) + { + struct domain *domain; + +- domain = talloc(context, struct domain); ++ domain = talloc_zero(context, struct domain); + if (!domain) { + errno = ENOMEM; + return NULL; +@@ -392,9 +399,6 @@ static int new_domain(struct domain *domain, int port, bool restore) + domain->conn->domain = domain; + domain->conn->id = domain->domid; + +- domain->nbentry = 0; +- domain->nbwatch = 0; +- + return 0; + } + +@@ -938,6 +942,28 @@ int domain_watch(struct connection *conn) + : 0; + } + ++void domain_outstanding_inc(struct connection *conn) ++{ ++ if (!conn || !conn->domain) ++ return; ++ conn->domain->nboutstanding++; ++} ++ ++void domain_outstanding_dec(struct connection *conn) ++{ ++ if (!conn || !conn->domain) ++ return; ++ conn->domain->nboutstanding--; ++} ++ ++void domain_outstanding_domid_dec(unsigned int domid) ++{ ++ struct domain *d = find_domain_by_domid(domid); ++ ++ if (d) ++ d->nboutstanding--; ++} ++ + static wrl_creditt wrl_config_writecost = WRL_FACTOR; + static wrl_creditt wrl_config_rate = WRL_RATE * WRL_FACTOR; + static wrl_creditt wrl_config_dburst = WRL_DBURST * WRL_FACTOR; +diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h +index 1e929b8f8c6f..4f51b005291a 100644 +--- a/tools/xenstore/xenstored_domain.h ++++ b/tools/xenstore/xenstored_domain.h +@@ -64,6 +64,9 @@ int domain_entry(struct connection *conn); + void domain_watch_inc(struct connection *conn); + void domain_watch_dec(struct connection *conn); + int domain_watch(struct connection *conn); ++void domain_outstanding_inc(struct connection *conn); ++void domain_outstanding_dec(struct connection *conn); ++void domain_outstanding_domid_dec(unsigned int domid); + + /* Special node permission handling. */ + int set_perms_special(struct connection *conn, const char *name, +diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c +index 205d9d8ea116..0755ffa375ba 100644 +--- a/tools/xenstore/xenstored_watch.c ++++ b/tools/xenstore/xenstored_watch.c +@@ -142,6 +142,7 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, + struct node *node, bool exact, struct node_perms *perms) + { + struct connection *i; ++ struct buffered_data *req; + struct watch *watch; + + /* During transactions, don't fire watches, but queue them. */ +@@ -150,6 +151,8 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, + return; + } + ++ req = domain_is_unprivileged(conn) ? conn->in : NULL; ++ + /* Create an event for each watch. */ + list_for_each_entry(i, &connections, list) { + /* introduce/release domain watches */ +@@ -164,12 +167,12 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, + list_for_each_entry(watch, &i->watches, list) { + if (exact) { + if (streq(name, watch->node)) +- send_event(i, ++ send_event(req, i, + get_watch_path(watch, name), + watch->token); + } else { + if (is_child(name, watch->node)) +- send_event(i, ++ send_event(req, i, + get_watch_path(watch, name), + watch->token); + } +@@ -269,8 +272,12 @@ int do_watch(struct connection *conn, struct buffered_data *in) + trace_create(watch, "watch"); + send_ack(conn, XS_WATCH); + +- /* We fire once up front: simplifies clients and restart. */ +- send_event(conn, get_watch_path(watch, watch->node), watch->token); ++ /* ++ * We fire once up front: simplifies clients and restart. ++ * This event will not be linked to the XS_WATCH request. ++ */ ++ send_event(NULL, conn, get_watch_path(watch, watch->node), ++ watch->token); + + return 0; + } diff --git a/xsa326-4.16-xenstored-06.patch b/xsa326-4.16-xenstored-06.patch new file mode 100644 index 0000000..b2be7ce --- /dev/null +++ b/xsa326-4.16-xenstored-06.patch @@ -0,0 +1,85 @@ +From c8057cb483abf2cd4060b39616423e19283fbd0a Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:08 +0200 +Subject: tools/xenstore: don't buffer multiple identical watch events + +A guest not reading its Xenstore response buffer fast enough might +pile up lots of Xenstore watch events buffered. Reduce the generated +load by dropping new events which already have an identical copy +pending. + +The special events "@..." are excluded from that handling as there are +known use cases where the handler is relying on each event to be sent +individually. + +This is part of XSA-326. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 488d540f3a32..f1fa97b8cf50 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -916,6 +916,7 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, + bdata->inhdr = true; + bdata->used = 0; + bdata->timeout_msec = 0; ++ bdata->watch_event = false; + + if (len <= DEFAULT_BUFFER_SIZE) + bdata->buffer = bdata->default_buffer; +@@ -948,7 +949,7 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, + void send_event(struct buffered_data *req, struct connection *conn, + const char *path, const char *token) + { +- struct buffered_data *bdata; ++ struct buffered_data *bdata, *bd; + unsigned int len; + + len = strlen(path) + 1 + strlen(token) + 1; +@@ -970,12 +971,29 @@ void send_event(struct buffered_data *req, struct connection *conn, + bdata->hdr.msg.type = XS_WATCH_EVENT; + bdata->hdr.msg.len = len; + ++ /* ++ * Check whether an identical event is pending already. ++ * Special events are excluded from that check. ++ */ ++ if (path[0] != '@') { ++ list_for_each_entry(bd, &conn->out_list, list) { ++ if (bd->watch_event && bd->hdr.msg.len == len && ++ !memcmp(bdata->buffer, bd->buffer, len)) { ++ trace("dropping duplicate watch %s %s for domain %u\n", ++ path, token, conn->id); ++ talloc_free(bdata); ++ return; ++ } ++ } ++ } ++ + if (timeout_watch_event_msec && domain_is_unprivileged(conn)) { + bdata->timeout_msec = get_now_msec() + timeout_watch_event_msec; + if (!conn->timeout_msec) + conn->timeout_msec = bdata->timeout_msec; + } + ++ bdata->watch_event = true; + bdata->pend.req = req; + if (req) + req->pend.ref.event_cnt++; +diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h +index db09f463a657..b9b50e81c7b4 100644 +--- a/tools/xenstore/xenstored_core.h ++++ b/tools/xenstore/xenstored_core.h +@@ -62,6 +62,9 @@ struct buffered_data + /* Are we still doing the header? */ + bool inhdr; + ++ /* Is this a watch event? */ ++ bool watch_event; ++ + /* How far are we? */ + unsigned int used; + diff --git a/xsa326-4.16-xenstored-07.patch b/xsa326-4.16-xenstored-07.patch new file mode 100644 index 0000000..dfcdc14 --- /dev/null +++ b/xsa326-4.16-xenstored-07.patch @@ -0,0 +1,53 @@ +From 5eac692b841633be3e85f0125c59fa02af103989 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:08 +0200 +Subject: tools/xenstore: fix connection->id usage + +Don't use conn->id for privilege checks, but domain_is_unprivileged(). + +This is part of XSA-326. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_control.c b/tools/xenstore/xenstored_control.c +index 7b4300ef7777..adb8d51b043b 100644 +--- a/tools/xenstore/xenstored_control.c ++++ b/tools/xenstore/xenstored_control.c +@@ -891,7 +891,7 @@ int do_control(struct connection *conn, struct buffered_data *in) + unsigned int cmd, num, off; + char **vec = NULL; + +- if (conn->id != 0) ++ if (domain_is_unprivileged(conn)) + return EACCES; + + off = get_string(in, 0); +diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h +index b9b50e81c7b4..b1a70488b989 100644 +--- a/tools/xenstore/xenstored_core.h ++++ b/tools/xenstore/xenstored_core.h +@@ -123,7 +123,7 @@ struct connection + /* The index of pollfd in global pollfd array */ + int pollfd_idx; + +- /* Who am I? 0 for socket connections. */ ++ /* Who am I? Domid of connection. */ + unsigned int id; + + /* Is this connection ignored? */ +diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c +index 54432907fc76..ee1b09031a3b 100644 +--- a/tools/xenstore/xenstored_transaction.c ++++ b/tools/xenstore/xenstored_transaction.c +@@ -477,7 +477,8 @@ int do_transaction_start(struct connection *conn, struct buffered_data *in) + if (conn->transaction) + return EBUSY; + +- if (conn->id && conn->transaction_started > quota_max_transaction) ++ if (domain_is_unprivileged(conn) && ++ conn->transaction_started > quota_max_transaction) + return ENOSPC; + + /* Attach transaction to input for autofree until it's complete */ diff --git a/xsa326-4.16-xenstored-08.patch b/xsa326-4.16-xenstored-08.patch new file mode 100644 index 0000000..5aa12c8 --- /dev/null +++ b/xsa326-4.16-xenstored-08.patch @@ -0,0 +1,326 @@ +From f9f3171441b5fcb3339cf612400794fc26cd2ec2 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:08 +0200 +Subject: tools/xenstore: simplify and fix per domain node accounting + +The accounting of nodes can be simplified now that each connection +holds the associated domid. + +Fix the node accounting to cover nodes created for a domain before it +has been introduced. This requires to react properly to an allocation +failure inside domain_entry_inc() by returning an error code. + +Especially in error paths the node accounting has to be fixed in some +cases. + +This is part of XSA-326 / CVE-2022-42313. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index f1fa97b8cf50..692d863fce35 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -638,7 +638,7 @@ struct node *read_node(struct connection *conn, const void *ctx, + + /* Permissions are struct xs_permissions. */ + node->perms.p = hdr->perms; +- if (domain_adjust_node_perms(node)) { ++ if (domain_adjust_node_perms(conn, node)) { + talloc_free(node); + return NULL; + } +@@ -660,7 +660,7 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, + void *p; + struct xs_tdb_record_hdr *hdr; + +- if (domain_adjust_node_perms(node)) ++ if (domain_adjust_node_perms(conn, node)) + return errno; + + data.dsize = sizeof(*hdr) +@@ -1272,13 +1272,17 @@ static struct node *construct_node(struct connection *conn, const void *ctx, + return NULL; + } + +-static int destroy_node(struct connection *conn, struct node *node) ++static void destroy_node_rm(struct node *node) + { + if (streq(node->name, "/")) + corrupt(NULL, "Destroying root node!"); + + tdb_delete(tdb_ctx, node->key); ++} + ++static int destroy_node(struct connection *conn, struct node *node) ++{ ++ destroy_node_rm(node); + domain_entry_dec(conn, node); + + /* +@@ -1328,8 +1332,12 @@ static struct node *create_node(struct connection *conn, const void *ctx, + goto err; + + /* Account for new node */ +- if (i->parent) +- domain_entry_inc(conn, i); ++ if (i->parent) { ++ if (domain_entry_inc(conn, i)) { ++ destroy_node_rm(i); ++ return NULL; ++ } ++ } + } + + return node; +@@ -1614,10 +1622,27 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) + old_perms = node->perms; + domain_entry_dec(conn, node); + node->perms = perms; +- domain_entry_inc(conn, node); ++ if (domain_entry_inc(conn, node)) { ++ node->perms = old_perms; ++ /* ++ * This should never fail because we had a reference on the ++ * domain before and Xenstored is single-threaded. ++ */ ++ domain_entry_inc(conn, node); ++ return ENOMEM; ++ } ++ ++ if (write_node(conn, node, false)) { ++ int saved_errno = errno; + +- if (write_node(conn, node, false)) ++ domain_entry_dec(conn, node); ++ node->perms = old_perms; ++ /* No failure possible as above. */ ++ domain_entry_inc(conn, node); ++ ++ errno = saved_errno; + return errno; ++ } + + fire_watches(conn, in, name, node, false, &old_perms); + send_ack(conn, XS_SET_PERMS); +@@ -3122,7 +3147,9 @@ void read_state_node(const void *ctx, const void *state) + set_tdb_key(name, &key); + if (write_node_raw(NULL, &key, node, true)) + barf("write node error restoring node"); +- domain_entry_inc(&conn, node); ++ ++ if (domain_entry_inc(&conn, node)) ++ barf("node accounting error restoring node"); + + talloc_free(node); + } +diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c +index 850085a92c76..260952e09096 100644 +--- a/tools/xenstore/xenstored_domain.c ++++ b/tools/xenstore/xenstored_domain.c +@@ -16,6 +16,7 @@ + along with this program; If not, see . + */ + ++#include + #include + #include + #include +@@ -363,6 +364,18 @@ static struct domain *find_or_alloc_domain(const void *ctx, unsigned int domid) + return domain ? : alloc_domain(ctx, domid); + } + ++static struct domain *find_or_alloc_existing_domain(unsigned int domid) ++{ ++ struct domain *domain; ++ xc_dominfo_t dominfo; ++ ++ domain = find_domain_struct(domid); ++ if (!domain && get_domain_info(domid, &dominfo)) ++ domain = alloc_domain(NULL, domid); ++ ++ return domain; ++} ++ + static int new_domain(struct domain *domain, int port, bool restore) + { + int rc; +@@ -782,30 +795,28 @@ void domain_deinit(void) + xenevtchn_unbind(xce_handle, virq_port); + } + +-void domain_entry_inc(struct connection *conn, struct node *node) ++int domain_entry_inc(struct connection *conn, struct node *node) + { + struct domain *d; ++ unsigned int domid; + + if (!conn) +- return; ++ return 0; + +- if (node->perms.p && node->perms.p[0].id != conn->id) { +- if (conn->transaction) { +- transaction_entry_inc(conn->transaction, +- node->perms.p[0].id); +- } else { +- d = find_domain_by_domid(node->perms.p[0].id); +- if (d) +- d->nbentry++; +- } +- } else if (conn->domain) { +- if (conn->transaction) { +- transaction_entry_inc(conn->transaction, +- conn->domain->domid); +- } else { +- conn->domain->nbentry++; +- } ++ domid = node->perms.p ? node->perms.p[0].id : conn->id; ++ ++ if (conn->transaction) { ++ transaction_entry_inc(conn->transaction, domid); ++ } else { ++ d = (domid == conn->id && conn->domain) ? conn->domain ++ : find_or_alloc_existing_domain(domid); ++ if (d) ++ d->nbentry++; ++ else ++ return ENOMEM; + } ++ ++ return 0; + } + + /* +@@ -841,7 +852,7 @@ static int chk_domain_generation(unsigned int domid, uint64_t gen) + * Remove permissions for no longer existing domains in order to avoid a new + * domain with the same domid inheriting the permissions. + */ +-int domain_adjust_node_perms(struct node *node) ++int domain_adjust_node_perms(struct connection *conn, struct node *node) + { + unsigned int i; + int ret; +@@ -851,8 +862,14 @@ int domain_adjust_node_perms(struct node *node) + return errno; + + /* If the owner doesn't exist any longer give it to priv domain. */ +- if (!ret) ++ if (!ret) { ++ /* ++ * In theory we'd need to update the number of dom0 nodes here, ++ * but we could be called for a read of the node. So better ++ * avoid the risk to overflow the node count of dom0. ++ */ + node->perms.p[0].id = priv_domid; ++ } + + for (i = 1; i < node->perms.num; i++) { + if (node->perms.p[i].perms & XS_PERM_IGNORE) +@@ -871,25 +888,25 @@ int domain_adjust_node_perms(struct node *node) + void domain_entry_dec(struct connection *conn, struct node *node) + { + struct domain *d; ++ unsigned int domid; + + if (!conn) + return; + +- if (node->perms.p && node->perms.p[0].id != conn->id) { +- if (conn->transaction) { +- transaction_entry_dec(conn->transaction, +- node->perms.p[0].id); +- } else { +- d = find_domain_by_domid(node->perms.p[0].id); +- if (d && d->nbentry) +- d->nbentry--; +- } +- } else if (conn->domain && conn->domain->nbentry) { +- if (conn->transaction) { +- transaction_entry_dec(conn->transaction, +- conn->domain->domid); ++ domid = node->perms.p ? node->perms.p[0].id : conn->id; ++ ++ if (conn->transaction) { ++ transaction_entry_dec(conn->transaction, domid); ++ } else { ++ d = (domid == conn->id && conn->domain) ? conn->domain ++ : find_domain_struct(domid); ++ if (d) { ++ d->nbentry--; + } else { +- conn->domain->nbentry--; ++ errno = ENOENT; ++ corrupt(conn, ++ "Node \"%s\" owned by non-existing domain %u\n", ++ node->name, domid); + } + } + } +@@ -899,13 +916,23 @@ int domain_entry_fix(unsigned int domid, int num, bool update) + struct domain *d; + int cnt; + +- d = find_domain_by_domid(domid); +- if (!d) +- return 0; ++ if (update) { ++ d = find_domain_struct(domid); ++ assert(d); ++ } else { ++ /* ++ * We are called first with update == false in order to catch ++ * any error. So do a possible allocation and check for error ++ * only in this case, as in the case of update == true nothing ++ * can go wrong anymore as the allocation already happened. ++ */ ++ d = find_or_alloc_existing_domain(domid); ++ if (!d) ++ return -1; ++ } + + cnt = d->nbentry + num; +- if (cnt < 0) +- cnt = 0; ++ assert(cnt >= 0); + + if (update) + d->nbentry = cnt; +diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h +index 4f51b005291a..d6519904d831 100644 +--- a/tools/xenstore/xenstored_domain.h ++++ b/tools/xenstore/xenstored_domain.h +@@ -54,10 +54,10 @@ const char *get_implicit_path(const struct connection *conn); + bool domain_is_unprivileged(struct connection *conn); + + /* Remove node permissions for no longer existing domains. */ +-int domain_adjust_node_perms(struct node *node); ++int domain_adjust_node_perms(struct connection *conn, struct node *node); + + /* Quota manipulation */ +-void domain_entry_inc(struct connection *conn, struct node *); ++int domain_entry_inc(struct connection *conn, struct node *); + void domain_entry_dec(struct connection *conn, struct node *); + int domain_entry_fix(unsigned int domid, int num, bool update); + int domain_entry(struct connection *conn); +diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c +index ee1b09031a3b..86caf6c398be 100644 +--- a/tools/xenstore/xenstored_transaction.c ++++ b/tools/xenstore/xenstored_transaction.c +@@ -519,8 +519,12 @@ static int transaction_fix_domains(struct transaction *trans, bool update) + + list_for_each_entry(d, &trans->changed_domains, list) { + cnt = domain_entry_fix(d->domid, d->nbentry, update); +- if (!update && cnt >= quota_nb_entry_per_domain) +- return ENOSPC; ++ if (!update) { ++ if (cnt >= quota_nb_entry_per_domain) ++ return ENOSPC; ++ if (cnt < 0) ++ return ENOMEM; ++ } + } + + return 0; diff --git a/xsa326-4.16-xenstored-09.patch b/xsa326-4.16-xenstored-09.patch new file mode 100644 index 0000000..e2c163c --- /dev/null +++ b/xsa326-4.16-xenstored-09.patch @@ -0,0 +1,244 @@ +From 71aac6f7e89d5c101adb9e82eea7031e16d34e46 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:09 +0200 +Subject: tools/xenstore: limit max number of nodes accessed in a transaction + +Today a guest is free to access as many nodes in a single transaction +as it wants. This can lead to unbounded memory consumption in Xenstore +as there is the need to keep track of all nodes having been accessed +during a transaction. + +In oxenstored the number of requests in a transaction is being limited +via a quota maxrequests (default is 1024). As multiple accesses of a +node are not problematic in C Xenstore, limit the number of accessed +nodes. + +In order to let read_node() detect a quota error in case too many nodes +are being accessed, check the return value of access_node() and return +NULL in case an error has been seen. Introduce __must_check and add it +to the access_node() prototype. + +This is part of XSA-326 / CVE-2022-42314. + +Reported-by: Julien Grall +Suggested-by: Julien Grall +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall + +diff --git a/tools/include/xen-tools/libs.h b/tools/include/xen-tools/libs.h +index a16e0c380709..bafc90e2f603 100644 +--- a/tools/include/xen-tools/libs.h ++++ b/tools/include/xen-tools/libs.h +@@ -63,4 +63,8 @@ + #define ROUNDUP(_x,_w) (((unsigned long)(_x)+(1UL<<(_w))-1) & ~((1UL<<(_w))-1)) + #endif + ++#ifndef __must_check ++#define __must_check __attribute__((__warn_unused_result__)) ++#endif ++ + #endif /* __XEN_TOOLS_LIBS__ */ +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 692d863fce35..f835aa1b2f1f 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -106,6 +106,7 @@ int quota_nb_watch_per_domain = 128; + int quota_max_entry_size = 2048; /* 2K */ + int quota_max_transaction = 10; + int quota_nb_perms_per_node = 5; ++int quota_trans_nodes = 1024; + int quota_max_path_len = XENSTORE_REL_PATH_MAX; + int quota_req_outstanding = 20; + +@@ -595,6 +596,7 @@ struct node *read_node(struct connection *conn, const void *ctx, + TDB_DATA key, data; + struct xs_tdb_record_hdr *hdr; + struct node *node; ++ int err; + + node = talloc(ctx, struct node); + if (!node) { +@@ -616,14 +618,13 @@ struct node *read_node(struct connection *conn, const void *ctx, + if (data.dptr == NULL) { + if (tdb_error(tdb_ctx) == TDB_ERR_NOEXIST) { + node->generation = NO_GENERATION; +- access_node(conn, node, NODE_ACCESS_READ, NULL); +- errno = ENOENT; ++ err = access_node(conn, node, NODE_ACCESS_READ, NULL); ++ errno = err ? : ENOENT; + } else { + log("TDB error on read: %s", tdb_errorstr(tdb_ctx)); + errno = EIO; + } +- talloc_free(node); +- return NULL; ++ goto error; + } + + node->parent = NULL; +@@ -638,19 +639,36 @@ struct node *read_node(struct connection *conn, const void *ctx, + + /* Permissions are struct xs_permissions. */ + node->perms.p = hdr->perms; +- if (domain_adjust_node_perms(conn, node)) { +- talloc_free(node); +- return NULL; +- } ++ if (domain_adjust_node_perms(conn, node)) ++ goto error; + + /* Data is binary blob (usually ascii, no nul). */ + node->data = node->perms.p + hdr->num_perms; + /* Children is strings, nul separated. */ + node->children = node->data + node->datalen; + +- access_node(conn, node, NODE_ACCESS_READ, NULL); ++ if (access_node(conn, node, NODE_ACCESS_READ, NULL)) ++ goto error; + + return node; ++ ++ error: ++ err = errno; ++ talloc_free(node); ++ errno = err; ++ return NULL; ++} ++ ++static bool read_node_can_propagate_errno(void) ++{ ++ /* ++ * 2 error cases for read_node() can always be propagated up: ++ * ENOMEM, because this has nothing to do with the node being in the ++ * data base or not, but is caused by a general lack of memory. ++ * ENOSPC, because this is related to hitting quota limits which need ++ * to be respected. ++ */ ++ return errno == ENOMEM || errno == ENOSPC; + } + + int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, +@@ -767,7 +785,7 @@ static int ask_parents(struct connection *conn, const void *ctx, + node = read_node(conn, ctx, name); + if (node) + break; +- if (errno == ENOMEM) ++ if (read_node_can_propagate_errno()) + return errno; + } while (!streq(name, "/")); + +@@ -829,7 +847,7 @@ static struct node *get_node(struct connection *conn, + } + } + /* Clean up errno if they weren't supposed to know. */ +- if (!node && errno != ENOMEM) ++ if (!node && !read_node_can_propagate_errno()) + errno = errno_from_parents(conn, ctx, name, errno, perm); + return node; + } +@@ -1235,7 +1253,7 @@ static struct node *construct_node(struct connection *conn, const void *ctx, + + /* If parent doesn't exist, create it. */ + parent = read_node(conn, parentname, parentname); +- if (!parent) ++ if (!parent && errno == ENOENT) + parent = construct_node(conn, ctx, parentname); + if (!parent) + return NULL; +@@ -1509,7 +1527,7 @@ static int _rm(struct connection *conn, const void *ctx, struct node *node, + + parent = read_node(conn, ctx, parentname); + if (!parent) +- return (errno == ENOMEM) ? ENOMEM : EINVAL; ++ return read_node_can_propagate_errno() ? errno : EINVAL; + node->parent = parent; + + return delete_node(conn, ctx, parent, node, false); +@@ -1539,7 +1557,7 @@ static int do_rm(struct connection *conn, struct buffered_data *in) + return 0; + } + /* Restore errno, just in case. */ +- if (errno != ENOMEM) ++ if (!read_node_can_propagate_errno()) + errno = ENOENT; + } + return errno; +@@ -2384,6 +2402,8 @@ static void usage(void) + " -M, --path-max limit the allowed Xenstore node path length,\n" + " -Q, --quota = set the quota to the value , allowed\n" + " quotas are:\n" ++" transaction-nodes: number of accessed node per\n" ++" transaction\n" + " outstanding: number of outstanding requests\n" + " -w, --timeout = set the timeout in seconds for ,\n" + " allowed timeout candidates are:\n" +@@ -2468,6 +2488,8 @@ static void set_quota(const char *arg) + val = get_optval_int(eq + 1); + if (what_matches(arg, "outstanding")) + quota_req_outstanding = val; ++ else if (what_matches(arg, "transaction-nodes")) ++ quota_trans_nodes = val; + else + barf("unknown quota \"%s\"\n", arg); + } +diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h +index b1a70488b989..245f9258235f 100644 +--- a/tools/xenstore/xenstored_core.h ++++ b/tools/xenstore/xenstored_core.h +@@ -268,6 +268,7 @@ extern int dom0_event; + extern int priv_domid; + extern int quota_nb_entry_per_domain; + extern int quota_req_outstanding; ++extern int quota_trans_nodes; + + extern unsigned int timeout_watch_event_msec; + +diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c +index 86caf6c398be..7bd41eb475e3 100644 +--- a/tools/xenstore/xenstored_transaction.c ++++ b/tools/xenstore/xenstored_transaction.c +@@ -156,6 +156,9 @@ struct transaction + /* Connection-local identifier for this transaction. */ + uint32_t id; + ++ /* Node counter. */ ++ unsigned int nodes; ++ + /* Generation when transaction started. */ + uint64_t generation; + +@@ -260,6 +263,11 @@ int access_node(struct connection *conn, struct node *node, + + i = find_accessed_node(trans, node->name); + if (!i) { ++ if (trans->nodes >= quota_trans_nodes && ++ domain_is_unprivileged(conn)) { ++ ret = ENOSPC; ++ goto err; ++ } + i = talloc_zero(trans, struct accessed_node); + if (!i) + goto nomem; +@@ -297,6 +305,7 @@ int access_node(struct connection *conn, struct node *node, + i->ta_node = true; + } + } ++ trans->nodes++; + list_add_tail(&i->list, &trans->accessed); + } + +diff --git a/tools/xenstore/xenstored_transaction.h b/tools/xenstore/xenstored_transaction.h +index 0093cac807e3..e3cbd6b23095 100644 +--- a/tools/xenstore/xenstored_transaction.h ++++ b/tools/xenstore/xenstored_transaction.h +@@ -39,8 +39,8 @@ void transaction_entry_inc(struct transaction *trans, unsigned int domid); + void transaction_entry_dec(struct transaction *trans, unsigned int domid); + + /* This node was accessed. */ +-int access_node(struct connection *conn, struct node *node, +- enum node_access_type type, TDB_DATA *key); ++int __must_check access_node(struct connection *conn, struct node *node, ++ enum node_access_type type, TDB_DATA *key); + + /* Queue watches for a modified node. */ + void queue_watches(struct connection *conn, const char *name, bool watch_exact); diff --git a/xsa326-4.16-xenstored-10.patch b/xsa326-4.16-xenstored-10.patch new file mode 100644 index 0000000..30e6b50 --- /dev/null +++ b/xsa326-4.16-xenstored-10.patch @@ -0,0 +1,88 @@ +From 90013d6a735491a7b93a6832eb2a51e5633254f5 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:09 +0200 +Subject: tools/xenstore: move the call of setup_structure() to dom0 + introduction + +Setting up the basic structure when introducing dom0 has the advantage +to be able to add proper node memory accounting for the added nodes +later. + +This makes it possible to do proper node accounting, too. + +An additional requirement to make that work fine is to correct the +owner of the created nodes to be dom0_domid instead of domid 0. + +This is part of XSA-326. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Acked-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index f835aa1b2f1f..5171d34c947e 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -2039,7 +2039,8 @@ static int tdb_flags; + static void manual_node(const char *name, const char *child) + { + struct node *node; +- struct xs_permissions perms = { .id = 0, .perms = XS_PERM_NONE }; ++ struct xs_permissions perms = { .id = dom0_domid, ++ .perms = XS_PERM_NONE }; + + node = talloc_zero(NULL, struct node); + if (!node) +@@ -2078,7 +2079,7 @@ static void tdb_logger(TDB_CONTEXT *tdb, int level, const char * fmt, ...) + } + } + +-static void setup_structure(bool live_update) ++void setup_structure(bool live_update) + { + char *tdbname; + +@@ -2101,6 +2102,7 @@ static void setup_structure(bool live_update) + manual_node("/", "tool"); + manual_node("/tool", "xenstored"); + manual_node("/tool/xenstored", NULL); ++ domain_entry_fix(dom0_domid, 3, true); + } + + check_store(); +@@ -2614,9 +2616,6 @@ int main(int argc, char *argv[]) + + init_pipe(reopen_log_pipe); + +- /* Setup the database */ +- setup_structure(live_update); +- + /* Listen to hypervisor. */ + if (!no_domain_init && !live_update) { + domain_init(-1); +diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h +index 245f9258235f..2c77ec7ee0f4 100644 +--- a/tools/xenstore/xenstored_core.h ++++ b/tools/xenstore/xenstored_core.h +@@ -231,6 +231,7 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, + struct node *read_node(struct connection *conn, const void *ctx, + const char *name); + ++void setup_structure(bool live_update); + struct connection *new_connection(const struct interface_funcs *funcs); + struct connection *get_connection_by_id(unsigned int conn_id); + void ignore_connection(struct connection *conn); +diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c +index 260952e09096..f04b7aae8a32 100644 +--- a/tools/xenstore/xenstored_domain.c ++++ b/tools/xenstore/xenstored_domain.c +@@ -470,6 +470,9 @@ static struct domain *introduce_domain(const void *ctx, + } + domain->interface = interface; + ++ if (is_master_domain) ++ setup_structure(restore); ++ + /* Now domain belongs to its connection. */ + talloc_steal(domain->conn, domain); + diff --git a/xsa326-4.16-xenstored-11.patch b/xsa326-4.16-xenstored-11.patch new file mode 100644 index 0000000..49f1497 --- /dev/null +++ b/xsa326-4.16-xenstored-11.patch @@ -0,0 +1,280 @@ +From 6af17b8bf52b9dfdc6a5ecd3efbcea9fddd57d91 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:09 +0200 +Subject: tools/xenstore: add infrastructure to keep track of per domain memory + usage + +The amount of memory a domain can consume in Xenstore is limited by +various quota today, but even with sane quota a domain can still +consume rather large memory quantities. + +Add the infrastructure for keeping track of the amount of memory a +domain is consuming in Xenstore. Note that this is only the memory a +domain has direct control over, so any internal administration data +needed by Xenstore only is not being accounted for. + +There are two quotas defined: a soft quota which will result in a +warning issued via syslog() when it is exceeded, and a hard quota +resulting in a stop of accepting further requests or watch events as +long as the hard quota would be violated by accepting those. + +Setting any of those quotas to 0 will disable it. + +As default values use 2MB per domain for the soft limit (this basically +covers the allowed case to create 1000 nodes needing 2kB each), and +2.5MB for the hard limit. + +This is part of XSA-326. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 5171d34c947e..b2bf6740d430 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -109,6 +109,8 @@ int quota_nb_perms_per_node = 5; + int quota_trans_nodes = 1024; + int quota_max_path_len = XENSTORE_REL_PATH_MAX; + int quota_req_outstanding = 20; ++int quota_memory_per_domain_soft = 2 * 1024 * 1024; /* 2 MB */ ++int quota_memory_per_domain_hard = 2 * 1024 * 1024 + 512 * 1024; /* 2.5 MB */ + + unsigned int timeout_watch_event_msec = 20000; + +@@ -2406,7 +2408,14 @@ static void usage(void) + " quotas are:\n" + " transaction-nodes: number of accessed node per\n" + " transaction\n" ++" memory: total used memory per domain for nodes,\n" ++" transactions, watches and requests, above\n" ++" which Xenstore will stop talking to domain\n" + " outstanding: number of outstanding requests\n" ++" -q, --quota-soft = set a soft quota to the value ,\n" ++" causing a warning to be issued via syslog() if the\n" ++" limit is violated, allowed quotas are:\n" ++" memory: see above\n" + " -w, --timeout = set the timeout in seconds for ,\n" + " allowed timeout candidates are:\n" + " watch-event: time a watch-event is kept pending\n" +@@ -2433,6 +2442,7 @@ static struct option options[] = { + { "perm-nb", 1, NULL, 'A' }, + { "path-max", 1, NULL, 'M' }, + { "quota", 1, NULL, 'Q' }, ++ { "quota-soft", 1, NULL, 'q' }, + { "timeout", 1, NULL, 'w' }, + { "no-recovery", 0, NULL, 'R' }, + { "internal-db", 0, NULL, 'I' }, +@@ -2480,7 +2490,7 @@ static void set_timeout(const char *arg) + barf("unknown timeout \"%s\"\n", arg); + } + +-static void set_quota(const char *arg) ++static void set_quota(const char *arg, bool soft) + { + const char *eq = strchr(arg, '='); + int val; +@@ -2488,11 +2498,16 @@ static void set_quota(const char *arg) + if (!eq) + barf("quotas must be specified via =\n"); + val = get_optval_int(eq + 1); +- if (what_matches(arg, "outstanding")) ++ if (what_matches(arg, "outstanding") && !soft) + quota_req_outstanding = val; +- else if (what_matches(arg, "transaction-nodes")) ++ else if (what_matches(arg, "transaction-nodes") && !soft) + quota_trans_nodes = val; +- else ++ else if (what_matches(arg, "memory")) { ++ if (soft) ++ quota_memory_per_domain_soft = val; ++ else ++ quota_memory_per_domain_hard = val; ++ } else + barf("unknown quota \"%s\"\n", arg); + } + +@@ -2510,7 +2525,7 @@ int main(int argc, char *argv[]) + orig_argc = argc; + orig_argv = argv; + +- while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:M:Q:T:RVW:w:U", ++ while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:M:Q:q:T:RVW:w:U", + options, NULL)) != -1) { + switch (opt) { + case 'D': +@@ -2561,7 +2576,10 @@ int main(int argc, char *argv[]) + quota_max_path_len); + break; + case 'Q': +- set_quota(optarg); ++ set_quota(optarg, false); ++ break; ++ case 'q': ++ set_quota(optarg, true); + break; + case 'w': + set_timeout(optarg); +diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h +index 2c77ec7ee0f4..373af18297bf 100644 +--- a/tools/xenstore/xenstored_core.h ++++ b/tools/xenstore/xenstored_core.h +@@ -270,6 +270,8 @@ extern int priv_domid; + extern int quota_nb_entry_per_domain; + extern int quota_req_outstanding; + extern int quota_trans_nodes; ++extern int quota_memory_per_domain_soft; ++extern int quota_memory_per_domain_hard; + + extern unsigned int timeout_watch_event_msec; + +diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c +index f04b7aae8a32..94fd561e9de4 100644 +--- a/tools/xenstore/xenstored_domain.c ++++ b/tools/xenstore/xenstored_domain.c +@@ -76,6 +76,13 @@ struct domain + /* number of entry from this domain in the store */ + int nbentry; + ++ /* Amount of memory allocated for this domain. */ ++ int memory; ++ bool soft_quota_reported; ++ bool hard_quota_reported; ++ time_t mem_last_msg; ++#define MEM_WARN_MINTIME_SEC 10 ++ + /* number of watch for this domain */ + int nbwatch; + +@@ -192,6 +199,9 @@ static bool domain_can_read(struct connection *conn) + return false; + if (conn->domain->nboutstanding >= quota_req_outstanding) + return false; ++ if (conn->domain->memory >= quota_memory_per_domain_hard && ++ quota_memory_per_domain_hard) ++ return false; + } + + return (intf->req_cons != intf->req_prod); +@@ -950,6 +960,89 @@ int domain_entry(struct connection *conn) + : 0; + } + ++static bool domain_chk_quota(struct domain *domain, int mem) ++{ ++ time_t now; ++ ++ if (!domain || !domid_is_unprivileged(domain->domid) || ++ (domain->conn && domain->conn->is_ignored)) ++ return false; ++ ++ now = time(NULL); ++ ++ if (mem >= quota_memory_per_domain_hard && ++ quota_memory_per_domain_hard) { ++ if (domain->hard_quota_reported) ++ return true; ++ syslog(LOG_ERR, "Domain %u exceeds hard memory quota, Xenstore interface to domain stalled\n", ++ domain->domid); ++ domain->mem_last_msg = now; ++ domain->hard_quota_reported = true; ++ return true; ++ } ++ ++ if (now - domain->mem_last_msg >= MEM_WARN_MINTIME_SEC) { ++ if (domain->hard_quota_reported) { ++ domain->mem_last_msg = now; ++ domain->hard_quota_reported = false; ++ syslog(LOG_INFO, "Domain %u below hard memory quota again\n", ++ domain->domid); ++ } ++ if (mem >= quota_memory_per_domain_soft && ++ quota_memory_per_domain_soft && ++ !domain->soft_quota_reported) { ++ domain->mem_last_msg = now; ++ domain->soft_quota_reported = true; ++ syslog(LOG_WARNING, "Domain %u exceeds soft memory quota\n", ++ domain->domid); ++ } ++ if (mem < quota_memory_per_domain_soft && ++ domain->soft_quota_reported) { ++ domain->mem_last_msg = now; ++ domain->soft_quota_reported = false; ++ syslog(LOG_INFO, "Domain %u below soft memory quota again\n", ++ domain->domid); ++ } ++ ++ } ++ ++ return false; ++} ++ ++int domain_memory_add(unsigned int domid, int mem, bool no_quota_check) ++{ ++ struct domain *domain; ++ ++ domain = find_domain_struct(domid); ++ if (domain) { ++ /* ++ * domain_chk_quota() will print warning and also store whether ++ * the soft/hard quota has been hit. So check no_quota_check ++ * *after*. ++ */ ++ if (domain_chk_quota(domain, domain->memory + mem) && ++ !no_quota_check) ++ return ENOMEM; ++ domain->memory += mem; ++ } else { ++ /* ++ * The domain the memory is to be accounted for should always ++ * exist, as accounting is done either for a domain related to ++ * the current connection, or for the domain owning a node ++ * (which is always existing, as the owner of the node is ++ * tested to exist and replaced by domid 0 if not). ++ * So not finding the related domain MUST be an error in the ++ * data base. ++ */ ++ errno = ENOENT; ++ corrupt(NULL, "Accounting called for non-existing domain %u\n", ++ domid); ++ return ENOENT; ++ } ++ ++ return 0; ++} ++ + void domain_watch_inc(struct connection *conn) + { + if (!conn || !conn->domain) +diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h +index d6519904d831..633c9a0a0a1f 100644 +--- a/tools/xenstore/xenstored_domain.h ++++ b/tools/xenstore/xenstored_domain.h +@@ -61,6 +61,26 @@ int domain_entry_inc(struct connection *conn, struct node *); + void domain_entry_dec(struct connection *conn, struct node *); + int domain_entry_fix(unsigned int domid, int num, bool update); + int domain_entry(struct connection *conn); ++int domain_memory_add(unsigned int domid, int mem, bool no_quota_check); ++ ++/* ++ * domain_memory_add_chk(): to be used when memory quota should be checked. ++ * Not to be used when specifying a negative mem value, as lowering the used ++ * memory should always be allowed. ++ */ ++static inline int domain_memory_add_chk(unsigned int domid, int mem) ++{ ++ return domain_memory_add(domid, mem, false); ++} ++/* ++ * domain_memory_add_nochk(): to be used when memory quota should not be ++ * checked, e.g. when lowering memory usage, or in an error case for undoing ++ * a previous memory adjustment. ++ */ ++static inline void domain_memory_add_nochk(unsigned int domid, int mem) ++{ ++ domain_memory_add(domid, mem, true); ++} + void domain_watch_inc(struct connection *conn); + void domain_watch_dec(struct connection *conn); + int domain_watch(struct connection *conn); diff --git a/xsa326-4.16-xenstored-12.patch b/xsa326-4.16-xenstored-12.patch new file mode 100644 index 0000000..517f2a0 --- /dev/null +++ b/xsa326-4.16-xenstored-12.patch @@ -0,0 +1,76 @@ +From ae7042f024af7584251f776a12d9bb24d13fecaf Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:09 +0200 +Subject: tools/xenstore: add memory accounting for responses + +Add the memory accounting for queued responses. + +In case adding a watch event for a guest is causing the hard memory +quota of that guest to be violated, the event is dropped. This will +ensure that it is impossible to drive another guest past its memory +quota by generating insane amounts of events for that guest. This is +especially important for protecting driver domains from that attack +vector. + +This is part of XSA-326 / CVE-2022-42315. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index b2bf6740d430..ecab6cfbbe15 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -260,6 +260,8 @@ static void free_buffered_data(struct buffered_data *out, + } + } + ++ domain_memory_add_nochk(conn->id, -out->hdr.msg.len - sizeof(out->hdr)); ++ + if (out->hdr.msg.type == XS_WATCH_EVENT) { + req = out->pend.req; + if (req) { +@@ -938,11 +940,14 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, + bdata->timeout_msec = 0; + bdata->watch_event = false; + +- if (len <= DEFAULT_BUFFER_SIZE) ++ if (len <= DEFAULT_BUFFER_SIZE) { + bdata->buffer = bdata->default_buffer; +- else { ++ /* Don't check quota, path might be used for returning error. */ ++ domain_memory_add_nochk(conn->id, len + sizeof(bdata->hdr)); ++ } else { + bdata->buffer = talloc_array(bdata, char, len); +- if (!bdata->buffer) { ++ if (!bdata->buffer || ++ domain_memory_add_chk(conn->id, len + sizeof(bdata->hdr))) { + send_error(conn, ENOMEM); + return; + } +@@ -1007,6 +1012,11 @@ void send_event(struct buffered_data *req, struct connection *conn, + } + } + ++ if (domain_memory_add_chk(conn->id, len + sizeof(bdata->hdr))) { ++ talloc_free(bdata); ++ return; ++ } ++ + if (timeout_watch_event_msec && domain_is_unprivileged(conn)) { + bdata->timeout_msec = get_now_msec() + timeout_watch_event_msec; + if (!conn->timeout_msec) +@@ -3039,6 +3049,12 @@ static void add_buffered_data(struct buffered_data *bdata, + */ + if (bdata->hdr.msg.type != XS_WATCH_EVENT) + domain_outstanding_inc(conn); ++ /* ++ * We are restoring the state after Live-Update and the new quota may ++ * be smaller. So ignore it. The limit will be applied for any resource ++ * after the state has been fully restored. ++ */ ++ domain_memory_add_nochk(conn->id, len + sizeof(bdata->hdr)); + } + + void read_state_buffered_data(const void *ctx, struct connection *conn, diff --git a/xsa326-4.16-xenstored-13.patch b/xsa326-4.16-xenstored-13.patch new file mode 100644 index 0000000..c37dd1e --- /dev/null +++ b/xsa326-4.16-xenstored-13.patch @@ -0,0 +1,89 @@ +From 4628ae0a56b037dcdc8a3e42c543c5b9fd9990cf Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:10 +0200 +Subject: tools/xenstore: add memory accounting for watches + +Add the memory accounting for registered watches. + +When a socket connection is destroyed, the associated watches are +removed, too. In order to keep memory accounting correct the watches +must be removed explicitly via a call of conn_delete_all_watches() from +destroy_conn(). + +This is part of XSA-326 / CVE-2022-42315. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index ecab6cfbbe15..d86942f5aa77 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -463,6 +463,7 @@ static int destroy_conn(void *_conn) + } + + conn_free_buffered_data(conn); ++ conn_delete_all_watches(conn); + list_for_each_entry(req, &conn->ref_list, list) + req->on_ref_list = false; + +diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c +index 0755ffa375ba..fdf9b2d653a0 100644 +--- a/tools/xenstore/xenstored_watch.c ++++ b/tools/xenstore/xenstored_watch.c +@@ -211,7 +211,7 @@ static int check_watch_path(struct connection *conn, const void *ctx, + } + + static struct watch *add_watch(struct connection *conn, char *path, char *token, +- bool relative) ++ bool relative, bool no_quota_check) + { + struct watch *watch; + +@@ -222,6 +222,9 @@ static struct watch *add_watch(struct connection *conn, char *path, char *token, + watch->token = talloc_strdup(watch, token); + if (!watch->node || !watch->token) + goto nomem; ++ if (domain_memory_add(conn->id, strlen(path) + strlen(token), ++ no_quota_check)) ++ goto nomem; + + if (relative) + watch->relative_path = get_implicit_path(conn); +@@ -265,7 +268,7 @@ int do_watch(struct connection *conn, struct buffered_data *in) + if (domain_watch(conn) > quota_nb_watch_per_domain) + return E2BIG; + +- watch = add_watch(conn, vec[0], vec[1], relative); ++ watch = add_watch(conn, vec[0], vec[1], relative, false); + if (!watch) + return errno; + +@@ -296,6 +299,8 @@ int do_unwatch(struct connection *conn, struct buffered_data *in) + list_for_each_entry(watch, &conn->watches, list) { + if (streq(watch->node, node) && streq(watch->token, vec[1])) { + list_del(&watch->list); ++ domain_memory_add_nochk(conn->id, -strlen(watch->node) - ++ strlen(watch->token)); + talloc_free(watch); + domain_watch_dec(conn); + send_ack(conn, XS_UNWATCH); +@@ -311,6 +316,8 @@ void conn_delete_all_watches(struct connection *conn) + + while ((watch = list_top(&conn->watches, struct watch, list))) { + list_del(&watch->list); ++ domain_memory_add_nochk(conn->id, -strlen(watch->node) - ++ strlen(watch->token)); + talloc_free(watch); + domain_watch_dec(conn); + } +@@ -373,7 +380,7 @@ void read_state_watch(const void *ctx, const void *state) + if (!path) + barf("allocation error for read watch"); + +- if (!add_watch(conn, path, token, relative)) ++ if (!add_watch(conn, path, token, relative, true)) + barf("error adding watch"); + } + diff --git a/xsa326-4.16-xenstored-14.patch b/xsa326-4.16-xenstored-14.patch new file mode 100644 index 0000000..df898c4 --- /dev/null +++ b/xsa326-4.16-xenstored-14.patch @@ -0,0 +1,334 @@ +From b8bd74e5e962955211ab0c5c1924ebf2bb526799 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:10 +0200 +Subject: tools/xenstore: add memory accounting for nodes + +Add the memory accounting for Xenstore nodes. In order to make this +not too complicated allow for some sloppiness when writing nodes. Any +hard quota violation will result in no further requests to be accepted. + +This is part of XSA-326 / CVE-2022-42315. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index d86942f5aa77..16504de42017 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -591,6 +591,117 @@ void set_tdb_key(const char *name, TDB_DATA *key) + key->dsize = strlen(name); + } + ++static void get_acc_data(TDB_DATA *key, struct node_account_data *acc) ++{ ++ TDB_DATA old_data; ++ struct xs_tdb_record_hdr *hdr; ++ ++ if (acc->memory < 0) { ++ old_data = tdb_fetch(tdb_ctx, *key); ++ /* No check for error, as the node might not exist. */ ++ if (old_data.dptr == NULL) { ++ acc->memory = 0; ++ } else { ++ hdr = (void *)old_data.dptr; ++ acc->memory = old_data.dsize; ++ acc->domid = hdr->perms[0].id; ++ } ++ talloc_free(old_data.dptr); ++ } ++} ++ ++/* ++ * Per-transaction nodes need to be accounted for the transaction owner. ++ * Those nodes are stored in the data base with the transaction generation ++ * count prepended (e.g. 123/local/domain/...). So testing for the node's ++ * key not to start with "/" is sufficient. ++ */ ++static unsigned int get_acc_domid(struct connection *conn, TDB_DATA *key, ++ unsigned int domid) ++{ ++ return (!conn || key->dptr[0] == '/') ? domid : conn->id; ++} ++ ++int do_tdb_write(struct connection *conn, TDB_DATA *key, TDB_DATA *data, ++ struct node_account_data *acc, bool no_quota_check) ++{ ++ struct xs_tdb_record_hdr *hdr = (void *)data->dptr; ++ struct node_account_data old_acc = {}; ++ unsigned int old_domid, new_domid; ++ int ret; ++ ++ if (!acc) ++ old_acc.memory = -1; ++ else ++ old_acc = *acc; ++ ++ get_acc_data(key, &old_acc); ++ old_domid = get_acc_domid(conn, key, old_acc.domid); ++ new_domid = get_acc_domid(conn, key, hdr->perms[0].id); ++ ++ /* ++ * Don't check for ENOENT, as we want to be able to switch orphaned ++ * nodes to new owners. ++ */ ++ if (old_acc.memory) ++ domain_memory_add_nochk(old_domid, ++ -old_acc.memory - key->dsize); ++ ret = domain_memory_add(new_domid, data->dsize + key->dsize, ++ no_quota_check); ++ if (ret) { ++ /* Error path, so no quota check. */ ++ if (old_acc.memory) ++ domain_memory_add_nochk(old_domid, ++ old_acc.memory + key->dsize); ++ return ret; ++ } ++ ++ /* TDB should set errno, but doesn't even set ecode AFAICT. */ ++ if (tdb_store(tdb_ctx, *key, *data, TDB_REPLACE) != 0) { ++ domain_memory_add_nochk(new_domid, -data->dsize - key->dsize); ++ /* Error path, so no quota check. */ ++ if (old_acc.memory) ++ domain_memory_add_nochk(old_domid, ++ old_acc.memory + key->dsize); ++ errno = EIO; ++ return errno; ++ } ++ ++ if (acc) { ++ /* Don't use new_domid, as it might be a transaction node. */ ++ acc->domid = hdr->perms[0].id; ++ acc->memory = data->dsize; ++ } ++ ++ return 0; ++} ++ ++int do_tdb_delete(struct connection *conn, TDB_DATA *key, ++ struct node_account_data *acc) ++{ ++ struct node_account_data tmp_acc; ++ unsigned int domid; ++ ++ if (!acc) { ++ acc = &tmp_acc; ++ acc->memory = -1; ++ } ++ ++ get_acc_data(key, acc); ++ ++ if (tdb_delete(tdb_ctx, *key)) { ++ errno = EIO; ++ return errno; ++ } ++ ++ if (acc->memory) { ++ domid = get_acc_domid(conn, key, acc->domid); ++ domain_memory_add_nochk(domid, -acc->memory - key->dsize); ++ } ++ ++ return 0; ++} ++ + /* + * If it fails, returns NULL and sets errno. + * Temporary memory allocations will be done with ctx. +@@ -644,9 +755,15 @@ struct node *read_node(struct connection *conn, const void *ctx, + + /* Permissions are struct xs_permissions. */ + node->perms.p = hdr->perms; ++ node->acc.domid = node->perms.p[0].id; ++ node->acc.memory = data.dsize; + if (domain_adjust_node_perms(conn, node)) + goto error; + ++ /* If owner is gone reset currently accounted memory size. */ ++ if (node->acc.domid != node->perms.p[0].id) ++ node->acc.memory = 0; ++ + /* Data is binary blob (usually ascii, no nul). */ + node->data = node->perms.p + hdr->num_perms; + /* Children is strings, nul separated. */ +@@ -715,12 +832,9 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, + p += node->datalen; + memcpy(p, node->children, node->childlen); + +- /* TDB should set errno, but doesn't even set ecode AFAICT. */ +- if (tdb_store(tdb_ctx, *key, data, TDB_REPLACE) != 0) { +- corrupt(conn, "Write of %s failed", key->dptr); +- errno = EIO; +- return errno; +- } ++ if (do_tdb_write(conn, key, &data, &node->acc, no_quota_check)) ++ return EIO; ++ + return 0; + } + +@@ -1222,7 +1336,7 @@ static void delete_node_single(struct connection *conn, struct node *node) + if (access_node(conn, node, NODE_ACCESS_DELETE, &key)) + return; + +- if (tdb_delete(tdb_ctx, key) != 0) { ++ if (do_tdb_delete(conn, &key, &node->acc) != 0) { + corrupt(conn, "Could not delete '%s'", node->name); + return; + } +@@ -1295,6 +1409,7 @@ static struct node *construct_node(struct connection *conn, const void *ctx, + /* No children, no data */ + node->children = node->data = NULL; + node->childlen = node->datalen = 0; ++ node->acc.memory = 0; + node->parent = parent; + return node; + +@@ -1303,17 +1418,17 @@ static struct node *construct_node(struct connection *conn, const void *ctx, + return NULL; + } + +-static void destroy_node_rm(struct node *node) ++static void destroy_node_rm(struct connection *conn, struct node *node) + { + if (streq(node->name, "/")) + corrupt(NULL, "Destroying root node!"); + +- tdb_delete(tdb_ctx, node->key); ++ do_tdb_delete(conn, &node->key, &node->acc); + } + + static int destroy_node(struct connection *conn, struct node *node) + { +- destroy_node_rm(node); ++ destroy_node_rm(conn, node); + domain_entry_dec(conn, node); + + /* +@@ -1365,7 +1480,7 @@ static struct node *create_node(struct connection *conn, const void *ctx, + /* Account for new node */ + if (i->parent) { + if (domain_entry_inc(conn, i)) { +- destroy_node_rm(i); ++ destroy_node_rm(conn, i); + return NULL; + } + } +@@ -2291,7 +2406,7 @@ static int clean_store_(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA val, + if (!hashtable_search(reachable, name)) { + log("clean_store: '%s' is orphaned!", name); + if (recovery) { +- tdb_delete(tdb, key); ++ do_tdb_delete(NULL, &key, NULL); + } + } + +@@ -3149,6 +3264,7 @@ void read_state_node(const void *ctx, const void *state) + if (!node) + barf("allocation error restoring node"); + ++ node->acc.memory = 0; + node->name = name; + node->generation = ++generation; + node->datalen = sn->data_len; +diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h +index 373af18297bf..da9ecce67f31 100644 +--- a/tools/xenstore/xenstored_core.h ++++ b/tools/xenstore/xenstored_core.h +@@ -176,6 +176,11 @@ struct node_perms { + struct xs_permissions *p; + }; + ++struct node_account_data { ++ unsigned int domid; ++ int memory; /* -1 if unknown */ ++}; ++ + struct node { + const char *name; + /* Key used to update TDB */ +@@ -198,6 +203,9 @@ struct node { + /* Children, each nul-terminated. */ + unsigned int childlen; + char *children; ++ ++ /* Allocation information for node currently in store. */ ++ struct node_account_data acc; + }; + + /* Return the only argument in the input. */ +@@ -306,6 +314,10 @@ extern xengnttab_handle **xgt_handle; + int remember_string(struct hashtable *hash, const char *str); + + void set_tdb_key(const char *name, TDB_DATA *key); ++int do_tdb_write(struct connection *conn, TDB_DATA *key, TDB_DATA *data, ++ struct node_account_data *acc, bool no_quota_check); ++int do_tdb_delete(struct connection *conn, TDB_DATA *key, ++ struct node_account_data *acc); + + void conn_free_buffered_data(struct connection *conn); + +diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c +index 7bd41eb475e3..ace9a11d77bb 100644 +--- a/tools/xenstore/xenstored_transaction.c ++++ b/tools/xenstore/xenstored_transaction.c +@@ -153,6 +153,9 @@ struct transaction + /* List of all transactions active on this connection. */ + struct list_head list; + ++ /* Connection this transaction is associated with. */ ++ struct connection *conn; ++ + /* Connection-local identifier for this transaction. */ + uint32_t id; + +@@ -286,6 +289,8 @@ int access_node(struct connection *conn, struct node *node, + + introduce = true; + i->ta_node = false; ++ /* acc.memory < 0 means "unknown, get size from TDB". */ ++ node->acc.memory = -1; + + /* + * Additional transaction-specific node for read type. We only +@@ -410,11 +415,11 @@ static int finalize_transaction(struct connection *conn, + goto err; + hdr = (void *)data.dptr; + hdr->generation = ++generation; +- ret = tdb_store(tdb_ctx, key, data, +- TDB_REPLACE); ++ ret = do_tdb_write(conn, &key, &data, NULL, ++ true); + talloc_free(data.dptr); + } else { +- ret = tdb_delete(tdb_ctx, key); ++ ret = do_tdb_delete(conn, &key, NULL); + } + if (ret) + goto err; +@@ -425,7 +430,7 @@ static int finalize_transaction(struct connection *conn, + } + } + +- if (i->ta_node && tdb_delete(tdb_ctx, ta_key)) ++ if (i->ta_node && do_tdb_delete(conn, &ta_key, NULL)) + goto err; + list_del(&i->list); + talloc_free(i); +@@ -453,7 +458,7 @@ static int destroy_transaction(void *_transaction) + i->node); + if (trans_name) { + set_tdb_key(trans_name, &key); +- tdb_delete(tdb_ctx, key); ++ do_tdb_delete(trans->conn, &key, NULL); + } + } + list_del(&i->list); +@@ -497,6 +502,7 @@ int do_transaction_start(struct connection *conn, struct buffered_data *in) + + INIT_LIST_HEAD(&trans->accessed); + INIT_LIST_HEAD(&trans->changed_domains); ++ trans->conn = conn; + trans->fail = false; + trans->generation = ++generation; + diff --git a/xsa326-4.16-xenstored-15.patch b/xsa326-4.16-xenstored-15.patch new file mode 100644 index 0000000..8a42a36 --- /dev/null +++ b/xsa326-4.16-xenstored-15.patch @@ -0,0 +1,54 @@ +From c55a1ea0a5ea7f6a3dc850cb015a49ba9ec571ab Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:10 +0200 +Subject: tools/xenstore: add exports for quota variables + +Some quota variables are not exported via header files. + +This is part of XSA-326. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Acked-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h +index da9ecce67f31..bfd3fc1e9df3 100644 +--- a/tools/xenstore/xenstored_core.h ++++ b/tools/xenstore/xenstored_core.h +@@ -275,6 +275,11 @@ extern TDB_CONTEXT *tdb_ctx; + extern int dom0_domid; + extern int dom0_event; + extern int priv_domid; ++extern int quota_nb_watch_per_domain; ++extern int quota_max_transaction; ++extern int quota_max_entry_size; ++extern int quota_nb_perms_per_node; ++extern int quota_max_path_len; + extern int quota_nb_entry_per_domain; + extern int quota_req_outstanding; + extern int quota_trans_nodes; +diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c +index ace9a11d77bb..28774813de83 100644 +--- a/tools/xenstore/xenstored_transaction.c ++++ b/tools/xenstore/xenstored_transaction.c +@@ -175,7 +175,6 @@ struct transaction + bool fail; + }; + +-extern int quota_max_transaction; + uint64_t generation; + + static struct accessed_node *find_accessed_node(struct transaction *trans, +diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c +index fdf9b2d653a0..85362bcce314 100644 +--- a/tools/xenstore/xenstored_watch.c ++++ b/tools/xenstore/xenstored_watch.c +@@ -31,8 +31,6 @@ + #include "xenstored_domain.h" + #include "xenstored_transaction.h" + +-extern int quota_nb_watch_per_domain; +- + struct watch + { + /* Watches on this connection */ diff --git a/xsa326-4.16-xenstored-16.patch b/xsa326-4.16-xenstored-16.patch new file mode 100644 index 0000000..c2a8953 --- /dev/null +++ b/xsa326-4.16-xenstored-16.patch @@ -0,0 +1,238 @@ +From 05cc2af50ba43431d6d50aff758e968833aab9c6 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:10 +0200 +Subject: tools/xenstore: add control command for setting and showing quota + +Add a xenstore-control command "quota" to: +- show current quota settings +- change quota settings +- show current quota related values of a domain + +Note that in the case the new quota is lower than existing one, +Xenstored may continue to handle requests from a domain exceeding the +new limit (depends on which one has been broken) and the amount of +resource used will not change. However the domain will not be able to +create more resource (associated to the quota) until it is back to below +the limit. + +This is part of XSA-326. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall + +diff --git a/docs/misc/xenstore.txt b/docs/misc/xenstore.txt +index 334dc8b6fdf5..a7d006519ae8 100644 +--- a/docs/misc/xenstore.txt ++++ b/docs/misc/xenstore.txt +@@ -366,6 +366,17 @@ CONTROL |[|] + print| + print to syslog (xenstore runs as daemon) or + to console (xenstore runs as stubdom) ++ quota|[set |] ++ without parameters: print the current quota settings ++ with "set ": set the quota to new value ++ (The admin should make sure all the domain usage is ++ below the quota. If it is not, then Xenstored may continue to ++ handle requests from the domain as long as the resource ++ violating the new quota setting isn't increased further) ++ with "": print quota related accounting data for ++ the domain ++ quota-soft|[set ] ++ like the "quota" command, but for soft-quota. + help + return list of supported commands for CONTROL + +diff --git a/tools/xenstore/xenstored_control.c b/tools/xenstore/xenstored_control.c +index adb8d51b043b..1031a81c3874 100644 +--- a/tools/xenstore/xenstored_control.c ++++ b/tools/xenstore/xenstored_control.c +@@ -196,6 +196,115 @@ static int do_control_log(void *ctx, struct connection *conn, + return 0; + } + ++struct quota { ++ const char *name; ++ int *quota; ++ const char *descr; ++}; ++ ++static const struct quota hard_quotas[] = { ++ { "nodes", "a_nb_entry_per_domain, "Nodes per domain" }, ++ { "watches", "a_nb_watch_per_domain, "Watches per domain" }, ++ { "transactions", "a_max_transaction, "Transactions per domain" }, ++ { "outstanding", "a_req_outstanding, ++ "Outstanding requests per domain" }, ++ { "transaction-nodes", "a_trans_nodes, ++ "Max. number of accessed nodes per transaction" }, ++ { "memory", "a_memory_per_domain_hard, ++ "Total Xenstore memory per domain (error level)" }, ++ { "node-size", "a_max_entry_size, "Max. size of a node" }, ++ { "path-max", "a_max_path_len, "Max. length of a node path" }, ++ { "permissions", "a_nb_perms_per_node, ++ "Max. number of permissions per node" }, ++ { NULL, NULL, NULL } ++}; ++ ++static const struct quota soft_quotas[] = { ++ { "memory", "a_memory_per_domain_soft, ++ "Total Xenstore memory per domain (warning level)" }, ++ { NULL, NULL, NULL } ++}; ++ ++static int quota_show_current(const void *ctx, struct connection *conn, ++ const struct quota *quotas) ++{ ++ char *resp; ++ unsigned int i; ++ ++ resp = talloc_strdup(ctx, "Quota settings:\n"); ++ if (!resp) ++ return ENOMEM; ++ ++ for (i = 0; quotas[i].quota; i++) { ++ resp = talloc_asprintf_append(resp, "%-17s: %8d %s\n", ++ quotas[i].name, *quotas[i].quota, ++ quotas[i].descr); ++ if (!resp) ++ return ENOMEM; ++ } ++ ++ send_reply(conn, XS_CONTROL, resp, strlen(resp) + 1); ++ ++ return 0; ++} ++ ++static int quota_set(const void *ctx, struct connection *conn, ++ char **vec, int num, const struct quota *quotas) ++{ ++ unsigned int i; ++ int val; ++ ++ if (num != 2) ++ return EINVAL; ++ ++ val = atoi(vec[1]); ++ if (val < 1) ++ return EINVAL; ++ ++ for (i = 0; quotas[i].quota; i++) { ++ if (!strcmp(vec[0], quotas[i].name)) { ++ *quotas[i].quota = val; ++ send_ack(conn, XS_CONTROL); ++ return 0; ++ } ++ } ++ ++ return EINVAL; ++} ++ ++static int quota_get(const void *ctx, struct connection *conn, ++ char **vec, int num) ++{ ++ if (num != 1) ++ return EINVAL; ++ ++ return domain_get_quota(ctx, conn, atoi(vec[0])); ++} ++ ++static int do_control_quota(void *ctx, struct connection *conn, ++ char **vec, int num) ++{ ++ if (num == 0) ++ return quota_show_current(ctx, conn, hard_quotas); ++ ++ if (!strcmp(vec[0], "set")) ++ return quota_set(ctx, conn, vec + 1, num - 1, hard_quotas); ++ ++ return quota_get(ctx, conn, vec, num); ++} ++ ++static int do_control_quota_s(void *ctx, struct connection *conn, ++ char **vec, int num) ++{ ++ if (num == 0) ++ return quota_show_current(ctx, conn, soft_quotas); ++ ++ if (!strcmp(vec[0], "set")) ++ return quota_set(ctx, conn, vec + 1, num - 1, soft_quotas); ++ ++ return EINVAL; ++} ++ + #ifdef __MINIOS__ + static int do_control_memreport(void *ctx, struct connection *conn, + char **vec, int num) +@@ -847,6 +956,8 @@ static struct cmd_s cmds[] = { + { "memreport", do_control_memreport, "[]" }, + #endif + { "print", do_control_print, "" }, ++ { "quota", do_control_quota, "[set |]" }, ++ { "quota-soft", do_control_quota_s, "[set ]" }, + { "help", do_control_help, "" }, + }; + +diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c +index 94fd561e9de4..e7c6886ccf47 100644 +--- a/tools/xenstore/xenstored_domain.c ++++ b/tools/xenstore/xenstored_domain.c +@@ -31,6 +31,7 @@ + #include "xenstored_domain.h" + #include "xenstored_transaction.h" + #include "xenstored_watch.h" ++#include "xenstored_control.h" + + #include + #include +@@ -345,6 +346,38 @@ static struct domain *find_domain_struct(unsigned int domid) + return NULL; + } + ++int domain_get_quota(const void *ctx, struct connection *conn, ++ unsigned int domid) ++{ ++ struct domain *d = find_domain_struct(domid); ++ char *resp; ++ int ta; ++ ++ if (!d) ++ return ENOENT; ++ ++ ta = d->conn ? d->conn->transaction_started : 0; ++ resp = talloc_asprintf(ctx, "Domain %u:\n", domid); ++ if (!resp) ++ return ENOMEM; ++ ++#define ent(t, e) \ ++ resp = talloc_asprintf_append(resp, "%-16s: %8d\n", #t, e); \ ++ if (!resp) return ENOMEM ++ ++ ent(nodes, d->nbentry); ++ ent(watches, d->nbwatch); ++ ent(transactions, ta); ++ ent(outstanding, d->nboutstanding); ++ ent(memory, d->memory); ++ ++#undef ent ++ ++ send_reply(conn, XS_CONTROL, resp, strlen(resp) + 1); ++ ++ return 0; ++} ++ + static struct domain *alloc_domain(const void *context, unsigned int domid) + { + struct domain *domain; +diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h +index 633c9a0a0a1f..904faa923afb 100644 +--- a/tools/xenstore/xenstored_domain.h ++++ b/tools/xenstore/xenstored_domain.h +@@ -87,6 +87,8 @@ int domain_watch(struct connection *conn); + void domain_outstanding_inc(struct connection *conn); + void domain_outstanding_dec(struct connection *conn); + void domain_outstanding_domid_dec(unsigned int domid); ++int domain_get_quota(const void *ctx, struct connection *conn, ++ unsigned int domid); + + /* Special node permission handling. */ + int set_perms_special(struct connection *conn, const char *name, diff --git a/xsa412-4.16.patch b/xsa412-4.16.patch new file mode 100644 index 0000000..f37fc21 --- /dev/null +++ b/xsa412-4.16.patch @@ -0,0 +1,245 @@ +From: Andrew Cooper +Subject: x86/vmx: Revert "VMX: use a single, global APIC access page" + +The claim "No accesses would ever go to this page." is false. A consequence +of how Intel's APIC Acceleration works, and Xen's choice to have per-domain +P2Ms (rather than per-vCPU P2Ms) means that the APIC page is fully read-write +to any vCPU which is not in xAPIC mode. + +This reverts commit 58850b9074d3e7affdf3bc94c84e417ecfa4d165. + +This is XSA-412 / CVE-2022-42327. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c +index d429d76c18c9..3f4276531322 100644 +--- a/xen/arch/x86/hvm/vmx/vmx.c ++++ b/xen/arch/x86/hvm/vmx/vmx.c +@@ -66,7 +66,8 @@ boolean_param("force-ept", opt_force_ept); + static void vmx_ctxt_switch_from(struct vcpu *v); + static void vmx_ctxt_switch_to(struct vcpu *v); + +-static int alloc_vlapic_mapping(void); ++static int vmx_alloc_vlapic_mapping(struct domain *d); ++static void vmx_free_vlapic_mapping(struct domain *d); + static void vmx_install_vlapic_mapping(struct vcpu *v); + static void vmx_update_guest_cr(struct vcpu *v, unsigned int cr, + unsigned int flags); +@@ -77,8 +78,6 @@ static int vmx_msr_read_intercept(unsigned int msr, uint64_t *msr_content); + static int vmx_msr_write_intercept(unsigned int msr, uint64_t msr_content); + static void vmx_invlpg(struct vcpu *v, unsigned long linear); + +-static mfn_t __read_mostly apic_access_mfn = INVALID_MFN_INITIALIZER; +- + /* Values for domain's ->arch.hvm_domain.pi_ops.flags. */ + #define PI_CSW_FROM (1u << 0) + #define PI_CSW_TO (1u << 1) +@@ -402,6 +401,7 @@ static int vmx_domain_initialise(struct domain *d) + .to = vmx_ctxt_switch_to, + .tail = vmx_do_resume, + }; ++ int rc; + + d->arch.ctxt_switch = &csw; + +@@ -411,15 +411,24 @@ static int vmx_domain_initialise(struct domain *d) + */ + d->arch.hvm.vmx.exec_sp = is_hardware_domain(d) || opt_ept_exec_sp; + ++ if ( (rc = vmx_alloc_vlapic_mapping(d)) != 0 ) ++ return rc; ++ + return 0; + } + ++static void vmx_domain_relinquish_resources(struct domain *d) ++{ ++ vmx_free_vlapic_mapping(d); ++} ++ + static void domain_creation_finished(struct domain *d) + { + gfn_t gfn = gaddr_to_gfn(APIC_DEFAULT_PHYS_BASE); ++ mfn_t apic_access_mfn = d->arch.hvm.vmx.apic_access_mfn; + bool ipat; + +- if ( !has_vlapic(d) || mfn_eq(apic_access_mfn, INVALID_MFN) ) ++ if ( mfn_eq(apic_access_mfn, _mfn(0)) ) + return; + + ASSERT(epte_get_entry_emt(d, gfn, apic_access_mfn, 0, &ipat, +@@ -2481,6 +2490,7 @@ static struct hvm_function_table __initdata vmx_function_table = { + .cpu_up_prepare = vmx_cpu_up_prepare, + .cpu_dead = vmx_cpu_dead, + .domain_initialise = vmx_domain_initialise, ++ .domain_relinquish_resources = vmx_domain_relinquish_resources, + .domain_creation_finished = domain_creation_finished, + .vcpu_initialise = vmx_vcpu_initialise, + .vcpu_destroy = vmx_vcpu_destroy, +@@ -2731,7 +2741,7 @@ const struct hvm_function_table * __init start_vmx(void) + { + set_in_cr4(X86_CR4_VMXE); + +- if ( vmx_vmcs_init() || alloc_vlapic_mapping() ) ++ if ( vmx_vmcs_init() ) + { + printk("VMX: failed to initialise.\n"); + return NULL; +@@ -3305,36 +3315,55 @@ static int vmx_msr_read_intercept(unsigned int msr, uint64_t *msr_content) + return X86EMUL_EXCEPTION; + } + +-static int __init alloc_vlapic_mapping(void) ++static int vmx_alloc_vlapic_mapping(struct domain *d) + { + struct page_info *pg; + mfn_t mfn; + +- if ( !cpu_has_vmx_virtualize_apic_accesses ) ++ if ( !has_vlapic(d) || !cpu_has_vmx_virtualize_apic_accesses ) + return 0; + +- pg = alloc_domheap_page(NULL, 0); ++ pg = alloc_domheap_page(d, MEMF_no_refcount); + if ( !pg ) + return -ENOMEM; + +- /* +- * Signal to shadow code that this page cannot be refcounted. This also +- * makes epte_get_entry_emt() recognize this page as "special". +- */ +- page_suppress_refcounting(pg); ++ if ( !get_page_and_type(pg, d, PGT_writable_page) ) ++ { ++ /* ++ * The domain can't possibly know about this page yet, so failure ++ * here is a clear indication of something fishy going on. ++ */ ++ domain_crash(d); ++ return -ENODATA; ++ } + + mfn = page_to_mfn(pg); + clear_domain_page(mfn); +- apic_access_mfn = mfn; ++ d->arch.hvm.vmx.apic_access_mfn = mfn; + + return 0; + } + ++static void vmx_free_vlapic_mapping(struct domain *d) ++{ ++ mfn_t mfn = d->arch.hvm.vmx.apic_access_mfn; ++ ++ d->arch.hvm.vmx.apic_access_mfn = _mfn(0); ++ if ( !mfn_eq(mfn, _mfn(0)) ) ++ { ++ struct page_info *pg = mfn_to_page(mfn); ++ ++ put_page_alloc_ref(pg); ++ put_page_and_type(pg); ++ } ++} ++ + static void vmx_install_vlapic_mapping(struct vcpu *v) + { ++ mfn_t apic_access_mfn = v->domain->arch.hvm.vmx.apic_access_mfn; + paddr_t virt_page_ma, apic_page_ma; + +- if ( !has_vlapic(v->domain) || mfn_eq(apic_access_mfn, INVALID_MFN) ) ++ if ( mfn_eq(apic_access_mfn, _mfn(0)) ) + return; + + ASSERT(cpu_has_vmx_virtualize_apic_accesses); +diff --git a/xen/arch/x86/mm/shadow/set.c b/xen/arch/x86/mm/shadow/set.c +index 87e9c6eeb219..bd6c68b547c9 100644 +--- a/xen/arch/x86/mm/shadow/set.c ++++ b/xen/arch/x86/mm/shadow/set.c +@@ -101,14 +101,6 @@ shadow_get_page_from_l1e(shadow_l1e_t sl1e, struct domain *d, p2m_type_t type) + owner = page_get_owner(pg); + } + +- /* +- * Check whether refcounting is suppressed on this page. For example, +- * VMX'es APIC access MFN is just a surrogate page. It doesn't actually +- * get accessed, and hence there's no need to refcount it. +- */ +- if ( pg && page_refcounting_suppressed(pg) ) +- return 0; +- + if ( owner == dom_io ) + owner = NULL; + +diff --git a/xen/arch/x86/mm/shadow/types.h b/xen/arch/x86/mm/shadow/types.h +index 6970e7d6ea4a..814a4018535a 100644 +--- a/xen/arch/x86/mm/shadow/types.h ++++ b/xen/arch/x86/mm/shadow/types.h +@@ -276,16 +276,9 @@ int shadow_set_l4e(struct domain *d, shadow_l4e_t *sl4e, + static void inline + shadow_put_page_from_l1e(shadow_l1e_t sl1e, struct domain *d) + { +- mfn_t mfn = shadow_l1e_get_mfn(sl1e); +- + if ( !shadow_mode_refcounts(d) ) + return; + +- if ( mfn_valid(mfn) && +- /* See the respective comment in shadow_get_page_from_l1e(). */ +- page_refcounting_suppressed(mfn_to_page(mfn)) ) +- return; +- + put_page_from_l1e(sl1e, d); + } + +diff --git a/xen/include/asm-x86/hvm/vmx/vmcs.h b/xen/include/asm-x86/hvm/vmx/vmcs.h +index 03c9ccf627ab..8073af323b96 100644 +--- a/xen/include/asm-x86/hvm/vmx/vmcs.h ++++ b/xen/include/asm-x86/hvm/vmx/vmcs.h +@@ -58,6 +58,7 @@ struct ept_data { + #define _VMX_DOMAIN_PML_ENABLED 0 + #define VMX_DOMAIN_PML_ENABLED (1ul << _VMX_DOMAIN_PML_ENABLED) + struct vmx_domain { ++ mfn_t apic_access_mfn; + /* VMX_DOMAIN_* */ + unsigned int status; + +diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h +index 7bdf9c2290d8..e1bcea57a8f5 100644 +--- a/xen/include/asm-x86/mm.h ++++ b/xen/include/asm-x86/mm.h +@@ -83,7 +83,7 @@ + #define PGC_state_offlined PG_mask(2, 6) + #define PGC_state_free PG_mask(3, 6) + #define page_state_is(pg, st) (((pg)->count_info&PGC_state) == PGC_state_##st) +-/* Page is not reference counted (see below for caveats) */ ++/* Page is not reference counted */ + #define _PGC_extra PG_shift(7) + #define PGC_extra PG_mask(1, 7) + +@@ -375,24 +375,6 @@ void zap_ro_mpt(mfn_t mfn); + + bool is_iomem_page(mfn_t mfn); + +-/* +- * Pages with no owner which may get passed to functions wanting to +- * refcount them can be marked PGC_extra to bypass this refcounting (which +- * would fail due to the lack of an owner). +- * +- * (For pages with owner PGC_extra has different meaning.) +- */ +-static inline void page_suppress_refcounting(struct page_info *pg) +-{ +- ASSERT(!page_get_owner(pg)); +- pg->count_info |= PGC_extra; +-} +- +-static inline bool page_refcounting_suppressed(const struct page_info *pg) +-{ +- return !page_get_owner(pg) && (pg->count_info & PGC_extra); +-} +- + struct platform_bad_page { + unsigned long mfn; + unsigned int order; diff --git a/xsa414.patch b/xsa414.patch new file mode 100644 index 0000000..27ab0c2 --- /dev/null +++ b/xsa414.patch @@ -0,0 +1,112 @@ +From: Julien Grall +Subject: tools/xenstore: create_node: Don't defer work to undo any changes on + failure + +XSA-115 extended destroy_node() to update the node accounting for the +connection. The implementation is assuming the connection is the parent +of the node, however all the nodes are allocated using a separate context +(see process_message()). This will result to crash (or corrupt) xenstored +as the pointer is wrongly used. + +In case of an error, any changes to the database or update to the +accounting will now be reverted in create_node() by calling directly +destroy_node(). This has the nice advantage to remove the loop to unset +the destructors in case of success. + +Take the opportunity to free the nodes right now as they are not +going to be reachable (the function returns NULL) and are just wasting +resources. + +This is XSA-414 / CVE-2022-42309. + +Reported-by: Julien Grall +Fixes: 0bfb2101f243 ("tools/xenstore: fix node accounting after failed node creation") +Signed-off-by: Julien Grall +Reviewed-by: Juergen Gross + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 8867f93431d4..c30d14cbf2ab 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -1084,9 +1084,8 @@ static struct node *construct_node(struct connection *conn, const void *ctx, + return NULL; + } + +-static int destroy_node(void *_node) ++static int destroy_node(struct connection *conn, struct node *node) + { +- struct node *node = _node; + TDB_DATA key; + + if (streq(node->name, "/")) +@@ -1095,7 +1094,7 @@ static int destroy_node(void *_node) + set_tdb_key(node->name, &key); + tdb_delete(tdb_ctx, key); + +- domain_entry_dec(talloc_parent(node), node); ++ domain_entry_dec(conn, node); + + return 0; + } +@@ -1104,7 +1103,8 @@ static struct node *create_node(struct connection *conn, const void *ctx, + const char *name, + void *data, unsigned int datalen) + { +- struct node *node, *i; ++ struct node *node, *i, *j; ++ int ret; + + node = construct_node(conn, ctx, name); + if (!node) +@@ -1126,23 +1126,40 @@ static struct node *create_node(struct connection *conn, const void *ctx, + /* i->parent is set for each new node, so check quota. */ + if (i->parent && + domain_entry(conn) >= quota_nb_entry_per_domain) { +- errno = ENOSPC; +- return NULL; ++ ret = ENOSPC; ++ goto err; + } +- if (write_node(conn, i, false)) +- return NULL; + +- /* Account for new node, set destructor for error case. */ +- if (i->parent) { ++ ret = write_node(conn, i, false); ++ if (ret) ++ goto err; ++ ++ /* Account for new node */ ++ if (i->parent) + domain_entry_inc(conn, i); +- talloc_set_destructor(i, destroy_node); +- } + } + +- /* OK, now remove destructors so they stay around */ +- for (i = node; i->parent; i = i->parent) +- talloc_set_destructor(i, NULL); + return node; ++ ++err: ++ /* ++ * We failed to update TDB for some of the nodes. Undo any work that ++ * have already been done. ++ */ ++ for (j = node; j != i; j = j->parent) ++ destroy_node(conn, j); ++ ++ /* We don't need to keep the nodes around, so free them. */ ++ i = node; ++ while (i) { ++ j = i; ++ i = i->parent; ++ talloc_free(j); ++ } ++ ++ errno = ret; ++ ++ return NULL; + } + + /* path, data... */ diff --git a/xsa415.patch b/xsa415.patch new file mode 100644 index 0000000..b6f6971 --- /dev/null +++ b/xsa415.patch @@ -0,0 +1,134 @@ +From: Julien Grall +Subject: tools/xenstore: Fail a transaction if it is not possible to create a + node + +Commit f2bebf72c4d5 "xenstore: rework of transaction handling" moved +out from copying the entire database everytime a new transaction is +opened to track the list of nodes changed. + +The content of all the nodes accessed during a transaction will be +temporarily stored in TDB using a different key. + +The function create_node() may write/update multiple nodes if the child +doesn't exist. In case of a failure, the function will revert any +changes (this include any update to TDB). Unfortunately, the function +which reverts the changes (i.e. destroy_node()) will not use the correct +key to delete any update or even request the transaction to fail. + +This means that if a client decide to go ahead with committing the +transaction, orphan nodes will be created because they were not linked +to an existing node (create_node() will write the nodes backwards). + +Once some nodes have been partially updated in a transaction, it is not +easily possible to undo any changes. So rather than continuing and hit +weird issue while committing, it is much saner to fail the transaction. + +This will have an impact on any client that decides to commit even if it +can't write a node. Although, it is not clear why a normal client would +want to do that... + +Lastly, update destroy_node() to use the correct key for deleting the +node. Rather than recreating it (this will allocate memory and +therefore fail), stash the key in the structure node. + +This is XSA-415 / CVE-2022-42310. + +Reported-by: Julien Grall +Signed-off-by: Julien Grall +Reviewed-by: Juergen Gross + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index c30d14cbf2ab..55b79e4c032e 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -562,15 +562,17 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, + return 0; + } + ++/* ++ * Write the node. If the node is written, caller can find the key used in ++ * node->key. This can later be used if the change needs to be reverted. ++ */ + static int write_node(struct connection *conn, struct node *node, + bool no_quota_check) + { +- TDB_DATA key; +- +- if (access_node(conn, node, NODE_ACCESS_WRITE, &key)) ++ if (access_node(conn, node, NODE_ACCESS_WRITE, &node->key)) + return errno; + +- return write_node_raw(conn, &key, node, no_quota_check); ++ return write_node_raw(conn, &node->key, node, no_quota_check); + } + + unsigned int perm_for_conn(struct connection *conn, +@@ -1086,16 +1088,21 @@ static struct node *construct_node(struct connection *conn, const void *ctx, + + static int destroy_node(struct connection *conn, struct node *node) + { +- TDB_DATA key; +- + if (streq(node->name, "/")) + corrupt(NULL, "Destroying root node!"); + +- set_tdb_key(node->name, &key); +- tdb_delete(tdb_ctx, key); ++ tdb_delete(tdb_ctx, node->key); + + domain_entry_dec(conn, node); + ++ /* ++ * It is not possible to easily revert the changes in a transaction. ++ * So if the failure happens in a transaction, mark it as fail to ++ * prevent any commit. ++ */ ++ if ( conn->transaction ) ++ fail_transaction(conn->transaction); ++ + return 0; + } + +diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h +index 742812a97469..7d0fe77e7989 100644 +--- a/tools/xenstore/xenstored_core.h ++++ b/tools/xenstore/xenstored_core.h +@@ -155,6 +155,8 @@ struct node_perms { + + struct node { + const char *name; ++ /* Key used to update TDB */ ++ TDB_DATA key; + + /* Parent (optional) */ + struct node *parent; +diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c +index cd07fb0f218b..faf6c930e42a 100644 +--- a/tools/xenstore/xenstored_transaction.c ++++ b/tools/xenstore/xenstored_transaction.c +@@ -580,6 +580,11 @@ void transaction_entry_dec(struct transaction *trans, unsigned int domid) + list_add_tail(&d->list, &trans->changed_domains); + } + ++void fail_transaction(struct transaction *trans) ++{ ++ trans->fail = true; ++} ++ + void conn_delete_all_transactions(struct connection *conn) + { + struct transaction *trans; +diff --git a/tools/xenstore/xenstored_transaction.h b/tools/xenstore/xenstored_transaction.h +index 43a162bea3f3..14062730e3c9 100644 +--- a/tools/xenstore/xenstored_transaction.h ++++ b/tools/xenstore/xenstored_transaction.h +@@ -46,6 +46,9 @@ int access_node(struct connection *conn, struct node *node, + int transaction_prepend(struct connection *conn, const char *name, + TDB_DATA *key); + ++/* Mark the transaction as failed. This will prevent it to be committed. */ ++void fail_transaction(struct transaction *trans); ++ + void conn_delete_all_transactions(struct connection *conn); + int check_transactions(struct hashtable *hash); + diff --git a/xsa416-4.16.patch b/xsa416-4.16.patch new file mode 100644 index 0000000..0df409a --- /dev/null +++ b/xsa416-4.16.patch @@ -0,0 +1,704 @@ +From 80d128b14482d2e9342184d2d9949367851c4d14 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:10 +0200 +Subject: tools/xenstore: don't use conn->in as context for temporary + allocations + +Using the struct buffered data pointer of the current processed request +for temporary data allocations has a major drawback: the used area (and +with that the temporary data) is freed only after the response of the +request has been written to the ring page or has been read via the +socket. This can happen much later in case a guest isn't reading its +responses fast enough. + +As the temporary data can be safely freed after creating the response, +add a temporary context for that purpose and use that for allocating +the temporary memory, as it was already the case before commit +cc0612464896 ("xenstore: add small default data buffer to internal +struct"). + +Some sub-functions need to gain the "const" attribute for the talloc +context. + +This is XSA-416 / CVE-2022-42319. + +Reported-by: Julien Grall +Fixes: cc0612464896 ("xenstore: add small default data buffer to internal struct") +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_control.c b/tools/xenstore/xenstored_control.c +index 1031a81c3874..d0350c6ad861 100644 +--- a/tools/xenstore/xenstored_control.c ++++ b/tools/xenstore/xenstored_control.c +@@ -155,7 +155,7 @@ bool lu_is_pending(void) + + struct cmd_s { + char *cmd; +- int (*func)(void *, struct connection *, char **, int); ++ int (*func)(const void *, struct connection *, char **, int); + char *pars; + /* + * max_pars can be used to limit the size of the parameter vector, +@@ -167,7 +167,7 @@ struct cmd_s { + unsigned int max_pars; + }; + +-static int do_control_check(void *ctx, struct connection *conn, ++static int do_control_check(const void *ctx, struct connection *conn, + char **vec, int num) + { + if (num) +@@ -179,7 +179,7 @@ static int do_control_check(void *ctx, struct connection *conn, + return 0; + } + +-static int do_control_log(void *ctx, struct connection *conn, ++static int do_control_log(const void *ctx, struct connection *conn, + char **vec, int num) + { + if (num != 1) +@@ -281,7 +281,7 @@ static int quota_get(const void *ctx, struct connection *conn, + return domain_get_quota(ctx, conn, atoi(vec[0])); + } + +-static int do_control_quota(void *ctx, struct connection *conn, ++static int do_control_quota(const void *ctx, struct connection *conn, + char **vec, int num) + { + if (num == 0) +@@ -293,7 +293,7 @@ static int do_control_quota(void *ctx, struct connection *conn, + return quota_get(ctx, conn, vec, num); + } + +-static int do_control_quota_s(void *ctx, struct connection *conn, ++static int do_control_quota_s(const void *ctx, struct connection *conn, + char **vec, int num) + { + if (num == 0) +@@ -306,7 +306,7 @@ static int do_control_quota_s(void *ctx, struct connection *conn, + } + + #ifdef __MINIOS__ +-static int do_control_memreport(void *ctx, struct connection *conn, ++static int do_control_memreport(const void *ctx, struct connection *conn, + char **vec, int num) + { + if (num) +@@ -318,7 +318,7 @@ static int do_control_memreport(void *ctx, struct connection *conn, + return 0; + } + #else +-static int do_control_logfile(void *ctx, struct connection *conn, ++static int do_control_logfile(const void *ctx, struct connection *conn, + char **vec, int num) + { + if (num != 1) +@@ -333,7 +333,7 @@ static int do_control_logfile(void *ctx, struct connection *conn, + return 0; + } + +-static int do_control_memreport(void *ctx, struct connection *conn, ++static int do_control_memreport(const void *ctx, struct connection *conn, + char **vec, int num) + { + FILE *fp; +@@ -373,7 +373,7 @@ static int do_control_memreport(void *ctx, struct connection *conn, + } + #endif + +-static int do_control_print(void *ctx, struct connection *conn, ++static int do_control_print(const void *ctx, struct connection *conn, + char **vec, int num) + { + if (num != 1) +@@ -875,7 +875,7 @@ static const char *lu_start(const void *ctx, struct connection *conn, + return NULL; + } + +-static int do_control_lu(void *ctx, struct connection *conn, ++static int do_control_lu(const void *ctx, struct connection *conn, + char **vec, int num) + { + const char *ret = NULL; +@@ -922,7 +922,7 @@ static int do_control_lu(void *ctx, struct connection *conn, + } + #endif + +-static int do_control_help(void *, struct connection *, char **, int); ++static int do_control_help(const void *, struct connection *, char **, int); + + static struct cmd_s cmds[] = { + { "check", do_control_check, "" }, +@@ -961,7 +961,7 @@ static struct cmd_s cmds[] = { + { "help", do_control_help, "" }, + }; + +-static int do_control_help(void *ctx, struct connection *conn, ++static int do_control_help(const void *ctx, struct connection *conn, + char **vec, int num) + { + int cmd, len = 0; +@@ -997,7 +997,8 @@ static int do_control_help(void *ctx, struct connection *conn, + return 0; + } + +-int do_control(struct connection *conn, struct buffered_data *in) ++int do_control(const void *ctx, struct connection *conn, ++ struct buffered_data *in) + { + unsigned int cmd, num, off; + char **vec = NULL; +@@ -1017,11 +1018,11 @@ int do_control(struct connection *conn, struct buffered_data *in) + num = xs_count_strings(in->buffer, in->used); + if (cmds[cmd].max_pars) + num = min(num, cmds[cmd].max_pars); +- vec = talloc_array(in, char *, num); ++ vec = talloc_array(ctx, char *, num); + if (!vec) + return ENOMEM; + if (get_strings(in, vec, num) < num) + return EIO; + +- return cmds[cmd].func(in, conn, vec + 1, num - 1); ++ return cmds[cmd].func(ctx, conn, vec + 1, num - 1); + } +diff --git a/tools/xenstore/xenstored_control.h b/tools/xenstore/xenstored_control.h +index 98b6fbcea2b1..a8cb76559ba1 100644 +--- a/tools/xenstore/xenstored_control.h ++++ b/tools/xenstore/xenstored_control.h +@@ -16,7 +16,8 @@ + along with this program; If not, see . + */ + +-int do_control(struct connection *conn, struct buffered_data *in); ++int do_control(const void *ctx, struct connection *conn, ++ struct buffered_data *in); + void lu_read_state(void); + + struct connection *lu_get_connection(void); +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 16504de42017..411cc0e44714 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -1248,11 +1248,13 @@ static struct node *get_node_canonicalized(struct connection *conn, + return get_node(conn, ctx, *canonical_name, perm); + } + +-static int send_directory(struct connection *conn, struct buffered_data *in) ++static int send_directory(const void *ctx, struct connection *conn, ++ struct buffered_data *in) + { + struct node *node; + +- node = get_node_canonicalized(conn, in, onearg(in), NULL, XS_PERM_READ); ++ node = get_node_canonicalized(conn, ctx, onearg(in), NULL, ++ XS_PERM_READ); + if (!node) + return errno; + +@@ -1261,7 +1263,7 @@ static int send_directory(struct connection *conn, struct buffered_data *in) + return 0; + } + +-static int send_directory_part(struct connection *conn, ++static int send_directory_part(const void *ctx, struct connection *conn, + struct buffered_data *in) + { + unsigned int off, len, maxlen, genlen; +@@ -1273,7 +1275,8 @@ static int send_directory_part(struct connection *conn, + return EINVAL; + + /* First arg is node name. */ +- node = get_node_canonicalized(conn, in, in->buffer, NULL, XS_PERM_READ); ++ node = get_node_canonicalized(conn, ctx, in->buffer, NULL, ++ XS_PERM_READ); + if (!node) + return errno; + +@@ -1300,7 +1303,7 @@ static int send_directory_part(struct connection *conn, + break; + } + +- data = talloc_array(in, char, genlen + len + 1); ++ data = talloc_array(ctx, char, genlen + len + 1); + if (!data) + return ENOMEM; + +@@ -1316,11 +1319,13 @@ static int send_directory_part(struct connection *conn, + return 0; + } + +-static int do_read(struct connection *conn, struct buffered_data *in) ++static int do_read(const void *ctx, struct connection *conn, ++ struct buffered_data *in) + { + struct node *node; + +- node = get_node_canonicalized(conn, in, onearg(in), NULL, XS_PERM_READ); ++ node = get_node_canonicalized(conn, ctx, onearg(in), NULL, ++ XS_PERM_READ); + if (!node) + return errno; + +@@ -1510,7 +1515,8 @@ static struct node *create_node(struct connection *conn, const void *ctx, + } + + /* path, data... */ +-static int do_write(struct connection *conn, struct buffered_data *in) ++static int do_write(const void *ctx, struct connection *conn, ++ struct buffered_data *in) + { + unsigned int offset, datalen; + struct node *node; +@@ -1524,12 +1530,12 @@ static int do_write(struct connection *conn, struct buffered_data *in) + offset = strlen(vec[0]) + 1; + datalen = in->used - offset; + +- node = get_node_canonicalized(conn, in, vec[0], &name, XS_PERM_WRITE); ++ node = get_node_canonicalized(conn, ctx, vec[0], &name, XS_PERM_WRITE); + if (!node) { + /* No permissions, invalid input? */ + if (errno != ENOENT) + return errno; +- node = create_node(conn, in, name, in->buffer + offset, ++ node = create_node(conn, ctx, name, in->buffer + offset, + datalen); + if (!node) + return errno; +@@ -1540,18 +1546,19 @@ static int do_write(struct connection *conn, struct buffered_data *in) + return errno; + } + +- fire_watches(conn, in, name, node, false, NULL); ++ fire_watches(conn, ctx, name, node, false, NULL); + send_ack(conn, XS_WRITE); + + return 0; + } + +-static int do_mkdir(struct connection *conn, struct buffered_data *in) ++static int do_mkdir(const void *ctx, struct connection *conn, ++ struct buffered_data *in) + { + struct node *node; + char *name; + +- node = get_node_canonicalized(conn, in, onearg(in), &name, ++ node = get_node_canonicalized(conn, ctx, onearg(in), &name, + XS_PERM_WRITE); + + /* If it already exists, fine. */ +@@ -1561,10 +1568,10 @@ static int do_mkdir(struct connection *conn, struct buffered_data *in) + return errno; + if (!name) + return ENOMEM; +- node = create_node(conn, in, name, NULL, 0); ++ node = create_node(conn, ctx, name, NULL, 0); + if (!node) + return errno; +- fire_watches(conn, in, name, node, false, NULL); ++ fire_watches(conn, ctx, name, node, false, NULL); + } + send_ack(conn, XS_MKDIR); + +@@ -1662,24 +1669,25 @@ static int _rm(struct connection *conn, const void *ctx, struct node *node, + } + + +-static int do_rm(struct connection *conn, struct buffered_data *in) ++static int do_rm(const void *ctx, struct connection *conn, ++ struct buffered_data *in) + { + struct node *node; + int ret; + char *name; + char *parentname; + +- node = get_node_canonicalized(conn, in, onearg(in), &name, ++ node = get_node_canonicalized(conn, ctx, onearg(in), &name, + XS_PERM_WRITE); + if (!node) { + /* Didn't exist already? Fine, if parent exists. */ + if (errno == ENOENT) { + if (!name) + return ENOMEM; +- parentname = get_parent(in, name); ++ parentname = get_parent(ctx, name); + if (!parentname) + return errno; +- node = read_node(conn, in, parentname); ++ node = read_node(conn, ctx, parentname); + if (node) { + send_ack(conn, XS_RM); + return 0; +@@ -1694,7 +1702,7 @@ static int do_rm(struct connection *conn, struct buffered_data *in) + if (streq(name, "/")) + return EINVAL; + +- ret = _rm(conn, in, node, name); ++ ret = _rm(conn, ctx, node, name); + if (ret) + return ret; + +@@ -1704,13 +1712,15 @@ static int do_rm(struct connection *conn, struct buffered_data *in) + } + + +-static int do_get_perms(struct connection *conn, struct buffered_data *in) ++static int do_get_perms(const void *ctx, struct connection *conn, ++ struct buffered_data *in) + { + struct node *node; + char *strings; + unsigned int len; + +- node = get_node_canonicalized(conn, in, onearg(in), NULL, XS_PERM_READ); ++ node = get_node_canonicalized(conn, ctx, onearg(in), NULL, ++ XS_PERM_READ); + if (!node) + return errno; + +@@ -1723,7 +1733,8 @@ static int do_get_perms(struct connection *conn, struct buffered_data *in) + return 0; + } + +-static int do_set_perms(struct connection *conn, struct buffered_data *in) ++static int do_set_perms(const void *ctx, struct connection *conn, ++ struct buffered_data *in) + { + struct node_perms perms, old_perms; + char *name, *permstr; +@@ -1740,7 +1751,7 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) + + permstr = in->buffer + strlen(in->buffer) + 1; + +- perms.p = talloc_array(in, struct xs_permissions, perms.num); ++ perms.p = talloc_array(ctx, struct xs_permissions, perms.num); + if (!perms.p) + return ENOMEM; + if (!xs_strings_to_perms(perms.p, perms.num, permstr)) +@@ -1755,7 +1766,7 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) + } + + /* We must own node to do this (tools can do this too). */ +- node = get_node_canonicalized(conn, in, in->buffer, &name, ++ node = get_node_canonicalized(conn, ctx, in->buffer, &name, + XS_PERM_WRITE | XS_PERM_OWNER); + if (!node) + return errno; +@@ -1790,7 +1801,7 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) + return errno; + } + +- fire_watches(conn, in, name, node, false, &old_perms); ++ fire_watches(conn, ctx, name, node, false, &old_perms); + send_ack(conn, XS_SET_PERMS); + + return 0; +@@ -1798,7 +1809,8 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) + + static struct { + const char *str; +- int (*func)(struct connection *conn, struct buffered_data *in); ++ int (*func)(const void *ctx, struct connection *conn, ++ struct buffered_data *in); + unsigned int flags; + #define XS_FLAG_NOTID (1U << 0) /* Ignore transaction id. */ + #define XS_FLAG_PRIV (1U << 1) /* Privileged domain only. */ +@@ -1874,6 +1886,7 @@ static void process_message(struct connection *conn, struct buffered_data *in) + struct transaction *trans; + enum xsd_sockmsg_type type = in->hdr.msg.type; + int ret; ++ void *ctx; + + /* At least send_error() and send_reply() expects conn->in == in */ + assert(conn->in == in); +@@ -1898,10 +1911,17 @@ static void process_message(struct connection *conn, struct buffered_data *in) + return; + } + ++ ctx = talloc_new(NULL); ++ if (!ctx) { ++ send_error(conn, ENOMEM); ++ return; ++ } ++ + assert(conn->transaction == NULL); + conn->transaction = trans; + +- ret = wire_funcs[type].func(conn, in); ++ ret = wire_funcs[type].func(ctx, conn, in); ++ talloc_free(ctx); + if (ret) + send_error(conn, ret); + +diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c +index e7c6886ccf47..fb732d0a14c3 100644 +--- a/tools/xenstore/xenstored_domain.c ++++ b/tools/xenstore/xenstored_domain.c +@@ -330,7 +330,7 @@ bool domain_is_unprivileged(struct connection *conn) + domid_is_unprivileged(conn->domain->domid); + } + +-static char *talloc_domain_path(void *context, unsigned int domid) ++static char *talloc_domain_path(const void *context, unsigned int domid) + { + return talloc_asprintf(context, "/local/domain/%u", domid); + } +@@ -534,7 +534,8 @@ static struct domain *introduce_domain(const void *ctx, + } + + /* domid, gfn, evtchn, path */ +-int do_introduce(struct connection *conn, struct buffered_data *in) ++int do_introduce(const void *ctx, struct connection *conn, ++ struct buffered_data *in) + { + struct domain *domain; + char *vec[3]; +@@ -552,7 +553,7 @@ int do_introduce(struct connection *conn, struct buffered_data *in) + if (port <= 0) + return EINVAL; + +- domain = introduce_domain(in, domid, port, false); ++ domain = introduce_domain(ctx, domid, port, false); + if (!domain) + return errno; + +@@ -575,7 +576,8 @@ static struct domain *find_connected_domain(unsigned int domid) + return domain; + } + +-int do_set_target(struct connection *conn, struct buffered_data *in) ++int do_set_target(const void *ctx, struct connection *conn, ++ struct buffered_data *in) + { + char *vec[2]; + unsigned int domid, tdomid; +@@ -619,7 +621,8 @@ static struct domain *onearg_domain(struct connection *conn, + } + + /* domid */ +-int do_release(struct connection *conn, struct buffered_data *in) ++int do_release(const void *ctx, struct connection *conn, ++ struct buffered_data *in) + { + struct domain *domain; + +@@ -634,7 +637,8 @@ int do_release(struct connection *conn, struct buffered_data *in) + return 0; + } + +-int do_resume(struct connection *conn, struct buffered_data *in) ++int do_resume(const void *ctx, struct connection *conn, ++ struct buffered_data *in) + { + struct domain *domain; + +@@ -649,7 +653,8 @@ int do_resume(struct connection *conn, struct buffered_data *in) + return 0; + } + +-int do_get_domain_path(struct connection *conn, struct buffered_data *in) ++int do_get_domain_path(const void *ctx, struct connection *conn, ++ struct buffered_data *in) + { + char *path; + const char *domid_str = onearg(in); +@@ -657,18 +662,17 @@ int do_get_domain_path(struct connection *conn, struct buffered_data *in) + if (!domid_str) + return EINVAL; + +- path = talloc_domain_path(conn, atoi(domid_str)); ++ path = talloc_domain_path(ctx, atoi(domid_str)); + if (!path) + return errno; + + send_reply(conn, XS_GET_DOMAIN_PATH, path, strlen(path) + 1); + +- talloc_free(path); +- + return 0; + } + +-int do_is_domain_introduced(struct connection *conn, struct buffered_data *in) ++int do_is_domain_introduced(const void *ctx, struct connection *conn, ++ struct buffered_data *in) + { + int result; + unsigned int domid; +@@ -689,7 +693,8 @@ int do_is_domain_introduced(struct connection *conn, struct buffered_data *in) + } + + /* Allow guest to reset all watches */ +-int do_reset_watches(struct connection *conn, struct buffered_data *in) ++int do_reset_watches(const void *ctx, struct connection *conn, ++ struct buffered_data *in) + { + conn_delete_all_watches(conn); + conn_delete_all_transactions(conn); +diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h +index 904faa923afb..b9e152890149 100644 +--- a/tools/xenstore/xenstored_domain.h ++++ b/tools/xenstore/xenstored_domain.h +@@ -24,25 +24,32 @@ void handle_event(void); + void check_domains(void); + + /* domid, mfn, eventchn, path */ +-int do_introduce(struct connection *conn, struct buffered_data *in); ++int do_introduce(const void *ctx, struct connection *conn, ++ struct buffered_data *in); + + /* domid */ +-int do_is_domain_introduced(struct connection *conn, struct buffered_data *in); ++int do_is_domain_introduced(const void *ctx, struct connection *conn, ++ struct buffered_data *in); + + /* domid */ +-int do_release(struct connection *conn, struct buffered_data *in); ++int do_release(const void *ctx, struct connection *conn, ++ struct buffered_data *in); + + /* domid */ +-int do_resume(struct connection *conn, struct buffered_data *in); ++int do_resume(const void *ctx, struct connection *conn, ++ struct buffered_data *in); + + /* domid, target */ +-int do_set_target(struct connection *conn, struct buffered_data *in); ++int do_set_target(const void *ctx, struct connection *conn, ++ struct buffered_data *in); + + /* domid */ +-int do_get_domain_path(struct connection *conn, struct buffered_data *in); ++int do_get_domain_path(const void *ctx, struct connection *conn, ++ struct buffered_data *in); + + /* Allow guest to reset all watches */ +-int do_reset_watches(struct connection *conn, struct buffered_data *in); ++int do_reset_watches(const void *ctx, struct connection *conn, ++ struct buffered_data *in); + + void domain_init(int evtfd); + void dom0_init(void); +diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c +index 28774813de83..3e3eb47326cc 100644 +--- a/tools/xenstore/xenstored_transaction.c ++++ b/tools/xenstore/xenstored_transaction.c +@@ -481,7 +481,8 @@ struct transaction *transaction_lookup(struct connection *conn, uint32_t id) + return ERR_PTR(-ENOENT); + } + +-int do_transaction_start(struct connection *conn, struct buffered_data *in) ++int do_transaction_start(const void *ctx, struct connection *conn, ++ struct buffered_data *in) + { + struct transaction *trans, *exists; + char id_str[20]; +@@ -494,8 +495,8 @@ int do_transaction_start(struct connection *conn, struct buffered_data *in) + conn->transaction_started > quota_max_transaction) + return ENOSPC; + +- /* Attach transaction to input for autofree until it's complete */ +- trans = talloc_zero(in, struct transaction); ++ /* Attach transaction to ctx for autofree until it's complete */ ++ trans = talloc_zero(ctx, struct transaction); + if (!trans) + return ENOMEM; + +@@ -544,7 +545,8 @@ static int transaction_fix_domains(struct transaction *trans, bool update) + return 0; + } + +-int do_transaction_end(struct connection *conn, struct buffered_data *in) ++int do_transaction_end(const void *ctx, struct connection *conn, ++ struct buffered_data *in) + { + const char *arg = onearg(in); + struct transaction *trans; +@@ -562,8 +564,8 @@ int do_transaction_end(struct connection *conn, struct buffered_data *in) + if (!conn->transaction_started) + conn->ta_start_time = 0; + +- /* Attach transaction to in for auto-cleanup */ +- talloc_steal(in, trans); ++ /* Attach transaction to ctx for auto-cleanup */ ++ talloc_steal(ctx, trans); + + if (streq(arg, "T")) { + if (trans->fail) +diff --git a/tools/xenstore/xenstored_transaction.h b/tools/xenstore/xenstored_transaction.h +index e3cbd6b23095..39d7f81c5127 100644 +--- a/tools/xenstore/xenstored_transaction.h ++++ b/tools/xenstore/xenstored_transaction.h +@@ -29,8 +29,10 @@ struct transaction; + + extern uint64_t generation; + +-int do_transaction_start(struct connection *conn, struct buffered_data *node); +-int do_transaction_end(struct connection *conn, struct buffered_data *in); ++int do_transaction_start(const void *ctx, struct connection *conn, ++ struct buffered_data *node); ++int do_transaction_end(const void *ctx, struct connection *conn, ++ struct buffered_data *in); + + struct transaction *transaction_lookup(struct connection *conn, uint32_t id); + +diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c +index 85362bcce314..316c08b7f754 100644 +--- a/tools/xenstore/xenstored_watch.c ++++ b/tools/xenstore/xenstored_watch.c +@@ -243,7 +243,7 @@ static struct watch *add_watch(struct connection *conn, char *path, char *token, + return NULL; + } + +-int do_watch(struct connection *conn, struct buffered_data *in) ++int do_watch(const void *ctx, struct connection *conn, struct buffered_data *in) + { + struct watch *watch; + char *vec[2]; +@@ -252,7 +252,7 @@ int do_watch(struct connection *conn, struct buffered_data *in) + if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) + return EINVAL; + +- errno = check_watch_path(conn, in, &(vec[0]), &relative); ++ errno = check_watch_path(conn, ctx, &(vec[0]), &relative); + if (errno) + return errno; + +@@ -283,7 +283,8 @@ int do_watch(struct connection *conn, struct buffered_data *in) + return 0; + } + +-int do_unwatch(struct connection *conn, struct buffered_data *in) ++int do_unwatch(const void *ctx, struct connection *conn, ++ struct buffered_data *in) + { + struct watch *watch; + char *node, *vec[2]; +@@ -291,7 +292,7 @@ int do_unwatch(struct connection *conn, struct buffered_data *in) + if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) + return EINVAL; + +- node = xenstore_canonicalize(conn, in, vec[0]); ++ node = xenstore_canonicalize(conn, ctx, vec[0]); + if (!node) + return ENOMEM; + list_for_each_entry(watch, &conn->watches, list) { +diff --git a/tools/xenstore/xenstored_watch.h b/tools/xenstore/xenstored_watch.h +index 0e693f0839cd..091890edca96 100644 +--- a/tools/xenstore/xenstored_watch.h ++++ b/tools/xenstore/xenstored_watch.h +@@ -21,8 +21,10 @@ + + #include "xenstored_core.h" + +-int do_watch(struct connection *conn, struct buffered_data *in); +-int do_unwatch(struct connection *conn, struct buffered_data *in); ++int do_watch(const void *ctx, struct connection *conn, ++ struct buffered_data *in); ++int do_unwatch(const void *ctx, struct connection *conn, ++ struct buffered_data *in); + + /* Fire all watches: !exact means all the children are affected (ie. rm). */ + void fire_watches(struct connection *conn, const void *tmp, const char *name, diff --git a/xsa417.patch b/xsa417.patch new file mode 100644 index 0000000..3f10bf7 --- /dev/null +++ b/xsa417.patch @@ -0,0 +1,135 @@ +From 67d5ecd609b8f12346eadb40e547cd7e01d825dc Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:10 +0200 +Subject: tools/xenstore: fix checking node permissions + +Today chk_domain_generation() is being used to check whether a node +permission entry is still valid or whether it is referring to a domain +no longer existing. This is done by comparing the node's and the +domain's generation count. + +In case no struct domain is existing for a checked domain, but the +domain itself is valid, chk_domain_generation() assumes it is being +called due to the first node created for a new domain and it will +return success. + +This might be wrong in case the checked permission is related to an +old domain, which has just been replaced with a new domain using the +same domid. + +Fix that by letting chk_domain_generation() fail in case a struct +domain isn't found. In order to cover the case of the first node for +a new domain try to allocate the needed struct domain explicitly when +processing the related SET_PERMS command. In case a referenced domain +isn't existing, flag the related permission to be ignored right away. + +This is XSA-417 / CVE-2022-42320. + +Reported-by: Juergen Gross +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 66bbeaf6bfb0..a0c176fa203e 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -1753,6 +1753,11 @@ static int do_set_perms(const void *ctx, struct connection *conn, + if (!xs_strings_to_perms(perms.p, perms.num, permstr)) + return errno; + ++ if (domain_alloc_permrefs(&perms) < 0) ++ return ENOMEM; ++ if (perms.p[0].perms & XS_PERM_IGNORE) ++ return ENOENT; ++ + /* First arg is node name. */ + if (strstarts(in->buffer, "@")) { + if (set_perms_special(conn, in->buffer, &perms)) +diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c +index b9ff4ded8360..98b401fdec30 100644 +--- a/tools/xenstore/xenstored_domain.c ++++ b/tools/xenstore/xenstored_domain.c +@@ -907,7 +907,6 @@ int domain_entry_inc(struct connection *conn, struct node *node) + * count (used for testing whether a node permission is older than a domain). + * + * Return values: +- * -1: error + * 0: domain has higher generation count (it is younger than a node with the + * given count), or domain isn't existing any longer + * 1: domain is older than the node +@@ -915,20 +914,38 @@ int domain_entry_inc(struct connection *conn, struct node *node) + static int chk_domain_generation(unsigned int domid, uint64_t gen) + { + struct domain *d; +- xc_dominfo_t dominfo; + + if (!xc_handle && domid == 0) + return 1; + + d = find_domain_struct(domid); +- if (d) +- return (d->generation <= gen) ? 1 : 0; + +- if (!get_domain_info(domid, &dominfo)) +- return 0; ++ return (d && d->generation <= gen) ? 1 : 0; ++} + +- d = alloc_domain(NULL, domid); +- return d ? 1 : -1; ++/* ++ * Allocate all missing struct domain referenced by a permission set. ++ * Any permission entries for not existing domains will be marked to be ++ * ignored. ++ */ ++int domain_alloc_permrefs(struct node_perms *perms) ++{ ++ unsigned int i, domid; ++ struct domain *d; ++ xc_dominfo_t dominfo; ++ ++ for (i = 0; i < perms->num; i++) { ++ domid = perms->p[i].id; ++ d = find_domain_struct(domid); ++ if (!d) { ++ if (!get_domain_info(domid, &dominfo)) ++ perms->p[i].perms |= XS_PERM_IGNORE; ++ else if (!alloc_domain(NULL, domid)) ++ return ENOMEM; ++ } ++ } ++ ++ return 0; + } + + /* +@@ -941,8 +958,6 @@ int domain_adjust_node_perms(struct connection *conn, struct node *node) + int ret; + + ret = chk_domain_generation(node->perms.p[0].id, node->generation); +- if (ret < 0) +- return errno; + + /* If the owner doesn't exist any longer give it to priv domain. */ + if (!ret) { +@@ -959,8 +974,6 @@ int domain_adjust_node_perms(struct connection *conn, struct node *node) + continue; + ret = chk_domain_generation(node->perms.p[i].id, + node->generation); +- if (ret < 0) +- return errno; + if (!ret) + node->perms.p[i].perms |= XS_PERM_IGNORE; + } +diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h +index 209442190911..7fe0a21d9e45 100644 +--- a/tools/xenstore/xenstored_domain.h ++++ b/tools/xenstore/xenstored_domain.h +@@ -63,6 +63,7 @@ bool domain_is_unprivileged(struct connection *conn); + + /* Remove node permissions for no longer existing domains. */ + int domain_adjust_node_perms(struct connection *conn, struct node *node); ++int domain_alloc_permrefs(struct node_perms *perms); + + /* Quota manipulation */ + int domain_entry_inc(struct connection *conn, struct node *); diff --git a/xsa418-4.16-01.patch b/xsa418-4.16-01.patch new file mode 100644 index 0000000..c4a17b4 --- /dev/null +++ b/xsa418-4.16-01.patch @@ -0,0 +1,119 @@ +From d1e6dca486599ab914af7b38b3782b237d3d603b Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:11 +0200 +Subject: tools/xenstore: remove recursion from construct_node() + +In order to reduce stack usage due to recursion, switch +construct_node() to use a loop instead. + +This is part of XSA-418 / CVE-2022-42321. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index c676ee4e4e4f..3907c35643e9 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -1377,45 +1377,69 @@ static int add_child(const void *ctx, struct node *parent, const char *name) + static struct node *construct_node(struct connection *conn, const void *ctx, + const char *name) + { +- struct node *parent, *node; +- char *parentname = get_parent(ctx, name); ++ const char **names = NULL; ++ unsigned int levels = 0; ++ struct node *node = NULL; ++ struct node *parent = NULL; ++ const char *parentname = talloc_strdup(ctx, name); + + if (!parentname) + return NULL; + +- /* If parent doesn't exist, create it. */ +- parent = read_node(conn, parentname, parentname); +- if (!parent && errno == ENOENT) +- parent = construct_node(conn, ctx, parentname); +- if (!parent) +- return NULL; ++ /* Walk the path up until an existing node is found. */ ++ while (!parent) { ++ names = talloc_realloc(ctx, names, const char *, levels + 1); ++ if (!names) ++ goto nomem; + +- /* Add child to parent. */ +- if (add_child(ctx, parent, name)) +- goto nomem; ++ /* ++ * names[0] is the name of the node to construct initially, ++ * names[1] is its parent, and so on. ++ */ ++ names[levels] = parentname; ++ parentname = get_parent(ctx, parentname); ++ if (!parentname) ++ return NULL; + +- /* Allocate node */ +- node = talloc(ctx, struct node); +- if (!node) +- goto nomem; +- node->name = talloc_strdup(node, name); +- if (!node->name) +- goto nomem; ++ /* Try to read parent node until we found an existing one. */ ++ parent = read_node(conn, ctx, parentname); ++ if (!parent && (errno != ENOENT || !strcmp(parentname, "/"))) ++ return NULL; + +- /* Inherit permissions, except unprivileged domains own what they create */ +- node->perms.num = parent->perms.num; +- node->perms.p = talloc_memdup(node, parent->perms.p, +- node->perms.num * sizeof(*node->perms.p)); +- if (!node->perms.p) +- goto nomem; +- if (domain_is_unprivileged(conn)) +- node->perms.p[0].id = conn->id; ++ levels++; ++ } ++ ++ /* Walk the path down again constructing the missing nodes. */ ++ for (; levels > 0; levels--) { ++ /* Add child to parent. */ ++ if (add_child(ctx, parent, names[levels - 1])) ++ goto nomem; ++ ++ /* Allocate node */ ++ node = talloc(ctx, struct node); ++ if (!node) ++ goto nomem; ++ node->name = talloc_steal(node, names[levels - 1]); ++ ++ /* Inherit permissions, unpriv domains own what they create. */ ++ node->perms.num = parent->perms.num; ++ node->perms.p = talloc_memdup(node, parent->perms.p, ++ node->perms.num * ++ sizeof(*node->perms.p)); ++ if (!node->perms.p) ++ goto nomem; ++ if (domain_is_unprivileged(conn)) ++ node->perms.p[0].id = conn->id; ++ ++ /* No children, no data */ ++ node->children = node->data = NULL; ++ node->childlen = node->datalen = 0; ++ node->acc.memory = 0; ++ node->parent = parent; ++ ++ parent = node; ++ } + +- /* No children, no data */ +- node->children = node->data = NULL; +- node->childlen = node->datalen = 0; +- node->acc.memory = 0; +- node->parent = parent; + return node; + + nomem: diff --git a/xsa418-4.16-02.patch b/xsa418-4.16-02.patch new file mode 100644 index 0000000..874bab2 --- /dev/null +++ b/xsa418-4.16-02.patch @@ -0,0 +1,103 @@ +From c13d85a2fe94bbf3cb8186b89324c5d1b4f9a61f Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:11 +0200 +Subject: tools/xenstore: don't let remove_child_entry() call corrupt() + +In case of write_node() returning an error, remove_child_entry() will +call corrupt() today. This could result in an endless recursion, as +remove_child_entry() is called by corrupt(), too: + +corrupt() + check_store() + check_store_() + remove_child_entry() + +Fix that by letting remove_child_entry() return an error instead and +let the caller decide what to do. + +This is part of XSA-418 / CVE-2022-42321. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 3907c35643e9..f433a45dc217 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -1608,15 +1608,15 @@ static void memdel(void *mem, unsigned off, unsigned len, unsigned total) + memmove(mem + off, mem + off + len, total - off - len); + } + +-static void remove_child_entry(struct connection *conn, struct node *node, +- size_t offset) ++static int remove_child_entry(struct connection *conn, struct node *node, ++ size_t offset) + { + size_t childlen = strlen(node->children + offset); + + memdel(node->children, offset, childlen + 1, node->childlen); + node->childlen -= childlen + 1; +- if (write_node(conn, node, true)) +- corrupt(conn, "Can't update parent node '%s'", node->name); ++ ++ return write_node(conn, node, true); + } + + static void delete_child(struct connection *conn, +@@ -1626,7 +1626,9 @@ static void delete_child(struct connection *conn, + + for (i = 0; i < node->childlen; i += strlen(node->children+i) + 1) { + if (streq(node->children+i, childname)) { +- remove_child_entry(conn, node, i); ++ if (remove_child_entry(conn, node, i)) ++ corrupt(conn, "Can't update parent node '%s'", ++ node->name); + return; + } + } +@@ -2325,6 +2327,17 @@ int remember_string(struct hashtable *hash, const char *str) + return hashtable_insert(hash, k, (void *)1); + } + ++static int rm_child_entry(struct node *node, size_t off, size_t len) ++{ ++ if (!recovery) ++ return off; ++ ++ if (remove_child_entry(NULL, node, off)) ++ log("check_store: child entry could not be removed from '%s'", ++ node->name); ++ ++ return off - len - 1; ++} + + /** + * A node has a children field that names the children of the node, separated +@@ -2377,12 +2390,7 @@ static int check_store_(const char *name, struct hashtable *reachable) + if (hashtable_search(children, childname)) { + log("check_store: '%s' is duplicated!", + childname); +- +- if (recovery) { +- remove_child_entry(NULL, node, +- i); +- i -= childlen + 1; +- } ++ i = rm_child_entry(node, i, childlen); + } + else { + if (!remember_string(children, +@@ -2399,11 +2407,7 @@ static int check_store_(const char *name, struct hashtable *reachable) + } else if (errno != ENOMEM) { + log("check_store: No child '%s' found!\n", + childname); +- +- if (recovery) { +- remove_child_entry(NULL, node, i); +- i -= childlen + 1; +- } ++ i = rm_child_entry(node, i, childlen); + } else { + log("check_store: ENOMEM"); + ret = ENOMEM; diff --git a/xsa418-4.16-03.patch b/xsa418-4.16-03.patch new file mode 100644 index 0000000..321ccf0 --- /dev/null +++ b/xsa418-4.16-03.patch @@ -0,0 +1,243 @@ +From aac9b51b6fbbbd16c910f69365345528c5bec106 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:11 +0200 +Subject: tools/xenstore: add generic treewalk function + +Add a generic function to walk the complete node tree. It will start +at "/" and descend recursively into each child, calling a function +specified by the caller. Depending on the return value of the user +specified function the walk will be aborted, continued, or the current +child will be skipped by not descending into its children. + +This is part of XSA-418 / CVE-2022-42321. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Acked-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index f433a45dc217..2cda3ee375ab 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -1838,6 +1838,135 @@ static int do_set_perms(const void *ctx, struct connection *conn, + return 0; + } + ++static char *child_name(const void *ctx, const char *s1, const char *s2) ++{ ++ if (strcmp(s1, "/")) ++ return talloc_asprintf(ctx, "%s/%s", s1, s2); ++ return talloc_asprintf(ctx, "/%s", s2); ++} ++ ++static int rm_from_parent(struct connection *conn, struct node *parent, ++ const char *name) ++{ ++ size_t off; ++ ++ if (!parent) ++ return WALK_TREE_ERROR_STOP; ++ ++ for (off = parent->childoff - 1; off && parent->children[off - 1]; ++ off--); ++ if (remove_child_entry(conn, parent, off)) { ++ log("treewalk: child entry could not be removed from '%s'", ++ parent->name); ++ return WALK_TREE_ERROR_STOP; ++ } ++ parent->childoff = off; ++ ++ return WALK_TREE_OK; ++} ++ ++static int walk_call_func(const void *ctx, struct connection *conn, ++ struct node *node, struct node *parent, void *arg, ++ int (*func)(const void *ctx, struct connection *conn, ++ struct node *node, void *arg)) ++{ ++ int ret; ++ ++ if (!func) ++ return WALK_TREE_OK; ++ ++ ret = func(ctx, conn, node, arg); ++ if (ret == WALK_TREE_RM_CHILDENTRY && parent) ++ ret = rm_from_parent(conn, parent, node->name); ++ ++ return ret; ++} ++ ++int walk_node_tree(const void *ctx, struct connection *conn, const char *root, ++ struct walk_funcs *funcs, void *arg) ++{ ++ int ret = 0; ++ void *tmpctx; ++ char *name; ++ struct node *node = NULL; ++ struct node *parent = NULL; ++ ++ tmpctx = talloc_new(ctx); ++ if (!tmpctx) { ++ errno = ENOMEM; ++ return WALK_TREE_ERROR_STOP; ++ } ++ name = talloc_strdup(tmpctx, root); ++ if (!name) { ++ errno = ENOMEM; ++ talloc_free(tmpctx); ++ return WALK_TREE_ERROR_STOP; ++ } ++ ++ /* Continue the walk until an error is returned. */ ++ while (ret >= 0) { ++ /* node == NULL possible only for the initial loop iteration. */ ++ if (node) { ++ /* Go one step up if ret or if last child finished. */ ++ if (ret || node->childoff >= node->childlen) { ++ parent = node->parent; ++ /* Call function AFTER processing a node. */ ++ ret = walk_call_func(ctx, conn, node, parent, ++ arg, funcs->exit); ++ /* Last node, so exit loop. */ ++ if (!parent) ++ break; ++ talloc_free(node); ++ /* Continue with parent. */ ++ node = parent; ++ continue; ++ } ++ /* Get next child of current node. */ ++ name = child_name(tmpctx, node->name, ++ node->children + node->childoff); ++ if (!name) { ++ ret = WALK_TREE_ERROR_STOP; ++ break; ++ } ++ /* Point to next child. */ ++ node->childoff += strlen(node->children + ++ node->childoff) + 1; ++ /* Descent into children. */ ++ parent = node; ++ } ++ /* Read next node (root node or next child). */ ++ node = read_node(conn, tmpctx, name); ++ if (!node) { ++ /* Child not found - should not happen! */ ++ /* ENOENT case can be handled by supplied function. */ ++ if (errno == ENOENT && funcs->enoent) ++ ret = funcs->enoent(ctx, conn, parent, name, ++ arg); ++ else ++ ret = WALK_TREE_ERROR_STOP; ++ if (!parent) ++ break; ++ if (ret == WALK_TREE_RM_CHILDENTRY) ++ ret = rm_from_parent(conn, parent, name); ++ if (ret < 0) ++ break; ++ talloc_free(name); ++ node = parent; ++ continue; ++ } ++ talloc_free(name); ++ node->parent = parent; ++ node->childoff = 0; ++ /* Call function BEFORE processing a node. */ ++ ret = walk_call_func(ctx, conn, node, parent, arg, ++ funcs->enter); ++ } ++ ++ talloc_free(tmpctx); ++ ++ return ret < 0 ? ret : WALK_TREE_OK; ++} ++ + static struct { + const char *str; + int (*func)(const void *ctx, struct connection *conn, +@@ -2305,18 +2434,6 @@ static int keys_equal_fn(void *key1, void *key2) + return 0 == strcmp((char *)key1, (char *)key2); + } + +- +-static char *child_name(const char *s1, const char *s2) +-{ +- if (strcmp(s1, "/")) { +- return talloc_asprintf(NULL, "%s/%s", s1, s2); +- } +- else { +- return talloc_asprintf(NULL, "/%s", s2); +- } +-} +- +- + int remember_string(struct hashtable *hash, const char *str) + { + char *k = malloc(strlen(str) + 1); +@@ -2376,7 +2493,7 @@ static int check_store_(const char *name, struct hashtable *reachable) + while (i < node->childlen && !ret) { + struct node *childnode; + size_t childlen = strlen(node->children + i); +- char * childname = child_name(node->name, ++ char * childname = child_name(NULL, node->name, + node->children + i); + + if (!childname) { +diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h +index bfd3fc1e9df3..2d9942171d92 100644 +--- a/tools/xenstore/xenstored_core.h ++++ b/tools/xenstore/xenstored_core.h +@@ -202,6 +202,7 @@ struct node { + + /* Children, each nul-terminated. */ + unsigned int childlen; ++ unsigned int childoff; /* Used by walk_node_tree() internally. */ + char *children; + + /* Allocation information for node currently in store. */ +@@ -338,6 +339,45 @@ void read_state_buffered_data(const void *ctx, struct connection *conn, + const struct xs_state_connection *sc); + void read_state_node(const void *ctx, const void *state); + ++/* ++ * Walk the node tree below root calling funcs->enter() and funcs->exit() for ++ * each node. funcs->enter() is being called when entering a node, so before ++ * any of the children of the node is processed. funcs->exit() is being ++ * called when leaving the node, so after all children have been processed. ++ * funcs->enoent() is being called when a node isn't existing. ++ * funcs->*() return values: ++ * < 0: tree walk is stopped, walk_node_tree() returns funcs->*() return value ++ * in case WALK_TREE_ERROR_STOP is returned, errno should be set ++ * WALK_TREE_OK: tree walk is continuing ++ * WALK_TREE_SKIP_CHILDREN: tree walk won't descend below current node, but ++ * walk continues ++ * WALK_TREE_RM_CHILDENTRY: Remove the child entry from its parent and write ++ * the modified parent node back to the data base, implies to not descend ++ * below the current node, but to continue the walk ++ * funcs->*() is allowed to modify the node it is called for in the data base. ++ * In case funcs->enter() is deleting the node, it must not return WALK_TREE_OK ++ * in order to avoid descending into no longer existing children. ++ */ ++/* Return values for funcs->*() and walk_node_tree(). */ ++#define WALK_TREE_SUCCESS_STOP -100 /* Stop walk early, no error. */ ++#define WALK_TREE_ERROR_STOP -1 /* Stop walk due to error. */ ++#define WALK_TREE_OK 0 /* No error. */ ++/* Return value for funcs->*() only. */ ++#define WALK_TREE_SKIP_CHILDREN 1 /* Don't recurse below current node. */ ++#define WALK_TREE_RM_CHILDENTRY 2 /* Remove child entry from parent. */ ++ ++struct walk_funcs { ++ int (*enter)(const void *ctx, struct connection *conn, ++ struct node *node, void *arg); ++ int (*exit)(const void *ctx, struct connection *conn, ++ struct node *node, void *arg); ++ int (*enoent)(const void *ctx, struct connection *conn, ++ struct node *parent, char *name, void *arg); ++}; ++ ++int walk_node_tree(const void *ctx, struct connection *conn, const char *root, ++ struct walk_funcs *funcs, void *arg); ++ + #endif /* _XENSTORED_CORE_H */ + + /* diff --git a/xsa418-4.16-04.patch b/xsa418-4.16-04.patch new file mode 100644 index 0000000..95de88f --- /dev/null +++ b/xsa418-4.16-04.patch @@ -0,0 +1,108 @@ +From bdc931fb5dcebbd8d0e44b5d8bd3fb9106ee8596 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:12 +0200 +Subject: tools/xenstore: simplify check_store() + +check_store() is using a hash table for storing all node names it has +found via walking the tree. Additionally it using another hash table +for all children of a node to detect duplicate child names. + +Simplify that by dropping the second hash table as the first one is +already holding all the needed information. + +This is part of XSA-418 / CVE-2022-42321. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 2cda3ee375ab..760f3c16c794 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -2477,50 +2477,34 @@ static int check_store_(const char *name, struct hashtable *reachable) + if (node) { + size_t i = 0; + +- struct hashtable * children = +- create_hashtable(16, hash_from_key_fn, keys_equal_fn); +- if (!children) { +- log("check_store create table: ENOMEM"); +- return ENOMEM; +- } +- + if (!remember_string(reachable, name)) { +- hashtable_destroy(children, 0); + log("check_store: ENOMEM"); + return ENOMEM; + } + + while (i < node->childlen && !ret) { +- struct node *childnode; ++ struct node *childnode = NULL; + size_t childlen = strlen(node->children + i); +- char * childname = child_name(NULL, node->name, +- node->children + i); ++ char *childname = child_name(NULL, node->name, ++ node->children + i); + + if (!childname) { + log("check_store: ENOMEM"); + ret = ENOMEM; + break; + } ++ ++ if (hashtable_search(reachable, childname)) { ++ log("check_store: '%s' is duplicated!", ++ childname); ++ i = rm_child_entry(node, i, childlen); ++ goto next; ++ } ++ + childnode = read_node(NULL, childname, childname); +- ++ + if (childnode) { +- if (hashtable_search(children, childname)) { +- log("check_store: '%s' is duplicated!", +- childname); +- i = rm_child_entry(node, i, childlen); +- } +- else { +- if (!remember_string(children, +- childname)) { +- log("check_store: ENOMEM"); +- talloc_free(childnode); +- talloc_free(childname); +- ret = ENOMEM; +- break; +- } +- ret = check_store_(childname, +- reachable); +- } ++ ret = check_store_(childname, reachable); + } else if (errno != ENOMEM) { + log("check_store: No child '%s' found!\n", + childname); +@@ -2530,19 +2514,18 @@ static int check_store_(const char *name, struct hashtable *reachable) + ret = ENOMEM; + } + ++ next: + talloc_free(childnode); + talloc_free(childname); + i += childlen + 1; + } + +- hashtable_destroy(children, 0 /* Don't free values (they are +- all (void *)1) */); + talloc_free(node); + } else if (errno != ENOMEM) { + /* Impossible, because no database should ever be without the + root, and otherwise, we've just checked in our caller + (which made a recursive call to get here). */ +- ++ + log("check_store: No child '%s' found: impossible!", name); + } else { + log("check_store: ENOMEM"); diff --git a/xsa418-4.16-05.patch b/xsa418-4.16-05.patch new file mode 100644 index 0000000..fca551e --- /dev/null +++ b/xsa418-4.16-05.patch @@ -0,0 +1,164 @@ +From 27817f0a7d6802be04e8f43a0900b02f881b28b2 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:12 +0200 +Subject: tools/xenstore: use treewalk for check_store() + +Instead of doing an open tree walk using call recursion, use +walk_node_tree() when checking the store for inconsistencies. + +This will reduce code size and avoid many nesting levels of function +calls which could potentially exhaust the stack. + +This is part of XSA-418 / CVE-2022-42321. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 760f3c16c794..efdd1888fd78 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -2444,18 +2444,6 @@ int remember_string(struct hashtable *hash, const char *str) + return hashtable_insert(hash, k, (void *)1); + } + +-static int rm_child_entry(struct node *node, size_t off, size_t len) +-{ +- if (!recovery) +- return off; +- +- if (remove_child_entry(NULL, node, off)) +- log("check_store: child entry could not be removed from '%s'", +- node->name); +- +- return off - len - 1; +-} +- + /** + * A node has a children field that names the children of the node, separated + * by NULs. We check whether there are entries in there that are duplicated +@@ -2469,70 +2457,29 @@ static int rm_child_entry(struct node *node, size_t off, size_t len) + * As we go, we record each node in the given reachable hashtable. These + * entries will be used later in clean_store. + */ +-static int check_store_(const char *name, struct hashtable *reachable) ++static int check_store_step(const void *ctx, struct connection *conn, ++ struct node *node, void *arg) + { +- struct node *node = read_node(NULL, name, name); +- int ret = 0; +- +- if (node) { +- size_t i = 0; +- +- if (!remember_string(reachable, name)) { +- log("check_store: ENOMEM"); +- return ENOMEM; +- } +- +- while (i < node->childlen && !ret) { +- struct node *childnode = NULL; +- size_t childlen = strlen(node->children + i); +- char *childname = child_name(NULL, node->name, +- node->children + i); +- +- if (!childname) { +- log("check_store: ENOMEM"); +- ret = ENOMEM; +- break; +- } ++ struct hashtable *reachable = arg; + +- if (hashtable_search(reachable, childname)) { +- log("check_store: '%s' is duplicated!", +- childname); +- i = rm_child_entry(node, i, childlen); +- goto next; +- } +- +- childnode = read_node(NULL, childname, childname); +- +- if (childnode) { +- ret = check_store_(childname, reachable); +- } else if (errno != ENOMEM) { +- log("check_store: No child '%s' found!\n", +- childname); +- i = rm_child_entry(node, i, childlen); +- } else { +- log("check_store: ENOMEM"); +- ret = ENOMEM; +- } ++ if (hashtable_search(reachable, (void *)node->name)) { ++ log("check_store: '%s' is duplicated!", node->name); ++ return recovery ? WALK_TREE_RM_CHILDENTRY ++ : WALK_TREE_SKIP_CHILDREN; ++ } + +- next: +- talloc_free(childnode); +- talloc_free(childname); +- i += childlen + 1; +- } ++ if (!remember_string(reachable, node->name)) ++ return WALK_TREE_ERROR_STOP; + +- talloc_free(node); +- } else if (errno != ENOMEM) { +- /* Impossible, because no database should ever be without the +- root, and otherwise, we've just checked in our caller +- (which made a recursive call to get here). */ ++ return WALK_TREE_OK; ++} + +- log("check_store: No child '%s' found: impossible!", name); +- } else { +- log("check_store: ENOMEM"); +- ret = ENOMEM; +- } ++static int check_store_enoent(const void *ctx, struct connection *conn, ++ struct node *parent, char *name, void *arg) ++{ ++ log("check_store: node '%s' not found", name); + +- return ret; ++ return recovery ? WALK_TREE_RM_CHILDENTRY : WALK_TREE_OK; + } + + +@@ -2581,24 +2528,28 @@ static void clean_store(struct hashtable *reachable) + + void check_store(void) + { +- char * root = talloc_strdup(NULL, "/"); +- struct hashtable * reachable = +- create_hashtable(16, hash_from_key_fn, keys_equal_fn); +- ++ struct hashtable *reachable; ++ struct walk_funcs walkfuncs = { ++ .enter = check_store_step, ++ .enoent = check_store_enoent, ++ }; ++ ++ reachable = create_hashtable(16, hash_from_key_fn, keys_equal_fn); + if (!reachable) { + log("check_store: ENOMEM"); + return; + } + + log("Checking store ..."); +- if (!check_store_(root, reachable) && +- !check_transactions(reachable)) ++ if (walk_node_tree(NULL, NULL, "/", &walkfuncs, reachable)) { ++ if (errno == ENOMEM) ++ log("check_store: ENOMEM"); ++ } else if (!check_transactions(reachable)) + clean_store(reachable); + log("Checking store complete."); + + hashtable_destroy(reachable, 0 /* Don't free values (they are all + (void *)1) */); +- talloc_free(root); + } + + diff --git a/xsa418-4.16-06.patch b/xsa418-4.16-06.patch new file mode 100644 index 0000000..d46c057 --- /dev/null +++ b/xsa418-4.16-06.patch @@ -0,0 +1,174 @@ +From 6ea0ffbd88b11f23779d763501ec1370b590bb2a Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:12 +0200 +Subject: tools/xenstore: use treewalk for deleting nodes + +Instead of doing an open tree walk using call recursion, use +walk_node_tree() when deleting a sub-tree of nodes. + +This will reduce code size and avoid many nesting levels of function +calls which could potentially exhaust the stack. + +This is part of XSA-418 / CVE-2022-42321. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Acked-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index efdd1888fd78..58fb651542ec 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -1334,21 +1334,6 @@ static int do_read(const void *ctx, struct connection *conn, + return 0; + } + +-static void delete_node_single(struct connection *conn, struct node *node) +-{ +- TDB_DATA key; +- +- if (access_node(conn, node, NODE_ACCESS_DELETE, &key)) +- return; +- +- if (do_tdb_delete(conn, &key, &node->acc) != 0) { +- corrupt(conn, "Could not delete '%s'", node->name); +- return; +- } +- +- domain_entry_dec(conn, node); +-} +- + /* Must not be / */ + static char *basename(const char *name) + { +@@ -1619,69 +1604,59 @@ static int remove_child_entry(struct connection *conn, struct node *node, + return write_node(conn, node, true); + } + +-static void delete_child(struct connection *conn, +- struct node *node, const char *childname) ++static int delete_child(struct connection *conn, ++ struct node *node, const char *childname) + { + unsigned int i; + + for (i = 0; i < node->childlen; i += strlen(node->children+i) + 1) { + if (streq(node->children+i, childname)) { +- if (remove_child_entry(conn, node, i)) +- corrupt(conn, "Can't update parent node '%s'", +- node->name); +- return; ++ errno = remove_child_entry(conn, node, i) ? EIO : 0; ++ return errno; + } + } + corrupt(conn, "Can't find child '%s' in %s", childname, node->name); ++ ++ errno = EIO; ++ return errno; + } + +-static int delete_node(struct connection *conn, const void *ctx, +- struct node *parent, struct node *node, bool watch_exact) ++static int delnode_sub(const void *ctx, struct connection *conn, ++ struct node *node, void *arg) + { +- char *name; ++ const char *root = arg; ++ bool watch_exact; ++ int ret; ++ TDB_DATA key; + +- /* Delete children. */ +- while (node->childlen) { +- struct node *child; ++ /* Any error here will probably be repeated for all following calls. */ ++ ret = access_node(conn, node, NODE_ACCESS_DELETE, &key); ++ if (ret > 0) ++ return WALK_TREE_SUCCESS_STOP; + +- name = talloc_asprintf(node, "%s/%s", node->name, +- node->children); +- child = name ? read_node(conn, node, name) : NULL; +- if (child) { +- if (delete_node(conn, ctx, node, child, true)) +- return errno; +- } else { +- trace("delete_node: Error deleting child '%s/%s'!\n", +- node->name, node->children); +- /* Quit deleting. */ +- errno = ENOMEM; +- return errno; +- } +- talloc_free(name); +- } ++ /* In case of error stop the walk. */ ++ if (!ret && do_tdb_delete(conn, &key, &node->acc)) ++ return WALK_TREE_SUCCESS_STOP; + + /* + * Fire the watches now, when we can still see the node permissions. + * This fine as we are single threaded and the next possible read will + * be handled only after the node has been really removed. +- */ ++ */ ++ watch_exact = strcmp(root, node->name); + fire_watches(conn, ctx, node->name, node, watch_exact, NULL); +- delete_node_single(conn, node); +- delete_child(conn, parent, basename(node->name)); +- talloc_free(node); + +- return 0; ++ domain_entry_dec(conn, node); ++ ++ return WALK_TREE_RM_CHILDENTRY; + } + +-static int _rm(struct connection *conn, const void *ctx, struct node *node, +- const char *name) ++static int _rm(struct connection *conn, const void *ctx, const char *name) + { +- /* +- * Deleting node by node, so the result is always consistent even in +- * case of a failure. +- */ + struct node *parent; + char *parentname = get_parent(ctx, name); ++ struct walk_funcs walkfuncs = { .exit = delnode_sub }; ++ int ret; + + if (!parentname) + return errno; +@@ -1689,9 +1664,21 @@ static int _rm(struct connection *conn, const void *ctx, struct node *node, + parent = read_node(conn, ctx, parentname); + if (!parent) + return read_node_can_propagate_errno() ? errno : EINVAL; +- node->parent = parent; + +- return delete_node(conn, ctx, parent, node, false); ++ ret = walk_node_tree(ctx, conn, name, &walkfuncs, (void *)name); ++ if (ret < 0) { ++ if (ret == WALK_TREE_ERROR_STOP) { ++ corrupt(conn, "error when deleting sub-nodes of %s\n", ++ name); ++ errno = EIO; ++ } ++ return errno; ++ } ++ ++ if (delete_child(conn, parent, basename(name))) ++ return errno; ++ ++ return 0; + } + + +@@ -1728,7 +1715,7 @@ static int do_rm(const void *ctx, struct connection *conn, + if (streq(name, "/")) + return EINVAL; + +- ret = _rm(conn, ctx, node, name); ++ ret = _rm(conn, ctx, name); + if (ret) + return ret; + diff --git a/xsa418-4.16-07.patch b/xsa418-4.16-07.patch new file mode 100644 index 0000000..9600d8c --- /dev/null +++ b/xsa418-4.16-07.patch @@ -0,0 +1,163 @@ +From 1ee281b18b52bec87335ea64ee74cc159e63d036 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:12 +0200 +Subject: tools/xenstore: use treewalk for creating node records + +Instead of doing an open tree walk using call recursion, use +walk_node_tree() when creating the node records during a live update. + +This will reduce code size and avoid many nesting levels of function +calls which could potentially exhaust the stack. + +This is part of XSA-418 / CVE-2022-42321. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 58fb651542ec..05d349778bb4 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -3120,101 +3120,76 @@ const char *dump_state_node_perms(FILE *fp, const struct xs_permissions *perms, + return NULL; + } + +-static const char *dump_state_node_tree(FILE *fp, char *path, +- unsigned int path_max_len) ++struct dump_node_data { ++ FILE *fp; ++ const char *err; ++}; ++ ++static int dump_state_node_err(struct dump_node_data *data, const char *err) + { +- unsigned int pathlen, childlen, p = 0; ++ data->err = err; ++ return WALK_TREE_ERROR_STOP; ++} ++ ++static int dump_state_node(const void *ctx, struct connection *conn, ++ struct node *node, void *arg) ++{ ++ struct dump_node_data *data = arg; ++ FILE *fp = data->fp; ++ unsigned int pathlen; + struct xs_state_record_header head; + struct xs_state_node sn; +- TDB_DATA key, data; +- const struct xs_tdb_record_hdr *hdr; +- const char *child; + const char *ret; + +- pathlen = strlen(path) + 1; +- +- set_tdb_key(path, &key); +- data = tdb_fetch(tdb_ctx, key); +- if (data.dptr == NULL) +- return "Error reading node"; +- +- /* Clean up in case of failure. */ +- talloc_steal(path, data.dptr); +- +- hdr = (void *)data.dptr; ++ pathlen = strlen(node->name) + 1; + + head.type = XS_STATE_TYPE_NODE; + head.length = sizeof(sn); + sn.conn_id = 0; + sn.ta_id = 0; + sn.ta_access = 0; +- sn.perm_n = hdr->num_perms; ++ sn.perm_n = node->perms.num; + sn.path_len = pathlen; +- sn.data_len = hdr->datalen; +- head.length += hdr->num_perms * sizeof(*sn.perms); ++ sn.data_len = node->datalen; ++ head.length += node->perms.num * sizeof(*sn.perms); + head.length += pathlen; +- head.length += hdr->datalen; ++ head.length += node->datalen; + head.length = ROUNDUP(head.length, 3); + + if (fwrite(&head, sizeof(head), 1, fp) != 1) +- return "Dump node state error"; ++ return dump_state_node_err(data, "Dump node head error"); + if (fwrite(&sn, sizeof(sn), 1, fp) != 1) +- return "Dump node state error"; ++ return dump_state_node_err(data, "Dump node state error"); + +- ret = dump_state_node_perms(fp, hdr->perms, hdr->num_perms); ++ ret = dump_state_node_perms(fp, node->perms.p, node->perms.num); + if (ret) +- return ret; ++ return dump_state_node_err(data, ret); ++ ++ if (fwrite(node->name, pathlen, 1, fp) != 1) ++ return dump_state_node_err(data, "Dump node path error"); + +- if (fwrite(path, pathlen, 1, fp) != 1) +- return "Dump node path error"; +- if (hdr->datalen && +- fwrite(hdr->perms + hdr->num_perms, hdr->datalen, 1, fp) != 1) +- return "Dump node data error"; ++ if (node->datalen && fwrite(node->data, node->datalen, 1, fp) != 1) ++ return dump_state_node_err(data, "Dump node data error"); + + ret = dump_state_align(fp); + if (ret) +- return ret; ++ return dump_state_node_err(data, ret); + +- child = (char *)(hdr->perms + hdr->num_perms) + hdr->datalen; +- +- /* +- * Use path for constructing children paths. +- * As we don't write out nodes without having written their parent +- * already we will never clobber a part of the path we'll need later. +- */ +- pathlen--; +- if (path[pathlen - 1] != '/') { +- path[pathlen] = '/'; +- pathlen++; +- } +- while (p < hdr->childlen) { +- childlen = strlen(child) + 1; +- if (pathlen + childlen > path_max_len) +- return "Dump node path length error"; +- strcpy(path + pathlen, child); +- ret = dump_state_node_tree(fp, path, path_max_len); +- if (ret) +- return ret; +- p += childlen; +- child += childlen; +- } +- +- talloc_free(data.dptr); +- +- return NULL; ++ return WALK_TREE_OK; + } + + const char *dump_state_nodes(FILE *fp, const void *ctx) + { +- char *path; +- +- path = talloc_size(ctx, XENSTORE_ABS_PATH_MAX + 1); +- if (!path) +- return "Path buffer allocation error"; ++ struct dump_node_data data = { ++ .fp = fp, ++ .err = "Dump node walk error" ++ }; ++ struct walk_funcs walkfuncs = { .enter = dump_state_node }; + +- strcpy(path, "/"); ++ if (walk_node_tree(ctx, NULL, "/", &walkfuncs, &data)) ++ return data.err; + +- return dump_state_node_tree(fp, path, XENSTORE_ABS_PATH_MAX + 1); ++ return NULL; + } + + void read_state_global(const void *ctx, const void *state) diff --git a/xsa419-oxenstored.patch b/xsa419-oxenstored.patch new file mode 100644 index 0000000..0ac365d --- /dev/null +++ b/xsa419-oxenstored.patch @@ -0,0 +1,85 @@ +From 09228369a549427294febe351372d7227e624da1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Date: Wed, 12 Oct 2022 19:13:06 +0100 +Subject: tools/ocaml/xenstored: Fix quota bypass on domain shutdown +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +XSA-322 fixed a domid reuse vulnerability by assigning Dom0 as the owner of +any nodes left after a domain is shutdown (e.g. outside its /local/domain/N +tree). + +However Dom0 has no quota on purpose, so this opened up another potential +attack vector. Avoid it by deleting these nodes instead of assigning them to +Dom0. + +This is part of XSA-419 / CVE-2022-42323. + +Reported-by: Juergen Gross +Fixes: c46eff921209 ("tools/ocaml/xenstored: clean up permissions for dead domains") +Signed-off-by: Edwin Török +Acked-by: Christian Lindig + +diff --git a/tools/ocaml/xenstored/perms.ml b/tools/ocaml/xenstored/perms.ml +index e8a16221f8fa..84f2503e8e29 100644 +--- a/tools/ocaml/xenstored/perms.ml ++++ b/tools/ocaml/xenstored/perms.ml +@@ -64,8 +64,7 @@ let get_owner perm = perm.owner + * *) + let remove_domid ~domid perm = + let acl = List.filter (fun (acl_domid, _) -> acl_domid <> domid) perm.acl in +- let owner = if perm.owner = domid then 0 else perm.owner in +- { perm with acl; owner } ++ if perm.owner = domid then None else Some { perm with acl; owner = perm.owner } + + let default0 = create 0 NONE [] + +diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml +index 20e67b142746..70f0c83de404 100644 +--- a/tools/ocaml/xenstored/store.ml ++++ b/tools/ocaml/xenstored/store.ml +@@ -87,10 +87,21 @@ let check_owner node connection = + + let rec recurse fct node = fct node; SymbolMap.iter (fun _ -> recurse fct) node.children + +-(** [recurse_map f tree] applies [f] on each node in the tree recursively *) +-let recurse_map f = ++(** [recurse_filter_map f tree] applies [f] on each node in the tree recursively, ++ possibly removing some nodes. ++ Note that the nodes removed this way won't generate watch events. ++*) ++let recurse_filter_map f = ++ let invalid = -1 in ++ let is_valid _ node = node.perms.owner <> invalid in + let rec walk node = +- f { node with children = SymbolMap.map walk node.children } ++ (* Map.filter_map is Ocaml 4.11+ only *) ++ let node = ++ { node with children = ++ SymbolMap.map walk node.children |> SymbolMap.filter is_valid } in ++ match f node with ++ | Some keep -> keep ++ | None -> { node with perms = {node.perms with owner = invalid } } + in + walk + +@@ -444,11 +455,13 @@ let setperms store perm path nperms = + + let reset_permissions store domid = + Logging.info "store|node" "Cleaning up xenstore ACLs for domid %d" domid; +- store.root <- Node.recurse_map (fun node -> +- let perms = Perms.Node.remove_domid ~domid node.perms in +- if perms <> node.perms then +- Logging.debug "store|node" "Changed permissions for node %s" (Node.get_name node); +- { node with perms } ++ store.root <- Node.recurse_filter_map (fun node -> ++ match Perms.Node.remove_domid ~domid node.perms with ++ | None -> None ++ | Some perms -> ++ if perms <> node.perms then ++ Logging.debug "store|node" "Changed permissions for node %s" (Node.get_name node); ++ Some { node with perms } + ) store.root + + type ops = { diff --git a/xsa419-xenstored-01.patch b/xsa419-xenstored-01.patch new file mode 100644 index 0000000..3409790 --- /dev/null +++ b/xsa419-xenstored-01.patch @@ -0,0 +1,289 @@ +From e4250bf8b39ed73623c75b0f1436ac7c1d45aba7 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:12 +0200 +Subject: tools/xenstore: remove nodes owned by destroyed domain + +In case a domain is removed from Xenstore, remove all nodes owned by +it per default. + +This tackles the problem that nodes might be created by a domain +outside its home path in Xenstore, leading to Xenstore hogging more +and more memory. Domain quota don't work in this case if the guest is +rebooting in between. + +Since XSA-322 ownership of such stale nodes is transferred to dom0, +which is helping against unintended access, but not against OOM of +Xenstore. + +As a fallback for weird cases add a Xenstore start parameter for +keeping today's way to handle stale nodes, adding the risk of Xenstore +hitting an OOM situation. + +This is part of XSA-419 / CVE-2022-42322. + +Reported-by: Juergen Gross +Fixes: 496306324d8d ("tools/xenstore: revoke access rights for removed domains") +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index bdc14679adf5..13e48aaa731c 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -80,6 +80,7 @@ static bool verbose = false; + LIST_HEAD(connections); + int tracefd = -1; + static bool recovery = true; ++bool keep_orphans = false; + static int reopen_log_pipe[2]; + static int reopen_log_pipe0_pollfd_idx = -1; + char *tracefile = NULL; +@@ -753,7 +754,7 @@ struct node *read_node(struct connection *conn, const void *ctx, + node->perms.p = hdr->perms; + node->acc.domid = node->perms.p[0].id; + node->acc.memory = data.dsize; +- if (domain_adjust_node_perms(conn, node)) ++ if (domain_adjust_node_perms(node)) + goto error; + + /* If owner is gone reset currently accounted memory size. */ +@@ -796,7 +797,7 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, + void *p; + struct xs_tdb_record_hdr *hdr; + +- if (domain_adjust_node_perms(conn, node)) ++ if (domain_adjust_node_perms(node)) + return errno; + + data.dsize = sizeof(*hdr) +@@ -1647,7 +1648,7 @@ static int delnode_sub(const void *ctx, struct connection *conn, + return WALK_TREE_RM_CHILDENTRY; + } + +-static int _rm(struct connection *conn, const void *ctx, const char *name) ++int rm_node(struct connection *conn, const void *ctx, const char *name) + { + struct node *parent; + char *parentname = get_parent(ctx, name); +@@ -1711,7 +1712,7 @@ static int do_rm(const void *ctx, struct connection *conn, + if (streq(name, "/")) + return EINVAL; + +- ret = _rm(conn, ctx, name); ++ ret = rm_node(conn, ctx, name); + if (ret) + return ret; + +@@ -2618,6 +2619,8 @@ static void usage(void) + " -R, --no-recovery to request that no recovery should be attempted when\n" + " the store is corrupted (debug only),\n" + " -I, --internal-db store database in memory, not on disk\n" ++" -K, --keep-orphans don't delete nodes owned by a domain when the\n" ++" domain is deleted (this is a security risk!)\n" + " -V, --verbose to request verbose execution.\n"); + } + +@@ -2642,6 +2645,7 @@ static struct option options[] = { + { "timeout", 1, NULL, 'w' }, + { "no-recovery", 0, NULL, 'R' }, + { "internal-db", 0, NULL, 'I' }, ++ { "keep-orphans", 0, NULL, 'K' }, + { "verbose", 0, NULL, 'V' }, + { "watch-nb", 1, NULL, 'W' }, + #ifndef NO_LIVE_UPDATE +@@ -2721,7 +2725,7 @@ int main(int argc, char *argv[]) + orig_argc = argc; + orig_argv = argv; + +- while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:M:Q:q:T:RVW:w:U", ++ while ((opt = getopt_long(argc, argv, "DE:F:HKNPS:t:A:M:Q:q:T:RVW:w:U", + options, NULL)) != -1) { + switch (opt) { + case 'D': +@@ -2757,6 +2761,9 @@ int main(int argc, char *argv[]) + case 'I': + tdb_flags = TDB_INTERNAL|TDB_NOLOCK; + break; ++ case 'K': ++ keep_orphans = true; ++ break; + case 'V': + verbose = true; + break; +diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h +index acb00ad96914..37006d508dbf 100644 +--- a/tools/xenstore/xenstored_core.h ++++ b/tools/xenstore/xenstored_core.h +@@ -240,6 +240,9 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, + struct node *read_node(struct connection *conn, const void *ctx, + const char *name); + ++/* Remove a node and its children. */ ++int rm_node(struct connection *conn, const void *ctx, const char *name); ++ + void setup_structure(bool live_update); + struct connection *new_connection(const struct interface_funcs *funcs); + struct connection *get_connection_by_id(unsigned int conn_id); +@@ -284,6 +287,7 @@ extern int quota_req_outstanding; + extern int quota_trans_nodes; + extern int quota_memory_per_domain_soft; + extern int quota_memory_per_domain_hard; ++extern bool keep_orphans; + + extern unsigned int timeout_watch_event_msec; + +diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c +index 98b401fdec30..84b7817cd5e6 100644 +--- a/tools/xenstore/xenstored_domain.c ++++ b/tools/xenstore/xenstored_domain.c +@@ -227,10 +227,64 @@ static void unmap_interface(void *interface) + xengnttab_unmap(*xgt_handle, interface, 1); + } + ++static int domain_tree_remove_sub(const void *ctx, struct connection *conn, ++ struct node *node, void *arg) ++{ ++ struct domain *domain = arg; ++ TDB_DATA key; ++ int ret = WALK_TREE_OK; ++ ++ if (node->perms.p[0].id != domain->domid) ++ return WALK_TREE_OK; ++ ++ if (keep_orphans) { ++ set_tdb_key(node->name, &key); ++ domain->nbentry--; ++ node->perms.p[0].id = priv_domid; ++ node->acc.memory = 0; ++ domain_entry_inc(NULL, node); ++ if (write_node_raw(NULL, &key, node, true)) { ++ /* That's unfortunate. We only can try to continue. */ ++ syslog(LOG_ERR, ++ "error when moving orphaned node %s to dom0\n", ++ node->name); ++ } else ++ trace("orphaned node %s moved to dom0\n", node->name); ++ } else { ++ if (rm_node(NULL, ctx, node->name)) { ++ /* That's unfortunate. We only can try to continue. */ ++ syslog(LOG_ERR, ++ "error when deleting orphaned node %s\n", ++ node->name); ++ } else ++ trace("orphaned node %s deleted\n", node->name); ++ ++ /* Skip children in all cases in order to avoid more errors. */ ++ ret = WALK_TREE_SKIP_CHILDREN; ++ } ++ ++ return domain->nbentry > 0 ? ret : WALK_TREE_SUCCESS_STOP; ++} ++ ++static void domain_tree_remove(struct domain *domain) ++{ ++ int ret; ++ struct walk_funcs walkfuncs = { .enter = domain_tree_remove_sub }; ++ ++ if (domain->nbentry > 0) { ++ ret = walk_node_tree(domain, NULL, "/", &walkfuncs, domain); ++ if (ret == WALK_TREE_ERROR_STOP) ++ syslog(LOG_ERR, ++ "error when looking for orphaned nodes\n"); ++ } ++} ++ + static int destroy_domain(void *_domain) + { + struct domain *domain = _domain; + ++ domain_tree_remove(domain); ++ + list_del(&domain->list); + + if (!domain->introduced) +@@ -883,15 +937,15 @@ int domain_entry_inc(struct connection *conn, struct node *node) + struct domain *d; + unsigned int domid; + +- if (!conn) ++ if (!node->perms.p) + return 0; + +- domid = node->perms.p ? node->perms.p[0].id : conn->id; ++ domid = node->perms.p[0].id; + +- if (conn->transaction) { ++ if (conn && conn->transaction) { + transaction_entry_inc(conn->transaction, domid); + } else { +- d = (domid == conn->id && conn->domain) ? conn->domain ++ d = (conn && domid == conn->id && conn->domain) ? conn->domain + : find_or_alloc_existing_domain(domid); + if (d) + d->nbentry++; +@@ -952,23 +1006,11 @@ int domain_alloc_permrefs(struct node_perms *perms) + * Remove permissions for no longer existing domains in order to avoid a new + * domain with the same domid inheriting the permissions. + */ +-int domain_adjust_node_perms(struct connection *conn, struct node *node) ++int domain_adjust_node_perms(struct node *node) + { + unsigned int i; + int ret; + +- ret = chk_domain_generation(node->perms.p[0].id, node->generation); +- +- /* If the owner doesn't exist any longer give it to priv domain. */ +- if (!ret) { +- /* +- * In theory we'd need to update the number of dom0 nodes here, +- * but we could be called for a read of the node. So better +- * avoid the risk to overflow the node count of dom0. +- */ +- node->perms.p[0].id = priv_domid; +- } +- + for (i = 1; i < node->perms.num; i++) { + if (node->perms.p[i].perms & XS_PERM_IGNORE) + continue; +@@ -986,15 +1028,15 @@ void domain_entry_dec(struct connection *conn, struct node *node) + struct domain *d; + unsigned int domid; + +- if (!conn) ++ if (!node->perms.p) + return; + + domid = node->perms.p ? node->perms.p[0].id : conn->id; + +- if (conn->transaction) { ++ if (conn && conn->transaction) { + transaction_entry_dec(conn->transaction, domid); + } else { +- d = (domid == conn->id && conn->domain) ? conn->domain ++ d = (conn && domid == conn->id && conn->domain) ? conn->domain + : find_domain_struct(domid); + if (d) { + d->nbentry--; +@@ -1113,7 +1155,7 @@ int domain_memory_add(unsigned int domid, int mem, bool no_quota_check) + * exist, as accounting is done either for a domain related to + * the current connection, or for the domain owning a node + * (which is always existing, as the owner of the node is +- * tested to exist and replaced by domid 0 if not). ++ * tested to exist and deleted or replaced by domid 0 if not). + * So not finding the related domain MUST be an error in the + * data base. + */ +diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h +index 7fe0a21d9e45..b38c82991dc6 100644 +--- a/tools/xenstore/xenstored_domain.h ++++ b/tools/xenstore/xenstored_domain.h +@@ -62,7 +62,7 @@ const char *get_implicit_path(const struct connection *conn); + bool domain_is_unprivileged(struct connection *conn); + + /* Remove node permissions for no longer existing domains. */ +-int domain_adjust_node_perms(struct connection *conn, struct node *node); ++int domain_adjust_node_perms(struct node *node); + int domain_alloc_permrefs(struct node_perms *perms); + + /* Quota manipulation */ diff --git a/xsa419-xenstored-02.patch b/xsa419-xenstored-02.patch new file mode 100644 index 0000000..e5d46a2 --- /dev/null +++ b/xsa419-xenstored-02.patch @@ -0,0 +1,93 @@ +From 929da557efea6c7d2340467d9a7fdae7fda6d2b1 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:13 +0200 +Subject: tools/xenstore: make the internal memory data base the default + +Having a file backed data base has the only advantage of being capable +to dump the contents of it while Xenstore is running, and potentially +using less swap space in case the data base can't be kept in memory. + +It has the major disadvantage of a huge performance overhead: switching +to keep the data base in memory only speeds up live update of xenstored +with 120000 nodes from 20 minutes to 11 seconds. A complete tree walk +of this configuration will be reduced from 7 seconds to 280 msecs +(measured by "xenstore-control check"). + +So make the internal memory data base the default and enhance the +"--internal-db" command line parameter to take an optional parameter +allowing to switch the internal data base back to the file based one. + +This is part of XSA-419. + +Reported-by: Juergen Gross +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall + +diff --git a/tools/helpers/init-xenstore-domain.c b/tools/helpers/init-xenstore-domain.c +index 2d9ab6f1c583..04e351ca29a8 100644 +--- a/tools/helpers/init-xenstore-domain.c ++++ b/tools/helpers/init-xenstore-domain.c +@@ -222,9 +222,9 @@ static int build(xc_interface *xch) + } + + if ( param ) +- snprintf(cmdline, 512, "--event %d --internal-db %s", rv, param); ++ snprintf(cmdline, 512, "--event %d %s", rv, param); + else +- snprintf(cmdline, 512, "--event %d --internal-db", rv); ++ snprintf(cmdline, 512, "--event %d", rv); + + dom->guest_domid = domid; + dom->cmdline = xc_dom_strdup(dom, cmdline); +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 13e48aaa731c..36fb4a832834 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -2308,7 +2308,7 @@ static void accept_connection(int sock) + } + #endif + +-static int tdb_flags; ++static int tdb_flags = TDB_INTERNAL | TDB_NOLOCK; + + /* We create initial nodes manually. */ + static void manual_node(const char *name, const char *child) +@@ -2618,7 +2618,8 @@ static void usage(void) + " watch-event: time a watch-event is kept pending\n" + " -R, --no-recovery to request that no recovery should be attempted when\n" + " the store is corrupted (debug only),\n" +-" -I, --internal-db store database in memory, not on disk\n" ++" -I, --internal-db [on|off] store database in memory, not on disk, default is\n" ++" memory, with \"--internal-db off\" it is on disk\n" + " -K, --keep-orphans don't delete nodes owned by a domain when the\n" + " domain is deleted (this is a security risk!)\n" + " -V, --verbose to request verbose execution.\n"); +@@ -2644,7 +2645,7 @@ static struct option options[] = { + { "quota-soft", 1, NULL, 'q' }, + { "timeout", 1, NULL, 'w' }, + { "no-recovery", 0, NULL, 'R' }, +- { "internal-db", 0, NULL, 'I' }, ++ { "internal-db", 2, NULL, 'I' }, + { "keep-orphans", 0, NULL, 'K' }, + { "verbose", 0, NULL, 'V' }, + { "watch-nb", 1, NULL, 'W' }, +@@ -2725,7 +2726,8 @@ int main(int argc, char *argv[]) + orig_argc = argc; + orig_argv = argv; + +- while ((opt = getopt_long(argc, argv, "DE:F:HKNPS:t:A:M:Q:q:T:RVW:w:U", ++ while ((opt = getopt_long(argc, argv, ++ "DE:F:HI::KNPS:t:A:M:Q:q:T:RVW:w:U", + options, NULL)) != -1) { + switch (opt) { + case 'D': +@@ -2759,7 +2761,8 @@ int main(int argc, char *argv[]) + tracefile = optarg; + break; + case 'I': +- tdb_flags = TDB_INTERNAL|TDB_NOLOCK; ++ if (optarg && !strcmp(optarg, "off")) ++ tdb_flags = 0; + break; + case 'K': + keep_orphans = true; diff --git a/xsa419-xenstored-03.patch b/xsa419-xenstored-03.patch new file mode 100644 index 0000000..38130c0 --- /dev/null +++ b/xsa419-xenstored-03.patch @@ -0,0 +1,44 @@ +From 54e63b7e7c42e4f975163809a01574e78552a6ab Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:13 +0200 +Subject: docs: enhance xenstore.txt with permissions description +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The permission scheme of Xenstore nodes is not really covered by +docs/misc/xenstore.txt, other than referring to the Xen wiki. + +Add a paragraph explaining the permissions of nodes, and especially +mentioning removal of nodes when a domain has been removed from +Xenstore. + +This is part of XSA-419. + +Reported-by: Juergen Gross +Signed-off-by: Juergen Gross +Reviewed-by: Edwin Török +Acked-by: Julien Grall + +diff --git a/docs/misc/xenstore.txt b/docs/misc/xenstore.txt +index 988ef89cba2d..44428ae3a755 100644 +--- a/docs/misc/xenstore.txt ++++ b/docs/misc/xenstore.txt +@@ -43,6 +43,17 @@ bytes are forbidden; clients specifying relative paths should keep + them to within 2048 bytes. (See XENSTORE_*_PATH_MAX in xs_wire.h.) + + ++Each node has one or multiple permission entries. Permissions are ++granted by domain-id, the first permission entry of each node specifies ++the owner of the node. Permissions of a node can be changed by the ++owner of the node, the owner can only be modified by the control ++domain (usually domain id 0). The owner always has the right to read ++and write the node, while other permissions can be setup to allow ++read and/or write access. When a domain is being removed from Xenstore ++nodes owned by that domain will be removed together with all of those ++nodes' children. ++ ++ + Communication with xenstore is via either sockets, or event channel + and shared memory, as specified in io/xs_wire.h: each message in + either direction is a header formatted as a struct xsd_sockmsg diff --git a/xsa420.patch b/xsa420.patch new file mode 100644 index 0000000..5d00dc2 --- /dev/null +++ b/xsa420.patch @@ -0,0 +1,68 @@ +From 210879456769ca211c6630f47399ca7a61a37f35 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Date: Wed, 12 Oct 2022 19:13:05 +0100 +Subject: tools/ocaml: Ensure packet size is never negative +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Integers in Ocaml have 63 or 31 bits of signed precision. + +On 64-bit builds of Ocaml, this is fine because a C uint32_t always fits +within a 63-bit signed integer. + +In 32-bit builds of Ocaml, this goes wrong. The C uint32_t is truncated +first (loses the top bit), then has a unsigned/signed mismatch. + +A "negative" value (i.e. a packet on the ring of between 1G and 2G in size) +will trigger an exception later in Bytes.make in xb.ml, and because the packet +is not removed from the ring, the exception re-triggers on every subsequent +query, creating a livelock. + +Fix both the source of the exception in Xb, and as defence in depth, mark the +domain as bad for any Invalid_argument exceptions to avoid the risk of +livelock. + +This is XSA-420 / CVE-2022-42324. + +Reported-by: Juergen Gross +Signed-off-by: Edwin Török +Acked-by: Christian Lindig + +diff --git a/tools/ocaml/libs/xb/partial.ml b/tools/ocaml/libs/xb/partial.ml +index b6e2a716e263..3aa8927eb7f0 100644 +--- a/tools/ocaml/libs/xb/partial.ml ++++ b/tools/ocaml/libs/xb/partial.ml +@@ -36,7 +36,7 @@ let of_string s = + This will leave the guest connection is a bad state and will + be hard to recover from without restarting the connection + (ie rebooting the guest) *) +- let dlen = min xenstore_payload_max dlen in ++ let dlen = max 0 (min xenstore_payload_max dlen) in + { + tid = tid; + rid = rid; +@@ -46,8 +46,8 @@ let of_string s = + } + + let append pkt s sz = +- if pkt.len > 4096 then failwith "Buffer.add: cannot grow buffer"; +- Buffer.add_string pkt.buf (String.sub s 0 sz) ++ if Buffer.length pkt.buf + sz > xenstore_payload_max then failwith "Buffer.add: cannot grow buffer"; ++ Buffer.add_substring pkt.buf s 0 sz + + let to_complete pkt = + pkt.len - (Buffer.length pkt.buf) +diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml +index 5f439fe59f47..f3a71b24ad94 100644 +--- a/tools/ocaml/xenstored/process.ml ++++ b/tools/ocaml/xenstored/process.ml +@@ -722,7 +722,7 @@ let do_input store cons doms con = + History.reconnect con; + info "%s reconnection complete" (Connection.get_domstr con); + None +- | Failure exp -> ++ | Invalid_argument exp | Failure exp -> + error "caught exception %s" exp; + error "got a bad client %s" (sprintf "%-8s" (Connection.get_domstr con)); + Connection.mark_as_bad con; diff --git a/xsa421-01.patch b/xsa421-01.patch new file mode 100644 index 0000000..10960ba --- /dev/null +++ b/xsa421-01.patch @@ -0,0 +1,40 @@ +From 9166869e7e6530befddfd8bb46ff37436a38efc1 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:13 +0200 +Subject: tools/xenstore: fix deleting node in transaction + +In case a node has been created in a transaction and it is later +deleted in the same transaction, the transaction will be terminated +with an error. + +As this error is encountered only when handling the deleted node at +transaction finalization, the transaction will have been performed +partially and without updating the accounting information. This will +enable a malicious guest to create arbitrary number of nodes. + +This is part of XSA-421 / CVE-2022-42325. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Tested-by: Julien Grall +Reviewed-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c +index 3e3eb47326cc..7ffe21bb5285 100644 +--- a/tools/xenstore/xenstored_transaction.c ++++ b/tools/xenstore/xenstored_transaction.c +@@ -418,7 +418,13 @@ static int finalize_transaction(struct connection *conn, + true); + talloc_free(data.dptr); + } else { +- ret = do_tdb_delete(conn, &key, NULL); ++ /* ++ * A node having been created and later deleted ++ * in this transaction will have no generation ++ * information stored. ++ */ ++ ret = (i->generation == NO_GENERATION) ++ ? 0 : do_tdb_delete(conn, &key, NULL); + } + if (ret) + goto err; diff --git a/xsa421-02.patch b/xsa421-02.patch new file mode 100644 index 0000000..1a0b5cd --- /dev/null +++ b/xsa421-02.patch @@ -0,0 +1,401 @@ +From 09fc22fea8a4689c5e563ba4a2fa959282071792 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 13 Sep 2022 07:35:14 +0200 +Subject: tools/xenstore: harden transaction finalization against errors + +When finalizing a transaction, any error occurring after checking for +conflicts will result in the transaction being performed only +partially today. Additionally accounting data will not be updated at +the end of the transaction, which might result in further problems +later. + +Avoid those problems by multiple modifications: + +- free any transaction specific nodes which don't need to be committed + as they haven't been written during the transaction as soon as their + generation count has been verified, this will reduce the risk of + out-of-memory situations + +- store the transaction specific node name in struct accessed_node in + order to avoid the need to allocate additional memory for it when + finalizing the transaction + +- don't stop the transaction finalization when hitting an error + condition, but try to continue to handle all modified nodes + +- in case of a detected error do the accounting update as needed and + call the data base checking only after that + +- if writing a node in a transaction is failing (e.g. due to a failed + quota check), fail the transaction, as prior changes to struct + accessed_node can't easily be undone in that case + +This is part of XSA-421 / CVE-2022-42326. + +Reported-by: Julien Grall +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall +Tested-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 36fb4a832834..476d5c6d51bd 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -723,8 +723,7 @@ struct node *read_node(struct connection *conn, const void *ctx, + return NULL; + } + +- if (transaction_prepend(conn, name, &key)) +- return NULL; ++ transaction_prepend(conn, name, &key); + + data = tdb_fetch(tdb_ctx, key); + +@@ -842,10 +841,21 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, + static int write_node(struct connection *conn, struct node *node, + bool no_quota_check) + { ++ int ret; ++ + if (access_node(conn, node, NODE_ACCESS_WRITE, &node->key)) + return errno; + +- return write_node_raw(conn, &node->key, node, no_quota_check); ++ ret = write_node_raw(conn, &node->key, node, no_quota_check); ++ if (ret && conn && conn->transaction) { ++ /* ++ * Reverting access_node() is hard, so just fail the ++ * transaction. ++ */ ++ fail_transaction(conn->transaction); ++ } ++ ++ return ret; + } + + unsigned int perm_for_conn(struct connection *conn, +diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c +index 7ffe21bb5285..ac854197cadb 100644 +--- a/tools/xenstore/xenstored_transaction.c ++++ b/tools/xenstore/xenstored_transaction.c +@@ -114,7 +114,8 @@ struct accessed_node + struct list_head list; + + /* The name of the node. */ +- char *node; ++ char *trans_name; /* Transaction specific name. */ ++ char *node; /* Main data base name. */ + + /* Generation count (or NO_GENERATION) for conflict checking. */ + uint64_t generation; +@@ -199,25 +200,20 @@ static char *transaction_get_node_name(void *ctx, struct transaction *trans, + * Prepend the transaction to name if node has been modified in the current + * transaction. + */ +-int transaction_prepend(struct connection *conn, const char *name, +- TDB_DATA *key) ++void transaction_prepend(struct connection *conn, const char *name, ++ TDB_DATA *key) + { +- char *tdb_name; ++ struct accessed_node *i; + +- if (!conn || !conn->transaction || +- !find_accessed_node(conn->transaction, name)) { +- set_tdb_key(name, key); +- return 0; ++ if (conn && conn->transaction) { ++ i = find_accessed_node(conn->transaction, name); ++ if (i) { ++ set_tdb_key(i->trans_name, key); ++ return; ++ } + } + +- tdb_name = transaction_get_node_name(conn->transaction, +- conn->transaction, name); +- if (!tdb_name) +- return errno; +- +- set_tdb_key(tdb_name, key); +- +- return 0; ++ set_tdb_key(name, key); + } + + /* +@@ -240,7 +236,6 @@ int access_node(struct connection *conn, struct node *node, + struct accessed_node *i = NULL; + struct transaction *trans; + TDB_DATA local_key; +- const char *trans_name = NULL; + int ret; + bool introduce = false; + +@@ -259,10 +254,6 @@ int access_node(struct connection *conn, struct node *node, + + trans = conn->transaction; + +- trans_name = transaction_get_node_name(node, trans, node->name); +- if (!trans_name) +- goto nomem; +- + i = find_accessed_node(trans, node->name); + if (!i) { + if (trans->nodes >= quota_trans_nodes && +@@ -273,9 +264,10 @@ int access_node(struct connection *conn, struct node *node, + i = talloc_zero(trans, struct accessed_node); + if (!i) + goto nomem; +- i->node = talloc_strdup(i, node->name); +- if (!i->node) ++ i->trans_name = transaction_get_node_name(i, trans, node->name); ++ if (!i->trans_name) + goto nomem; ++ i->node = strchr(i->trans_name, '/') + 1; + if (node->generation != NO_GENERATION && node->perms.num) { + i->perms.p = talloc_array(i, struct xs_permissions, + node->perms.num); +@@ -302,7 +294,7 @@ int access_node(struct connection *conn, struct node *node, + i->generation = node->generation; + i->check_gen = true; + if (node->generation != NO_GENERATION) { +- set_tdb_key(trans_name, &local_key); ++ set_tdb_key(i->trans_name, &local_key); + ret = write_node_raw(conn, &local_key, node, true); + if (ret) + goto err; +@@ -321,7 +313,7 @@ int access_node(struct connection *conn, struct node *node, + return -1; + + if (key) { +- set_tdb_key(trans_name, key); ++ set_tdb_key(i->trans_name, key); + if (type == NODE_ACCESS_WRITE) + i->ta_node = true; + if (type == NODE_ACCESS_DELETE) +@@ -333,7 +325,6 @@ int access_node(struct connection *conn, struct node *node, + nomem: + ret = ENOMEM; + err: +- talloc_free((void *)trans_name); + talloc_free(i); + trans->fail = true; + errno = ret; +@@ -371,100 +362,90 @@ void queue_watches(struct connection *conn, const char *name, bool watch_exact) + * base. + */ + static int finalize_transaction(struct connection *conn, +- struct transaction *trans) ++ struct transaction *trans, bool *is_corrupt) + { +- struct accessed_node *i; ++ struct accessed_node *i, *n; + TDB_DATA key, ta_key, data; + struct xs_tdb_record_hdr *hdr; + uint64_t gen; +- char *trans_name; +- int ret; + +- list_for_each_entry(i, &trans->accessed, list) { +- if (!i->check_gen) +- continue; ++ list_for_each_entry_safe(i, n, &trans->accessed, list) { ++ if (i->check_gen) { ++ set_tdb_key(i->node, &key); ++ data = tdb_fetch(tdb_ctx, key); ++ hdr = (void *)data.dptr; ++ if (!data.dptr) { ++ if (tdb_error(tdb_ctx) != TDB_ERR_NOEXIST) ++ return EIO; ++ gen = NO_GENERATION; ++ } else ++ gen = hdr->generation; ++ talloc_free(data.dptr); ++ if (i->generation != gen) ++ return EAGAIN; ++ } + +- set_tdb_key(i->node, &key); +- data = tdb_fetch(tdb_ctx, key); +- hdr = (void *)data.dptr; +- if (!data.dptr) { +- if (tdb_error(tdb_ctx) != TDB_ERR_NOEXIST) +- return EIO; +- gen = NO_GENERATION; +- } else +- gen = hdr->generation; +- talloc_free(data.dptr); +- if (i->generation != gen) +- return EAGAIN; ++ /* Entries for unmodified nodes can be removed early. */ ++ if (!i->modified) { ++ if (i->ta_node) { ++ set_tdb_key(i->trans_name, &ta_key); ++ if (do_tdb_delete(conn, &ta_key, NULL)) ++ return EIO; ++ } ++ list_del(&i->list); ++ talloc_free(i); ++ } + } + + while ((i = list_top(&trans->accessed, struct accessed_node, list))) { +- trans_name = transaction_get_node_name(i, trans, i->node); +- if (!trans_name) +- /* We are doomed: the transaction is only partial. */ +- goto err; +- +- set_tdb_key(trans_name, &ta_key); +- +- if (i->modified) { +- set_tdb_key(i->node, &key); +- if (i->ta_node) { +- data = tdb_fetch(tdb_ctx, ta_key); +- if (!data.dptr) +- goto err; ++ set_tdb_key(i->node, &key); ++ if (i->ta_node) { ++ set_tdb_key(i->trans_name, &ta_key); ++ data = tdb_fetch(tdb_ctx, ta_key); ++ if (data.dptr) { + hdr = (void *)data.dptr; + hdr->generation = ++generation; +- ret = do_tdb_write(conn, &key, &data, NULL, +- true); ++ *is_corrupt |= do_tdb_write(conn, &key, &data, ++ NULL, true); + talloc_free(data.dptr); ++ if (do_tdb_delete(conn, &ta_key, NULL)) ++ *is_corrupt = true; + } else { +- /* +- * A node having been created and later deleted +- * in this transaction will have no generation +- * information stored. +- */ +- ret = (i->generation == NO_GENERATION) +- ? 0 : do_tdb_delete(conn, &key, NULL); +- } +- if (ret) +- goto err; +- if (i->fire_watch) { +- fire_watches(conn, trans, i->node, NULL, +- i->watch_exact, +- i->perms.p ? &i->perms : NULL); ++ *is_corrupt = true; + } ++ } else { ++ /* ++ * A node having been created and later deleted ++ * in this transaction will have no generation ++ * information stored. ++ */ ++ *is_corrupt |= (i->generation == NO_GENERATION) ++ ? false ++ : do_tdb_delete(conn, &key, NULL); + } ++ if (i->fire_watch) ++ fire_watches(conn, trans, i->node, NULL, i->watch_exact, ++ i->perms.p ? &i->perms : NULL); + +- if (i->ta_node && do_tdb_delete(conn, &ta_key, NULL)) +- goto err; + list_del(&i->list); + talloc_free(i); + } + + return 0; +- +-err: +- corrupt(conn, "Partial transaction"); +- return EIO; + } + + static int destroy_transaction(void *_transaction) + { + struct transaction *trans = _transaction; + struct accessed_node *i; +- char *trans_name; + TDB_DATA key; + + wrl_ntransactions--; + trace_destroy(trans, "transaction"); + while ((i = list_top(&trans->accessed, struct accessed_node, list))) { + if (i->ta_node) { +- trans_name = transaction_get_node_name(i, trans, +- i->node); +- if (trans_name) { +- set_tdb_key(trans_name, &key); +- do_tdb_delete(trans->conn, &key, NULL); +- } ++ set_tdb_key(i->trans_name, &key); ++ do_tdb_delete(trans->conn, &key, NULL); + } + list_del(&i->list); + talloc_free(i); +@@ -556,6 +537,7 @@ int do_transaction_end(const void *ctx, struct connection *conn, + { + const char *arg = onearg(in); + struct transaction *trans; ++ bool is_corrupt = false; + int ret; + + if (!arg || (!streq(arg, "T") && !streq(arg, "F"))) +@@ -579,13 +561,17 @@ int do_transaction_end(const void *ctx, struct connection *conn, + ret = transaction_fix_domains(trans, false); + if (ret) + return ret; +- if (finalize_transaction(conn, trans)) +- return EAGAIN; ++ ret = finalize_transaction(conn, trans, &is_corrupt); ++ if (ret) ++ return ret; + + wrl_apply_debit_trans_commit(conn); + + /* fix domain entry for each changed domain */ + transaction_fix_domains(trans, true); ++ ++ if (is_corrupt) ++ corrupt(conn, "transaction inconsistency"); + } + send_ack(conn, XS_TRANSACTION_END); + +@@ -660,7 +646,7 @@ int check_transactions(struct hashtable *hash) + struct connection *conn; + struct transaction *trans; + struct accessed_node *i; +- char *tname, *tnode; ++ char *tname; + + list_for_each_entry(conn, &connections, list) { + list_for_each_entry(trans, &conn->transaction_list, list) { +@@ -672,11 +658,8 @@ int check_transactions(struct hashtable *hash) + list_for_each_entry(i, &trans->accessed, list) { + if (!i->ta_node) + continue; +- tnode = transaction_get_node_name(tname, trans, +- i->node); +- if (!tnode || !remember_string(hash, tnode)) ++ if (!remember_string(hash, i->trans_name)) + goto nomem; +- talloc_free(tnode); + } + + talloc_free(tname); +diff --git a/tools/xenstore/xenstored_transaction.h b/tools/xenstore/xenstored_transaction.h +index 39d7f81c5127..3417303f9427 100644 +--- a/tools/xenstore/xenstored_transaction.h ++++ b/tools/xenstore/xenstored_transaction.h +@@ -48,8 +48,8 @@ int __must_check access_node(struct connection *conn, struct node *node, + void queue_watches(struct connection *conn, const char *name, bool watch_exact); + + /* Prepend the transaction to name if appropriate. */ +-int transaction_prepend(struct connection *conn, const char *name, +- TDB_DATA *key); ++void transaction_prepend(struct connection *conn, const char *name, ++ TDB_DATA *key); + + /* Mark the transaction as failed. This will prevent it to be committed. */ + void fail_transaction(struct transaction *trans); From 0aca66e99a22f8f083168c244fa67dc7993fb157 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 8 Nov 2022 22:29:29 +0000 Subject: [PATCH 050/137] x86: Multiple speculative security issues --- xen.spec | 9 +++- xsa422-4.16-1.patch | 70 +++++++++++++++++++++++++++++++ xsa422-4.16-2.patch | 100 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 xsa422-4.16-1.patch create mode 100644 xsa422-4.16-2.patch diff --git a/xen.spec b/xen.spec index db387f5..27ed461 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.16.2 -Release: 3%{?dist} +Release: 4%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -169,6 +169,8 @@ Patch101: xsa419-xenstored-03.patch Patch102: xsa420.patch Patch103: xsa421-01.patch Patch104: xsa421-02.patch +Patch105: xsa422-4.16-1.patch +Patch106: xsa422-4.16-2.patch %if %build_qemutrad @@ -439,6 +441,8 @@ manage Xen virtual machines. %patch102 -p1 %patch103 -p1 %patch104 -p1 +%patch105 -p1 +%patch106 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -1054,6 +1058,9 @@ fi %endif %changelog +* Tue Nov 08 2022 Michael Young - 4.16.2-4 +- x86: Multiple speculative security issues [XSA-422, CVE-2022-23824] + * Tue Nov 01 2022 Michael Young - 4.16.2-3 - x86: unintended memory sharing between guests [XSA-412, CVE-2022-42327] - Xenstore: Guests can crash xenstored [XSA-414, CVE-2022-42309] diff --git a/xsa422-4.16-1.patch b/xsa422-4.16-1.patch new file mode 100644 index 0000000..1d36873 --- /dev/null +++ b/xsa422-4.16-1.patch @@ -0,0 +1,70 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Enumeration for IBPB_RET + +The IBPB_RET bit indicates that the CPU's implementation of MSR_PRED_CMD.IBPB +does flush the RSB/RAS too. + +This is part of XSA-422 / CVE-2022-23824. + +Signed-off-by: Andrew Cooper +Acked-by: Jan Beulich + +diff --git a/tools/libs/light/libxl_cpuid.c b/tools/libs/light/libxl_cpuid.c +index bf6fdee360a9..691d5c6b2a68 100644 +--- a/tools/libs/light/libxl_cpuid.c ++++ b/tools/libs/light/libxl_cpuid.c +@@ -289,6 +289,7 @@ int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str) + {"ssb-no", 0x80000008, NA, CPUID_REG_EBX, 26, 1}, + {"psfd", 0x80000008, NA, CPUID_REG_EBX, 28, 1}, + {"btc-no", 0x80000008, NA, CPUID_REG_EBX, 29, 1}, ++ {"ibpb-ret", 0x80000008, NA, CPUID_REG_EBX, 30, 1}, + + {"nc", 0x80000008, NA, CPUID_REG_ECX, 0, 8}, + {"apicidsize", 0x80000008, NA, CPUID_REG_ECX, 12, 4}, +diff --git a/tools/misc/xen-cpuid.c b/tools/misc/xen-cpuid.c +index fe22f5f5b68b..cd094427dd4c 100644 +--- a/tools/misc/xen-cpuid.c ++++ b/tools/misc/xen-cpuid.c +@@ -159,6 +159,7 @@ static const char *const str_e8b[32] = + [24] = "amd-ssbd", [25] = "virt-ssbd", + [26] = "ssb-no", + [28] = "psfd", [29] = "btc-no", ++ [30] = "ibpb-ret", + }; + + static const char *const str_7d0[32] = +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 0f4bad3d3abb..16a562d3a172 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -419,7 +419,7 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + * Hardware read-only information, stating immunity to certain issues, or + * suggestions of which mitigation to use. + */ +- printk(" Hardware hints:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", ++ printk(" Hardware hints:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", + (caps & ARCH_CAPS_RDCL_NO) ? " RDCL_NO" : "", + (caps & ARCH_CAPS_IBRS_ALL) ? " IBRS_ALL" : "", + (caps & ARCH_CAPS_RSBA) ? " RSBA" : "", +@@ -436,7 +436,8 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + (e8b & cpufeat_mask(X86_FEATURE_STIBP_ALWAYS)) ? " STIBP_ALWAYS" : "", + (e8b & cpufeat_mask(X86_FEATURE_IBRS_FAST)) ? " IBRS_FAST" : "", + (e8b & cpufeat_mask(X86_FEATURE_IBRS_SAME_MODE)) ? " IBRS_SAME_MODE" : "", +- (e8b & cpufeat_mask(X86_FEATURE_BTC_NO)) ? " BTC_NO" : ""); ++ (e8b & cpufeat_mask(X86_FEATURE_BTC_NO)) ? " BTC_NO" : "", ++ (e8b & cpufeat_mask(X86_FEATURE_IBPB_RET)) ? " IBPB_RET" : ""); + + /* Hardware features which need driving to mitigate issues. */ + printk(" Hardware features:%s%s%s%s%s%s%s%s%s%s%s%s\n", +diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h +index e7b8167800a2..e0731221404c 100644 +--- a/xen/include/public/arch-x86/cpufeatureset.h ++++ b/xen/include/public/arch-x86/cpufeatureset.h +@@ -267,6 +267,7 @@ XEN_CPUFEATURE(VIRT_SSBD, 8*32+25) /* MSR_VIRT_SPEC_CTRL.SSBD */ + XEN_CPUFEATURE(SSB_NO, 8*32+26) /*A Hardware not vulnerable to SSB */ + XEN_CPUFEATURE(PSFD, 8*32+28) /*S MSR_SPEC_CTRL.PSFD */ + XEN_CPUFEATURE(BTC_NO, 8*32+29) /*A Hardware not vulnerable to Branch Type Confusion */ ++XEN_CPUFEATURE(IBPB_RET, 8*32+30) /*A IBPB clears RSB/RAS too. */ + + /* Intel-defined CPU features, CPUID level 0x00000007:0.edx, word 9 */ + XEN_CPUFEATURE(AVX512_4VNNIW, 9*32+ 2) /*A AVX512 Neural Network Instructions */ diff --git a/xsa422-4.16-2.patch b/xsa422-4.16-2.patch new file mode 100644 index 0000000..a1a2f8d --- /dev/null +++ b/xsa422-4.16-2.patch @@ -0,0 +1,100 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Mitigate IBPB not flushing the RSB/RAS + +Introduce spec_ctrl_new_guest_context() to encapsulate all logic pertaining to +using MSR_PRED_CMD for a new guest context, even if it only has one user +presently. + +Introduce X86_BUG_IBPB_NO_RET, and use it extend spec_ctrl_new_guest_context() +with a manual fixup for hardware which mis-implements IBPB. + +This is part of XSA-422 / CVE-2022-23824. + +Signed-off-by: Andrew Cooper +Acked-by: Jan Beulich + +diff --git a/xen/arch/x86/asm-macros.c b/xen/arch/x86/asm-macros.c +index 7e536b0d82f5..891d86c7655c 100644 +--- a/xen/arch/x86/asm-macros.c ++++ b/xen/arch/x86/asm-macros.c +@@ -1,2 +1,3 @@ + #include + #include ++#include +diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c +index 3fab2364be8d..3080cde62b5b 100644 +--- a/xen/arch/x86/domain.c ++++ b/xen/arch/x86/domain.c +@@ -2092,7 +2092,7 @@ void context_switch(struct vcpu *prev, struct vcpu *next) + */ + if ( *last_id != next_id ) + { +- wrmsrl(MSR_PRED_CMD, PRED_CMD_IBPB); ++ spec_ctrl_new_guest_context(); + *last_id = next_id; + } + } +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 16a562d3a172..90d86fe5cb47 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -805,6 +805,14 @@ static void __init ibpb_calculations(void) + } + + /* ++ * AMD/Hygon CPUs to date (June 2022) don't flush the the RAS. Future ++ * CPUs are expected to enumerate IBPB_RET when this has been fixed. ++ * Until then, cover the difference with the software sequence. ++ */ ++ if ( boot_cpu_has(X86_FEATURE_IBPB) && !boot_cpu_has(X86_FEATURE_IBPB_RET) ) ++ setup_force_cpu_cap(X86_BUG_IBPB_NO_RET); ++ ++ /* + * IBPB-on-entry mitigations for Branch Type Confusion. + * + * IBPB && !BTC_NO selects all AMD/Hygon hardware, not known to be safe, +diff --git a/xen/include/asm-x86/cpufeatures.h b/xen/include/asm-x86/cpufeatures.h +index 672c9ee22ba2..ecc1bb09505a 100644 +--- a/xen/include/asm-x86/cpufeatures.h ++++ b/xen/include/asm-x86/cpufeatures.h +@@ -49,6 +49,7 @@ XEN_CPUFEATURE(IBPB_ENTRY_HVM, X86_SYNTH(29)) /* MSR_PRED_CMD used by Xen for + #define X86_BUG_FPU_PTRS X86_BUG( 0) /* (F)X{SAVE,RSTOR} doesn't save/restore FOP/FIP/FDP. */ + #define X86_BUG_NULL_SEG X86_BUG( 1) /* NULL-ing a selector preserves the base and limit. */ + #define X86_BUG_CLFLUSH_MFENCE X86_BUG( 2) /* MFENCE needed to serialise CLFLUSH */ ++#define X86_BUG_IBPB_NO_RET X86_BUG( 3) /* IBPB doesn't flush the RSB/RAS */ + + /* Total number of capability words, inc synth and bug words. */ + #define NCAPINTS (FSCAPINTS + X86_NR_SYNTH + X86_NR_BUG) /* N 32-bit words worth of info */ +diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h +index 9403b81dc7af..6a77c3937844 100644 +--- a/xen/include/asm-x86/spec_ctrl.h ++++ b/xen/include/asm-x86/spec_ctrl.h +@@ -65,6 +65,28 @@ + void init_speculation_mitigations(void); + void spec_ctrl_init_domain(struct domain *d); + ++/* ++ * Switch to a new guest prediction context. ++ * ++ * This flushes all indirect branch predictors (BTB, RSB/RAS), so guest code ++ * which has previously run on this CPU can't attack subsequent guest code. ++ * ++ * As this flushes the RSB/RAS, it destroys the predictions of the calling ++ * context. For best performace, arrange for this to be used when we're going ++ * to jump out of the current context, e.g. with reset_stack_and_jump(). ++ * ++ * For hardware which mis-implements IBPB, fix up by flushing the RSB/RAS ++ * manually. ++ */ ++static always_inline void spec_ctrl_new_guest_context(void) ++{ ++ wrmsrl(MSR_PRED_CMD, PRED_CMD_IBPB); ++ ++ /* (ab)use alternative_input() to specify clobbers. */ ++ alternative_input("", "DO_OVERWRITE_RSB", X86_BUG_IBPB_NO_RET, ++ : "rax", "rcx"); ++} ++ + extern int8_t opt_ibpb_ctxt_switch; + extern bool opt_ssbd; + extern int8_t opt_eager_fpu; From a54439cc72f1434e97eb4242873d7540ef5c4b6a Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 13 Dec 2022 20:46:36 +0000 Subject: [PATCH 051/137] update to xen-4.17.0 rebase xen.fedora.systemd.patch and xen.canonicalize.patch remove or adjust patches now included or superceded upstream /var/lib/xenstored has moved to /run/xenstored --- .gitignore | 2 +- sources | 2 +- xen.canonicalize.patch | 4 +- xen.efi.build.patch | 19 +- xen.fedora.systemd.patch | 35 +- xen.gcc10.fixes.patch | 10 - xen.spec | 139 +-- xsa326-4.16-oxenstored-01.patch | 55 -- xsa326-4.16-oxenstored-02.patch | 94 -- xsa326-4.16-oxenstored-03.patch | 119 --- xsa326-4.16-oxenstored-04.patch | 85 -- xsa326-4.16-oxenstored-05.patch | 214 ----- xsa326-4.16-oxenstored-06.patch | 127 --- xsa326-4.16-oxenstored-07.patch | 872 ------------------ xsa326-4.16-oxenstored-08.patch | 49 - xsa326-4.16-xenstored-01.patch | 205 ---- xsa326-4.16-xenstored-02.patch | 108 --- xsa326-4.16-xenstored-03.patch | 192 ---- xsa326-4.16-xenstored-04.patch | 302 ------ xsa326-4.16-xenstored-05.patch | 443 --------- xsa326-4.16-xenstored-06.patch | 85 -- xsa326-4.16-xenstored-07.patch | 53 -- xsa326-4.16-xenstored-08.patch | 326 ------- xsa326-4.16-xenstored-09.patch | 244 ----- xsa326-4.16-xenstored-10.patch | 88 -- xsa326-4.16-xenstored-11.patch | 280 ------ xsa326-4.16-xenstored-12.patch | 76 -- xsa326-4.16-xenstored-13.patch | 89 -- xsa326-4.16-xenstored-14.patch | 334 ------- xsa326-4.16-xenstored-15.patch | 54 -- xsa326-4.16-xenstored-16.patch | 238 ----- xsa376.patch | 145 --- ...-arch-specific-default-paging-memory.patch | 147 --- ...struct-the-P2M-pages-pool-for-guests.patch | 187 ---- ...plement-XEN_DOMCTL_shadow_op-for-Arm.patch | 106 --- ...-and-free-P2M-pages-from-the-P2M-poo.patch | 287 ------ xsa410-4.16-01.patch | 59 -- xsa410-4.16-02.patch | 165 ---- xsa410-4.16-03.patch | 113 --- xsa410-4.16-04.patch | 62 -- xsa410-4.16-05.patch | 60 -- xsa410-4.16-06.patch | 255 ----- xsa410-4.16-07.patch | 82 -- xsa410-4.16-08.patch | 96 -- xsa410-4.16-09.patch | 159 ---- xsa410-4.16-10.patch | 171 ---- xsa411.patch | 55 -- xsa412-4.16.patch | 245 ----- xsa414.patch | 112 --- xsa415.patch | 134 --- xsa416-4.16.patch | 704 -------------- xsa417.patch | 135 --- xsa418-4.16-01.patch | 119 --- xsa418-4.16-02.patch | 103 --- xsa418-4.16-03.patch | 243 ----- xsa418-4.16-04.patch | 108 --- xsa418-4.16-05.patch | 164 ---- xsa418-4.16-06.patch | 174 ---- xsa418-4.16-07.patch | 163 ---- xsa419-oxenstored.patch | 85 -- xsa419-xenstored-01.patch | 289 ------ xsa419-xenstored-02.patch | 93 -- xsa419-xenstored-03.patch | 44 - xsa420.patch | 68 -- xsa421-01.patch | 40 - xsa421-02.patch | 401 -------- xsa422-4.16-1.patch | 70 -- xsa422-4.16-2.patch | 100 -- 68 files changed, 35 insertions(+), 10651 deletions(-) delete mode 100644 xen.gcc10.fixes.patch delete mode 100644 xsa326-4.16-oxenstored-01.patch delete mode 100644 xsa326-4.16-oxenstored-02.patch delete mode 100644 xsa326-4.16-oxenstored-03.patch delete mode 100644 xsa326-4.16-oxenstored-04.patch delete mode 100644 xsa326-4.16-oxenstored-05.patch delete mode 100644 xsa326-4.16-oxenstored-06.patch delete mode 100644 xsa326-4.16-oxenstored-07.patch delete mode 100644 xsa326-4.16-oxenstored-08.patch delete mode 100644 xsa326-4.16-xenstored-01.patch delete mode 100644 xsa326-4.16-xenstored-02.patch delete mode 100644 xsa326-4.16-xenstored-03.patch delete mode 100644 xsa326-4.16-xenstored-04.patch delete mode 100644 xsa326-4.16-xenstored-05.patch delete mode 100644 xsa326-4.16-xenstored-06.patch delete mode 100644 xsa326-4.16-xenstored-07.patch delete mode 100644 xsa326-4.16-xenstored-08.patch delete mode 100644 xsa326-4.16-xenstored-09.patch delete mode 100644 xsa326-4.16-xenstored-10.patch delete mode 100644 xsa326-4.16-xenstored-11.patch delete mode 100644 xsa326-4.16-xenstored-12.patch delete mode 100644 xsa326-4.16-xenstored-13.patch delete mode 100644 xsa326-4.16-xenstored-14.patch delete mode 100644 xsa326-4.16-xenstored-15.patch delete mode 100644 xsa326-4.16-xenstored-16.patch delete mode 100644 xsa376.patch delete mode 100644 xsa409-4.13-0001-libxl-docs-Use-arch-specific-default-paging-memory.patch delete mode 100644 xsa409-4.13-0002-xen-arm-Construct-the-P2M-pages-pool-for-guests.patch delete mode 100644 xsa409-4.13-0003-xen-arm-libxl-Implement-XEN_DOMCTL_shadow_op-for-Arm.patch delete mode 100644 xsa409-4.13-0004-xen-arm-Allocate-and-free-P2M-pages-from-the-P2M-poo.patch delete mode 100644 xsa410-4.16-01.patch delete mode 100644 xsa410-4.16-02.patch delete mode 100644 xsa410-4.16-03.patch delete mode 100644 xsa410-4.16-04.patch delete mode 100644 xsa410-4.16-05.patch delete mode 100644 xsa410-4.16-06.patch delete mode 100644 xsa410-4.16-07.patch delete mode 100644 xsa410-4.16-08.patch delete mode 100644 xsa410-4.16-09.patch delete mode 100644 xsa410-4.16-10.patch delete mode 100644 xsa411.patch delete mode 100644 xsa412-4.16.patch delete mode 100644 xsa414.patch delete mode 100644 xsa415.patch delete mode 100644 xsa416-4.16.patch delete mode 100644 xsa417.patch delete mode 100644 xsa418-4.16-01.patch delete mode 100644 xsa418-4.16-02.patch delete mode 100644 xsa418-4.16-03.patch delete mode 100644 xsa418-4.16-04.patch delete mode 100644 xsa418-4.16-05.patch delete mode 100644 xsa418-4.16-06.patch delete mode 100644 xsa418-4.16-07.patch delete mode 100644 xsa419-oxenstored.patch delete mode 100644 xsa419-xenstored-01.patch delete mode 100644 xsa419-xenstored-02.patch delete mode 100644 xsa419-xenstored-03.patch delete mode 100644 xsa420.patch delete mode 100644 xsa421-01.patch delete mode 100644 xsa421-02.patch delete mode 100644 xsa422-4.16-1.patch delete mode 100644 xsa422-4.16-2.patch diff --git a/.gitignore b/.gitignore index f41c7f1..139f059 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.16.2.tar.gz +/xen-4.17.0.tar.gz diff --git a/sources b/sources index f6803eb..435fdc0 100644 --- a/sources +++ b/sources @@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.16.2.tar.gz) = b6cd036c1073798dffa167ca14c954fbdfb4c0ef99662f7c435e7e5de687d1bde8856ff6bd030d0d2e661bd17ab631551f01b2cc728cad7e70b59aaa6e692783 +SHA512 (xen-4.17.0.tar.gz) = 8aa7c3025c81127a4f653411bc1cbe8fc27b767a2f96a2a454329a6773cb74d714ecc1ff011a8e7169c6b83b2cc5cb39c39798bd6b1178df75ce9ff00f5a1011 diff --git a/xen.canonicalize.patch b/xen.canonicalize.patch index e339530..f3ae37d 100644 --- a/xen.canonicalize.patch +++ b/xen.canonicalize.patch @@ -13,8 +13,8 @@ if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) return EINVAL; -- node = canonicalize(conn, in, vec[0]); -+ node = xenstore_canonicalize(conn, in, vec[0]); +- node = canonicalize(conn, ctx, vec[0]); ++ node = xenstore_canonicalize(conn, ctx, vec[0]); if (!node) return ENOMEM; list_for_each_entry(watch, &conn->watches, list) { diff --git a/xen.efi.build.patch b/xen.efi.build.patch index d0c160b..06ee249 100644 --- a/xen.efi.build.patch +++ b/xen.efi.build.patch @@ -1,12 +1,13 @@ ---- xen-4.16.0/xen/arch/x86/Makefile.orig 2021-11-30 11:42:42.000000000 +0000 -+++ xen-4.16.0/xen/arch/x86/Makefile 2022-03-17 22:43:21.077990559 +0000 -@@ -127,7 +127,8 @@ - CFLAGS-$(XEN_BUILD_EFI) += -DXEN_BUILD_EFI +--- xen-4.17.0/xen/arch/x86/arch.mk.orig 2022-12-08 18:03:08.000000000 +0000 ++++ xen-4.17.0/xen/arch/x86/arch.mk 2022-12-10 12:13:11.043182208 +0000 +@@ -94,7 +94,9 @@ + XEN_BUILD_EFI := $(call if-success,$(CC) $(CFLAGS) -c $(srctree)/$(efi-check).c -o $(efi-check).o,y) # Check if the linker supports PE. --EFI_LDFLAGS = $(patsubst -m%,-mi386pep,$(XEN_LDFLAGS)) --subsystem=10 -+#EFI_LDFLAGS = $(patsubst -m%,-mi386pep,$(XEN_LDFLAGS)) --subsystem=10 +-EFI_LDFLAGS := $(patsubst -m%,-mi386pep,$(LDFLAGS)) --subsystem=10 ++#EFI_LDFLAGS := $(patsubst -m%,-mi386pep,$(LDFLAGS)) --subsystem=10 ++# use a reduced set of options from LDFLAGS +EFI_LDFLAGS = --as-needed --build-id=sha1 -mi386pep --subsystem=10 - XEN_BUILD_PE := $(if $(XEN_BUILD_EFI),$(call ld-option,$(EFI_LDFLAGS) --image-base=0x100000000 -o efi/check.efi efi/check.o)) - # If the above failed, it may be merely because of the linker not dealing well - # with debug info. Try again with stripping it. + LD_PE_check_cmd = $(call ld-option,$(EFI_LDFLAGS) --image-base=0x100000000 -o $(efi-check).efi $(efi-check).o) + XEN_BUILD_PE := $(LD_PE_check_cmd) + diff --git a/xen.fedora.systemd.patch b/xen.fedora.systemd.patch index 3b75ed0..ac56245 100644 --- a/xen.fedora.systemd.patch +++ b/xen.fedora.systemd.patch @@ -1,7 +1,6 @@ -diff -uN xen-4.5.0/tools/hotplug/Linux/systemd.orig/Makefile xen-4.5.0/tools/hotplug/Linux/systemd/Makefile ---- xen-4.5.0/tools/hotplug/Linux/systemd.orig/Makefile 2015-01-12 16:53:24.000000000 +0000 -+++ xen-4.5.0/tools/hotplug/Linux/systemd/Makefile 2015-01-25 22:23:26.000000000 +0000 -@@ -14,7 +14,8 @@ +--- xen-4.17.0/tools/hotplug/Linux/systemd/Makefile.orig 2022-12-08 18:03:08.000000000 +0000 ++++ xen-4.17.0/tools/hotplug/Linux/systemd/Makefile 2022-12-09 19:47:53.227189371 +0000 +@@ -10,7 +10,8 @@ XEN_SYSTEMD_SERVICE += xen-qemu-dom0-disk-backend.service XEN_SYSTEMD_SERVICE += xendomains.service XEN_SYSTEMD_SERVICE += xen-watchdog.service @@ -10,16 +9,7 @@ diff -uN xen-4.5.0/tools/hotplug/Linux/systemd.orig/Makefile xen-4.5.0/tools/hot +XEN_SYSTEMD_SERVICE += oxenstored.service XEN_SYSTEMD_SERVICE += xendriverdomain.service - ALL_XEN_SYSTEMD = $(XEN_SYSTEMD_MODULES) \ -diff -uN xen-4.5.0/tools/hotplug/Linux/systemd.orig/var-lib-xenstored.mount.in xen-4.5.0/tools/hotplug/Linux/systemd/var-lib-xenstored.mount.in ---- xen-4.5.0/tools/hotplug/Linux/systemd.orig/var-lib-xenstored.mount.in 2015-01-12 16:53:24.000000000 +0000 -+++ xen-4.5.0/tools/hotplug/Linux/systemd/var-lib-xenstored.mount.in 2015-01-25 22:28:59.000000000 +0000 -@@ -9,4 +9,4 @@ - What=xenstore - Where=@XEN_LIB_STORED@ - Type=tmpfs --Options=mode=755 -+Options=mode=755,context="system_u:object_r:xenstored_var_lib_t:s0" + ALL_XEN_SYSTEMD := $(XEN_SYSTEMD_MODULES) \ diff -uN xen-4.5.0/tools/hotplug/Linux/systemd.orig/xenconsoled.service.in xen-4.5.0/tools/hotplug/Linux/systemd/xenconsoled.service.in --- xen-4.5.0/tools/hotplug/Linux/systemd.orig/xenconsoled.service.in 2015-01-12 16:53:24.000000000 +0000 +++ xen-4.5.0/tools/hotplug/Linux/systemd/xenconsoled.service.in 2015-01-25 22:30:26.000000000 +0000 @@ -59,27 +49,26 @@ diff -uN xen-4.5.0/tools/hotplug/Linux/systemd.orig/xen-qemu-dom0-disk-backend.s Before=xendomains.service libvirtd.service libvirt-guests.service RefuseManualStop=true ConditionPathExists=/proc/xen/capabilities ---- xen-4.6.0/tools/configure.ac.orig 2015-02-15 16:47:22.000000000 +0000 -+++ xen-4.6.0/tools/configure.ac 2015-03-01 16:18:30.493647587 +0000 -@@ -382,9 +382,9 @@ +--- xen-4.17.0/tools/configure.ac.orig 2022-12-08 18:03:08.000000000 +0000 ++++ xen-4.17.0/tools/configure.ac 2022-12-09 19:50:24.773193862 +0000 +@@ -481,8 +481,8 @@ AS_IF([test "x$systemd" = "xy"], [ AC_CONFIG_FILES([ + hotplug/Linux/systemd/oxenstored.service hotplug/Linux/systemd/proc-xen.mount - hotplug/Linux/systemd/var-lib-xenstored.mount - hotplug/Linux/systemd/xen-init-dom0.service hotplug/Linux/systemd/xen-qemu-dom0-disk-backend.service hotplug/Linux/systemd/xen-watchdog.service hotplug/Linux/systemd/xenconsoled.service ---- xen-4.6.0/tools/configure.orig 2015-02-15 16:47:22.000000000 +0000 -+++ xen-4.6.0/tools/configure 2015-03-01 16:20:10.648285840 +0000 -@@ -8995,7 +8995,7 @@ +--- xen-4.17.0/tools/configure.orig 2022-12-08 18:03:08.000000000 +0000 ++++ xen-4.17.0/tools/configure 2022-12-09 19:51:43.278708226 +0000 +@@ -10081,7 +10081,7 @@ if test "x$systemd" = "xy"; then : -- ac_config_files="$ac_config_files hotplug/Linux/systemd/proc-xen.mount hotplug/Linux/systemd/var-lib-xenstored.mount hotplug/Linux/systemd/xen-init-dom0.service hotplug/Linux/systemd/xen-qemu-dom0-disk-backend.service hotplug/Linux/systemd/xen-watchdog.service hotplug/Linux/systemd/xenconsoled.service hotplug/Linux/systemd/xendomains.service hotplug/Linux/systemd/xendriverdomain.service hotplug/Linux/systemd/xenstored.service" -+ ac_config_files="$ac_config_files hotplug/Linux/systemd/oxenstored.service hotplug/Linux/systemd/proc-xen.mount hotplug/Linux/systemd/var-lib-xenstored.mount hotplug/Linux/systemd/xen-qemu-dom0-disk-backend.service hotplug/Linux/systemd/xen-watchdog.service hotplug/Linux/systemd/xenconsoled.service hotplug/Linux/systemd/xendomains.service hotplug/Linux/systemd/xendriverdomain.service hotplug/Linux/systemd/xenstored.service" +- ac_config_files="$ac_config_files hotplug/Linux/systemd/proc-xen.mount hotplug/Linux/systemd/xen-init-dom0.service hotplug/Linux/systemd/xen-qemu-dom0-disk-backend.service hotplug/Linux/systemd/xen-watchdog.service hotplug/Linux/systemd/xenconsoled.service hotplug/Linux/systemd/xendomains.service hotplug/Linux/systemd/xendriverdomain.service hotplug/Linux/systemd/xenstored.service" ++ ac_config_files="$ac_config_files hotplug/Linux/systemd/oxenstored.service hotplug/Linux/systemd/proc-xen.mount hotplug/Linux/systemd/xen-qemu-dom0-disk-backend.service hotplug/Linux/systemd/xen-watchdog.service hotplug/Linux/systemd/xenconsoled.service hotplug/Linux/systemd/xendomains.service hotplug/Linux/systemd/xendriverdomain.service hotplug/Linux/systemd/xenstored.service" fi diff --git a/xen.gcc10.fixes.patch b/xen.gcc10.fixes.patch deleted file mode 100644 index 657989b..0000000 --- a/xen.gcc10.fixes.patch +++ /dev/null @@ -1,10 +0,0 @@ ---- xen-4.13.0/tools/xenpmd/Makefile.orig 2019-12-17 14:23:09.000000000 +0000 -+++ xen-4.13.0/tools/xenpmd/Makefile 2020-01-22 22:13:16.564873608 +0000 -@@ -3,6 +3,7 @@ - - CFLAGS += -Werror - CFLAGS += $(CFLAGS_libxenstore) -+CFLAGS += -Wno-error=format-truncation - - LDLIBS += $(LDLIBS_libxenstore) - diff --git a/xen.spec b/xen.spec index 27ed461..989eaba 100644 --- a/xen.spec +++ b/xen.spec @@ -50,12 +50,12 @@ %endif # Hypervisor ABI -%define hv_abi 4.16 +%define hv_abi 4.17 Summary: Xen is a virtual machine monitor Name: xen -Version: 4.16.2 -Release: 4%{?dist} +Version: 4.17.0 +Release: 1%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -106,71 +106,9 @@ Patch38: qemu.trad.CVE-2017-8309.patch Patch39: qemu.trad.CVE-2017-9330.patch Patch40: xen.drop.brctl.patch Patch41: xen.gcc9.fixes.patch -Patch42: xen.gcc10.fixes.patch Patch43: xen.gcc11.fixes.patch -Patch44: xsa376.patch Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch -Patch47: xsa410-4.16-01.patch -Patch48: xsa410-4.16-02.patch -Patch49: xsa410-4.16-03.patch -Patch50: xsa410-4.16-04.patch -Patch51: xsa410-4.16-05.patch -Patch52: xsa410-4.16-06.patch -Patch53: xsa410-4.16-07.patch -Patch54: xsa410-4.16-08.patch -Patch55: xsa410-4.16-09.patch -Patch56: xsa410-4.16-10.patch -Patch57: xsa409-4.13-0001-libxl-docs-Use-arch-specific-default-paging-memory.patch -Patch58: xsa409-4.13-0002-xen-arm-Construct-the-P2M-pages-pool-for-guests.patch -Patch59: xsa409-4.13-0003-xen-arm-libxl-Implement-XEN_DOMCTL_shadow_op-for-Arm.patch -Patch60: xsa409-4.13-0004-xen-arm-Allocate-and-free-P2M-pages-from-the-P2M-poo.patch -Patch61: xsa411.patch -Patch62: xsa412-4.16.patch -Patch63: xsa414.patch -Patch64: xsa415.patch -Patch65: xsa326-4.16-oxenstored-01.patch -Patch66: xsa326-4.16-oxenstored-02.patch -Patch67: xsa326-4.16-oxenstored-03.patch -Patch68: xsa326-4.16-oxenstored-04.patch -Patch69: xsa326-4.16-oxenstored-05.patch -Patch70: xsa326-4.16-oxenstored-06.patch -Patch71: xsa326-4.16-oxenstored-07.patch -Patch72: xsa326-4.16-oxenstored-08.patch -Patch73: xsa326-4.16-xenstored-01.patch -Patch74: xsa326-4.16-xenstored-02.patch -Patch75: xsa326-4.16-xenstored-03.patch -Patch76: xsa326-4.16-xenstored-04.patch -Patch77: xsa326-4.16-xenstored-05.patch -Patch78: xsa326-4.16-xenstored-06.patch -Patch79: xsa326-4.16-xenstored-07.patch -Patch80: xsa326-4.16-xenstored-08.patch -Patch81: xsa326-4.16-xenstored-09.patch -Patch82: xsa326-4.16-xenstored-10.patch -Patch83: xsa326-4.16-xenstored-11.patch -Patch84: xsa326-4.16-xenstored-12.patch -Patch85: xsa326-4.16-xenstored-13.patch -Patch86: xsa326-4.16-xenstored-14.patch -Patch87: xsa326-4.16-xenstored-15.patch -Patch88: xsa326-4.16-xenstored-16.patch -Patch89: xsa416-4.16.patch -Patch90: xsa417.patch -Patch91: xsa418-4.16-01.patch -Patch92: xsa418-4.16-02.patch -Patch93: xsa418-4.16-03.patch -Patch94: xsa418-4.16-04.patch -Patch95: xsa418-4.16-05.patch -Patch96: xsa418-4.16-06.patch -Patch97: xsa418-4.16-07.patch -Patch98: xsa419-oxenstored.patch -Patch99: xsa419-xenstored-01.patch -Patch100: xsa419-xenstored-02.patch -Patch101: xsa419-xenstored-03.patch -Patch102: xsa420.patch -Patch103: xsa421-01.patch -Patch104: xsa421-02.patch -Patch105: xsa422-4.16-1.patch -Patch106: xsa422-4.16-2.patch %if %build_qemutrad @@ -378,71 +316,9 @@ manage Xen virtual machines. %patch37 -p1 %patch40 -p1 %patch41 -p1 -%patch42 -p1 %patch43 -p1 -%patch44 -p1 %patch45 -p1 %patch46 -p1 -%patch47 -p1 -%patch48 -p1 -%patch49 -p1 -%patch50 -p1 -%patch51 -p1 -%patch52 -p1 -%patch53 -p1 -%patch54 -p1 -%patch55 -p1 -%patch56 -p1 -%patch57 -p1 -%patch58 -p1 -%patch59 -p1 -%patch60 -p1 -%patch61 -p1 -%patch62 -p1 -%patch63 -p1 -%patch64 -p1 -%patch65 -p1 -%patch66 -p1 -%patch67 -p1 -%patch68 -p1 -%patch69 -p1 -%patch70 -p1 -%patch71 -p1 -%patch72 -p1 -%patch73 -p1 -%patch74 -p1 -%patch75 -p1 -%patch76 -p1 -%patch77 -p1 -%patch78 -p1 -%patch79 -p1 -%patch80 -p1 -%patch81 -p1 -%patch82 -p1 -%patch83 -p1 -%patch84 -p1 -%patch85 -p1 -%patch86 -p1 -%patch87 -p1 -%patch88 -p1 -%patch89 -p1 -%patch90 -p1 -%patch91 -p1 -%patch92 -p1 -%patch93 -p1 -%patch94 -p1 -%patch95 -p1 -%patch96 -p1 -%patch97 -p1 -%patch98 -p1 -%patch99 -p1 -%patch100 -p1 -%patch101 -p1 -%patch102 -p1 -%patch103 -p1 -%patch104 -p1 -%patch105 -p1 -%patch106 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -846,7 +722,6 @@ fi %{_sysconfdir}/bash_completion.d/xl %{_unitdir}/proc-xen.mount -%{_unitdir}/var-lib-xenstored.mount %{_unitdir}/xenstored.service %{_unitdir}/xenconsoled.service %{_unitdir}/xen-watchdog.service @@ -929,8 +804,6 @@ fi %dir %{_localstatedir}/lib/%{name} %dir %{_localstatedir}/lib/%{name}/dump %dir %{_localstatedir}/lib/%{name}/images -# Xenstore persistent state -%dir %{_localstatedir}/lib/xenstored # Xenstore runtime state %ghost %{_localstatedir}/run/xenstored @@ -1058,6 +931,12 @@ fi %endif %changelog +* Tue Dec 13 2022 Michael Young - 4.17.0-1 +- update to xen-4.17.0 + rebase xen.fedora.systemd.patch and xen.canonicalize.patch + remove or adjust patches now included or superceded upstream + /var/lib/xenstored has moved to /run/xenstored + * Tue Nov 08 2022 Michael Young - 4.16.2-4 - x86: Multiple speculative security issues [XSA-422, CVE-2022-23824] diff --git a/xsa326-4.16-oxenstored-01.patch b/xsa326-4.16-oxenstored-01.patch deleted file mode 100644 index 848a5d0..0000000 --- a/xsa326-4.16-oxenstored-01.patch +++ /dev/null @@ -1,55 +0,0 @@ -From 8d6bb4ac40619877130533b11655829101b31d04 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Date: Wed, 12 Oct 2022 19:13:01 +0100 -Subject: tools/ocaml/xenstored: Synchronise defaults with oxenstore.conf.in -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -We currently have 2 different set of defaults in upstream Xen git tree: -* defined in the source code, only used if there is no config file -* defined in the oxenstored.conf.in upstream Xen - -An oxenstored.conf file is not mandatory, and if missing, maxrequests in -particular has an unsafe default. - -Resync the defaults from oxenstored.conf.in into the source code. - -This is part of XSA-326 / CVE-2022-42316. - -Reported-by: Julien Grall -Signed-off-by: Edwin Török -Acked-by: Christian Lindig - -diff --git a/tools/ocaml/xenstored/define.ml b/tools/ocaml/xenstored/define.ml -index ebe18b8e312c..6b06f808595b 100644 ---- a/tools/ocaml/xenstored/define.ml -+++ b/tools/ocaml/xenstored/define.ml -@@ -21,9 +21,9 @@ let xs_daemon_socket = Paths.xen_run_stored ^ "/socket" - - let default_config_dir = Paths.xen_config_dir - --let maxwatch = ref (50) --let maxtransaction = ref (20) --let maxrequests = ref (-1) (* maximum requests per transaction *) -+let maxwatch = ref (100) -+let maxtransaction = ref (10) -+let maxrequests = ref (1024) (* maximum requests per transaction *) - - let conflict_burst_limit = ref 5.0 - let conflict_max_history_seconds = ref 0.05 -diff --git a/tools/ocaml/xenstored/quota.ml b/tools/ocaml/xenstored/quota.ml -index abcac912805a..6e3d6401ae89 100644 ---- a/tools/ocaml/xenstored/quota.ml -+++ b/tools/ocaml/xenstored/quota.ml -@@ -20,8 +20,8 @@ exception Transaction_opened - - let warn fmt = Logging.warn "quota" fmt - let activate = ref true --let maxent = ref (10000) --let maxsize = ref (4096) -+let maxent = ref (1000) -+let maxsize = ref (2048) - - type t = { - maxent: int; (* max entities per domU *) diff --git a/xsa326-4.16-oxenstored-02.patch b/xsa326-4.16-oxenstored-02.patch deleted file mode 100644 index 7680efb..0000000 --- a/xsa326-4.16-oxenstored-02.patch +++ /dev/null @@ -1,94 +0,0 @@ -From 78d5af44ab13bb18c87b6ad75e505bd374379cb3 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Date: Thu, 28 Jul 2022 17:08:15 +0100 -Subject: tools/ocaml/xenstored: Check for maxrequests before performing - operations -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Previously we'd perform the operation, record the updated tree in the -transaction record, then try to insert a watchop path and the reply packet. - -If we exceeded max requests we would've returned EQUOTA, but still: -* have performed the operation on the transaction's tree -* have recorded the watchop, making this queue effectively unbounded - -It is better if we check whether we'd have room to store the operation before -performing the transaction, and raise EQUOTA there. Then the transaction -record won't grow. - -This is part of XSA-326 / CVE-2022-42317. - -Reported-by: Julien Grall -Signed-off-by: Edwin Török -Acked-by: Christian Lindig - -diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml -index 27790d4a5c41..dd58e6979cf9 100644 ---- a/tools/ocaml/xenstored/process.ml -+++ b/tools/ocaml/xenstored/process.ml -@@ -389,6 +389,7 @@ let input_handle_error ~cons ~doms ~fct ~con ~t ~req = - let reply_error e = - Packet.Error e in - try -+ Transaction.check_quota_exn ~perm:(Connection.get_perm con) t; - fct con t doms cons req.Packet.data - with - | Define.Invalid_path -> reply_error "EINVAL" -@@ -681,9 +682,10 @@ let process_packet ~store ~cons ~doms ~con ~req = - in - - let response = try -+ Transaction.check_quota_exn ~perm:(Connection.get_perm con) t; - if tid <> Transaction.none then - (* Remember the request and response for this operation in case we need to replay the transaction *) -- Transaction.add_operation ~perm:(Connection.get_perm con) t req response; -+ Transaction.add_operation t req response; - response - with Quota.Limit_reached -> - Packet.Error "EQUOTA" -diff --git a/tools/ocaml/xenstored/transaction.ml b/tools/ocaml/xenstored/transaction.ml -index 17b1bdf2eaf9..294143e2335b 100644 ---- a/tools/ocaml/xenstored/transaction.ml -+++ b/tools/ocaml/xenstored/transaction.ml -@@ -85,6 +85,7 @@ type t = { - oldroot: Store.Node.t; - mutable paths: (Xenbus.Xb.Op.operation * Store.Path.t) list; - mutable operations: (Packet.request * Packet.response) list; -+ mutable quota_reached: bool; - mutable read_lowpath: Store.Path.t option; - mutable write_lowpath: Store.Path.t option; - } -@@ -127,6 +128,7 @@ let make ?(internal=false) id store = - oldroot = Store.get_root store; - paths = []; - operations = []; -+ quota_reached = false; - read_lowpath = None; - write_lowpath = None; - } in -@@ -143,13 +145,19 @@ let get_root t = Store.get_root t.store - - let is_read_only t = t.paths = [] - let add_wop t ty path = t.paths <- (ty, path) :: t.paths --let add_operation ~perm t request response = -+let get_operations t = List.rev t.operations -+ -+let check_quota_exn ~perm t = - if !Define.maxrequests >= 0 - && not (Perms.Connection.is_dom0 perm) -- && List.length t.operations >= !Define.maxrequests -- then raise Quota.Limit_reached; -+ && (t.quota_reached || List.length t.operations >= !Define.maxrequests) -+ then begin -+ t.quota_reached <- true; -+ raise Quota.Limit_reached; -+ end -+ -+let add_operation t request response = - t.operations <- (request, response) :: t.operations --let get_operations t = List.rev t.operations - let set_read_lowpath t path = t.read_lowpath <- get_lowest path t.read_lowpath - let set_write_lowpath t path = t.write_lowpath <- get_lowest path t.write_lowpath - diff --git a/xsa326-4.16-oxenstored-03.patch b/xsa326-4.16-oxenstored-03.patch deleted file mode 100644 index 0b42411..0000000 --- a/xsa326-4.16-oxenstored-03.patch +++ /dev/null @@ -1,119 +0,0 @@ -From 600c45e49c2060e077c06ab19078da89aa8e2e08 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Date: Wed, 12 Oct 2022 19:13:07 +0100 -Subject: tools/ocaml: GC parameter tuning -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -By default the OCaml garbage collector would return memory to the OS only -after unused memory is 5x live memory. Tweak this to 120% instead, which -would match the major GC speed. - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Edwin Török -Acked-by: Christian Lindig - -diff --git a/tools/ocaml/xenstored/define.ml b/tools/ocaml/xenstored/define.ml -index 6b06f808595b..ba63a8147e09 100644 ---- a/tools/ocaml/xenstored/define.ml -+++ b/tools/ocaml/xenstored/define.ml -@@ -25,6 +25,7 @@ let maxwatch = ref (100) - let maxtransaction = ref (10) - let maxrequests = ref (1024) (* maximum requests per transaction *) - -+let gc_max_overhead = ref 120 (* 120% see comment in xenstored.ml *) - let conflict_burst_limit = ref 5.0 - let conflict_max_history_seconds = ref 0.05 - let conflict_rate_limit_is_aggregate = ref true -diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml -index d44ae673c42a..3b57ad016dfb 100644 ---- a/tools/ocaml/xenstored/xenstored.ml -+++ b/tools/ocaml/xenstored/xenstored.ml -@@ -104,6 +104,7 @@ let parse_config filename = - ("quota-maxsize", Config.Set_int Quota.maxsize); - ("quota-maxrequests", Config.Set_int Define.maxrequests); - ("quota-path-max", Config.Set_int Define.path_max); -+ ("gc-max-overhead", Config.Set_int Define.gc_max_overhead); - ("test-eagain", Config.Set_bool Transaction.test_eagain); - ("persistent", Config.Set_bool Disk.enable); - ("xenstored-log-file", Config.String Logging.set_xenstored_log_destination); -@@ -265,6 +266,67 @@ let to_file store cons fds file = - (fun () -> close_out channel) - end - -+(* -+ By default OCaml's GC only returns memory to the OS when it exceeds a -+ configurable 'max overhead' setting. -+ The default is 500%, that is 5/6th of the OCaml heap needs to be free -+ and only 1/6th live for a compaction to be triggerred that would -+ release memory back to the OS. -+ If the limit is not hit then the OCaml process can reuse that memory -+ for its own purposes, but other processes won't be able to use it. -+ -+ There is also a 'space overhead' setting that controls how much work -+ each major GC slice does, and by default aims at having no more than -+ 80% or 120% (depending on version) garbage values compared to live -+ values. -+ This doesn't have as much relevance to memory returned to the OS as -+ long as space_overhead <= max_overhead, because compaction is only -+ triggerred at the end of major GC cycles. -+ -+ The defaults are too large once the program starts using ~100MiB of -+ memory, at which point ~500MiB would be unavailable to other processes -+ (which would be fine if this was the main process in this VM, but it is -+ not). -+ -+ Max overhead can also be set to 0, however this is for testing purposes -+ only (setting it lower than 'space overhead' wouldn't help because the -+ major GC wouldn't run fast enough, and compaction does have a -+ performance cost: we can only compact contiguous regions, so memory has -+ to be moved around). -+ -+ Max overhead controls how often the heap is compacted, which is useful -+ if there are burst of activity followed by long periods of idle state, -+ or if a domain quits, etc. Compaction returns memory to the OS. -+ -+ wasted = live * space_overhead / 100 -+ -+ For globally overriding the GC settings one can use OCAMLRUNPARAM, -+ however we provide a config file override to be consistent with other -+ oxenstored settings. -+ -+ One might want to dynamically adjust the overhead setting based on used -+ memory, i.e. to use a fixed upper bound in bytes, not percentage. However -+ measurements show that such adjustments increase GC overhead massively, -+ while still not guaranteeing that memory is returned any more quickly -+ than with a percentage based setting. -+ -+ The allocation policy could also be tweaked, e.g. first fit would reduce -+ fragmentation and thus memory usage, but the documentation warns that it -+ can be sensibly slower, and indeed one of our own testcases can trigger -+ such a corner case where it is multiple times slower, so it is best to keep -+ the default allocation policy (next-fit/best-fit depending on version). -+ -+ There are other tweaks that can be attempted in the future, e.g. setting -+ 'ulimit -v' to 75% of RAM, however getting the kernel to actually return -+ NULL from allocations is difficult even with that setting, and without a -+ NULL the emergency GC won't be triggerred. -+ Perhaps cgroup limits could help, but for now tweak the safest only. -+*) -+ -+let tweak_gc () = -+ Gc.set { (Gc.get ()) with Gc.max_overhead = !Define.gc_max_overhead } -+ -+ - let _ = - let cf = do_argv in - let pidfile = -@@ -274,6 +336,8 @@ let _ = - default_pidfile - in - -+ tweak_gc (); -+ - (try - Unixext.mkdir_rec (Filename.dirname pidfile) 0o755 - with _ -> diff --git a/xsa326-4.16-oxenstored-04.patch b/xsa326-4.16-oxenstored-04.patch deleted file mode 100644 index 0f2534b..0000000 --- a/xsa326-4.16-oxenstored-04.patch +++ /dev/null @@ -1,85 +0,0 @@ -From fd6d9cd3d20e496bdbf3e0a07354f65de0bcf4ae Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Date: Fri, 29 Jul 2022 18:53:29 +0100 -Subject: tools/ocaml/libs/xb: hide type of Xb.t -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Hiding the type will make it easier to change the implementation -in the future without breaking code that relies on it. - -No functional change. - -Signed-off-by: Edwin Török -Acked-by: Christian Lindig -(cherry picked from commit 7ade30a1451734d041363c750a65d322e25b47ba) - -Reported-by: Julien Grall -diff --git a/tools/ocaml/libs/xb/xb.ml b/tools/ocaml/libs/xb/xb.ml -index 104d319d7747..8404ddd8a682 100644 ---- a/tools/ocaml/libs/xb/xb.ml -+++ b/tools/ocaml/libs/xb/xb.ml -@@ -196,6 +196,9 @@ let peek_output con = Queue.peek con.pkt_out - let input_len con = Queue.length con.pkt_in - let has_in_packet con = Queue.length con.pkt_in > 0 - let get_in_packet con = Queue.pop con.pkt_in -+let has_partial_input con = match con.partial_in with -+ | HaveHdr _ -> true -+ | NoHdr (n, _) -> n < Partial.header_size () - let has_more_input con = - match con.backend with - | Fd _ -> false -diff --git a/tools/ocaml/libs/xb/xb.mli b/tools/ocaml/libs/xb/xb.mli -index 3a00da6cddc1..794e35bb343e 100644 ---- a/tools/ocaml/libs/xb/xb.mli -+++ b/tools/ocaml/libs/xb/xb.mli -@@ -66,13 +66,7 @@ type backend_mmap = { - type backend_fd = { fd : Unix.file_descr; } - type backend = Fd of backend_fd | Xenmmap of backend_mmap - type partial_buf = HaveHdr of Partial.pkt | NoHdr of int * bytes --type t = { -- backend : backend; -- pkt_in : Packet.t Queue.t; -- pkt_out : Packet.t Queue.t; -- mutable partial_in : partial_buf; -- mutable partial_out : string; --} -+type t - val init_partial_in : unit -> partial_buf - val reconnect : t -> unit - val queue : t -> Packet.t -> unit -@@ -97,6 +91,7 @@ val has_output : t -> bool - val peek_output : t -> Packet.t - val input_len : t -> int - val has_in_packet : t -> bool -+val has_partial_input : t -> bool - val get_in_packet : t -> Packet.t - val has_more_input : t -> bool - val is_selectable : t -> bool -diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/connection.ml -index 65f99ea6f28a..38b47363a173 100644 ---- a/tools/ocaml/xenstored/connection.ml -+++ b/tools/ocaml/xenstored/connection.ml -@@ -125,9 +125,7 @@ let get_perm con = - let set_target con target_domid = - con.perm <- Perms.Connection.set_target (get_perm con) ~perms:[Perms.READ; Perms.WRITE] target_domid - --let is_backend_mmap con = match con.xb.Xenbus.Xb.backend with -- | Xenbus.Xb.Xenmmap _ -> true -- | _ -> false -+let is_backend_mmap con = Xenbus.Xb.is_mmap con.xb - - let send_reply con tid rid ty data = - if (String.length data) > xenstore_payload_max && (is_backend_mmap con) then -@@ -280,9 +278,7 @@ let get_transaction con tid = - - let do_input con = Xenbus.Xb.input con.xb - let has_input con = Xenbus.Xb.has_in_packet con.xb --let has_partial_input con = match con.xb.Xenbus.Xb.partial_in with -- | HaveHdr _ -> true -- | NoHdr (n, _) -> n < Xenbus.Partial.header_size () -+let has_partial_input con = Xenbus.Xb.has_partial_input con.xb - let pop_in con = Xenbus.Xb.get_in_packet con.xb - let has_more_input con = Xenbus.Xb.has_more_input con.xb - diff --git a/xsa326-4.16-oxenstored-05.patch b/xsa326-4.16-oxenstored-05.patch deleted file mode 100644 index 8b253a9..0000000 --- a/xsa326-4.16-oxenstored-05.patch +++ /dev/null @@ -1,214 +0,0 @@ -From f13fe5903361953e4ccf8602b9c8df7e64568d55 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Date: Wed, 12 Oct 2022 19:13:02 +0100 -Subject: tools/ocaml: Change Xb.input to return Packet.t option -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The queue here would only ever hold at most one element. This will simplify -follow-up patches. - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Edwin Török -Acked-by: Christian Lindig - -diff --git a/tools/ocaml/libs/xb/xb.ml b/tools/ocaml/libs/xb/xb.ml -index 8404ddd8a682..165fd4a1edf4 100644 ---- a/tools/ocaml/libs/xb/xb.ml -+++ b/tools/ocaml/libs/xb/xb.ml -@@ -45,7 +45,6 @@ type partial_buf = HaveHdr of Partial.pkt | NoHdr of int * bytes - type t = - { - backend: backend; -- pkt_in: Packet.t Queue.t; - pkt_out: Packet.t Queue.t; - mutable partial_in: partial_buf; - mutable partial_out: string; -@@ -62,7 +61,6 @@ let reconnect t = match t.backend with - Xs_ring.close backend.mmap; - backend.eventchn_notify (); - (* Clear our old connection state *) -- Queue.clear t.pkt_in; - Queue.clear t.pkt_out; - t.partial_in <- init_partial_in (); - t.partial_out <- "" -@@ -124,7 +122,6 @@ let output con = - - (* NB: can throw Reconnect *) - let input con = -- let newpacket = ref false in - let to_read = - match con.partial_in with - | HaveHdr partial_pkt -> Partial.to_complete partial_pkt -@@ -143,21 +140,19 @@ let input con = - if Partial.to_complete partial_pkt = 0 then ( - let pkt = Packet.of_partialpkt partial_pkt in - con.partial_in <- init_partial_in (); -- Queue.push pkt con.pkt_in; -- newpacket := true -- ) -+ Some pkt -+ ) else None - | NoHdr (i, buf) -> - (* we complete the partial header *) - if sz > 0 then - Bytes.blit b 0 buf (Partial.header_size () - i) sz; - con.partial_in <- if sz = i then -- HaveHdr (Partial.of_string (Bytes.to_string buf)) else NoHdr (i - sz, buf) -- ); -- !newpacket -+ HaveHdr (Partial.of_string (Bytes.to_string buf)) else NoHdr (i - sz, buf); -+ None -+ ) - - let newcon backend = { - backend = backend; -- pkt_in = Queue.create (); - pkt_out = Queue.create (); - partial_in = init_partial_in (); - partial_out = ""; -@@ -193,9 +188,6 @@ let has_output con = has_new_output con || has_old_output con - - let peek_output con = Queue.peek con.pkt_out - --let input_len con = Queue.length con.pkt_in --let has_in_packet con = Queue.length con.pkt_in > 0 --let get_in_packet con = Queue.pop con.pkt_in - let has_partial_input con = match con.partial_in with - | HaveHdr _ -> true - | NoHdr (n, _) -> n < Partial.header_size () -diff --git a/tools/ocaml/libs/xb/xb.mli b/tools/ocaml/libs/xb/xb.mli -index 794e35bb343e..91c682162cea 100644 ---- a/tools/ocaml/libs/xb/xb.mli -+++ b/tools/ocaml/libs/xb/xb.mli -@@ -77,7 +77,7 @@ val write_fd : backend_fd -> 'a -> string -> int -> int - val write_mmap : backend_mmap -> 'a -> string -> int -> int - val write : t -> string -> int -> int - val output : t -> bool --val input : t -> bool -+val input : t -> Packet.t option - val newcon : backend -> t - val open_fd : Unix.file_descr -> t - val open_mmap : Xenmmap.mmap_interface -> (unit -> unit) -> t -@@ -89,10 +89,7 @@ val has_new_output : t -> bool - val has_old_output : t -> bool - val has_output : t -> bool - val peek_output : t -> Packet.t --val input_len : t -> int --val has_in_packet : t -> bool - val has_partial_input : t -> bool --val get_in_packet : t -> Packet.t - val has_more_input : t -> bool - val is_selectable : t -> bool - val get_fd : t -> Unix.file_descr -diff --git a/tools/ocaml/libs/xs/xsraw.ml b/tools/ocaml/libs/xs/xsraw.ml -index d982fb24dbb1..451f8b38dbcc 100644 ---- a/tools/ocaml/libs/xs/xsraw.ml -+++ b/tools/ocaml/libs/xs/xsraw.ml -@@ -94,26 +94,18 @@ let pkt_send con = - done - - (* receive one packet - can sleep *) --let pkt_recv con = -- let workdone = ref false in -- while not !workdone -- do -- workdone := Xb.input con.xb -- done; -- Xb.get_in_packet con.xb -+let rec pkt_recv con = -+ match Xb.input con.xb with -+ | Some packet -> packet -+ | None -> pkt_recv con - - let pkt_recv_timeout con timeout = - let fd = Xb.get_fd con.xb in - let r, _, _ = Unix.select [ fd ] [] [] timeout in - if r = [] then - true, None -- else ( -- let workdone = Xb.input con.xb in -- if workdone then -- false, (Some (Xb.get_in_packet con.xb)) -- else -- false, None -- ) -+ else -+ false, Xb.input con.xb - - let queue_watchevent con data = - let ls = split_string ~limit:2 '\000' data in -diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/connection.ml -index 38b47363a173..cc20e047d2b9 100644 ---- a/tools/ocaml/xenstored/connection.ml -+++ b/tools/ocaml/xenstored/connection.ml -@@ -277,9 +277,7 @@ let get_transaction con tid = - Hashtbl.find con.transactions tid - - let do_input con = Xenbus.Xb.input con.xb --let has_input con = Xenbus.Xb.has_in_packet con.xb - let has_partial_input con = Xenbus.Xb.has_partial_input con.xb --let pop_in con = Xenbus.Xb.get_in_packet con.xb - let has_more_input con = Xenbus.Xb.has_more_input con.xb - - let has_output con = Xenbus.Xb.has_output con.xb -@@ -307,7 +305,7 @@ let is_bad con = match con.dom with None -> false | Some dom -> Domain.is_bad_do - Restrictions below can be relaxed once xenstored learns to dump more - of its live state in a safe way *) - let has_extra_connection_data con = -- let has_in = has_input con || has_partial_input con in -+ let has_in = has_partial_input con in - let has_out = has_output con in - let has_socket = con.dom = None in - let has_nondefault_perms = make_perm con.dom <> con.perm in -diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml -index 6a3435c265d3..2d67456a2aa0 100644 ---- a/tools/ocaml/xenstored/process.ml -+++ b/tools/ocaml/xenstored/process.ml -@@ -195,10 +195,9 @@ let parse_live_update args = - | _ when Unix.gettimeofday () < t.deadline -> false - | l -> - warn "timeout reached: have to wait, migrate or shutdown %d domains:" (List.length l); -- let msgs = List.rev_map (fun con -> Printf.sprintf "%s: %d tx, in: %b, out: %b, perm: %s" -+ let msgs = List.rev_map (fun con -> Printf.sprintf "%s: %d tx, out: %b, perm: %s" - (Connection.get_domstr con) - (Connection.number_of_transactions con) -- (Connection.has_input con) - (Connection.has_output con) - (Connection.get_perm con |> Perms.Connection.to_string) - ) l in -@@ -705,16 +704,17 @@ let do_input store cons doms con = - info "%s requests a reconnect" (Connection.get_domstr con); - History.reconnect con; - info "%s reconnection complete" (Connection.get_domstr con); -- false -+ None - | Failure exp -> - error "caught exception %s" exp; - error "got a bad client %s" (sprintf "%-8s" (Connection.get_domstr con)); - Connection.mark_as_bad con; -- false -+ None - in - -- if newpacket then ( -- let packet = Connection.pop_in con in -+ match newpacket with -+ | None -> () -+ | Some packet -> - let tid, rid, ty, data = Xenbus.Xb.Packet.unpack packet in - let req = {Packet.tid=tid; Packet.rid=rid; Packet.ty=ty; Packet.data=data} in - -@@ -724,8 +724,7 @@ let do_input store cons doms con = - (Xenbus.Xb.Op.to_string ty) (sanitize_data data); *) - process_packet ~store ~cons ~doms ~con ~req; - write_access_log ~ty ~tid ~con:(Connection.get_domstr con) ~data; -- Connection.incr_ops con; -- ) -+ Connection.incr_ops con - - let do_output _store _cons _doms con = - if Connection.has_output con then ( diff --git a/xsa326-4.16-oxenstored-06.patch b/xsa326-4.16-oxenstored-06.patch deleted file mode 100644 index c8ebc34..0000000 --- a/xsa326-4.16-oxenstored-06.patch +++ /dev/null @@ -1,127 +0,0 @@ -From 2440a8b69a118fe14e73eb6cab4a050922866f1a Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Date: Wed, 12 Oct 2022 19:13:03 +0100 -Subject: tools/ocaml/xb: Add BoundedQueue -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Ensures we cannot store more than [capacity] elements in a [Queue]. Replacing -all Queue with this module will then ensure at compile time that all Queues -are correctly bound checked. - -Each element in the queue has a class with its own limits. This, in a -subsequent change, will ensure that command responses can proceed during a -flood of watch events. - -No functional change. - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Edwin Török -Acked-by: Christian Lindig - -diff --git a/tools/ocaml/libs/xb/xb.ml b/tools/ocaml/libs/xb/xb.ml -index 165fd4a1edf4..4197a3888a68 100644 ---- a/tools/ocaml/libs/xb/xb.ml -+++ b/tools/ocaml/libs/xb/xb.ml -@@ -17,6 +17,98 @@ - module Op = struct include Op end - module Packet = struct include Packet end - -+module BoundedQueue : sig -+ type ('a, 'b) t -+ -+ (** [create ~capacity ~classify ~limit] creates a queue with maximum [capacity] elements. -+ This is burst capacity, each element is further classified according to [classify], -+ and each class can have its own [limit]. -+ [capacity] is enforced as an overall limit. -+ The [limit] can be dynamic, and can be smaller than the number of elements already queued of that class, -+ in which case those elements are considered to use "burst capacity". -+ *) -+ val create: capacity:int -> classify:('a -> 'b) -> limit:('b -> int) -> ('a, 'b) t -+ -+ (** [clear q] discards all elements from [q] *) -+ val clear: ('a, 'b) t -> unit -+ -+ (** [can_push q] when [length q < capacity]. *) -+ val can_push: ('a, 'b) t -> 'b -> bool -+ -+ (** [push e q] adds [e] at the end of queue [q] if [can_push q], or returns [None]. *) -+ val push: 'a -> ('a, 'b) t -> unit option -+ -+ (** [pop q] removes and returns first element in [q], or raises [Queue.Empty]. *) -+ val pop: ('a, 'b) t -> 'a -+ -+ (** [peek q] returns the first element in [q], or raises [Queue.Empty]. *) -+ val peek : ('a, 'b) t -> 'a -+ -+ (** [length q] returns the current number of elements in [q] *) -+ val length: ('a, 'b) t -> int -+ -+ (** [debug string_of_class q] prints queue usage statistics in an unspecified internal format. *) -+ val debug: ('b -> string) -> (_, 'b) t -> string -+end = struct -+ type ('a, 'b) t = -+ { q: 'a Queue.t -+ ; capacity: int -+ ; classify: 'a -> 'b -+ ; limit: 'b -> int -+ ; class_count: ('b, int) Hashtbl.t -+ } -+ -+ let create ~capacity ~classify ~limit = -+ { capacity; q = Queue.create (); classify; limit; class_count = Hashtbl.create 3 } -+ -+ let get_count t classification = try Hashtbl.find t.class_count classification with Not_found -> 0 -+ -+ let can_push_internal t classification class_count = -+ Queue.length t.q < t.capacity && class_count < t.limit classification -+ -+ let ok = Some () -+ -+ let push e t = -+ let classification = t.classify e in -+ let class_count = get_count t classification in -+ if can_push_internal t classification class_count then begin -+ Queue.push e t.q; -+ Hashtbl.replace t.class_count classification (class_count + 1); -+ ok -+ end -+ else -+ None -+ -+ let can_push t classification = -+ can_push_internal t classification @@ get_count t classification -+ -+ let clear t = -+ Queue.clear t.q; -+ Hashtbl.reset t.class_count -+ -+ let pop t = -+ let e = Queue.pop t.q in -+ let classification = t.classify e in -+ let () = match get_count t classification - 1 with -+ | 0 -> Hashtbl.remove t.class_count classification (* reduces memusage *) -+ | n -> Hashtbl.replace t.class_count classification n -+ in -+ e -+ -+ let peek t = Queue.peek t.q -+ let length t = Queue.length t.q -+ -+ let debug string_of_class t = -+ let b = Buffer.create 128 in -+ Printf.bprintf b "BoundedQueue capacity: %d, used: {" t.capacity; -+ Hashtbl.iter (fun packet_class count -> -+ Printf.bprintf b " %s: %d" (string_of_class packet_class) count -+ ) t.class_count; -+ Printf.bprintf b "}"; -+ Buffer.contents b -+end -+ -+ - exception End_of_file - exception Eagain - exception Noent diff --git a/xsa326-4.16-oxenstored-07.patch b/xsa326-4.16-oxenstored-07.patch deleted file mode 100644 index ef02467..0000000 --- a/xsa326-4.16-oxenstored-07.patch +++ /dev/null @@ -1,872 +0,0 @@ -From bc0f05e6f3a3c93c853ceffd1f6d2022dc30fb77 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Date: Wed, 12 Oct 2022 19:13:04 +0100 -Subject: tools/ocaml: Limit maximum in-flight requests / outstanding replies -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Introduce a limit on the number of outstanding reply packets in the xenbus -queue. This limits the number of in-flight requests: when the output queue is -full we'll stop processing inputs until the output queue has room again. - -To avoid a busy loop on the Unix socket we only add it to the watched input -file descriptor set if we'd be able to call `input` on it. Even though Dom0 -is trusted and exempt from quotas a flood of events might cause a backlog -where events are produced faster than daemons in Dom0 can consume them, which -could lead to an unbounded queue size and OOM. - -Therefore the xenbus queue limit must apply to all connections, Dom0 is not -exempt from it, although if everything works correctly it will eventually -catch up. - -This prevents a malicious guest from sending more commands while it has -outstanding watch events or command replies in its input ring. However if it -can cause the generation of watch events by other means (e.g. by Dom0, or -another cooperative guest) and stop reading its own ring then watch events -would've queued up without limit. - -The xenstore protocol doesn't have a back-pressure mechanism, and doesn't -allow dropping watch events. In fact, dropping watch events is known to break -some pieces of normal functionality. This leaves little choice to safely -implement the xenstore protocol without exposing the xenstore daemon to -out-of-memory attacks. - -Implement the fix as pipes with bounded buffers: -* Use a bounded buffer for watch events -* The watch structure will have a bounded receiving pipe of watch events -* The source will have an "overflow" pipe of pending watch events it couldn't - deliver - -Items are queued up on one end and are sent as far along the pipe as possible: - - source domain -> watch -> xenbus of target -> xenstore ring/socket of target - -If the pipe is "full" at any point then back-pressure is applied and we prevent -more items from being queued up. For the source domain this means that we'll -stop accepting new commands as long as its pipe buffer is not empty. - -Before we try to enqueue an item we first check whether it is possible to send -it further down the pipe, by attempting to recursively flush the pipes. This -ensures that we retain the order of events as much as possible. - -We might break causality of watch events if the target domain's queue is full -and we need to start using the watch's queue. This is a breaking change in -the xenstore protocol, but only for domains which are not processing their -incoming ring as expected. - -When a watch is deleted its entire pending queue is dropped (no code is needed -for that, because it is part of the 'watch' type). - -There is a cache of watches that have pending events that we attempt to flush -at every cycle if possible. - -Introduce 3 limits here: -* quota-maxwatchevents on watch event destination: when this is hit the - source will not be allowed to queue up more watch events. -* quota-maxoustanding which is the number of responses not read from the ring: - once exceeded, no more inputs are processed until all outstanding replies - are consumed by the client. -* overflow queue on the watch event source: all watches that cannot be stored - on destination are queued up here, a single command can trigger multiple - watches (e.g. due to recursion). - -The overflow queue currently doesn't have an upper bound, it is difficult to -accurately calculate one as it depends on whether you are Dom0 and how many -watches each path has registered and how many watch events you can trigger -with a single command (e.g. a commit). However these events were already -using memory, this just moves them elsewhere, and as long as we correctly -block a domain it shouldn't result in unbounded memory usage. - -Note that Dom0 is not excluded from these checks, it is important that Dom0 is -especially not excluded when it is the source, since there are many ways in -which a guest could trigger Dom0 to send it watch events. - -This should protect against malicious frontends as long as the backend follows -the PV xenstore protocol and only exposes paths needed by the frontend, and -changes those paths at most once as a reaction to guest events, or protocol -state. - -The queue limits are per watch, and per domain-pair, so even if one -communication channel would be "blocked", others would keep working, and the -domain itself won't get blocked as long as it doesn't overflow the queue of -watch events. - -Similarly a malicious backend could cause the frontend to get blocked, but -this watch queue protects the frontend as well as long as it follows the PV -protocol. (Although note that protection against malicious backends is only a -best effort at the moment) - -This is part of XSA-326 / CVE-2022-42318. - -Reported-by: Julien Grall -Signed-off-by: Edwin Török -Acked-by: Christian Lindig - -diff --git a/tools/ocaml/libs/xb/xb.ml b/tools/ocaml/libs/xb/xb.ml -index 4197a3888a68..b292ed7a874d 100644 ---- a/tools/ocaml/libs/xb/xb.ml -+++ b/tools/ocaml/libs/xb/xb.ml -@@ -134,14 +134,44 @@ type backend = Fd of backend_fd | Xenmmap of backend_mmap - - type partial_buf = HaveHdr of Partial.pkt | NoHdr of int * bytes - -+(* -+ separate capacity reservation for replies and watch events: -+ this allows a domain to keep working even when under a constant flood of -+ watch events -+*) -+type capacity = { maxoutstanding: int; maxwatchevents: int } -+ -+module Queue = BoundedQueue -+ -+type packet_class = -+ | CommandReply -+ | Watchevent -+ -+let string_of_packet_class = function -+ | CommandReply -> "command_reply" -+ | Watchevent -> "watch_event" -+ - type t = - { - backend: backend; -- pkt_out: Packet.t Queue.t; -+ pkt_out: (Packet.t, packet_class) Queue.t; - mutable partial_in: partial_buf; - mutable partial_out: string; -+ capacity: capacity - } - -+let to_read con = -+ match con.partial_in with -+ | HaveHdr partial_pkt -> Partial.to_complete partial_pkt -+ | NoHdr (i, _) -> i -+ -+let debug t = -+ Printf.sprintf "XenBus state: partial_in: %d needed, partial_out: %d bytes, pkt_out: %d packets, %s" -+ (to_read t) -+ (String.length t.partial_out) -+ (Queue.length t.pkt_out) -+ (BoundedQueue.debug string_of_packet_class t.pkt_out) -+ - let init_partial_in () = NoHdr - (Partial.header_size (), Bytes.make (Partial.header_size()) '\000') - -@@ -199,7 +229,8 @@ let output con = - let s = if String.length con.partial_out > 0 then - con.partial_out - else if Queue.length con.pkt_out > 0 then -- Packet.to_string (Queue.pop con.pkt_out) -+ let pkt = Queue.pop con.pkt_out in -+ Packet.to_string pkt - else - "" in - (* send data from s, and save the unsent data to partial_out *) -@@ -212,12 +243,15 @@ let output con = - (* after sending one packet, partial is empty *) - con.partial_out = "" - -+(* we can only process an input packet if we're guaranteed to have room -+ to store the response packet *) -+let can_input con = Queue.can_push con.pkt_out CommandReply -+ - (* NB: can throw Reconnect *) - let input con = -- let to_read = -- match con.partial_in with -- | HaveHdr partial_pkt -> Partial.to_complete partial_pkt -- | NoHdr (i, _) -> i in -+ if not (can_input con) then None -+ else -+ let to_read = to_read con in - - (* try to get more data from input stream *) - let b = Bytes.make to_read '\000' in -@@ -243,11 +277,22 @@ let input con = - None - ) - --let newcon backend = { -+let classify t = -+ match t.Packet.ty with -+ | Op.Watchevent -> Watchevent -+ | _ -> CommandReply -+ -+let newcon ~capacity backend = -+ let limit = function -+ | CommandReply -> capacity.maxoutstanding -+ | Watchevent -> capacity.maxwatchevents -+ in -+ { - backend = backend; -- pkt_out = Queue.create (); -+ pkt_out = Queue.create ~capacity:(capacity.maxoutstanding + capacity.maxwatchevents) ~classify ~limit; - partial_in = init_partial_in (); - partial_out = ""; -+ capacity = capacity; - } - - let open_fd fd = newcon (Fd { fd = fd; }) -diff --git a/tools/ocaml/libs/xb/xb.mli b/tools/ocaml/libs/xb/xb.mli -index 91c682162cea..71b2754ca788 100644 ---- a/tools/ocaml/libs/xb/xb.mli -+++ b/tools/ocaml/libs/xb/xb.mli -@@ -66,10 +66,11 @@ type backend_mmap = { - type backend_fd = { fd : Unix.file_descr; } - type backend = Fd of backend_fd | Xenmmap of backend_mmap - type partial_buf = HaveHdr of Partial.pkt | NoHdr of int * bytes -+type capacity = { maxoutstanding: int; maxwatchevents: int } - type t - val init_partial_in : unit -> partial_buf - val reconnect : t -> unit --val queue : t -> Packet.t -> unit -+val queue : t -> Packet.t -> unit option - val read_fd : backend_fd -> 'a -> bytes -> int -> int - val read_mmap : backend_mmap -> 'a -> bytes -> int -> int - val read : t -> bytes -> int -> int -@@ -78,13 +79,14 @@ val write_mmap : backend_mmap -> 'a -> string -> int -> int - val write : t -> string -> int -> int - val output : t -> bool - val input : t -> Packet.t option --val newcon : backend -> t --val open_fd : Unix.file_descr -> t --val open_mmap : Xenmmap.mmap_interface -> (unit -> unit) -> t -+val newcon : capacity:capacity -> backend -> t -+val open_fd : Unix.file_descr -> capacity:capacity -> t -+val open_mmap : Xenmmap.mmap_interface -> (unit -> unit) -> capacity:capacity -> t - val close : t -> unit - val is_fd : t -> bool - val is_mmap : t -> bool - val output_len : t -> int -+val can_input: t -> bool - val has_new_output : t -> bool - val has_old_output : t -> bool - val has_output : t -> bool -@@ -93,3 +95,4 @@ val has_partial_input : t -> bool - val has_more_input : t -> bool - val is_selectable : t -> bool - val get_fd : t -> Unix.file_descr -+val debug: t -> string -diff --git a/tools/ocaml/libs/xs/queueop.ml b/tools/ocaml/libs/xs/queueop.ml -index 9ff5bbd529ce..4e532cdaeacb 100644 ---- a/tools/ocaml/libs/xs/queueop.ml -+++ b/tools/ocaml/libs/xs/queueop.ml -@@ -16,9 +16,10 @@ - open Xenbus - - let data_concat ls = (String.concat "\000" ls) ^ "\000" -+let queue con pkt = let r = Xb.queue con pkt in assert (r <> None) - let queue_path ty (tid: int) (path: string) con = - let data = data_concat [ path; ] in -- Xb.queue con (Xb.Packet.create tid 0 ty data) -+ queue con (Xb.Packet.create tid 0 ty data) - - (* operations *) - let directory tid path con = queue_path Xb.Op.Directory tid path con -@@ -27,48 +28,48 @@ let read tid path con = queue_path Xb.Op.Read tid path con - let getperms tid path con = queue_path Xb.Op.Getperms tid path con - - let debug commands con = -- Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Debug (data_concat commands)) -+ queue con (Xb.Packet.create 0 0 Xb.Op.Debug (data_concat commands)) - - let watch path data con = - let data = data_concat [ path; data; ] in -- Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Watch data) -+ queue con (Xb.Packet.create 0 0 Xb.Op.Watch data) - - let unwatch path data con = - let data = data_concat [ path; data; ] in -- Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Unwatch data) -+ queue con (Xb.Packet.create 0 0 Xb.Op.Unwatch data) - - let transaction_start con = -- Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Transaction_start (data_concat [])) -+ queue con (Xb.Packet.create 0 0 Xb.Op.Transaction_start (data_concat [])) - - let transaction_end tid commit con = - let data = data_concat [ (if commit then "T" else "F"); ] in -- Xb.queue con (Xb.Packet.create tid 0 Xb.Op.Transaction_end data) -+ queue con (Xb.Packet.create tid 0 Xb.Op.Transaction_end data) - - let introduce domid mfn port con = - let data = data_concat [ Printf.sprintf "%u" domid; - Printf.sprintf "%nu" mfn; - string_of_int port; ] in -- Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Introduce data) -+ queue con (Xb.Packet.create 0 0 Xb.Op.Introduce data) - - let release domid con = - let data = data_concat [ Printf.sprintf "%u" domid; ] in -- Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Release data) -+ queue con (Xb.Packet.create 0 0 Xb.Op.Release data) - - let resume domid con = - let data = data_concat [ Printf.sprintf "%u" domid; ] in -- Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Resume data) -+ queue con (Xb.Packet.create 0 0 Xb.Op.Resume data) - - let getdomainpath domid con = - let data = data_concat [ Printf.sprintf "%u" domid; ] in -- Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Getdomainpath data) -+ queue con (Xb.Packet.create 0 0 Xb.Op.Getdomainpath data) - - let write tid path value con = - let data = path ^ "\000" ^ value (* no NULL at the end *) in -- Xb.queue con (Xb.Packet.create tid 0 Xb.Op.Write data) -+ queue con (Xb.Packet.create tid 0 Xb.Op.Write data) - - let mkdir tid path con = queue_path Xb.Op.Mkdir tid path con - let rm tid path con = queue_path Xb.Op.Rm tid path con - - let setperms tid path perms con = - let data = data_concat [ path; perms ] in -- Xb.queue con (Xb.Packet.create tid 0 Xb.Op.Setperms data) -+ queue con (Xb.Packet.create tid 0 Xb.Op.Setperms data) -diff --git a/tools/ocaml/libs/xs/xsraw.ml b/tools/ocaml/libs/xs/xsraw.ml -index 451f8b38dbcc..cbd17280600c 100644 ---- a/tools/ocaml/libs/xs/xsraw.ml -+++ b/tools/ocaml/libs/xs/xsraw.ml -@@ -36,8 +36,10 @@ type con = { - let close con = - Xb.close con.xb - -+let capacity = { Xb.maxoutstanding = 1; maxwatchevents = 0; } -+ - let open_fd fd = { -- xb = Xb.open_fd fd; -+ xb = Xb.open_fd ~capacity fd; - watchevents = Queue.create (); - } - -diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/connection.ml -index cc20e047d2b9..9624a5f9da2c 100644 ---- a/tools/ocaml/xenstored/connection.ml -+++ b/tools/ocaml/xenstored/connection.ml -@@ -20,12 +20,84 @@ open Stdext - - let xenstore_payload_max = 4096 (* xen/include/public/io/xs_wire.h *) - -+type 'a bounded_sender = 'a -> unit option -+(** a bounded sender accepts an ['a] item and returns: -+ None - if there is no room to accept the item -+ Some () - if it has successfully accepted/sent the item -+ *) -+ -+module BoundedPipe : sig -+ type 'a t -+ -+ (** [create ~capacity ~destination] creates a bounded pipe with a -+ local buffer holding at most [capacity] items. Once the buffer is -+ full it will not accept further items. items from the pipe are -+ flushed into [destination] as long as it accepts items. The -+ destination could be another pipe. -+ *) -+ val create: capacity:int -> destination:'a bounded_sender -> 'a t -+ -+ (** [is_empty t] returns whether the local buffer of [t] is empty. *) -+ val is_empty : _ t -> bool -+ -+ (** [length t] the number of items in the internal buffer *) -+ val length: _ t -> int -+ -+ (** [flush_pipe t] sends as many items from the local buffer as possible, -+ which could be none. *) -+ val flush_pipe: _ t -> unit -+ -+ (** [push t item] tries to [flush_pipe] and then push [item] -+ into the pipe if its [capacity] allows. -+ Returns [None] if there is no more room -+ *) -+ val push : 'a t -> 'a bounded_sender -+end = struct -+ (* items are enqueued in [q], and then flushed to [connect_to] *) -+ type 'a t = -+ { q: 'a Queue.t -+ ; destination: 'a bounded_sender -+ ; capacity: int -+ } -+ -+ let create ~capacity ~destination = -+ { q = Queue.create (); capacity; destination } -+ -+ let rec flush_pipe t = -+ if not Queue.(is_empty t.q) then -+ let item = Queue.peek t.q in -+ match t.destination item with -+ | None -> () (* no room *) -+ | Some () -> -+ (* successfully sent item to next stage *) -+ let _ = Queue.pop t.q in -+ (* continue trying to send more items *) -+ flush_pipe t -+ -+ let push t item = -+ (* first try to flush as many items from this pipe as possible to make room, -+ it is important to do this first to preserve the order of the items -+ *) -+ flush_pipe t; -+ if Queue.length t.q < t.capacity then begin -+ (* enqueue, instead of sending directly. -+ this ensures that [out] sees the items in the same order as we receive them -+ *) -+ Queue.push item t.q; -+ Some (flush_pipe t) -+ end else None -+ -+ let is_empty t = Queue.is_empty t.q -+ let length t = Queue.length t.q -+end -+ - type watch = { - con: t; - token: string; - path: string; - base: string; - is_relative: bool; -+ pending_watchevents: Xenbus.Xb.Packet.t BoundedPipe.t; - } - - and t = { -@@ -38,8 +110,36 @@ and t = { - anonid: int; - mutable stat_nb_ops: int; - mutable perm: Perms.Connection.t; -+ pending_source_watchevents: (watch * Xenbus.Xb.Packet.t) BoundedPipe.t - } - -+module Watch = struct -+ module T = struct -+ type t = watch -+ -+ let compare w1 w2 = -+ (* cannot compare watches from different connections *) -+ assert (w1.con == w2.con); -+ match String.compare w1.token w2.token with -+ | 0 -> String.compare w1.path w2.path -+ | n -> n -+ end -+ module Set = Set.Make(T) -+ -+ let flush_events t = -+ BoundedPipe.flush_pipe t.pending_watchevents; -+ not (BoundedPipe.is_empty t.pending_watchevents) -+ -+ let pending_watchevents t = -+ BoundedPipe.length t.pending_watchevents -+end -+ -+let source_flush_watchevents t = -+ BoundedPipe.flush_pipe t.pending_source_watchevents -+ -+let source_pending_watchevents t = -+ BoundedPipe.length t.pending_source_watchevents -+ - let mark_as_bad con = - match con.dom with - |None -> () -@@ -67,7 +167,8 @@ let watch_create ~con ~path ~token = { - token = token; - path = path; - base = get_path con; -- is_relative = path.[0] <> '/' && path.[0] <> '@' -+ is_relative = path.[0] <> '/' && path.[0] <> '@'; -+ pending_watchevents = BoundedPipe.create ~capacity:!Define.maxwatchevents ~destination:(Xenbus.Xb.queue con.xb) - } - - let get_con w = w.con -@@ -93,6 +194,9 @@ let make_perm dom = - Perms.Connection.create ~perms:[Perms.READ; Perms.WRITE] domid - - let create xbcon dom = -+ let destination (watch, pkt) = -+ BoundedPipe.push watch.pending_watchevents pkt -+ in - let id = - match dom with - | None -> let old = !anon_id_next in incr anon_id_next; old -@@ -109,6 +213,16 @@ let create xbcon dom = - anonid = id; - stat_nb_ops = 0; - perm = make_perm dom; -+ -+ (* the actual capacity will be lower, this is used as an overflow -+ buffer: anything that doesn't fit elsewhere gets put here, only -+ limited by the amount of watches that you can generate with a -+ single xenstore command (which is finite, although possibly very -+ large in theory for Dom0). Once the pipe here has any contents the -+ domain is blocked from sending more commands until it is empty -+ again though. -+ *) -+ pending_source_watchevents = BoundedPipe.create ~capacity:Sys.max_array_length ~destination - } - in - Logging.new_connection ~tid:Transaction.none ~con:(get_domstr con); -@@ -127,11 +241,17 @@ let set_target con target_domid = - - let is_backend_mmap con = Xenbus.Xb.is_mmap con.xb - --let send_reply con tid rid ty data = -+let packet_of con tid rid ty data = - if (String.length data) > xenstore_payload_max && (is_backend_mmap con) then -- Xenbus.Xb.queue con.xb (Xenbus.Xb.Packet.create tid rid Xenbus.Xb.Op.Error "E2BIG\000") -+ Xenbus.Xb.Packet.create tid rid Xenbus.Xb.Op.Error "E2BIG\000" - else -- Xenbus.Xb.queue con.xb (Xenbus.Xb.Packet.create tid rid ty data) -+ Xenbus.Xb.Packet.create tid rid ty data -+ -+let send_reply con tid rid ty data = -+ let result = Xenbus.Xb.queue con.xb (packet_of con tid rid ty data) in -+ (* should never happen: we only process an input packet when there is room for an output packet *) -+ (* and the limit for replies is different from the limit for watch events *) -+ assert (result <> None) - - let send_error con tid rid err = send_reply con tid rid Xenbus.Xb.Op.Error (err ^ "\000") - let send_ack con tid rid ty = send_reply con tid rid ty "OK\000" -@@ -181,11 +301,11 @@ let del_watch con path token = - apath, w - - let del_watches con = -- Hashtbl.clear con.watches; -+ Hashtbl.reset con.watches; - con.nb_watches <- 0 - - let del_transactions con = -- Hashtbl.clear con.transactions -+ Hashtbl.reset con.transactions - - let list_watches con = - let ll = Hashtbl.fold -@@ -208,21 +328,29 @@ let lookup_watch_perm path = function - let lookup_watch_perms oldroot root path = - lookup_watch_perm path oldroot @ lookup_watch_perm path (Some root) - --let fire_single_watch_unchecked watch = -+let fire_single_watch_unchecked source watch = - let data = Utils.join_by_null [watch.path; watch.token; ""] in -- send_reply watch.con Transaction.none 0 Xenbus.Xb.Op.Watchevent data -+ let pkt = packet_of watch.con Transaction.none 0 Xenbus.Xb.Op.Watchevent data in -+ -+ match BoundedPipe.push source.pending_source_watchevents (watch, pkt) with -+ | Some () -> () (* packet queued *) -+ | None -> -+ (* a well behaved Dom0 shouldn't be able to trigger this, -+ if it happens it is likely a Dom0 bug causing runaway memory usage -+ *) -+ failwith "watch event overflow, cannot happen" - --let fire_single_watch (oldroot, root) watch = -+let fire_single_watch source (oldroot, root) watch = - let abspath = get_watch_path watch.con watch.path |> Store.Path.of_string in - let perms = lookup_watch_perms oldroot root abspath in - if Perms.can_fire_watch watch.con.perm perms then -- fire_single_watch_unchecked watch -+ fire_single_watch_unchecked source watch - else - let perms = perms |> List.map (Perms.Node.to_string ~sep:" ") |> String.concat ", " in - let con = get_domstr watch.con in - Logging.watch_not_fired ~con perms (Store.Path.to_string abspath) - --let fire_watch roots watch path = -+let fire_watch source roots watch path = - let new_path = - if watch.is_relative && path.[0] = '/' - then begin -@@ -232,7 +360,7 @@ let fire_watch roots watch path = - end else - path - in -- fire_single_watch roots { watch with path = new_path } -+ fire_single_watch source roots { watch with path = new_path } - - (* Search for a valid unused transaction id. *) - let rec valid_transaction_id con proposed_id = -@@ -280,6 +408,7 @@ let do_input con = Xenbus.Xb.input con.xb - let has_partial_input con = Xenbus.Xb.has_partial_input con.xb - let has_more_input con = Xenbus.Xb.has_more_input con.xb - -+let can_input con = Xenbus.Xb.can_input con.xb && BoundedPipe.is_empty con.pending_source_watchevents - let has_output con = Xenbus.Xb.has_output con.xb - let has_old_output con = Xenbus.Xb.has_old_output con.xb - let has_new_output con = Xenbus.Xb.has_new_output con.xb -@@ -323,7 +452,7 @@ let prevents_live_update con = not (is_bad con) - && (has_extra_connection_data con || has_transaction_data con) - - let has_more_work con = -- has_more_input con || not (has_old_output con) && has_new_output con -+ (has_more_input con && can_input con) || not (has_old_output con) && has_new_output con - - let incr_ops con = con.stat_nb_ops <- con.stat_nb_ops + 1 - -diff --git a/tools/ocaml/xenstored/connections.ml b/tools/ocaml/xenstored/connections.ml -index 3c7429fe7f61..7d68c583b43a 100644 ---- a/tools/ocaml/xenstored/connections.ml -+++ b/tools/ocaml/xenstored/connections.ml -@@ -22,22 +22,30 @@ type t = { - domains: (int, Connection.t) Hashtbl.t; - ports: (Xeneventchn.t, Connection.t) Hashtbl.t; - mutable watches: Connection.watch list Trie.t; -+ mutable has_pending_watchevents: Connection.Watch.Set.t - } - - let create () = { - anonymous = Hashtbl.create 37; - domains = Hashtbl.create 37; - ports = Hashtbl.create 37; -- watches = Trie.create () -+ watches = Trie.create (); -+ has_pending_watchevents = Connection.Watch.Set.empty; - } - -+let get_capacity () = -+ (* not multiplied by maxwatch on purpose: 2nd queue in watch itself! *) -+ { Xenbus.Xb.maxoutstanding = !Define.maxoutstanding; maxwatchevents = !Define.maxwatchevents } -+ - let add_anonymous cons fd = -- let xbcon = Xenbus.Xb.open_fd fd in -+ let capacity = get_capacity () in -+ let xbcon = Xenbus.Xb.open_fd fd ~capacity in - let con = Connection.create xbcon None in - Hashtbl.add cons.anonymous (Xenbus.Xb.get_fd xbcon) con - - let add_domain cons dom = -- let xbcon = Xenbus.Xb.open_mmap (Domain.get_interface dom) (fun () -> Domain.notify dom) in -+ let capacity = get_capacity () in -+ let xbcon = Xenbus.Xb.open_mmap ~capacity (Domain.get_interface dom) (fun () -> Domain.notify dom) in - let con = Connection.create xbcon (Some dom) in - Hashtbl.add cons.domains (Domain.get_id dom) con; - match Domain.get_port dom with -@@ -48,7 +56,9 @@ let select ?(only_if = (fun _ -> true)) cons = - Hashtbl.fold (fun _ con (ins, outs) -> - if (only_if con) then ( - let fd = Connection.get_fd con in -- (fd :: ins, if Connection.has_output con then fd :: outs else outs) -+ let in_fds = if Connection.can_input con then fd :: ins else ins in -+ let out_fds = if Connection.has_output con then fd :: outs else outs in -+ in_fds, out_fds - ) else (ins, outs) - ) - cons.anonymous ([], []) -@@ -67,10 +77,17 @@ let del_watches_of_con con watches = - | [] -> None - | ws -> Some ws - -+let del_watches cons con = -+ Connection.del_watches con; -+ cons.watches <- Trie.map (del_watches_of_con con) cons.watches; -+ cons.has_pending_watchevents <- -+ cons.has_pending_watchevents |> Connection.Watch.Set.filter @@ fun w -> -+ Connection.get_con w != con -+ - let del_anonymous cons con = - try - Hashtbl.remove cons.anonymous (Connection.get_fd con); -- cons.watches <- Trie.map (del_watches_of_con con) cons.watches; -+ del_watches cons con; - Connection.close con - with exn -> - debug "del anonymous %s" (Printexc.to_string exn) -@@ -85,7 +102,7 @@ let del_domain cons id = - | Some p -> Hashtbl.remove cons.ports p - | None -> ()) - | None -> ()); -- cons.watches <- Trie.map (del_watches_of_con con) cons.watches; -+ del_watches cons con; - Connection.close con - with exn -> - debug "del domain %u: %s" id (Printexc.to_string exn) -@@ -136,31 +153,33 @@ let del_watch cons con path token = - cons.watches <- Trie.set cons.watches key watches; - watch - --let del_watches cons con = -- Connection.del_watches con; -- cons.watches <- Trie.map (del_watches_of_con con) cons.watches -- - (* path is absolute *) --let fire_watches ?oldroot root cons path recurse = -+let fire_watches ?oldroot source root cons path recurse = - let key = key_of_path path in - let path = Store.Path.to_string path in - let roots = oldroot, root in - let fire_watch _ = function - | None -> () -- | Some watches -> List.iter (fun w -> Connection.fire_watch roots w path) watches -+ | Some watches -> List.iter (fun w -> Connection.fire_watch source roots w path) watches - in - let fire_rec _x = function - | None -> () - | Some watches -> -- List.iter (Connection.fire_single_watch roots) watches -+ List.iter (Connection.fire_single_watch source roots) watches - in - Trie.iter_path fire_watch cons.watches key; - if recurse then - Trie.iter fire_rec (Trie.sub cons.watches key) - -+let send_watchevents cons con = -+ cons.has_pending_watchevents <- -+ cons.has_pending_watchevents |> Connection.Watch.Set.filter Connection.Watch.flush_events; -+ Connection.source_flush_watchevents con -+ - let fire_spec_watches root cons specpath = -+ let source = find_domain cons 0 in - iter cons (fun con -> -- List.iter (Connection.fire_single_watch (None, root)) (Connection.get_watches con specpath)) -+ List.iter (Connection.fire_single_watch source (None, root)) (Connection.get_watches con specpath)) - - let set_target cons domain target_domain = - let con = find_domain cons domain in -@@ -197,6 +216,16 @@ let debug cons = - let domains = Hashtbl.fold (fun _ con accu -> Connection.debug con :: accu) cons.domains [] in - String.concat "" (domains @ anonymous) - -+let debug_watchevents cons con = -+ (* == (physical equality) -+ has to be used here because w.con.xb.backend might contain a [unit->unit] value causing regular -+ comparison to fail due to having a 'functional value' which cannot be compared. -+ *) -+ let s = cons.has_pending_watchevents |> Connection.Watch.Set.filter (fun w -> w.con == con) in -+ let pending = s |> Connection.Watch.Set.elements -+ |> List.map (fun w -> Connection.Watch.pending_watchevents w) |> List.fold_left (+) 0 in -+ Printf.sprintf "Watches with pending events: %d, pending events total: %d" (Connection.Watch.Set.cardinal s) pending -+ - let filter ~f cons = - let fold _ v acc = if f v then v :: acc else acc in - [] -diff --git a/tools/ocaml/xenstored/define.ml b/tools/ocaml/xenstored/define.ml -index ba63a8147e09..327b6d795ec7 100644 ---- a/tools/ocaml/xenstored/define.ml -+++ b/tools/ocaml/xenstored/define.ml -@@ -24,6 +24,13 @@ let default_config_dir = Paths.xen_config_dir - let maxwatch = ref (100) - let maxtransaction = ref (10) - let maxrequests = ref (1024) (* maximum requests per transaction *) -+let maxoutstanding = ref (1024) (* maximum outstanding requests, i.e. in-flight requests / domain *) -+let maxwatchevents = ref (1024) -+(* -+ maximum outstanding watch events per watch, -+ recommended >= maxoutstanding to avoid blocking backend transactions due to -+ malicious frontends -+ *) - - let gc_max_overhead = ref 120 (* 120% see comment in xenstored.ml *) - let conflict_burst_limit = ref 5.0 -diff --git a/tools/ocaml/xenstored/oxenstored.conf.in b/tools/ocaml/xenstored/oxenstored.conf.in -index 4ae48e42d47d..9d034e744b4b 100644 ---- a/tools/ocaml/xenstored/oxenstored.conf.in -+++ b/tools/ocaml/xenstored/oxenstored.conf.in -@@ -62,6 +62,8 @@ quota-maxwatch = 100 - quota-transaction = 10 - quota-maxrequests = 1024 - quota-path-max = 1024 -+quota-maxoutstanding = 1024 -+quota-maxwatchevents = 1024 - - # Activate filed base backend - persistent = false -diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml -index 2d67456a2aa0..6dcedfda86e4 100644 ---- a/tools/ocaml/xenstored/process.ml -+++ b/tools/ocaml/xenstored/process.ml -@@ -57,7 +57,7 @@ let split_one_path data con = - | path :: "" :: [] -> Store.Path.create path (Connection.get_path con) - | _ -> raise Invalid_Cmd_Args - --let process_watch t cons = -+let process_watch source t cons = - let oldroot = t.Transaction.oldroot in - let newroot = Store.get_root t.store in - let ops = Transaction.get_paths t |> List.rev in -@@ -67,8 +67,9 @@ let process_watch t cons = - | Xenbus.Xb.Op.Rm -> true, None, oldroot - | Xenbus.Xb.Op.Setperms -> false, Some oldroot, newroot - | _ -> raise (Failure "huh ?") in -- Connections.fire_watches ?oldroot root cons (snd op) recurse in -- List.iter (fun op -> do_op_watch op cons) ops -+ Connections.fire_watches ?oldroot source root cons (snd op) recurse in -+ List.iter (fun op -> do_op_watch op cons) ops; -+ Connections.send_watchevents cons source - - let create_implicit_path t perm path = - let dirname = Store.Path.get_parent path in -@@ -234,6 +235,20 @@ let do_debug con t _domains cons data = - | "watches" :: _ -> - let watches = Connections.debug cons in - Some (watches ^ "\000") -+ | "xenbus" :: domid :: _ -> -+ let domid = int_of_string domid in -+ let con = Connections.find_domain cons domid in -+ let s = Printf.sprintf "xenbus: %s; overflow queue length: %d, can_input: %b, has_more_input: %b, has_old_output: %b, has_new_output: %b, has_more_work: %b. pending: %s" -+ (Xenbus.Xb.debug con.xb) -+ (Connection.source_pending_watchevents con) -+ (Connection.can_input con) -+ (Connection.has_more_input con) -+ (Connection.has_old_output con) -+ (Connection.has_new_output con) -+ (Connection.has_more_work con) -+ (Connections.debug_watchevents cons con) -+ in -+ Some s - | "mfn" :: domid :: _ -> - let domid = int_of_string domid in - let con = Connections.find_domain cons domid in -@@ -342,7 +357,7 @@ let reply_ack fct con t doms cons data = - fct con t doms cons data; - Packet.Ack (fun () -> - if Transaction.get_id t = Transaction.none then -- process_watch t cons -+ process_watch con t cons - ) - - let reply_data fct con t doms cons data = -@@ -501,7 +516,7 @@ let do_watch con t _domains cons data = - Packet.Ack (fun () -> - (* xenstore.txt says this watch is fired immediately, - implying even if path doesn't exist or is unreadable *) -- Connection.fire_single_watch_unchecked watch) -+ Connection.fire_single_watch_unchecked con watch) - - let do_unwatch con _t _domains cons data = - let (node, token) = -@@ -532,7 +547,7 @@ let do_transaction_end con t domains cons data = - if not success then - raise Transaction_again; - if commit then begin -- process_watch t cons; -+ process_watch con t cons; - match t.Transaction.ty with - | Transaction.No -> - () (* no need to record anything *) -@@ -699,7 +714,8 @@ let process_packet ~store ~cons ~doms ~con ~req = - let do_input store cons doms con = - let newpacket = - try -- Connection.do_input con -+ if Connection.can_input con then Connection.do_input con -+ else None - with Xenbus.Xb.Reconnect -> - info "%s requests a reconnect" (Connection.get_domstr con); - History.reconnect con; -@@ -727,6 +743,7 @@ let do_input store cons doms con = - Connection.incr_ops con - - let do_output _store _cons _doms con = -+ Connection.source_flush_watchevents con; - if Connection.has_output con then ( - if Connection.has_new_output con then ( - let packet = Connection.peek_output con in -diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml -index 3b57ad016dfb..c799e20f1145 100644 ---- a/tools/ocaml/xenstored/xenstored.ml -+++ b/tools/ocaml/xenstored/xenstored.ml -@@ -103,6 +103,8 @@ let parse_config filename = - ("quota-maxentity", Config.Set_int Quota.maxent); - ("quota-maxsize", Config.Set_int Quota.maxsize); - ("quota-maxrequests", Config.Set_int Define.maxrequests); -+ ("quota-maxoutstanding", Config.Set_int Define.maxoutstanding); -+ ("quota-maxwatchevents", Config.Set_int Define.maxwatchevents); - ("quota-path-max", Config.Set_int Define.path_max); - ("gc-max-overhead", Config.Set_int Define.gc_max_overhead); - ("test-eagain", Config.Set_bool Transaction.test_eagain); diff --git a/xsa326-4.16-oxenstored-08.patch b/xsa326-4.16-oxenstored-08.patch deleted file mode 100644 index 8a47200..0000000 --- a/xsa326-4.16-oxenstored-08.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 09aa10649f75a262028e9a9b7d859ef7efb23d54 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Thu, 29 Sep 2022 13:07:35 +0200 -Subject: SUPPORT.md: clarify support of untrusted driver domains with - oxenstored - -Add a support statement for the scope of support regarding different -Xenstore variants. Especially oxenstored does not (yet) have security -support of untrusted driver domains, as those might drive oxenstored -out of memory by creating lots of watch events for the guests they are -servicing. - -Add a statement regarding Live Update support of oxenstored. - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Acked-by: George Dunlap -Acked-by: Julien Grall -Reviewed-by: Christian Lindig - -diff --git a/SUPPORT.md b/SUPPORT.md -index 85726102eab8..7d0cb34c8f6f 100644 ---- a/SUPPORT.md -+++ b/SUPPORT.md -@@ -179,13 +179,18 @@ Support for running qemu-xen device model in a linux stubdomain. - - Status: Tech Preview - --## Liveupdate of C xenstored daemon -+## Xenstore - -- Status: Tech Preview -+### C xenstored daemon - --## Liveupdate of OCaml xenstored daemon -+ Status: Supported -+ Status, Liveupdate: Tech Preview - -- Status: Tech Preview -+### OCaml xenstored daemon -+ -+ Status: Supported -+ Status, untrusted driver domains: Supported, not security supported -+ Status, Liveupdate: Not functional - - ## Toolstack/3rd party - diff --git a/xsa326-4.16-xenstored-01.patch b/xsa326-4.16-xenstored-01.patch deleted file mode 100644 index b4a4b0e..0000000 --- a/xsa326-4.16-xenstored-01.patch +++ /dev/null @@ -1,205 +0,0 @@ -From 5192f13a41661b1c1b9e0889d57c0f5b41925c39 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:07 +0200 -Subject: tools/xenstore: split up send_reply() - -Today send_reply() is used for both, normal request replies and watch -events. - -Split it up into send_reply() and send_event(). This will be used to -add some event specific handling. - -add_event() can be merged into send_event(), removing the need for an -intermediate memory allocation. - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index e9c9695fd16e..249ad5ec6fb1 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -767,49 +767,32 @@ static void send_error(struct connection *conn, int error) - void send_reply(struct connection *conn, enum xsd_sockmsg_type type, - const void *data, unsigned int len) - { -- struct buffered_data *bdata; -+ struct buffered_data *bdata = conn->in; -+ -+ assert(type != XS_WATCH_EVENT); - - if ( len > XENSTORE_PAYLOAD_MAX ) { - send_error(conn, E2BIG); - return; - } - -- /* Replies reuse the request buffer, events need a new one. */ -- if (type != XS_WATCH_EVENT) { -- bdata = conn->in; -- /* Drop asynchronous responses, e.g. errors for watch events. */ -- if (!bdata) -- return; -- bdata->inhdr = true; -- bdata->used = 0; -- conn->in = NULL; -- } else { -- /* Message is a child of the connection for auto-cleanup. */ -- bdata = new_buffer(conn); -+ if (!bdata) -+ return; -+ bdata->inhdr = true; -+ bdata->used = 0; - -- /* -- * Allocation failure here is unfortunate: we have no way to -- * tell anybody about it. -- */ -- if (!bdata) -- return; -- } - if (len <= DEFAULT_BUFFER_SIZE) - bdata->buffer = bdata->default_buffer; -- else -+ else { - bdata->buffer = talloc_array(bdata, char, len); -- if (!bdata->buffer) { -- if (type == XS_WATCH_EVENT) { -- /* Same as above: no way to tell someone. */ -- talloc_free(bdata); -+ if (!bdata->buffer) { -+ send_error(conn, ENOMEM); - return; - } -- /* re-establish request buffer for sending ENOMEM. */ -- conn->in = bdata; -- send_error(conn, ENOMEM); -- return; - } - -+ conn->in = NULL; -+ - /* Update relevant header fields and fill in the message body. */ - bdata->hdr.msg.type = type; - bdata->hdr.msg.len = len; -@@ -817,8 +800,39 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, - - /* Queue for later transmission. */ - list_add_tail(&bdata->list, &conn->out_list); -+} - -- return; -+/* -+ * Send a watch event. -+ * As this is not directly related to the current command, errors can't be -+ * reported. -+ */ -+void send_event(struct connection *conn, const char *path, const char *token) -+{ -+ struct buffered_data *bdata; -+ unsigned int len; -+ -+ len = strlen(path) + 1 + strlen(token) + 1; -+ /* Don't try to send over-long events. */ -+ if (len > XENSTORE_PAYLOAD_MAX) -+ return; -+ -+ bdata = new_buffer(conn); -+ if (!bdata) -+ return; -+ -+ bdata->buffer = talloc_array(bdata, char, len); -+ if (!bdata->buffer) { -+ talloc_free(bdata); -+ return; -+ } -+ strcpy(bdata->buffer, path); -+ strcpy(bdata->buffer + strlen(path) + 1, token); -+ bdata->hdr.msg.type = XS_WATCH_EVENT; -+ bdata->hdr.msg.len = len; -+ -+ /* Queue for later transmission. */ -+ list_add_tail(&bdata->list, &conn->out_list); - } - - /* Some routines (write, mkdir, etc) just need a non-error return */ -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index 0004fa848c83..9af9af4390bd 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -187,6 +187,7 @@ unsigned int get_string(const struct buffered_data *data, unsigned int offset); - - void send_reply(struct connection *conn, enum xsd_sockmsg_type type, - const void *data, unsigned int len); -+void send_event(struct connection *conn, const char *path, const char *token); - - /* Some routines (write, mkdir, etc) just need a non-error return */ - void send_ack(struct connection *conn, enum xsd_sockmsg_type type); -diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c -index aca0a71bada1..99a2c266b28a 100644 ---- a/tools/xenstore/xenstored_watch.c -+++ b/tools/xenstore/xenstored_watch.c -@@ -86,35 +86,6 @@ static const char *get_watch_path(const struct watch *watch, const char *name) - } - - /* -- * Send a watch event. -- * Temporary memory allocations are done with ctx. -- */ --static void add_event(struct connection *conn, -- const void *ctx, -- struct watch *watch, -- const char *name) --{ -- /* Data to send (node\0token\0). */ -- unsigned int len; -- char *data; -- -- name = get_watch_path(watch, name); -- -- len = strlen(name) + 1 + strlen(watch->token) + 1; -- /* Don't try to send over-long events. */ -- if (len > XENSTORE_PAYLOAD_MAX) -- return; -- -- data = talloc_array(ctx, char, len); -- if (!data) -- return; -- strcpy(data, name); -- strcpy(data + strlen(name) + 1, watch->token); -- send_reply(conn, XS_WATCH_EVENT, data, len); -- talloc_free(data); --} -- --/* - * Check permissions of a specific watch to fire: - * Either the node itself or its parent have to be readable by the connection - * the watch has been setup for. In case a watch event is created due to -@@ -190,10 +161,14 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, - list_for_each_entry(watch, &i->watches, list) { - if (exact) { - if (streq(name, watch->node)) -- add_event(i, ctx, watch, name); -+ send_event(i, -+ get_watch_path(watch, name), -+ watch->token); - } else { - if (is_child(name, watch->node)) -- add_event(i, ctx, watch, name); -+ send_event(i, -+ get_watch_path(watch, name), -+ watch->token); - } - } - } -@@ -292,7 +267,7 @@ int do_watch(struct connection *conn, struct buffered_data *in) - send_ack(conn, XS_WATCH); - - /* We fire once up front: simplifies clients and restart. */ -- add_event(conn, in, watch, watch->node); -+ send_event(conn, get_watch_path(watch, watch->node), watch->token); - - return 0; - } diff --git a/xsa326-4.16-xenstored-02.patch b/xsa326-4.16-xenstored-02.patch deleted file mode 100644 index 540ab03..0000000 --- a/xsa326-4.16-xenstored-02.patch +++ /dev/null @@ -1,108 +0,0 @@ -From 0a4c86f8a8febd85610496470123adfc4fbc1c5d Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:07 +0200 -Subject: tools/xenstore: add helpers to free struct buffered_data - -Add two helpers for freeing struct buffered_data: free_buffered_data() -for freeing one instance and conn_free_buffered_data() for freeing all -instances for a connection. - -This is avoiding duplicated code and will help later when more actions -are needed when freeing a struct buffered_data. - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 249ad5ec6fb1..527a1ebdeded 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -211,6 +211,21 @@ void reopen_log(void) - } - } - -+static void free_buffered_data(struct buffered_data *out, -+ struct connection *conn) -+{ -+ list_del(&out->list); -+ talloc_free(out); -+} -+ -+void conn_free_buffered_data(struct connection *conn) -+{ -+ struct buffered_data *out; -+ -+ while ((out = list_top(&conn->out_list, struct buffered_data, list))) -+ free_buffered_data(out, conn); -+} -+ - static bool write_messages(struct connection *conn) - { - int ret; -@@ -254,8 +269,7 @@ static bool write_messages(struct connection *conn) - - trace_io(conn, out, 1); - -- list_del(&out->list); -- talloc_free(out); -+ free_buffered_data(out, conn); - - return true; - } -@@ -1506,18 +1520,12 @@ static struct { - */ - void ignore_connection(struct connection *conn) - { -- struct buffered_data *out, *tmp; -- - trace("CONN %p ignored\n", conn); - - conn->is_ignored = true; - conn_delete_all_watches(conn); - conn_delete_all_transactions(conn); -- -- list_for_each_entry_safe(out, tmp, &conn->out_list, list) { -- list_del(&out->list); -- talloc_free(out); -- } -+ conn_free_buffered_data(conn); - - talloc_free(conn->in); - conn->in = NULL; -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index 9af9af4390bd..e7ee87825c3b 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -276,6 +276,8 @@ int remember_string(struct hashtable *hash, const char *str); - - void set_tdb_key(const char *name, TDB_DATA *key); - -+void conn_free_buffered_data(struct connection *conn); -+ - const char *dump_state_global(FILE *fp); - const char *dump_state_buffered_data(FILE *fp, const struct connection *c, - struct xs_state_connection *sc); -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index d03c7d93a9e7..93c4c1edcdd1 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -411,15 +411,10 @@ static struct domain *find_domain_by_domid(unsigned int domid) - static void domain_conn_reset(struct domain *domain) - { - struct connection *conn = domain->conn; -- struct buffered_data *out; - - conn_delete_all_watches(conn); - conn_delete_all_transactions(conn); -- -- while ((out = list_top(&conn->out_list, struct buffered_data, list))) { -- list_del(&out->list); -- talloc_free(out); -- } -+ conn_free_buffered_data(conn); - - talloc_free(conn->in); - diff --git a/xsa326-4.16-xenstored-03.patch b/xsa326-4.16-xenstored-03.patch deleted file mode 100644 index a3a0d81..0000000 --- a/xsa326-4.16-xenstored-03.patch +++ /dev/null @@ -1,192 +0,0 @@ -From a6c4198242bf69bea1825492b7665b559023390c Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:07 +0200 -Subject: tools/xenstore: reduce number of watch events - -When removing a watched node outside of a transaction, two watch events -are being produced instead of just a single one. - -When finalizing a transaction watch events can be generated for each -node which is being modified, even if outside a transaction such -modifications might not have resulted in a watch event. - -This happens e.g.: - -- for nodes which are only modified due to added/removed child entries -- for nodes being removed or created implicitly (e.g. creation of a/b/c - is implicitly creating a/b, resulting in watch events for a, a/b and - a/b/c instead of a/b/c only) - -Avoid these additional watch events, in order to reduce the needed -memory inside Xenstore for queueing them. - -This is being achieved by adding event flags to struct accessed_node -specifying whether an event should be triggered, and whether it should -be an exact match of the modified path. Both flags can be set from -fire_watches() instead of implying them only. - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 527a1ebdeded..bf2243873901 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1295,7 +1295,7 @@ static void delete_child(struct connection *conn, - } - - static int delete_node(struct connection *conn, const void *ctx, -- struct node *parent, struct node *node) -+ struct node *parent, struct node *node, bool watch_exact) - { - char *name; - -@@ -1307,7 +1307,7 @@ static int delete_node(struct connection *conn, const void *ctx, - node->children); - child = name ? read_node(conn, node, name) : NULL; - if (child) { -- if (delete_node(conn, ctx, node, child)) -+ if (delete_node(conn, ctx, node, child, true)) - return errno; - } else { - trace("delete_node: Error deleting child '%s/%s'!\n", -@@ -1319,7 +1319,12 @@ static int delete_node(struct connection *conn, const void *ctx, - talloc_free(name); - } - -- fire_watches(conn, ctx, node->name, node, true, NULL); -+ /* -+ * Fire the watches now, when we can still see the node permissions. -+ * This fine as we are single threaded and the next possible read will -+ * be handled only after the node has been really removed. -+ */ -+ fire_watches(conn, ctx, node->name, node, watch_exact, NULL); - delete_node_single(conn, node); - delete_child(conn, parent, basename(node->name)); - talloc_free(node); -@@ -1345,13 +1350,7 @@ static int _rm(struct connection *conn, const void *ctx, struct node *node, - return (errno == ENOMEM) ? ENOMEM : EINVAL; - node->parent = parent; - -- /* -- * Fire the watches now, when we can still see the node permissions. -- * This fine as we are single threaded and the next possible read will -- * be handled only after the node has been really removed. -- */ -- fire_watches(conn, ctx, name, node, false, NULL); -- return delete_node(conn, ctx, parent, node); -+ return delete_node(conn, ctx, parent, node, false); - } - - -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index faf6c930e42a..54432907fc76 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -130,6 +130,10 @@ struct accessed_node - - /* Transaction node in data base? */ - bool ta_node; -+ -+ /* Watch event flags. */ -+ bool fire_watch; -+ bool watch_exact; - }; - - struct changed_domain -@@ -324,6 +328,29 @@ int access_node(struct connection *conn, struct node *node, - } - - /* -+ * A watch event should be fired for a node modified inside a transaction. -+ * Set the corresponding information. A non-exact event is replacing an exact -+ * one, but not the other way round. -+ */ -+void queue_watches(struct connection *conn, const char *name, bool watch_exact) -+{ -+ struct accessed_node *i; -+ -+ i = find_accessed_node(conn->transaction, name); -+ if (!i) { -+ conn->transaction->fail = true; -+ return; -+ } -+ -+ if (!i->fire_watch) { -+ i->fire_watch = true; -+ i->watch_exact = watch_exact; -+ } else if (!watch_exact) { -+ i->watch_exact = false; -+ } -+} -+ -+/* - * Finalize transaction: - * Walk through accessed nodes and check generation against global data. - * If all entries match, read the transaction entries and write them without -@@ -377,15 +404,15 @@ static int finalize_transaction(struct connection *conn, - ret = tdb_store(tdb_ctx, key, data, - TDB_REPLACE); - talloc_free(data.dptr); -- if (ret) -- goto err; -- fire_watches(conn, trans, i->node, NULL, false, -- i->perms.p ? &i->perms : NULL); - } else { -- fire_watches(conn, trans, i->node, NULL, false, -+ ret = tdb_delete(tdb_ctx, key); -+ } -+ if (ret) -+ goto err; -+ if (i->fire_watch) { -+ fire_watches(conn, trans, i->node, NULL, -+ i->watch_exact, - i->perms.p ? &i->perms : NULL); -- if (tdb_delete(tdb_ctx, key)) -- goto err; - } - } - -diff --git a/tools/xenstore/xenstored_transaction.h b/tools/xenstore/xenstored_transaction.h -index 14062730e3c9..0093cac807e3 100644 ---- a/tools/xenstore/xenstored_transaction.h -+++ b/tools/xenstore/xenstored_transaction.h -@@ -42,6 +42,9 @@ void transaction_entry_dec(struct transaction *trans, unsigned int domid); - int access_node(struct connection *conn, struct node *node, - enum node_access_type type, TDB_DATA *key); - -+/* Queue watches for a modified node. */ -+void queue_watches(struct connection *conn, const char *name, bool watch_exact); -+ - /* Prepend the transaction to name if appropriate. */ - int transaction_prepend(struct connection *conn, const char *name, - TDB_DATA *key); -diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c -index 99a2c266b28a..205d9d8ea116 100644 ---- a/tools/xenstore/xenstored_watch.c -+++ b/tools/xenstore/xenstored_watch.c -@@ -29,6 +29,7 @@ - #include "xenstore_lib.h" - #include "utils.h" - #include "xenstored_domain.h" -+#include "xenstored_transaction.h" - - extern int quota_nb_watch_per_domain; - -@@ -143,9 +144,11 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, - struct connection *i; - struct watch *watch; - -- /* During transactions, don't fire watches. */ -- if (conn && conn->transaction) -+ /* During transactions, don't fire watches, but queue them. */ -+ if (conn && conn->transaction) { -+ queue_watches(conn, name, exact); - return; -+ } - - /* Create an event for each watch. */ - list_for_each_entry(i, &connections, list) { diff --git a/xsa326-4.16-xenstored-04.patch b/xsa326-4.16-xenstored-04.patch deleted file mode 100644 index facbba4..0000000 --- a/xsa326-4.16-xenstored-04.patch +++ /dev/null @@ -1,302 +0,0 @@ -From 2feed737530592688382c655680982e10951c1ec Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:07 +0200 -Subject: tools/xenstore: let unread watch events time out - -A future modification will limit the number of outstanding requests -for a domain, where "outstanding" means that the response of the -request or any resulting watch event hasn't been consumed yet. - -In order to avoid a malicious guest being capable to block other guests -by not reading watch events, add a timeout for watch events. In case a -watch event hasn't been consumed after this timeout, it is being -deleted. Set the default timeout to 20 seconds (a random value being -not too high). - -In order to support to specify other timeout values in future, use a -generic command line option for that purpose: - ---timeout|-w watch-event= - -This is part of XSA-326 / CVE-2022-42311. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index bf2243873901..45244c021cd3 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -108,6 +108,8 @@ int quota_max_transaction = 10; - int quota_nb_perms_per_node = 5; - int quota_max_path_len = XENSTORE_REL_PATH_MAX; - -+unsigned int timeout_watch_event_msec = 20000; -+ - void trace(const char *fmt, ...) - { - va_list arglist; -@@ -211,19 +213,92 @@ void reopen_log(void) - } - } - -+static uint64_t get_now_msec(void) -+{ -+ struct timespec now_ts; -+ -+ if (clock_gettime(CLOCK_MONOTONIC, &now_ts)) -+ barf_perror("Could not find time (clock_gettime failed)"); -+ -+ return now_ts.tv_sec * 1000 + now_ts.tv_nsec / 1000000; -+} -+ - static void free_buffered_data(struct buffered_data *out, - struct connection *conn) - { -+ struct buffered_data *req; -+ - list_del(&out->list); -+ -+ /* -+ * Update conn->timeout_msec with the next found timeout value in the -+ * queued pending requests. -+ */ -+ if (out->timeout_msec) { -+ conn->timeout_msec = 0; -+ list_for_each_entry(req, &conn->out_list, list) { -+ if (req->timeout_msec) { -+ conn->timeout_msec = req->timeout_msec; -+ break; -+ } -+ } -+ } -+ - talloc_free(out); - } - -+static void check_event_timeout(struct connection *conn, uint64_t msecs, -+ int *ptimeout) -+{ -+ uint64_t delta; -+ struct buffered_data *out, *tmp; -+ -+ if (!conn->timeout_msec) -+ return; -+ -+ delta = conn->timeout_msec - msecs; -+ if (conn->timeout_msec <= msecs) { -+ delta = 0; -+ list_for_each_entry_safe(out, tmp, &conn->out_list, list) { -+ /* -+ * Only look at buffers with timeout and no data -+ * already written to the ring. -+ */ -+ if (out->timeout_msec && out->inhdr && !out->used) { -+ if (out->timeout_msec > msecs) { -+ conn->timeout_msec = out->timeout_msec; -+ delta = conn->timeout_msec - msecs; -+ break; -+ } -+ -+ /* -+ * Free out without updating conn->timeout_msec, -+ * as the update is done in this loop already. -+ */ -+ out->timeout_msec = 0; -+ trace("watch event path %s for domain %u timed out\n", -+ out->buffer, conn->id); -+ free_buffered_data(out, conn); -+ } -+ } -+ if (!delta) { -+ conn->timeout_msec = 0; -+ return; -+ } -+ } -+ -+ if (*ptimeout == -1 || *ptimeout > delta) -+ *ptimeout = delta; -+} -+ - void conn_free_buffered_data(struct connection *conn) - { - struct buffered_data *out; - - while ((out = list_top(&conn->out_list, struct buffered_data, list))) - free_buffered_data(out, conn); -+ -+ conn->timeout_msec = 0; - } - - static bool write_messages(struct connection *conn) -@@ -411,6 +486,7 @@ static void initialize_fds(int *p_sock_pollfd_idx, int *ptimeout) - { - struct connection *conn; - struct wrl_timestampt now; -+ uint64_t msecs; - - if (fds) - memset(fds, 0, sizeof(struct pollfd) * current_array_size); -@@ -431,10 +507,12 @@ static void initialize_fds(int *p_sock_pollfd_idx, int *ptimeout) - - wrl_gettime_now(&now); - wrl_log_periodic(now); -+ msecs = get_now_msec(); - - list_for_each_entry(conn, &connections, list) { - if (conn->domain) { - wrl_check_timeout(conn->domain, now, ptimeout); -+ check_event_timeout(conn, msecs, ptimeout); - if (conn_can_read(conn) || - (conn_can_write(conn) && - !list_empty(&conn->out_list))) -@@ -794,6 +872,7 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, - return; - bdata->inhdr = true; - bdata->used = 0; -+ bdata->timeout_msec = 0; - - if (len <= DEFAULT_BUFFER_SIZE) - bdata->buffer = bdata->default_buffer; -@@ -845,6 +924,12 @@ void send_event(struct connection *conn, const char *path, const char *token) - bdata->hdr.msg.type = XS_WATCH_EVENT; - bdata->hdr.msg.len = len; - -+ if (timeout_watch_event_msec && domain_is_unprivileged(conn)) { -+ bdata->timeout_msec = get_now_msec() + timeout_watch_event_msec; -+ if (!conn->timeout_msec) -+ conn->timeout_msec = bdata->timeout_msec; -+ } -+ - /* Queue for later transmission. */ - list_add_tail(&bdata->list, &conn->out_list); - } -@@ -2201,6 +2286,9 @@ static void usage(void) - " -t, --transaction limit the number of transaction allowed per domain,\n" - " -A, --perm-nb limit the number of permissions per node,\n" - " -M, --path-max limit the allowed Xenstore node path length,\n" -+" -w, --timeout = set the timeout in seconds for ,\n" -+" allowed timeout candidates are:\n" -+" watch-event: time a watch-event is kept pending\n" - " -R, --no-recovery to request that no recovery should be attempted when\n" - " the store is corrupted (debug only),\n" - " -I, --internal-db store database in memory, not on disk\n" -@@ -2223,6 +2311,7 @@ static struct option options[] = { - { "transaction", 1, NULL, 't' }, - { "perm-nb", 1, NULL, 'A' }, - { "path-max", 1, NULL, 'M' }, -+ { "timeout", 1, NULL, 'w' }, - { "no-recovery", 0, NULL, 'R' }, - { "internal-db", 0, NULL, 'I' }, - { "verbose", 0, NULL, 'V' }, -@@ -2236,6 +2325,39 @@ int dom0_domid = 0; - int dom0_event = 0; - int priv_domid = 0; - -+static int get_optval_int(const char *arg) -+{ -+ char *end; -+ long val; -+ -+ val = strtol(arg, &end, 10); -+ if (!*arg || *end || val < 0 || val > INT_MAX) -+ barf("invalid parameter value \"%s\"\n", arg); -+ -+ return val; -+} -+ -+static bool what_matches(const char *arg, const char *what) -+{ -+ unsigned int what_len = strlen(what); -+ -+ return !strncmp(arg, what, what_len) && arg[what_len] == '='; -+} -+ -+static void set_timeout(const char *arg) -+{ -+ const char *eq = strchr(arg, '='); -+ int val; -+ -+ if (!eq) -+ barf("quotas must be specified via =\n"); -+ val = get_optval_int(eq + 1); -+ if (what_matches(arg, "watch-event")) -+ timeout_watch_event_msec = val * 1000; -+ else -+ barf("unknown timeout \"%s\"\n", arg); -+} -+ - int main(int argc, char *argv[]) - { - int opt; -@@ -2250,7 +2372,7 @@ int main(int argc, char *argv[]) - orig_argc = argc; - orig_argv = argv; - -- while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:M:T:RVW:U", options, -+ while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:M:T:RVW:w:U", options, - NULL)) != -1) { - switch (opt) { - case 'D': -@@ -2300,6 +2422,9 @@ int main(int argc, char *argv[]) - quota_max_path_len = min(XENSTORE_REL_PATH_MAX, - quota_max_path_len); - break; -+ case 'w': -+ set_timeout(optarg); -+ break; - case 'e': - dom0_event = strtol(optarg, NULL, 10); - break; -@@ -2741,6 +2866,12 @@ static void add_buffered_data(struct buffered_data *bdata, - barf("error restoring buffered data"); - - memcpy(bdata->buffer, data, len); -+ if (bdata->hdr.msg.type == XS_WATCH_EVENT && timeout_watch_event_msec && -+ domain_is_unprivileged(conn)) { -+ bdata->timeout_msec = get_now_msec() + timeout_watch_event_msec; -+ if (!conn->timeout_msec) -+ conn->timeout_msec = bdata->timeout_msec; -+ } - - /* Queue for later transmission. */ - list_add_tail(&bdata->list, &conn->out_list); -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index e7ee87825c3b..8a81fc693f01 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -27,6 +27,7 @@ - #include - #include - #include -+#include - #include - - #include "xenstore_lib.h" -@@ -67,6 +68,8 @@ struct buffered_data - char raw[sizeof(struct xsd_sockmsg)]; - } hdr; - -+ uint64_t timeout_msec; -+ - /* The actual data. */ - char *buffer; - char default_buffer[DEFAULT_BUFFER_SIZE]; -@@ -118,6 +121,7 @@ struct connection - - /* Buffered output data */ - struct list_head out_list; -+ uint64_t timeout_msec; - - /* Transaction context for current request (NULL if none). */ - struct transaction *transaction; -@@ -244,6 +248,8 @@ extern int dom0_event; - extern int priv_domid; - extern int quota_nb_entry_per_domain; - -+extern unsigned int timeout_watch_event_msec; -+ - /* Map the kernel's xenstore page. */ - void *xenbus_map(void); - void unmap_xenbus(void *interface); diff --git a/xsa326-4.16-xenstored-05.patch b/xsa326-4.16-xenstored-05.patch deleted file mode 100644 index 77f9c25..0000000 --- a/xsa326-4.16-xenstored-05.patch +++ /dev/null @@ -1,443 +0,0 @@ -From 2eee122a45eb4a218596b103ce7f0759a824cf2e Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:08 +0200 -Subject: tools/xenstore: limit outstanding requests - -Add another quota for limiting the number of outstanding requests of a -guest. As the way to specify quotas on the command line is becoming -rather nasty, switch to a new scheme using [--quota|-Q] = -allowing to add more quotas in future easily. - -Set the default value to 20 (basically a random value not seeming to -be too high or too low). - -A request is said to be outstanding if any message generated by this -request (the direct response plus potential watch events) is not yet -completely stored into a ring buffer. The initial watch event sent as -a result of registering a watch is an exception. - -Note that across a live update the relation to buffered watch events -for other domains is lost. - -Use talloc_zero() for allocating the domain structure in order to have -all per-domain quota zeroed initially. - -This is part of XSA-326 / CVE-2022-42312. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Acked-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 45244c021cd3..488d540f3a32 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -107,6 +107,7 @@ int quota_max_entry_size = 2048; /* 2K */ - int quota_max_transaction = 10; - int quota_nb_perms_per_node = 5; - int quota_max_path_len = XENSTORE_REL_PATH_MAX; -+int quota_req_outstanding = 20; - - unsigned int timeout_watch_event_msec = 20000; - -@@ -223,12 +224,24 @@ static uint64_t get_now_msec(void) - return now_ts.tv_sec * 1000 + now_ts.tv_nsec / 1000000; - } - -+/* -+ * Remove a struct buffered_data from the list of outgoing data. -+ * A struct buffered_data related to a request having caused watch events to be -+ * sent is kept until all those events have been written out. -+ * Each watch event is referencing the related request via pend.req, while the -+ * number of watch events caused by a request is kept in pend.ref.event_cnt -+ * (those two cases are mutually exclusive, so the two fields can share memory -+ * via a union). -+ * The struct buffered_data is freed only if no related watch event is -+ * referencing it. The related return data can be freed right away. -+ */ - static void free_buffered_data(struct buffered_data *out, - struct connection *conn) - { - struct buffered_data *req; - - list_del(&out->list); -+ out->on_out_list = false; - - /* - * Update conn->timeout_msec with the next found timeout value in the -@@ -244,6 +257,30 @@ static void free_buffered_data(struct buffered_data *out, - } - } - -+ if (out->hdr.msg.type == XS_WATCH_EVENT) { -+ req = out->pend.req; -+ if (req) { -+ req->pend.ref.event_cnt--; -+ if (!req->pend.ref.event_cnt && !req->on_out_list) { -+ if (req->on_ref_list) { -+ domain_outstanding_domid_dec( -+ req->pend.ref.domid); -+ list_del(&req->list); -+ } -+ talloc_free(req); -+ } -+ } -+ } else if (out->pend.ref.event_cnt) { -+ /* Hang out off from conn. */ -+ talloc_steal(NULL, out); -+ if (out->buffer != out->default_buffer) -+ talloc_free(out->buffer); -+ list_add(&out->list, &conn->ref_list); -+ out->on_ref_list = true; -+ return; -+ } else -+ domain_outstanding_dec(conn); -+ - talloc_free(out); - } - -@@ -405,6 +442,7 @@ int delay_request(struct connection *conn, struct buffered_data *in, - static int destroy_conn(void *_conn) - { - struct connection *conn = _conn; -+ struct buffered_data *req; - - /* Flush outgoing if possible, but don't block. */ - if (!conn->domain) { -@@ -418,6 +456,11 @@ static int destroy_conn(void *_conn) - break; - close(conn->fd); - } -+ -+ conn_free_buffered_data(conn); -+ list_for_each_entry(req, &conn->ref_list, list) -+ req->on_ref_list = false; -+ - if (conn->target) - talloc_unlink(conn, conn->target); - list_del(&conn->list); -@@ -893,6 +936,8 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, - - /* Queue for later transmission. */ - list_add_tail(&bdata->list, &conn->out_list); -+ bdata->on_out_list = true; -+ domain_outstanding_inc(conn); - } - - /* -@@ -900,7 +945,8 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, - * As this is not directly related to the current command, errors can't be - * reported. - */ --void send_event(struct connection *conn, const char *path, const char *token) -+void send_event(struct buffered_data *req, struct connection *conn, -+ const char *path, const char *token) - { - struct buffered_data *bdata; - unsigned int len; -@@ -930,8 +976,13 @@ void send_event(struct connection *conn, const char *path, const char *token) - conn->timeout_msec = bdata->timeout_msec; - } - -+ bdata->pend.req = req; -+ if (req) -+ req->pend.ref.event_cnt++; -+ - /* Queue for later transmission. */ - list_add_tail(&bdata->list, &conn->out_list); -+ bdata->on_out_list = true; - } - - /* Some routines (write, mkdir, etc) just need a non-error return */ -@@ -1740,6 +1791,7 @@ static void handle_input(struct connection *conn) - return; - } - in = conn->in; -+ in->pend.ref.domid = conn->id; - - /* Not finished header yet? */ - if (in->inhdr) { -@@ -1808,6 +1860,7 @@ struct connection *new_connection(const struct interface_funcs *funcs) - new->is_stalled = false; - new->transaction_started = 0; - INIT_LIST_HEAD(&new->out_list); -+ INIT_LIST_HEAD(&new->ref_list); - INIT_LIST_HEAD(&new->watches); - INIT_LIST_HEAD(&new->transaction_list); - INIT_LIST_HEAD(&new->delayed); -@@ -2286,6 +2339,9 @@ static void usage(void) - " -t, --transaction limit the number of transaction allowed per domain,\n" - " -A, --perm-nb limit the number of permissions per node,\n" - " -M, --path-max limit the allowed Xenstore node path length,\n" -+" -Q, --quota = set the quota to the value , allowed\n" -+" quotas are:\n" -+" outstanding: number of outstanding requests\n" - " -w, --timeout = set the timeout in seconds for ,\n" - " allowed timeout candidates are:\n" - " watch-event: time a watch-event is kept pending\n" -@@ -2311,6 +2367,7 @@ static struct option options[] = { - { "transaction", 1, NULL, 't' }, - { "perm-nb", 1, NULL, 'A' }, - { "path-max", 1, NULL, 'M' }, -+ { "quota", 1, NULL, 'Q' }, - { "timeout", 1, NULL, 'w' }, - { "no-recovery", 0, NULL, 'R' }, - { "internal-db", 0, NULL, 'I' }, -@@ -2358,6 +2415,20 @@ static void set_timeout(const char *arg) - barf("unknown timeout \"%s\"\n", arg); - } - -+static void set_quota(const char *arg) -+{ -+ const char *eq = strchr(arg, '='); -+ int val; -+ -+ if (!eq) -+ barf("quotas must be specified via =\n"); -+ val = get_optval_int(eq + 1); -+ if (what_matches(arg, "outstanding")) -+ quota_req_outstanding = val; -+ else -+ barf("unknown quota \"%s\"\n", arg); -+} -+ - int main(int argc, char *argv[]) - { - int opt; -@@ -2372,8 +2443,8 @@ int main(int argc, char *argv[]) - orig_argc = argc; - orig_argv = argv; - -- while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:M:T:RVW:w:U", options, -- NULL)) != -1) { -+ while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:M:Q:T:RVW:w:U", -+ options, NULL)) != -1) { - switch (opt) { - case 'D': - no_domain_init = true; -@@ -2422,6 +2493,9 @@ int main(int argc, char *argv[]) - quota_max_path_len = min(XENSTORE_REL_PATH_MAX, - quota_max_path_len); - break; -+ case 'Q': -+ set_quota(optarg); -+ break; - case 'w': - set_timeout(optarg); - break; -@@ -2875,6 +2949,14 @@ static void add_buffered_data(struct buffered_data *bdata, - - /* Queue for later transmission. */ - list_add_tail(&bdata->list, &conn->out_list); -+ bdata->on_out_list = true; -+ /* -+ * Watch events are never "outstanding", but the request causing them -+ * are instead kept "outstanding" until all watch events caused by that -+ * request have been delivered. -+ */ -+ if (bdata->hdr.msg.type != XS_WATCH_EVENT) -+ domain_outstanding_inc(conn); - } - - void read_state_buffered_data(const void *ctx, struct connection *conn, -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index 8a81fc693f01..db09f463a657 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -56,6 +56,8 @@ struct xs_state_connection; - struct buffered_data - { - struct list_head list; -+ bool on_out_list; -+ bool on_ref_list; - - /* Are we still doing the header? */ - bool inhdr; -@@ -63,6 +65,17 @@ struct buffered_data - /* How far are we? */ - unsigned int used; - -+ /* Outstanding request accounting. */ -+ union { -+ /* ref is being used for requests. */ -+ struct { -+ unsigned int event_cnt; /* # of outstanding events. */ -+ unsigned int domid; /* domid of request. */ -+ } ref; -+ /* req is being used for watch events. */ -+ struct buffered_data *req; /* request causing event. */ -+ } pend; -+ - union { - struct xsd_sockmsg msg; - char raw[sizeof(struct xsd_sockmsg)]; -@@ -123,6 +136,9 @@ struct connection - struct list_head out_list; - uint64_t timeout_msec; - -+ /* Referenced requests no longer pending. */ -+ struct list_head ref_list; -+ - /* Transaction context for current request (NULL if none). */ - struct transaction *transaction; - -@@ -191,7 +207,8 @@ unsigned int get_string(const struct buffered_data *data, unsigned int offset); - - void send_reply(struct connection *conn, enum xsd_sockmsg_type type, - const void *data, unsigned int len); --void send_event(struct connection *conn, const char *path, const char *token); -+void send_event(struct buffered_data *req, struct connection *conn, -+ const char *path, const char *token); - - /* Some routines (write, mkdir, etc) just need a non-error return */ - void send_ack(struct connection *conn, enum xsd_sockmsg_type type); -@@ -247,6 +264,7 @@ extern int dom0_domid; - extern int dom0_event; - extern int priv_domid; - extern int quota_nb_entry_per_domain; -+extern int quota_req_outstanding; - - extern unsigned int timeout_watch_event_msec; - -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index 93c4c1edcdd1..850085a92c76 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -78,6 +78,9 @@ struct domain - /* number of watch for this domain */ - int nbwatch; - -+ /* Number of outstanding requests. */ -+ int nboutstanding; -+ - /* write rate limit */ - wrl_creditt wrl_credit; /* [ -wrl_config_writecost, +_dburst ] */ - struct wrl_timestampt wrl_timestamp; -@@ -183,8 +186,12 @@ static bool domain_can_read(struct connection *conn) - { - struct xenstore_domain_interface *intf = conn->domain->interface; - -- if (domain_is_unprivileged(conn) && conn->domain->wrl_credit < 0) -- return false; -+ if (domain_is_unprivileged(conn)) { -+ if (conn->domain->wrl_credit < 0) -+ return false; -+ if (conn->domain->nboutstanding >= quota_req_outstanding) -+ return false; -+ } - - return (intf->req_cons != intf->req_prod); - } -@@ -331,7 +338,7 @@ static struct domain *alloc_domain(const void *context, unsigned int domid) - { - struct domain *domain; - -- domain = talloc(context, struct domain); -+ domain = talloc_zero(context, struct domain); - if (!domain) { - errno = ENOMEM; - return NULL; -@@ -392,9 +399,6 @@ static int new_domain(struct domain *domain, int port, bool restore) - domain->conn->domain = domain; - domain->conn->id = domain->domid; - -- domain->nbentry = 0; -- domain->nbwatch = 0; -- - return 0; - } - -@@ -938,6 +942,28 @@ int domain_watch(struct connection *conn) - : 0; - } - -+void domain_outstanding_inc(struct connection *conn) -+{ -+ if (!conn || !conn->domain) -+ return; -+ conn->domain->nboutstanding++; -+} -+ -+void domain_outstanding_dec(struct connection *conn) -+{ -+ if (!conn || !conn->domain) -+ return; -+ conn->domain->nboutstanding--; -+} -+ -+void domain_outstanding_domid_dec(unsigned int domid) -+{ -+ struct domain *d = find_domain_by_domid(domid); -+ -+ if (d) -+ d->nboutstanding--; -+} -+ - static wrl_creditt wrl_config_writecost = WRL_FACTOR; - static wrl_creditt wrl_config_rate = WRL_RATE * WRL_FACTOR; - static wrl_creditt wrl_config_dburst = WRL_DBURST * WRL_FACTOR; -diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h -index 1e929b8f8c6f..4f51b005291a 100644 ---- a/tools/xenstore/xenstored_domain.h -+++ b/tools/xenstore/xenstored_domain.h -@@ -64,6 +64,9 @@ int domain_entry(struct connection *conn); - void domain_watch_inc(struct connection *conn); - void domain_watch_dec(struct connection *conn); - int domain_watch(struct connection *conn); -+void domain_outstanding_inc(struct connection *conn); -+void domain_outstanding_dec(struct connection *conn); -+void domain_outstanding_domid_dec(unsigned int domid); - - /* Special node permission handling. */ - int set_perms_special(struct connection *conn, const char *name, -diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c -index 205d9d8ea116..0755ffa375ba 100644 ---- a/tools/xenstore/xenstored_watch.c -+++ b/tools/xenstore/xenstored_watch.c -@@ -142,6 +142,7 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, - struct node *node, bool exact, struct node_perms *perms) - { - struct connection *i; -+ struct buffered_data *req; - struct watch *watch; - - /* During transactions, don't fire watches, but queue them. */ -@@ -150,6 +151,8 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, - return; - } - -+ req = domain_is_unprivileged(conn) ? conn->in : NULL; -+ - /* Create an event for each watch. */ - list_for_each_entry(i, &connections, list) { - /* introduce/release domain watches */ -@@ -164,12 +167,12 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, - list_for_each_entry(watch, &i->watches, list) { - if (exact) { - if (streq(name, watch->node)) -- send_event(i, -+ send_event(req, i, - get_watch_path(watch, name), - watch->token); - } else { - if (is_child(name, watch->node)) -- send_event(i, -+ send_event(req, i, - get_watch_path(watch, name), - watch->token); - } -@@ -269,8 +272,12 @@ int do_watch(struct connection *conn, struct buffered_data *in) - trace_create(watch, "watch"); - send_ack(conn, XS_WATCH); - -- /* We fire once up front: simplifies clients and restart. */ -- send_event(conn, get_watch_path(watch, watch->node), watch->token); -+ /* -+ * We fire once up front: simplifies clients and restart. -+ * This event will not be linked to the XS_WATCH request. -+ */ -+ send_event(NULL, conn, get_watch_path(watch, watch->node), -+ watch->token); - - return 0; - } diff --git a/xsa326-4.16-xenstored-06.patch b/xsa326-4.16-xenstored-06.patch deleted file mode 100644 index b2be7ce..0000000 --- a/xsa326-4.16-xenstored-06.patch +++ /dev/null @@ -1,85 +0,0 @@ -From c8057cb483abf2cd4060b39616423e19283fbd0a Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:08 +0200 -Subject: tools/xenstore: don't buffer multiple identical watch events - -A guest not reading its Xenstore response buffer fast enough might -pile up lots of Xenstore watch events buffered. Reduce the generated -load by dropping new events which already have an identical copy -pending. - -The special events "@..." are excluded from that handling as there are -known use cases where the handler is relying on each event to be sent -individually. - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 488d540f3a32..f1fa97b8cf50 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -916,6 +916,7 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, - bdata->inhdr = true; - bdata->used = 0; - bdata->timeout_msec = 0; -+ bdata->watch_event = false; - - if (len <= DEFAULT_BUFFER_SIZE) - bdata->buffer = bdata->default_buffer; -@@ -948,7 +949,7 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, - void send_event(struct buffered_data *req, struct connection *conn, - const char *path, const char *token) - { -- struct buffered_data *bdata; -+ struct buffered_data *bdata, *bd; - unsigned int len; - - len = strlen(path) + 1 + strlen(token) + 1; -@@ -970,12 +971,29 @@ void send_event(struct buffered_data *req, struct connection *conn, - bdata->hdr.msg.type = XS_WATCH_EVENT; - bdata->hdr.msg.len = len; - -+ /* -+ * Check whether an identical event is pending already. -+ * Special events are excluded from that check. -+ */ -+ if (path[0] != '@') { -+ list_for_each_entry(bd, &conn->out_list, list) { -+ if (bd->watch_event && bd->hdr.msg.len == len && -+ !memcmp(bdata->buffer, bd->buffer, len)) { -+ trace("dropping duplicate watch %s %s for domain %u\n", -+ path, token, conn->id); -+ talloc_free(bdata); -+ return; -+ } -+ } -+ } -+ - if (timeout_watch_event_msec && domain_is_unprivileged(conn)) { - bdata->timeout_msec = get_now_msec() + timeout_watch_event_msec; - if (!conn->timeout_msec) - conn->timeout_msec = bdata->timeout_msec; - } - -+ bdata->watch_event = true; - bdata->pend.req = req; - if (req) - req->pend.ref.event_cnt++; -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index db09f463a657..b9b50e81c7b4 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -62,6 +62,9 @@ struct buffered_data - /* Are we still doing the header? */ - bool inhdr; - -+ /* Is this a watch event? */ -+ bool watch_event; -+ - /* How far are we? */ - unsigned int used; - diff --git a/xsa326-4.16-xenstored-07.patch b/xsa326-4.16-xenstored-07.patch deleted file mode 100644 index dfcdc14..0000000 --- a/xsa326-4.16-xenstored-07.patch +++ /dev/null @@ -1,53 +0,0 @@ -From 5eac692b841633be3e85f0125c59fa02af103989 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:08 +0200 -Subject: tools/xenstore: fix connection->id usage - -Don't use conn->id for privilege checks, but domain_is_unprivileged(). - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_control.c b/tools/xenstore/xenstored_control.c -index 7b4300ef7777..adb8d51b043b 100644 ---- a/tools/xenstore/xenstored_control.c -+++ b/tools/xenstore/xenstored_control.c -@@ -891,7 +891,7 @@ int do_control(struct connection *conn, struct buffered_data *in) - unsigned int cmd, num, off; - char **vec = NULL; - -- if (conn->id != 0) -+ if (domain_is_unprivileged(conn)) - return EACCES; - - off = get_string(in, 0); -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index b9b50e81c7b4..b1a70488b989 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -123,7 +123,7 @@ struct connection - /* The index of pollfd in global pollfd array */ - int pollfd_idx; - -- /* Who am I? 0 for socket connections. */ -+ /* Who am I? Domid of connection. */ - unsigned int id; - - /* Is this connection ignored? */ -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index 54432907fc76..ee1b09031a3b 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -477,7 +477,8 @@ int do_transaction_start(struct connection *conn, struct buffered_data *in) - if (conn->transaction) - return EBUSY; - -- if (conn->id && conn->transaction_started > quota_max_transaction) -+ if (domain_is_unprivileged(conn) && -+ conn->transaction_started > quota_max_transaction) - return ENOSPC; - - /* Attach transaction to input for autofree until it's complete */ diff --git a/xsa326-4.16-xenstored-08.patch b/xsa326-4.16-xenstored-08.patch deleted file mode 100644 index 5aa12c8..0000000 --- a/xsa326-4.16-xenstored-08.patch +++ /dev/null @@ -1,326 +0,0 @@ -From f9f3171441b5fcb3339cf612400794fc26cd2ec2 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:08 +0200 -Subject: tools/xenstore: simplify and fix per domain node accounting - -The accounting of nodes can be simplified now that each connection -holds the associated domid. - -Fix the node accounting to cover nodes created for a domain before it -has been introduced. This requires to react properly to an allocation -failure inside domain_entry_inc() by returning an error code. - -Especially in error paths the node accounting has to be fixed in some -cases. - -This is part of XSA-326 / CVE-2022-42313. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index f1fa97b8cf50..692d863fce35 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -638,7 +638,7 @@ struct node *read_node(struct connection *conn, const void *ctx, - - /* Permissions are struct xs_permissions. */ - node->perms.p = hdr->perms; -- if (domain_adjust_node_perms(node)) { -+ if (domain_adjust_node_perms(conn, node)) { - talloc_free(node); - return NULL; - } -@@ -660,7 +660,7 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, - void *p; - struct xs_tdb_record_hdr *hdr; - -- if (domain_adjust_node_perms(node)) -+ if (domain_adjust_node_perms(conn, node)) - return errno; - - data.dsize = sizeof(*hdr) -@@ -1272,13 +1272,17 @@ static struct node *construct_node(struct connection *conn, const void *ctx, - return NULL; - } - --static int destroy_node(struct connection *conn, struct node *node) -+static void destroy_node_rm(struct node *node) - { - if (streq(node->name, "/")) - corrupt(NULL, "Destroying root node!"); - - tdb_delete(tdb_ctx, node->key); -+} - -+static int destroy_node(struct connection *conn, struct node *node) -+{ -+ destroy_node_rm(node); - domain_entry_dec(conn, node); - - /* -@@ -1328,8 +1332,12 @@ static struct node *create_node(struct connection *conn, const void *ctx, - goto err; - - /* Account for new node */ -- if (i->parent) -- domain_entry_inc(conn, i); -+ if (i->parent) { -+ if (domain_entry_inc(conn, i)) { -+ destroy_node_rm(i); -+ return NULL; -+ } -+ } - } - - return node; -@@ -1614,10 +1622,27 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) - old_perms = node->perms; - domain_entry_dec(conn, node); - node->perms = perms; -- domain_entry_inc(conn, node); -+ if (domain_entry_inc(conn, node)) { -+ node->perms = old_perms; -+ /* -+ * This should never fail because we had a reference on the -+ * domain before and Xenstored is single-threaded. -+ */ -+ domain_entry_inc(conn, node); -+ return ENOMEM; -+ } -+ -+ if (write_node(conn, node, false)) { -+ int saved_errno = errno; - -- if (write_node(conn, node, false)) -+ domain_entry_dec(conn, node); -+ node->perms = old_perms; -+ /* No failure possible as above. */ -+ domain_entry_inc(conn, node); -+ -+ errno = saved_errno; - return errno; -+ } - - fire_watches(conn, in, name, node, false, &old_perms); - send_ack(conn, XS_SET_PERMS); -@@ -3122,7 +3147,9 @@ void read_state_node(const void *ctx, const void *state) - set_tdb_key(name, &key); - if (write_node_raw(NULL, &key, node, true)) - barf("write node error restoring node"); -- domain_entry_inc(&conn, node); -+ -+ if (domain_entry_inc(&conn, node)) -+ barf("node accounting error restoring node"); - - talloc_free(node); - } -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index 850085a92c76..260952e09096 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -16,6 +16,7 @@ - along with this program; If not, see . - */ - -+#include - #include - #include - #include -@@ -363,6 +364,18 @@ static struct domain *find_or_alloc_domain(const void *ctx, unsigned int domid) - return domain ? : alloc_domain(ctx, domid); - } - -+static struct domain *find_or_alloc_existing_domain(unsigned int domid) -+{ -+ struct domain *domain; -+ xc_dominfo_t dominfo; -+ -+ domain = find_domain_struct(domid); -+ if (!domain && get_domain_info(domid, &dominfo)) -+ domain = alloc_domain(NULL, domid); -+ -+ return domain; -+} -+ - static int new_domain(struct domain *domain, int port, bool restore) - { - int rc; -@@ -782,30 +795,28 @@ void domain_deinit(void) - xenevtchn_unbind(xce_handle, virq_port); - } - --void domain_entry_inc(struct connection *conn, struct node *node) -+int domain_entry_inc(struct connection *conn, struct node *node) - { - struct domain *d; -+ unsigned int domid; - - if (!conn) -- return; -+ return 0; - -- if (node->perms.p && node->perms.p[0].id != conn->id) { -- if (conn->transaction) { -- transaction_entry_inc(conn->transaction, -- node->perms.p[0].id); -- } else { -- d = find_domain_by_domid(node->perms.p[0].id); -- if (d) -- d->nbentry++; -- } -- } else if (conn->domain) { -- if (conn->transaction) { -- transaction_entry_inc(conn->transaction, -- conn->domain->domid); -- } else { -- conn->domain->nbentry++; -- } -+ domid = node->perms.p ? node->perms.p[0].id : conn->id; -+ -+ if (conn->transaction) { -+ transaction_entry_inc(conn->transaction, domid); -+ } else { -+ d = (domid == conn->id && conn->domain) ? conn->domain -+ : find_or_alloc_existing_domain(domid); -+ if (d) -+ d->nbentry++; -+ else -+ return ENOMEM; - } -+ -+ return 0; - } - - /* -@@ -841,7 +852,7 @@ static int chk_domain_generation(unsigned int domid, uint64_t gen) - * Remove permissions for no longer existing domains in order to avoid a new - * domain with the same domid inheriting the permissions. - */ --int domain_adjust_node_perms(struct node *node) -+int domain_adjust_node_perms(struct connection *conn, struct node *node) - { - unsigned int i; - int ret; -@@ -851,8 +862,14 @@ int domain_adjust_node_perms(struct node *node) - return errno; - - /* If the owner doesn't exist any longer give it to priv domain. */ -- if (!ret) -+ if (!ret) { -+ /* -+ * In theory we'd need to update the number of dom0 nodes here, -+ * but we could be called for a read of the node. So better -+ * avoid the risk to overflow the node count of dom0. -+ */ - node->perms.p[0].id = priv_domid; -+ } - - for (i = 1; i < node->perms.num; i++) { - if (node->perms.p[i].perms & XS_PERM_IGNORE) -@@ -871,25 +888,25 @@ int domain_adjust_node_perms(struct node *node) - void domain_entry_dec(struct connection *conn, struct node *node) - { - struct domain *d; -+ unsigned int domid; - - if (!conn) - return; - -- if (node->perms.p && node->perms.p[0].id != conn->id) { -- if (conn->transaction) { -- transaction_entry_dec(conn->transaction, -- node->perms.p[0].id); -- } else { -- d = find_domain_by_domid(node->perms.p[0].id); -- if (d && d->nbentry) -- d->nbentry--; -- } -- } else if (conn->domain && conn->domain->nbentry) { -- if (conn->transaction) { -- transaction_entry_dec(conn->transaction, -- conn->domain->domid); -+ domid = node->perms.p ? node->perms.p[0].id : conn->id; -+ -+ if (conn->transaction) { -+ transaction_entry_dec(conn->transaction, domid); -+ } else { -+ d = (domid == conn->id && conn->domain) ? conn->domain -+ : find_domain_struct(domid); -+ if (d) { -+ d->nbentry--; - } else { -- conn->domain->nbentry--; -+ errno = ENOENT; -+ corrupt(conn, -+ "Node \"%s\" owned by non-existing domain %u\n", -+ node->name, domid); - } - } - } -@@ -899,13 +916,23 @@ int domain_entry_fix(unsigned int domid, int num, bool update) - struct domain *d; - int cnt; - -- d = find_domain_by_domid(domid); -- if (!d) -- return 0; -+ if (update) { -+ d = find_domain_struct(domid); -+ assert(d); -+ } else { -+ /* -+ * We are called first with update == false in order to catch -+ * any error. So do a possible allocation and check for error -+ * only in this case, as in the case of update == true nothing -+ * can go wrong anymore as the allocation already happened. -+ */ -+ d = find_or_alloc_existing_domain(domid); -+ if (!d) -+ return -1; -+ } - - cnt = d->nbentry + num; -- if (cnt < 0) -- cnt = 0; -+ assert(cnt >= 0); - - if (update) - d->nbentry = cnt; -diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h -index 4f51b005291a..d6519904d831 100644 ---- a/tools/xenstore/xenstored_domain.h -+++ b/tools/xenstore/xenstored_domain.h -@@ -54,10 +54,10 @@ const char *get_implicit_path(const struct connection *conn); - bool domain_is_unprivileged(struct connection *conn); - - /* Remove node permissions for no longer existing domains. */ --int domain_adjust_node_perms(struct node *node); -+int domain_adjust_node_perms(struct connection *conn, struct node *node); - - /* Quota manipulation */ --void domain_entry_inc(struct connection *conn, struct node *); -+int domain_entry_inc(struct connection *conn, struct node *); - void domain_entry_dec(struct connection *conn, struct node *); - int domain_entry_fix(unsigned int domid, int num, bool update); - int domain_entry(struct connection *conn); -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index ee1b09031a3b..86caf6c398be 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -519,8 +519,12 @@ static int transaction_fix_domains(struct transaction *trans, bool update) - - list_for_each_entry(d, &trans->changed_domains, list) { - cnt = domain_entry_fix(d->domid, d->nbentry, update); -- if (!update && cnt >= quota_nb_entry_per_domain) -- return ENOSPC; -+ if (!update) { -+ if (cnt >= quota_nb_entry_per_domain) -+ return ENOSPC; -+ if (cnt < 0) -+ return ENOMEM; -+ } - } - - return 0; diff --git a/xsa326-4.16-xenstored-09.patch b/xsa326-4.16-xenstored-09.patch deleted file mode 100644 index e2c163c..0000000 --- a/xsa326-4.16-xenstored-09.patch +++ /dev/null @@ -1,244 +0,0 @@ -From 71aac6f7e89d5c101adb9e82eea7031e16d34e46 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:09 +0200 -Subject: tools/xenstore: limit max number of nodes accessed in a transaction - -Today a guest is free to access as many nodes in a single transaction -as it wants. This can lead to unbounded memory consumption in Xenstore -as there is the need to keep track of all nodes having been accessed -during a transaction. - -In oxenstored the number of requests in a transaction is being limited -via a quota maxrequests (default is 1024). As multiple accesses of a -node are not problematic in C Xenstore, limit the number of accessed -nodes. - -In order to let read_node() detect a quota error in case too many nodes -are being accessed, check the return value of access_node() and return -NULL in case an error has been seen. Introduce __must_check and add it -to the access_node() prototype. - -This is part of XSA-326 / CVE-2022-42314. - -Reported-by: Julien Grall -Suggested-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/include/xen-tools/libs.h b/tools/include/xen-tools/libs.h -index a16e0c380709..bafc90e2f603 100644 ---- a/tools/include/xen-tools/libs.h -+++ b/tools/include/xen-tools/libs.h -@@ -63,4 +63,8 @@ - #define ROUNDUP(_x,_w) (((unsigned long)(_x)+(1UL<<(_w))-1) & ~((1UL<<(_w))-1)) - #endif - -+#ifndef __must_check -+#define __must_check __attribute__((__warn_unused_result__)) -+#endif -+ - #endif /* __XEN_TOOLS_LIBS__ */ -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 692d863fce35..f835aa1b2f1f 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -106,6 +106,7 @@ int quota_nb_watch_per_domain = 128; - int quota_max_entry_size = 2048; /* 2K */ - int quota_max_transaction = 10; - int quota_nb_perms_per_node = 5; -+int quota_trans_nodes = 1024; - int quota_max_path_len = XENSTORE_REL_PATH_MAX; - int quota_req_outstanding = 20; - -@@ -595,6 +596,7 @@ struct node *read_node(struct connection *conn, const void *ctx, - TDB_DATA key, data; - struct xs_tdb_record_hdr *hdr; - struct node *node; -+ int err; - - node = talloc(ctx, struct node); - if (!node) { -@@ -616,14 +618,13 @@ struct node *read_node(struct connection *conn, const void *ctx, - if (data.dptr == NULL) { - if (tdb_error(tdb_ctx) == TDB_ERR_NOEXIST) { - node->generation = NO_GENERATION; -- access_node(conn, node, NODE_ACCESS_READ, NULL); -- errno = ENOENT; -+ err = access_node(conn, node, NODE_ACCESS_READ, NULL); -+ errno = err ? : ENOENT; - } else { - log("TDB error on read: %s", tdb_errorstr(tdb_ctx)); - errno = EIO; - } -- talloc_free(node); -- return NULL; -+ goto error; - } - - node->parent = NULL; -@@ -638,19 +639,36 @@ struct node *read_node(struct connection *conn, const void *ctx, - - /* Permissions are struct xs_permissions. */ - node->perms.p = hdr->perms; -- if (domain_adjust_node_perms(conn, node)) { -- talloc_free(node); -- return NULL; -- } -+ if (domain_adjust_node_perms(conn, node)) -+ goto error; - - /* Data is binary blob (usually ascii, no nul). */ - node->data = node->perms.p + hdr->num_perms; - /* Children is strings, nul separated. */ - node->children = node->data + node->datalen; - -- access_node(conn, node, NODE_ACCESS_READ, NULL); -+ if (access_node(conn, node, NODE_ACCESS_READ, NULL)) -+ goto error; - - return node; -+ -+ error: -+ err = errno; -+ talloc_free(node); -+ errno = err; -+ return NULL; -+} -+ -+static bool read_node_can_propagate_errno(void) -+{ -+ /* -+ * 2 error cases for read_node() can always be propagated up: -+ * ENOMEM, because this has nothing to do with the node being in the -+ * data base or not, but is caused by a general lack of memory. -+ * ENOSPC, because this is related to hitting quota limits which need -+ * to be respected. -+ */ -+ return errno == ENOMEM || errno == ENOSPC; - } - - int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, -@@ -767,7 +785,7 @@ static int ask_parents(struct connection *conn, const void *ctx, - node = read_node(conn, ctx, name); - if (node) - break; -- if (errno == ENOMEM) -+ if (read_node_can_propagate_errno()) - return errno; - } while (!streq(name, "/")); - -@@ -829,7 +847,7 @@ static struct node *get_node(struct connection *conn, - } - } - /* Clean up errno if they weren't supposed to know. */ -- if (!node && errno != ENOMEM) -+ if (!node && !read_node_can_propagate_errno()) - errno = errno_from_parents(conn, ctx, name, errno, perm); - return node; - } -@@ -1235,7 +1253,7 @@ static struct node *construct_node(struct connection *conn, const void *ctx, - - /* If parent doesn't exist, create it. */ - parent = read_node(conn, parentname, parentname); -- if (!parent) -+ if (!parent && errno == ENOENT) - parent = construct_node(conn, ctx, parentname); - if (!parent) - return NULL; -@@ -1509,7 +1527,7 @@ static int _rm(struct connection *conn, const void *ctx, struct node *node, - - parent = read_node(conn, ctx, parentname); - if (!parent) -- return (errno == ENOMEM) ? ENOMEM : EINVAL; -+ return read_node_can_propagate_errno() ? errno : EINVAL; - node->parent = parent; - - return delete_node(conn, ctx, parent, node, false); -@@ -1539,7 +1557,7 @@ static int do_rm(struct connection *conn, struct buffered_data *in) - return 0; - } - /* Restore errno, just in case. */ -- if (errno != ENOMEM) -+ if (!read_node_can_propagate_errno()) - errno = ENOENT; - } - return errno; -@@ -2384,6 +2402,8 @@ static void usage(void) - " -M, --path-max limit the allowed Xenstore node path length,\n" - " -Q, --quota = set the quota to the value , allowed\n" - " quotas are:\n" -+" transaction-nodes: number of accessed node per\n" -+" transaction\n" - " outstanding: number of outstanding requests\n" - " -w, --timeout = set the timeout in seconds for ,\n" - " allowed timeout candidates are:\n" -@@ -2468,6 +2488,8 @@ static void set_quota(const char *arg) - val = get_optval_int(eq + 1); - if (what_matches(arg, "outstanding")) - quota_req_outstanding = val; -+ else if (what_matches(arg, "transaction-nodes")) -+ quota_trans_nodes = val; - else - barf("unknown quota \"%s\"\n", arg); - } -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index b1a70488b989..245f9258235f 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -268,6 +268,7 @@ extern int dom0_event; - extern int priv_domid; - extern int quota_nb_entry_per_domain; - extern int quota_req_outstanding; -+extern int quota_trans_nodes; - - extern unsigned int timeout_watch_event_msec; - -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index 86caf6c398be..7bd41eb475e3 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -156,6 +156,9 @@ struct transaction - /* Connection-local identifier for this transaction. */ - uint32_t id; - -+ /* Node counter. */ -+ unsigned int nodes; -+ - /* Generation when transaction started. */ - uint64_t generation; - -@@ -260,6 +263,11 @@ int access_node(struct connection *conn, struct node *node, - - i = find_accessed_node(trans, node->name); - if (!i) { -+ if (trans->nodes >= quota_trans_nodes && -+ domain_is_unprivileged(conn)) { -+ ret = ENOSPC; -+ goto err; -+ } - i = talloc_zero(trans, struct accessed_node); - if (!i) - goto nomem; -@@ -297,6 +305,7 @@ int access_node(struct connection *conn, struct node *node, - i->ta_node = true; - } - } -+ trans->nodes++; - list_add_tail(&i->list, &trans->accessed); - } - -diff --git a/tools/xenstore/xenstored_transaction.h b/tools/xenstore/xenstored_transaction.h -index 0093cac807e3..e3cbd6b23095 100644 ---- a/tools/xenstore/xenstored_transaction.h -+++ b/tools/xenstore/xenstored_transaction.h -@@ -39,8 +39,8 @@ void transaction_entry_inc(struct transaction *trans, unsigned int domid); - void transaction_entry_dec(struct transaction *trans, unsigned int domid); - - /* This node was accessed. */ --int access_node(struct connection *conn, struct node *node, -- enum node_access_type type, TDB_DATA *key); -+int __must_check access_node(struct connection *conn, struct node *node, -+ enum node_access_type type, TDB_DATA *key); - - /* Queue watches for a modified node. */ - void queue_watches(struct connection *conn, const char *name, bool watch_exact); diff --git a/xsa326-4.16-xenstored-10.patch b/xsa326-4.16-xenstored-10.patch deleted file mode 100644 index 30e6b50..0000000 --- a/xsa326-4.16-xenstored-10.patch +++ /dev/null @@ -1,88 +0,0 @@ -From 90013d6a735491a7b93a6832eb2a51e5633254f5 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:09 +0200 -Subject: tools/xenstore: move the call of setup_structure() to dom0 - introduction - -Setting up the basic structure when introducing dom0 has the advantage -to be able to add proper node memory accounting for the added nodes -later. - -This makes it possible to do proper node accounting, too. - -An additional requirement to make that work fine is to correct the -owner of the created nodes to be dom0_domid instead of domid 0. - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Acked-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index f835aa1b2f1f..5171d34c947e 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -2039,7 +2039,8 @@ static int tdb_flags; - static void manual_node(const char *name, const char *child) - { - struct node *node; -- struct xs_permissions perms = { .id = 0, .perms = XS_PERM_NONE }; -+ struct xs_permissions perms = { .id = dom0_domid, -+ .perms = XS_PERM_NONE }; - - node = talloc_zero(NULL, struct node); - if (!node) -@@ -2078,7 +2079,7 @@ static void tdb_logger(TDB_CONTEXT *tdb, int level, const char * fmt, ...) - } - } - --static void setup_structure(bool live_update) -+void setup_structure(bool live_update) - { - char *tdbname; - -@@ -2101,6 +2102,7 @@ static void setup_structure(bool live_update) - manual_node("/", "tool"); - manual_node("/tool", "xenstored"); - manual_node("/tool/xenstored", NULL); -+ domain_entry_fix(dom0_domid, 3, true); - } - - check_store(); -@@ -2614,9 +2616,6 @@ int main(int argc, char *argv[]) - - init_pipe(reopen_log_pipe); - -- /* Setup the database */ -- setup_structure(live_update); -- - /* Listen to hypervisor. */ - if (!no_domain_init && !live_update) { - domain_init(-1); -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index 245f9258235f..2c77ec7ee0f4 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -231,6 +231,7 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, - struct node *read_node(struct connection *conn, const void *ctx, - const char *name); - -+void setup_structure(bool live_update); - struct connection *new_connection(const struct interface_funcs *funcs); - struct connection *get_connection_by_id(unsigned int conn_id); - void ignore_connection(struct connection *conn); -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index 260952e09096..f04b7aae8a32 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -470,6 +470,9 @@ static struct domain *introduce_domain(const void *ctx, - } - domain->interface = interface; - -+ if (is_master_domain) -+ setup_structure(restore); -+ - /* Now domain belongs to its connection. */ - talloc_steal(domain->conn, domain); - diff --git a/xsa326-4.16-xenstored-11.patch b/xsa326-4.16-xenstored-11.patch deleted file mode 100644 index 49f1497..0000000 --- a/xsa326-4.16-xenstored-11.patch +++ /dev/null @@ -1,280 +0,0 @@ -From 6af17b8bf52b9dfdc6a5ecd3efbcea9fddd57d91 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:09 +0200 -Subject: tools/xenstore: add infrastructure to keep track of per domain memory - usage - -The amount of memory a domain can consume in Xenstore is limited by -various quota today, but even with sane quota a domain can still -consume rather large memory quantities. - -Add the infrastructure for keeping track of the amount of memory a -domain is consuming in Xenstore. Note that this is only the memory a -domain has direct control over, so any internal administration data -needed by Xenstore only is not being accounted for. - -There are two quotas defined: a soft quota which will result in a -warning issued via syslog() when it is exceeded, and a hard quota -resulting in a stop of accepting further requests or watch events as -long as the hard quota would be violated by accepting those. - -Setting any of those quotas to 0 will disable it. - -As default values use 2MB per domain for the soft limit (this basically -covers the allowed case to create 1000 nodes needing 2kB each), and -2.5MB for the hard limit. - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 5171d34c947e..b2bf6740d430 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -109,6 +109,8 @@ int quota_nb_perms_per_node = 5; - int quota_trans_nodes = 1024; - int quota_max_path_len = XENSTORE_REL_PATH_MAX; - int quota_req_outstanding = 20; -+int quota_memory_per_domain_soft = 2 * 1024 * 1024; /* 2 MB */ -+int quota_memory_per_domain_hard = 2 * 1024 * 1024 + 512 * 1024; /* 2.5 MB */ - - unsigned int timeout_watch_event_msec = 20000; - -@@ -2406,7 +2408,14 @@ static void usage(void) - " quotas are:\n" - " transaction-nodes: number of accessed node per\n" - " transaction\n" -+" memory: total used memory per domain for nodes,\n" -+" transactions, watches and requests, above\n" -+" which Xenstore will stop talking to domain\n" - " outstanding: number of outstanding requests\n" -+" -q, --quota-soft = set a soft quota to the value ,\n" -+" causing a warning to be issued via syslog() if the\n" -+" limit is violated, allowed quotas are:\n" -+" memory: see above\n" - " -w, --timeout = set the timeout in seconds for ,\n" - " allowed timeout candidates are:\n" - " watch-event: time a watch-event is kept pending\n" -@@ -2433,6 +2442,7 @@ static struct option options[] = { - { "perm-nb", 1, NULL, 'A' }, - { "path-max", 1, NULL, 'M' }, - { "quota", 1, NULL, 'Q' }, -+ { "quota-soft", 1, NULL, 'q' }, - { "timeout", 1, NULL, 'w' }, - { "no-recovery", 0, NULL, 'R' }, - { "internal-db", 0, NULL, 'I' }, -@@ -2480,7 +2490,7 @@ static void set_timeout(const char *arg) - barf("unknown timeout \"%s\"\n", arg); - } - --static void set_quota(const char *arg) -+static void set_quota(const char *arg, bool soft) - { - const char *eq = strchr(arg, '='); - int val; -@@ -2488,11 +2498,16 @@ static void set_quota(const char *arg) - if (!eq) - barf("quotas must be specified via =\n"); - val = get_optval_int(eq + 1); -- if (what_matches(arg, "outstanding")) -+ if (what_matches(arg, "outstanding") && !soft) - quota_req_outstanding = val; -- else if (what_matches(arg, "transaction-nodes")) -+ else if (what_matches(arg, "transaction-nodes") && !soft) - quota_trans_nodes = val; -- else -+ else if (what_matches(arg, "memory")) { -+ if (soft) -+ quota_memory_per_domain_soft = val; -+ else -+ quota_memory_per_domain_hard = val; -+ } else - barf("unknown quota \"%s\"\n", arg); - } - -@@ -2510,7 +2525,7 @@ int main(int argc, char *argv[]) - orig_argc = argc; - orig_argv = argv; - -- while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:M:Q:T:RVW:w:U", -+ while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:M:Q:q:T:RVW:w:U", - options, NULL)) != -1) { - switch (opt) { - case 'D': -@@ -2561,7 +2576,10 @@ int main(int argc, char *argv[]) - quota_max_path_len); - break; - case 'Q': -- set_quota(optarg); -+ set_quota(optarg, false); -+ break; -+ case 'q': -+ set_quota(optarg, true); - break; - case 'w': - set_timeout(optarg); -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index 2c77ec7ee0f4..373af18297bf 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -270,6 +270,8 @@ extern int priv_domid; - extern int quota_nb_entry_per_domain; - extern int quota_req_outstanding; - extern int quota_trans_nodes; -+extern int quota_memory_per_domain_soft; -+extern int quota_memory_per_domain_hard; - - extern unsigned int timeout_watch_event_msec; - -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index f04b7aae8a32..94fd561e9de4 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -76,6 +76,13 @@ struct domain - /* number of entry from this domain in the store */ - int nbentry; - -+ /* Amount of memory allocated for this domain. */ -+ int memory; -+ bool soft_quota_reported; -+ bool hard_quota_reported; -+ time_t mem_last_msg; -+#define MEM_WARN_MINTIME_SEC 10 -+ - /* number of watch for this domain */ - int nbwatch; - -@@ -192,6 +199,9 @@ static bool domain_can_read(struct connection *conn) - return false; - if (conn->domain->nboutstanding >= quota_req_outstanding) - return false; -+ if (conn->domain->memory >= quota_memory_per_domain_hard && -+ quota_memory_per_domain_hard) -+ return false; - } - - return (intf->req_cons != intf->req_prod); -@@ -950,6 +960,89 @@ int domain_entry(struct connection *conn) - : 0; - } - -+static bool domain_chk_quota(struct domain *domain, int mem) -+{ -+ time_t now; -+ -+ if (!domain || !domid_is_unprivileged(domain->domid) || -+ (domain->conn && domain->conn->is_ignored)) -+ return false; -+ -+ now = time(NULL); -+ -+ if (mem >= quota_memory_per_domain_hard && -+ quota_memory_per_domain_hard) { -+ if (domain->hard_quota_reported) -+ return true; -+ syslog(LOG_ERR, "Domain %u exceeds hard memory quota, Xenstore interface to domain stalled\n", -+ domain->domid); -+ domain->mem_last_msg = now; -+ domain->hard_quota_reported = true; -+ return true; -+ } -+ -+ if (now - domain->mem_last_msg >= MEM_WARN_MINTIME_SEC) { -+ if (domain->hard_quota_reported) { -+ domain->mem_last_msg = now; -+ domain->hard_quota_reported = false; -+ syslog(LOG_INFO, "Domain %u below hard memory quota again\n", -+ domain->domid); -+ } -+ if (mem >= quota_memory_per_domain_soft && -+ quota_memory_per_domain_soft && -+ !domain->soft_quota_reported) { -+ domain->mem_last_msg = now; -+ domain->soft_quota_reported = true; -+ syslog(LOG_WARNING, "Domain %u exceeds soft memory quota\n", -+ domain->domid); -+ } -+ if (mem < quota_memory_per_domain_soft && -+ domain->soft_quota_reported) { -+ domain->mem_last_msg = now; -+ domain->soft_quota_reported = false; -+ syslog(LOG_INFO, "Domain %u below soft memory quota again\n", -+ domain->domid); -+ } -+ -+ } -+ -+ return false; -+} -+ -+int domain_memory_add(unsigned int domid, int mem, bool no_quota_check) -+{ -+ struct domain *domain; -+ -+ domain = find_domain_struct(domid); -+ if (domain) { -+ /* -+ * domain_chk_quota() will print warning and also store whether -+ * the soft/hard quota has been hit. So check no_quota_check -+ * *after*. -+ */ -+ if (domain_chk_quota(domain, domain->memory + mem) && -+ !no_quota_check) -+ return ENOMEM; -+ domain->memory += mem; -+ } else { -+ /* -+ * The domain the memory is to be accounted for should always -+ * exist, as accounting is done either for a domain related to -+ * the current connection, or for the domain owning a node -+ * (which is always existing, as the owner of the node is -+ * tested to exist and replaced by domid 0 if not). -+ * So not finding the related domain MUST be an error in the -+ * data base. -+ */ -+ errno = ENOENT; -+ corrupt(NULL, "Accounting called for non-existing domain %u\n", -+ domid); -+ return ENOENT; -+ } -+ -+ return 0; -+} -+ - void domain_watch_inc(struct connection *conn) - { - if (!conn || !conn->domain) -diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h -index d6519904d831..633c9a0a0a1f 100644 ---- a/tools/xenstore/xenstored_domain.h -+++ b/tools/xenstore/xenstored_domain.h -@@ -61,6 +61,26 @@ int domain_entry_inc(struct connection *conn, struct node *); - void domain_entry_dec(struct connection *conn, struct node *); - int domain_entry_fix(unsigned int domid, int num, bool update); - int domain_entry(struct connection *conn); -+int domain_memory_add(unsigned int domid, int mem, bool no_quota_check); -+ -+/* -+ * domain_memory_add_chk(): to be used when memory quota should be checked. -+ * Not to be used when specifying a negative mem value, as lowering the used -+ * memory should always be allowed. -+ */ -+static inline int domain_memory_add_chk(unsigned int domid, int mem) -+{ -+ return domain_memory_add(domid, mem, false); -+} -+/* -+ * domain_memory_add_nochk(): to be used when memory quota should not be -+ * checked, e.g. when lowering memory usage, or in an error case for undoing -+ * a previous memory adjustment. -+ */ -+static inline void domain_memory_add_nochk(unsigned int domid, int mem) -+{ -+ domain_memory_add(domid, mem, true); -+} - void domain_watch_inc(struct connection *conn); - void domain_watch_dec(struct connection *conn); - int domain_watch(struct connection *conn); diff --git a/xsa326-4.16-xenstored-12.patch b/xsa326-4.16-xenstored-12.patch deleted file mode 100644 index 517f2a0..0000000 --- a/xsa326-4.16-xenstored-12.patch +++ /dev/null @@ -1,76 +0,0 @@ -From ae7042f024af7584251f776a12d9bb24d13fecaf Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:09 +0200 -Subject: tools/xenstore: add memory accounting for responses - -Add the memory accounting for queued responses. - -In case adding a watch event for a guest is causing the hard memory -quota of that guest to be violated, the event is dropped. This will -ensure that it is impossible to drive another guest past its memory -quota by generating insane amounts of events for that guest. This is -especially important for protecting driver domains from that attack -vector. - -This is part of XSA-326 / CVE-2022-42315. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index b2bf6740d430..ecab6cfbbe15 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -260,6 +260,8 @@ static void free_buffered_data(struct buffered_data *out, - } - } - -+ domain_memory_add_nochk(conn->id, -out->hdr.msg.len - sizeof(out->hdr)); -+ - if (out->hdr.msg.type == XS_WATCH_EVENT) { - req = out->pend.req; - if (req) { -@@ -938,11 +940,14 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, - bdata->timeout_msec = 0; - bdata->watch_event = false; - -- if (len <= DEFAULT_BUFFER_SIZE) -+ if (len <= DEFAULT_BUFFER_SIZE) { - bdata->buffer = bdata->default_buffer; -- else { -+ /* Don't check quota, path might be used for returning error. */ -+ domain_memory_add_nochk(conn->id, len + sizeof(bdata->hdr)); -+ } else { - bdata->buffer = talloc_array(bdata, char, len); -- if (!bdata->buffer) { -+ if (!bdata->buffer || -+ domain_memory_add_chk(conn->id, len + sizeof(bdata->hdr))) { - send_error(conn, ENOMEM); - return; - } -@@ -1007,6 +1012,11 @@ void send_event(struct buffered_data *req, struct connection *conn, - } - } - -+ if (domain_memory_add_chk(conn->id, len + sizeof(bdata->hdr))) { -+ talloc_free(bdata); -+ return; -+ } -+ - if (timeout_watch_event_msec && domain_is_unprivileged(conn)) { - bdata->timeout_msec = get_now_msec() + timeout_watch_event_msec; - if (!conn->timeout_msec) -@@ -3039,6 +3049,12 @@ static void add_buffered_data(struct buffered_data *bdata, - */ - if (bdata->hdr.msg.type != XS_WATCH_EVENT) - domain_outstanding_inc(conn); -+ /* -+ * We are restoring the state after Live-Update and the new quota may -+ * be smaller. So ignore it. The limit will be applied for any resource -+ * after the state has been fully restored. -+ */ -+ domain_memory_add_nochk(conn->id, len + sizeof(bdata->hdr)); - } - - void read_state_buffered_data(const void *ctx, struct connection *conn, diff --git a/xsa326-4.16-xenstored-13.patch b/xsa326-4.16-xenstored-13.patch deleted file mode 100644 index c37dd1e..0000000 --- a/xsa326-4.16-xenstored-13.patch +++ /dev/null @@ -1,89 +0,0 @@ -From 4628ae0a56b037dcdc8a3e42c543c5b9fd9990cf Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:10 +0200 -Subject: tools/xenstore: add memory accounting for watches - -Add the memory accounting for registered watches. - -When a socket connection is destroyed, the associated watches are -removed, too. In order to keep memory accounting correct the watches -must be removed explicitly via a call of conn_delete_all_watches() from -destroy_conn(). - -This is part of XSA-326 / CVE-2022-42315. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index ecab6cfbbe15..d86942f5aa77 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -463,6 +463,7 @@ static int destroy_conn(void *_conn) - } - - conn_free_buffered_data(conn); -+ conn_delete_all_watches(conn); - list_for_each_entry(req, &conn->ref_list, list) - req->on_ref_list = false; - -diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c -index 0755ffa375ba..fdf9b2d653a0 100644 ---- a/tools/xenstore/xenstored_watch.c -+++ b/tools/xenstore/xenstored_watch.c -@@ -211,7 +211,7 @@ static int check_watch_path(struct connection *conn, const void *ctx, - } - - static struct watch *add_watch(struct connection *conn, char *path, char *token, -- bool relative) -+ bool relative, bool no_quota_check) - { - struct watch *watch; - -@@ -222,6 +222,9 @@ static struct watch *add_watch(struct connection *conn, char *path, char *token, - watch->token = talloc_strdup(watch, token); - if (!watch->node || !watch->token) - goto nomem; -+ if (domain_memory_add(conn->id, strlen(path) + strlen(token), -+ no_quota_check)) -+ goto nomem; - - if (relative) - watch->relative_path = get_implicit_path(conn); -@@ -265,7 +268,7 @@ int do_watch(struct connection *conn, struct buffered_data *in) - if (domain_watch(conn) > quota_nb_watch_per_domain) - return E2BIG; - -- watch = add_watch(conn, vec[0], vec[1], relative); -+ watch = add_watch(conn, vec[0], vec[1], relative, false); - if (!watch) - return errno; - -@@ -296,6 +299,8 @@ int do_unwatch(struct connection *conn, struct buffered_data *in) - list_for_each_entry(watch, &conn->watches, list) { - if (streq(watch->node, node) && streq(watch->token, vec[1])) { - list_del(&watch->list); -+ domain_memory_add_nochk(conn->id, -strlen(watch->node) - -+ strlen(watch->token)); - talloc_free(watch); - domain_watch_dec(conn); - send_ack(conn, XS_UNWATCH); -@@ -311,6 +316,8 @@ void conn_delete_all_watches(struct connection *conn) - - while ((watch = list_top(&conn->watches, struct watch, list))) { - list_del(&watch->list); -+ domain_memory_add_nochk(conn->id, -strlen(watch->node) - -+ strlen(watch->token)); - talloc_free(watch); - domain_watch_dec(conn); - } -@@ -373,7 +380,7 @@ void read_state_watch(const void *ctx, const void *state) - if (!path) - barf("allocation error for read watch"); - -- if (!add_watch(conn, path, token, relative)) -+ if (!add_watch(conn, path, token, relative, true)) - barf("error adding watch"); - } - diff --git a/xsa326-4.16-xenstored-14.patch b/xsa326-4.16-xenstored-14.patch deleted file mode 100644 index df898c4..0000000 --- a/xsa326-4.16-xenstored-14.patch +++ /dev/null @@ -1,334 +0,0 @@ -From b8bd74e5e962955211ab0c5c1924ebf2bb526799 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:10 +0200 -Subject: tools/xenstore: add memory accounting for nodes - -Add the memory accounting for Xenstore nodes. In order to make this -not too complicated allow for some sloppiness when writing nodes. Any -hard quota violation will result in no further requests to be accepted. - -This is part of XSA-326 / CVE-2022-42315. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index d86942f5aa77..16504de42017 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -591,6 +591,117 @@ void set_tdb_key(const char *name, TDB_DATA *key) - key->dsize = strlen(name); - } - -+static void get_acc_data(TDB_DATA *key, struct node_account_data *acc) -+{ -+ TDB_DATA old_data; -+ struct xs_tdb_record_hdr *hdr; -+ -+ if (acc->memory < 0) { -+ old_data = tdb_fetch(tdb_ctx, *key); -+ /* No check for error, as the node might not exist. */ -+ if (old_data.dptr == NULL) { -+ acc->memory = 0; -+ } else { -+ hdr = (void *)old_data.dptr; -+ acc->memory = old_data.dsize; -+ acc->domid = hdr->perms[0].id; -+ } -+ talloc_free(old_data.dptr); -+ } -+} -+ -+/* -+ * Per-transaction nodes need to be accounted for the transaction owner. -+ * Those nodes are stored in the data base with the transaction generation -+ * count prepended (e.g. 123/local/domain/...). So testing for the node's -+ * key not to start with "/" is sufficient. -+ */ -+static unsigned int get_acc_domid(struct connection *conn, TDB_DATA *key, -+ unsigned int domid) -+{ -+ return (!conn || key->dptr[0] == '/') ? domid : conn->id; -+} -+ -+int do_tdb_write(struct connection *conn, TDB_DATA *key, TDB_DATA *data, -+ struct node_account_data *acc, bool no_quota_check) -+{ -+ struct xs_tdb_record_hdr *hdr = (void *)data->dptr; -+ struct node_account_data old_acc = {}; -+ unsigned int old_domid, new_domid; -+ int ret; -+ -+ if (!acc) -+ old_acc.memory = -1; -+ else -+ old_acc = *acc; -+ -+ get_acc_data(key, &old_acc); -+ old_domid = get_acc_domid(conn, key, old_acc.domid); -+ new_domid = get_acc_domid(conn, key, hdr->perms[0].id); -+ -+ /* -+ * Don't check for ENOENT, as we want to be able to switch orphaned -+ * nodes to new owners. -+ */ -+ if (old_acc.memory) -+ domain_memory_add_nochk(old_domid, -+ -old_acc.memory - key->dsize); -+ ret = domain_memory_add(new_domid, data->dsize + key->dsize, -+ no_quota_check); -+ if (ret) { -+ /* Error path, so no quota check. */ -+ if (old_acc.memory) -+ domain_memory_add_nochk(old_domid, -+ old_acc.memory + key->dsize); -+ return ret; -+ } -+ -+ /* TDB should set errno, but doesn't even set ecode AFAICT. */ -+ if (tdb_store(tdb_ctx, *key, *data, TDB_REPLACE) != 0) { -+ domain_memory_add_nochk(new_domid, -data->dsize - key->dsize); -+ /* Error path, so no quota check. */ -+ if (old_acc.memory) -+ domain_memory_add_nochk(old_domid, -+ old_acc.memory + key->dsize); -+ errno = EIO; -+ return errno; -+ } -+ -+ if (acc) { -+ /* Don't use new_domid, as it might be a transaction node. */ -+ acc->domid = hdr->perms[0].id; -+ acc->memory = data->dsize; -+ } -+ -+ return 0; -+} -+ -+int do_tdb_delete(struct connection *conn, TDB_DATA *key, -+ struct node_account_data *acc) -+{ -+ struct node_account_data tmp_acc; -+ unsigned int domid; -+ -+ if (!acc) { -+ acc = &tmp_acc; -+ acc->memory = -1; -+ } -+ -+ get_acc_data(key, acc); -+ -+ if (tdb_delete(tdb_ctx, *key)) { -+ errno = EIO; -+ return errno; -+ } -+ -+ if (acc->memory) { -+ domid = get_acc_domid(conn, key, acc->domid); -+ domain_memory_add_nochk(domid, -acc->memory - key->dsize); -+ } -+ -+ return 0; -+} -+ - /* - * If it fails, returns NULL and sets errno. - * Temporary memory allocations will be done with ctx. -@@ -644,9 +755,15 @@ struct node *read_node(struct connection *conn, const void *ctx, - - /* Permissions are struct xs_permissions. */ - node->perms.p = hdr->perms; -+ node->acc.domid = node->perms.p[0].id; -+ node->acc.memory = data.dsize; - if (domain_adjust_node_perms(conn, node)) - goto error; - -+ /* If owner is gone reset currently accounted memory size. */ -+ if (node->acc.domid != node->perms.p[0].id) -+ node->acc.memory = 0; -+ - /* Data is binary blob (usually ascii, no nul). */ - node->data = node->perms.p + hdr->num_perms; - /* Children is strings, nul separated. */ -@@ -715,12 +832,9 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, - p += node->datalen; - memcpy(p, node->children, node->childlen); - -- /* TDB should set errno, but doesn't even set ecode AFAICT. */ -- if (tdb_store(tdb_ctx, *key, data, TDB_REPLACE) != 0) { -- corrupt(conn, "Write of %s failed", key->dptr); -- errno = EIO; -- return errno; -- } -+ if (do_tdb_write(conn, key, &data, &node->acc, no_quota_check)) -+ return EIO; -+ - return 0; - } - -@@ -1222,7 +1336,7 @@ static void delete_node_single(struct connection *conn, struct node *node) - if (access_node(conn, node, NODE_ACCESS_DELETE, &key)) - return; - -- if (tdb_delete(tdb_ctx, key) != 0) { -+ if (do_tdb_delete(conn, &key, &node->acc) != 0) { - corrupt(conn, "Could not delete '%s'", node->name); - return; - } -@@ -1295,6 +1409,7 @@ static struct node *construct_node(struct connection *conn, const void *ctx, - /* No children, no data */ - node->children = node->data = NULL; - node->childlen = node->datalen = 0; -+ node->acc.memory = 0; - node->parent = parent; - return node; - -@@ -1303,17 +1418,17 @@ static struct node *construct_node(struct connection *conn, const void *ctx, - return NULL; - } - --static void destroy_node_rm(struct node *node) -+static void destroy_node_rm(struct connection *conn, struct node *node) - { - if (streq(node->name, "/")) - corrupt(NULL, "Destroying root node!"); - -- tdb_delete(tdb_ctx, node->key); -+ do_tdb_delete(conn, &node->key, &node->acc); - } - - static int destroy_node(struct connection *conn, struct node *node) - { -- destroy_node_rm(node); -+ destroy_node_rm(conn, node); - domain_entry_dec(conn, node); - - /* -@@ -1365,7 +1480,7 @@ static struct node *create_node(struct connection *conn, const void *ctx, - /* Account for new node */ - if (i->parent) { - if (domain_entry_inc(conn, i)) { -- destroy_node_rm(i); -+ destroy_node_rm(conn, i); - return NULL; - } - } -@@ -2291,7 +2406,7 @@ static int clean_store_(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA val, - if (!hashtable_search(reachable, name)) { - log("clean_store: '%s' is orphaned!", name); - if (recovery) { -- tdb_delete(tdb, key); -+ do_tdb_delete(NULL, &key, NULL); - } - } - -@@ -3149,6 +3264,7 @@ void read_state_node(const void *ctx, const void *state) - if (!node) - barf("allocation error restoring node"); - -+ node->acc.memory = 0; - node->name = name; - node->generation = ++generation; - node->datalen = sn->data_len; -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index 373af18297bf..da9ecce67f31 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -176,6 +176,11 @@ struct node_perms { - struct xs_permissions *p; - }; - -+struct node_account_data { -+ unsigned int domid; -+ int memory; /* -1 if unknown */ -+}; -+ - struct node { - const char *name; - /* Key used to update TDB */ -@@ -198,6 +203,9 @@ struct node { - /* Children, each nul-terminated. */ - unsigned int childlen; - char *children; -+ -+ /* Allocation information for node currently in store. */ -+ struct node_account_data acc; - }; - - /* Return the only argument in the input. */ -@@ -306,6 +314,10 @@ extern xengnttab_handle **xgt_handle; - int remember_string(struct hashtable *hash, const char *str); - - void set_tdb_key(const char *name, TDB_DATA *key); -+int do_tdb_write(struct connection *conn, TDB_DATA *key, TDB_DATA *data, -+ struct node_account_data *acc, bool no_quota_check); -+int do_tdb_delete(struct connection *conn, TDB_DATA *key, -+ struct node_account_data *acc); - - void conn_free_buffered_data(struct connection *conn); - -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index 7bd41eb475e3..ace9a11d77bb 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -153,6 +153,9 @@ struct transaction - /* List of all transactions active on this connection. */ - struct list_head list; - -+ /* Connection this transaction is associated with. */ -+ struct connection *conn; -+ - /* Connection-local identifier for this transaction. */ - uint32_t id; - -@@ -286,6 +289,8 @@ int access_node(struct connection *conn, struct node *node, - - introduce = true; - i->ta_node = false; -+ /* acc.memory < 0 means "unknown, get size from TDB". */ -+ node->acc.memory = -1; - - /* - * Additional transaction-specific node for read type. We only -@@ -410,11 +415,11 @@ static int finalize_transaction(struct connection *conn, - goto err; - hdr = (void *)data.dptr; - hdr->generation = ++generation; -- ret = tdb_store(tdb_ctx, key, data, -- TDB_REPLACE); -+ ret = do_tdb_write(conn, &key, &data, NULL, -+ true); - talloc_free(data.dptr); - } else { -- ret = tdb_delete(tdb_ctx, key); -+ ret = do_tdb_delete(conn, &key, NULL); - } - if (ret) - goto err; -@@ -425,7 +430,7 @@ static int finalize_transaction(struct connection *conn, - } - } - -- if (i->ta_node && tdb_delete(tdb_ctx, ta_key)) -+ if (i->ta_node && do_tdb_delete(conn, &ta_key, NULL)) - goto err; - list_del(&i->list); - talloc_free(i); -@@ -453,7 +458,7 @@ static int destroy_transaction(void *_transaction) - i->node); - if (trans_name) { - set_tdb_key(trans_name, &key); -- tdb_delete(tdb_ctx, key); -+ do_tdb_delete(trans->conn, &key, NULL); - } - } - list_del(&i->list); -@@ -497,6 +502,7 @@ int do_transaction_start(struct connection *conn, struct buffered_data *in) - - INIT_LIST_HEAD(&trans->accessed); - INIT_LIST_HEAD(&trans->changed_domains); -+ trans->conn = conn; - trans->fail = false; - trans->generation = ++generation; - diff --git a/xsa326-4.16-xenstored-15.patch b/xsa326-4.16-xenstored-15.patch deleted file mode 100644 index 8a42a36..0000000 --- a/xsa326-4.16-xenstored-15.patch +++ /dev/null @@ -1,54 +0,0 @@ -From c55a1ea0a5ea7f6a3dc850cb015a49ba9ec571ab Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:10 +0200 -Subject: tools/xenstore: add exports for quota variables - -Some quota variables are not exported via header files. - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Acked-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index da9ecce67f31..bfd3fc1e9df3 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -275,6 +275,11 @@ extern TDB_CONTEXT *tdb_ctx; - extern int dom0_domid; - extern int dom0_event; - extern int priv_domid; -+extern int quota_nb_watch_per_domain; -+extern int quota_max_transaction; -+extern int quota_max_entry_size; -+extern int quota_nb_perms_per_node; -+extern int quota_max_path_len; - extern int quota_nb_entry_per_domain; - extern int quota_req_outstanding; - extern int quota_trans_nodes; -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index ace9a11d77bb..28774813de83 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -175,7 +175,6 @@ struct transaction - bool fail; - }; - --extern int quota_max_transaction; - uint64_t generation; - - static struct accessed_node *find_accessed_node(struct transaction *trans, -diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c -index fdf9b2d653a0..85362bcce314 100644 ---- a/tools/xenstore/xenstored_watch.c -+++ b/tools/xenstore/xenstored_watch.c -@@ -31,8 +31,6 @@ - #include "xenstored_domain.h" - #include "xenstored_transaction.h" - --extern int quota_nb_watch_per_domain; -- - struct watch - { - /* Watches on this connection */ diff --git a/xsa326-4.16-xenstored-16.patch b/xsa326-4.16-xenstored-16.patch deleted file mode 100644 index c2a8953..0000000 --- a/xsa326-4.16-xenstored-16.patch +++ /dev/null @@ -1,238 +0,0 @@ -From 05cc2af50ba43431d6d50aff758e968833aab9c6 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:10 +0200 -Subject: tools/xenstore: add control command for setting and showing quota - -Add a xenstore-control command "quota" to: -- show current quota settings -- change quota settings -- show current quota related values of a domain - -Note that in the case the new quota is lower than existing one, -Xenstored may continue to handle requests from a domain exceeding the -new limit (depends on which one has been broken) and the amount of -resource used will not change. However the domain will not be able to -create more resource (associated to the quota) until it is back to below -the limit. - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/docs/misc/xenstore.txt b/docs/misc/xenstore.txt -index 334dc8b6fdf5..a7d006519ae8 100644 ---- a/docs/misc/xenstore.txt -+++ b/docs/misc/xenstore.txt -@@ -366,6 +366,17 @@ CONTROL |[|] - print| - print to syslog (xenstore runs as daemon) or - to console (xenstore runs as stubdom) -+ quota|[set |] -+ without parameters: print the current quota settings -+ with "set ": set the quota to new value -+ (The admin should make sure all the domain usage is -+ below the quota. If it is not, then Xenstored may continue to -+ handle requests from the domain as long as the resource -+ violating the new quota setting isn't increased further) -+ with "": print quota related accounting data for -+ the domain -+ quota-soft|[set ] -+ like the "quota" command, but for soft-quota. - help - return list of supported commands for CONTROL - -diff --git a/tools/xenstore/xenstored_control.c b/tools/xenstore/xenstored_control.c -index adb8d51b043b..1031a81c3874 100644 ---- a/tools/xenstore/xenstored_control.c -+++ b/tools/xenstore/xenstored_control.c -@@ -196,6 +196,115 @@ static int do_control_log(void *ctx, struct connection *conn, - return 0; - } - -+struct quota { -+ const char *name; -+ int *quota; -+ const char *descr; -+}; -+ -+static const struct quota hard_quotas[] = { -+ { "nodes", "a_nb_entry_per_domain, "Nodes per domain" }, -+ { "watches", "a_nb_watch_per_domain, "Watches per domain" }, -+ { "transactions", "a_max_transaction, "Transactions per domain" }, -+ { "outstanding", "a_req_outstanding, -+ "Outstanding requests per domain" }, -+ { "transaction-nodes", "a_trans_nodes, -+ "Max. number of accessed nodes per transaction" }, -+ { "memory", "a_memory_per_domain_hard, -+ "Total Xenstore memory per domain (error level)" }, -+ { "node-size", "a_max_entry_size, "Max. size of a node" }, -+ { "path-max", "a_max_path_len, "Max. length of a node path" }, -+ { "permissions", "a_nb_perms_per_node, -+ "Max. number of permissions per node" }, -+ { NULL, NULL, NULL } -+}; -+ -+static const struct quota soft_quotas[] = { -+ { "memory", "a_memory_per_domain_soft, -+ "Total Xenstore memory per domain (warning level)" }, -+ { NULL, NULL, NULL } -+}; -+ -+static int quota_show_current(const void *ctx, struct connection *conn, -+ const struct quota *quotas) -+{ -+ char *resp; -+ unsigned int i; -+ -+ resp = talloc_strdup(ctx, "Quota settings:\n"); -+ if (!resp) -+ return ENOMEM; -+ -+ for (i = 0; quotas[i].quota; i++) { -+ resp = talloc_asprintf_append(resp, "%-17s: %8d %s\n", -+ quotas[i].name, *quotas[i].quota, -+ quotas[i].descr); -+ if (!resp) -+ return ENOMEM; -+ } -+ -+ send_reply(conn, XS_CONTROL, resp, strlen(resp) + 1); -+ -+ return 0; -+} -+ -+static int quota_set(const void *ctx, struct connection *conn, -+ char **vec, int num, const struct quota *quotas) -+{ -+ unsigned int i; -+ int val; -+ -+ if (num != 2) -+ return EINVAL; -+ -+ val = atoi(vec[1]); -+ if (val < 1) -+ return EINVAL; -+ -+ for (i = 0; quotas[i].quota; i++) { -+ if (!strcmp(vec[0], quotas[i].name)) { -+ *quotas[i].quota = val; -+ send_ack(conn, XS_CONTROL); -+ return 0; -+ } -+ } -+ -+ return EINVAL; -+} -+ -+static int quota_get(const void *ctx, struct connection *conn, -+ char **vec, int num) -+{ -+ if (num != 1) -+ return EINVAL; -+ -+ return domain_get_quota(ctx, conn, atoi(vec[0])); -+} -+ -+static int do_control_quota(void *ctx, struct connection *conn, -+ char **vec, int num) -+{ -+ if (num == 0) -+ return quota_show_current(ctx, conn, hard_quotas); -+ -+ if (!strcmp(vec[0], "set")) -+ return quota_set(ctx, conn, vec + 1, num - 1, hard_quotas); -+ -+ return quota_get(ctx, conn, vec, num); -+} -+ -+static int do_control_quota_s(void *ctx, struct connection *conn, -+ char **vec, int num) -+{ -+ if (num == 0) -+ return quota_show_current(ctx, conn, soft_quotas); -+ -+ if (!strcmp(vec[0], "set")) -+ return quota_set(ctx, conn, vec + 1, num - 1, soft_quotas); -+ -+ return EINVAL; -+} -+ - #ifdef __MINIOS__ - static int do_control_memreport(void *ctx, struct connection *conn, - char **vec, int num) -@@ -847,6 +956,8 @@ static struct cmd_s cmds[] = { - { "memreport", do_control_memreport, "[]" }, - #endif - { "print", do_control_print, "" }, -+ { "quota", do_control_quota, "[set |]" }, -+ { "quota-soft", do_control_quota_s, "[set ]" }, - { "help", do_control_help, "" }, - }; - -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index 94fd561e9de4..e7c6886ccf47 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -31,6 +31,7 @@ - #include "xenstored_domain.h" - #include "xenstored_transaction.h" - #include "xenstored_watch.h" -+#include "xenstored_control.h" - - #include - #include -@@ -345,6 +346,38 @@ static struct domain *find_domain_struct(unsigned int domid) - return NULL; - } - -+int domain_get_quota(const void *ctx, struct connection *conn, -+ unsigned int domid) -+{ -+ struct domain *d = find_domain_struct(domid); -+ char *resp; -+ int ta; -+ -+ if (!d) -+ return ENOENT; -+ -+ ta = d->conn ? d->conn->transaction_started : 0; -+ resp = talloc_asprintf(ctx, "Domain %u:\n", domid); -+ if (!resp) -+ return ENOMEM; -+ -+#define ent(t, e) \ -+ resp = talloc_asprintf_append(resp, "%-16s: %8d\n", #t, e); \ -+ if (!resp) return ENOMEM -+ -+ ent(nodes, d->nbentry); -+ ent(watches, d->nbwatch); -+ ent(transactions, ta); -+ ent(outstanding, d->nboutstanding); -+ ent(memory, d->memory); -+ -+#undef ent -+ -+ send_reply(conn, XS_CONTROL, resp, strlen(resp) + 1); -+ -+ return 0; -+} -+ - static struct domain *alloc_domain(const void *context, unsigned int domid) - { - struct domain *domain; -diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h -index 633c9a0a0a1f..904faa923afb 100644 ---- a/tools/xenstore/xenstored_domain.h -+++ b/tools/xenstore/xenstored_domain.h -@@ -87,6 +87,8 @@ int domain_watch(struct connection *conn); - void domain_outstanding_inc(struct connection *conn); - void domain_outstanding_dec(struct connection *conn); - void domain_outstanding_domid_dec(unsigned int domid); -+int domain_get_quota(const void *ctx, struct connection *conn, -+ unsigned int domid); - - /* Special node permission handling. */ - int set_perms_special(struct connection *conn, const char *name, diff --git a/xsa376.patch b/xsa376.patch deleted file mode 100644 index 08e0bff..0000000 --- a/xsa376.patch +++ /dev/null @@ -1,145 +0,0 @@ -From 02d3a57d6466363b316b60ffbba414a4a2cb90c5 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Thu, 25 Nov 2021 13:38:29 +0100 -Subject: [PATCH] SUPPORT.md: limit support statement for Linux and Windows - frontends - -Change the support state of Linux and Windows pv frontends from -"supported" to "supported with caveats" in order to reflect that the -frontends can probably be harmed by their respective backends. - -Some of the Linux frontends have been hardened already. - -This is XSA-376 - -Signed-off-by: Juergen Gross ---- - SUPPORT.md | 57 +++++++++++++++++++++++++++++++++++++++++++++--------- - 1 file changed, 48 insertions(+), 9 deletions(-) - -diff --git a/SUPPORT.md b/SUPPORT.md -index 3a34933c89..6e3e305b01 100644 ---- a/SUPPORT.md -+++ b/SUPPORT.md -@@ -411,7 +411,11 @@ Guest-side driver capable of speaking the Xen PV block protocol - Status, FreeBSD: Supported, Security support external - Status, NetBSD: Supported, Security support external - Status, OpenBSD: Supported, Security support external -- Status, Windows: Supported -+ Status, Windows: Supported, with caveats -+ -+Windows frontend currently trusts the backend; -+bugs in the frontend which allow backend to cause mischief will not be -+considered security vulnerabilities. - - ### Netfront - -@@ -421,20 +425,32 @@ Guest-side driver capable of speaking the Xen PV networking protocol - Status, FreeBSD: Supported, Security support external - Status, NetBSD: Supported, Security support external - Status, OpenBSD: Supported, Security support external -- Status, Windows: Supported -+ Status, Windows: Supported, with caveats -+ -+Windows frontend currently trusts the backend; -+bugs in the frontend which allow backend to cause mischief will not be -+considered security vulnerabilities. - - ### PV Framebuffer (frontend) - - Guest-side driver capable of speaking the Xen PV Framebuffer protocol - -- Status, Linux (xen-fbfront): Supported -+ Status, Linux (xen-fbfront): Supported, with caveats -+ -+Linux frontend currently trusts the backend; -+bugs in the frontend which allow backend to cause mischief will not be -+considered security vulnerabilities. - - ### PV display (frontend) - - Guest-side driver capable of speaking the Xen PV display protocol - -- Status, Linux: Supported (outside of "backend allocation" mode) -- Status, Linux: Experimental (in "backend allocation" mode) -+ Status, Linux, outside of "backend allocation" mode: Supported, with caveats -+ Status, Linux, "backend allocation" mode: Experimental -+ -+Linux frontend currently trusts the backend; -+bugs in the frontend which allow backend to cause mischief will not be -+considered security vulnerabilities. - - ### PV Console (frontend) - -@@ -443,7 +459,11 @@ Guest-side driver capable of speaking the Xen PV console protocol - Status, Linux (hvc_xen): Supported - Status, FreeBSD: Supported, Security support external - Status, NetBSD: Supported, Security support external -- Status, Windows: Supported -+ Status, Windows: Supported, with caveats -+ -+Windows frontend currently trusts the backend; -+bugs in the frontend which allow backend to cause mischief will not be -+considered security vulnerabilities. - - ### PV keyboard (frontend) - -@@ -451,11 +471,19 @@ Guest-side driver capable of speaking the Xen PV keyboard protocol. - Note that the "keyboard protocol" includes mouse / pointer / - multi-touch support as well. - -- Status, Linux (xen-kbdfront): Supported -+ Status, Linux (xen-kbdfront): Supported, with caveats -+ -+Linux frontend currently trusts the backend; -+bugs in the frontend which allow backend to cause mischief will not be -+considered security vulnerabilities. - - ### PV USB (frontend) - -- Status, Linux: Supported -+ Status, Linux: Supported, with caveats -+ -+Linux frontend currently trusts the backend; -+bugs in the frontend which allow backend to cause mischief will not be -+considered security vulnerabilities. - - ### PV SCSI protocol (frontend) - -@@ -464,6 +492,10 @@ multi-touch support as well. - NB that while the PV SCSI frontend is in Linux and tested regularly, - there is currently no xl support. - -+Linux frontend currently trusts the backend; -+bugs in the frontend which allow backend to cause mischief will not be -+considered security vulnerabilities. -+ - ### PV TPM (frontend) - - Guest-side driver capable of speaking the Xen PV TPM protocol -@@ -486,7 +518,11 @@ Guest-side driver capable of making pv system calls - - Guest-side driver capable of speaking the Xen PV sound protocol - -- Status, Linux: Supported -+ Status, Linux: Supported, with caveats -+ -+Linux frontend currently trusts the backend; -+bugs in the frontend which allow backend to cause mischief will not be -+considered security vulnerabilities. - - ## Virtual device support, host side - -@@ -987,6 +1023,9 @@ are given the following labels: - - This feature is security supported - by a different organization (not the XenProject). -+ The extent of support is defined by that organization. -+ It might be limited, e.g. like described in **Supported, with caveats** -+ below. - See **External security support** below. - - * **Supported, with caveats** --- -2.26.2 - diff --git a/xsa409-4.13-0001-libxl-docs-Use-arch-specific-default-paging-memory.patch b/xsa409-4.13-0001-libxl-docs-Use-arch-specific-default-paging-memory.patch deleted file mode 100644 index 9aca0a7..0000000 --- a/xsa409-4.13-0001-libxl-docs-Use-arch-specific-default-paging-memory.patch +++ /dev/null @@ -1,147 +0,0 @@ -From bd4a7db4001364fd03a80a2e73b81c46aaa44e9c Mon Sep 17 00:00:00 2001 -From: Henry Wang -Date: Mon, 22 Aug 2022 01:35:09 +0000 -Subject: [PATCH 1/4] libxl, docs: Use arch-specific default paging memory - -The default paging memory (descibed in `shadow_memory` entry in xl -config) in libxl is used to determine the memory pool size for xl -guests. Currently this size is only used for x86, and contains a part -of RAM to shadow the resident processes. Since on Arm there is no -shadow mode guests, so the part of RAM to shadow the resident processes -is not necessary. Therefore, this commit splits the function -`libxl_get_required_shadow_memory()` to arch specific helpers and -renamed the helper to `libxl__arch_get_required_paging_memory()`. - -On x86, this helper calls the original value from -`libxl_get_required_shadow_memory()` so no functional change intended. - -On Arm, this helper returns 1MB per vcpu plus 4KB per MiB of RAM -for the P2M map and additional 512KB. - -Also update the xl.cfg documentation to add Arm documentation -according to code changes and correct the comment style following Xen -coding style. - -This is part of CVE-2022-33747 / XSA-409. - -Suggested-by: Julien Grall -Signed-off-by: Henry Wang -Reviewed-by: Anthony PERARD ---- - docs/man/xl.cfg.5.pod.in | 5 +++++ - tools/libs/light/libxl_arch.h | 4 ++++ - tools/libs/light/libxl_arm.c | 14 ++++++++++++++ - tools/libs/light/libxl_utils.c | 9 ++------- - tools/libs/light/libxl_x86.c | 13 +++++++++++++ - 5 files changed, 38 insertions(+), 7 deletions(-) - -diff --git a/docs/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in -index b98d1613987e..eda1e77ebd06 100644 ---- a/docs/man/xl.cfg.5.pod.in -+++ b/docs/man/xl.cfg.5.pod.in -@@ -1768,6 +1768,11 @@ are not using hardware assisted paging (i.e. you are using shadow - mode) and your guest workload consists of a very large number of - similar processes then increasing this value may improve performance. - -+On Arm, this field is used to determine the size of the guest P2M pages -+pool, and the default value is 1MB per vCPU plus 4KB per MB of RAM for -+the P2M map and additional 512KB for extended regions. Users should -+adjust this value if bigger P2M pool size is needed. -+ - =back - - =head3 Processor and Platform Features -diff --git a/tools/libs/light/libxl_arch.h b/tools/libs/light/libxl_arch.h -index 1522ecb97f72..5a060c2c3033 100644 ---- a/tools/libs/light/libxl_arch.h -+++ b/tools/libs/light/libxl_arch.h -@@ -90,6 +90,10 @@ void libxl__arch_update_domain_config(libxl__gc *gc, - libxl_domain_config *dst, - const libxl_domain_config *src); - -+_hidden -+unsigned long libxl__arch_get_required_paging_memory(unsigned long maxmem_kb, -+ unsigned int smp_cpus); -+ - #if defined(__i386__) || defined(__x86_64__) - - #define LAPIC_BASE_ADDRESS 0xfee00000 -diff --git a/tools/libs/light/libxl_arm.c b/tools/libs/light/libxl_arm.c -index eef1de093914..73a95e83af24 100644 ---- a/tools/libs/light/libxl_arm.c -+++ b/tools/libs/light/libxl_arm.c -@@ -154,6 +154,20 @@ out: - return rc; - } - -+unsigned long libxl__arch_get_required_paging_memory(unsigned long maxmem_kb, -+ unsigned int smp_cpus) -+{ -+ /* -+ * 256 pages (1MB) per vcpu, -+ * plus 1 page per MiB of RAM for the P2M map, -+ * plus 1 page per MiB of extended region. This default value is 128 MiB -+ * which should be enough for domains that are not running backend. -+ * This is higher than the minimum that Xen would allocate if no value -+ * were given (but the Xen minimum is for safety, not performance). -+ */ -+ return 4 * (256 * smp_cpus + maxmem_kb / 1024 + 128); -+} -+ - static struct arch_info { - const char *guest_type; - const char *timer_compat; -diff --git a/tools/libs/light/libxl_utils.c b/tools/libs/light/libxl_utils.c -index 4699c4a0a36f..e276c0ee9cc3 100644 ---- a/tools/libs/light/libxl_utils.c -+++ b/tools/libs/light/libxl_utils.c -@@ -18,6 +18,7 @@ - #include - - #include "libxl_internal.h" -+#include "libxl_arch.h" - #include "_paths.h" - - #ifndef LIBXL_HAVE_NONCONST_LIBXL_BASENAME_RETURN_VALUE -@@ -39,13 +40,7 @@ char *libxl_basename(const char *name) - - unsigned long libxl_get_required_shadow_memory(unsigned long maxmem_kb, unsigned int smp_cpus) - { -- /* 256 pages (1MB) per vcpu, -- plus 1 page per MiB of RAM for the P2M map, -- plus 1 page per MiB of RAM to shadow the resident processes. -- This is higher than the minimum that Xen would allocate if no value -- were given (but the Xen minimum is for safety, not performance). -- */ -- return 4 * (256 * smp_cpus + 2 * (maxmem_kb / 1024)); -+ return libxl__arch_get_required_paging_memory(maxmem_kb, smp_cpus); - } - - char *libxl_domid_to_name(libxl_ctx *ctx, uint32_t domid) -diff --git a/tools/libs/light/libxl_x86.c b/tools/libs/light/libxl_x86.c -index 1feadebb1852..51362893cf98 100644 ---- a/tools/libs/light/libxl_x86.c -+++ b/tools/libs/light/libxl_x86.c -@@ -882,6 +882,19 @@ void libxl__arch_update_domain_config(libxl__gc *gc, - libxl_defbool_val(src->b_info.arch_x86.msr_relaxed)); - } - -+unsigned long libxl__arch_get_required_paging_memory(unsigned long maxmem_kb, -+ unsigned int smp_cpus) -+{ -+ /* -+ * 256 pages (1MB) per vcpu, -+ * plus 1 page per MiB of RAM for the P2M map, -+ * plus 1 page per MiB of RAM to shadow the resident processes. -+ * This is higher than the minimum that Xen would allocate if no value -+ * were given (but the Xen minimum is for safety, not performance). -+ */ -+ return 4 * (256 * smp_cpus + 2 * (maxmem_kb / 1024)); -+} -+ - /* - * Local variables: - * mode: C --- -2.37.1 - diff --git a/xsa409-4.13-0002-xen-arm-Construct-the-P2M-pages-pool-for-guests.patch b/xsa409-4.13-0002-xen-arm-Construct-the-P2M-pages-pool-for-guests.patch deleted file mode 100644 index 8c03657..0000000 --- a/xsa409-4.13-0002-xen-arm-Construct-the-P2M-pages-pool-for-guests.patch +++ /dev/null @@ -1,187 +0,0 @@ -From 419a4bbc20cf7c5d7d9dedae59fb8049922e6a2c Mon Sep 17 00:00:00 2001 -From: Henry Wang -Date: Mon, 6 Jun 2022 06:17:28 +0000 -Subject: [PATCH 2/4] xen/arm: Construct the P2M pages pool for guests - -This commit constructs the p2m pages pool for guests from the -data structure and helper perspective. - -This is implemented by: - -- Adding a `struct paging_domain` which contains a freelist, a -counter variable and a spinlock to `struct arch_domain` to -indicate the free p2m pages and the number of p2m total pages in -the p2m pages pool. - -- Adding a helper `p2m_get_allocation` to get the p2m pool size. - -- Adding a helper `p2m_set_allocation` to set the p2m pages pool -size. This helper should be called before allocating memory for -a guest. - -- Adding a helper `p2m_teardown_allocation` to free the p2m pages -pool. This helper should be called during the xl domain destory. - -This is part of CVE-2022-33747 / XSA-409. - -Signed-off-by: Henry Wang -Reviewed-by: Stefano Stabellini ---- - xen/arch/arm/p2m.c | 88 ++++++++++++++++++++++++++++++++++++ - xen/include/asm-arm/domain.h | 10 ++++ - xen/include/asm-arm/p2m.h | 4 ++ - 3 files changed, 102 insertions(+) - -diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c -index 3bcd1e897e88..79f3d37f5230 100644 ---- a/xen/arch/arm/p2m.c -+++ b/xen/arch/arm/p2m.c -@@ -50,6 +50,92 @@ static uint64_t generate_vttbr(uint16_t vmid, mfn_t root_mfn) - return (mfn_to_maddr(root_mfn) | ((uint64_t)vmid << 48)); - } - -+/* Return the size of the pool, rounded up to the nearest MB */ -+unsigned int p2m_get_allocation(struct domain *d) -+{ -+ unsigned long nr_pages = ACCESS_ONCE(d->arch.paging.p2m_total_pages); -+ -+ return ROUNDUP(nr_pages, 1 << (20 - PAGE_SHIFT)) >> (20 - PAGE_SHIFT); -+} -+ -+/* -+ * Set the pool of pages to the required number of pages. -+ * Returns 0 for success, non-zero for failure. -+ * Call with d->arch.paging.lock held. -+ */ -+int p2m_set_allocation(struct domain *d, unsigned long pages, bool *preempted) -+{ -+ struct page_info *pg; -+ -+ ASSERT(spin_is_locked(&d->arch.paging.lock)); -+ -+ for ( ; ; ) -+ { -+ if ( d->arch.paging.p2m_total_pages < pages ) -+ { -+ /* Need to allocate more memory from domheap */ -+ pg = alloc_domheap_page(NULL, 0); -+ if ( pg == NULL ) -+ { -+ printk(XENLOG_ERR "Failed to allocate P2M pages.\n"); -+ return -ENOMEM; -+ } -+ ACCESS_ONCE(d->arch.paging.p2m_total_pages) = -+ d->arch.paging.p2m_total_pages + 1; -+ page_list_add_tail(pg, &d->arch.paging.p2m_freelist); -+ } -+ else if ( d->arch.paging.p2m_total_pages > pages ) -+ { -+ /* Need to return memory to domheap */ -+ pg = page_list_remove_head(&d->arch.paging.p2m_freelist); -+ if( pg ) -+ { -+ ACCESS_ONCE(d->arch.paging.p2m_total_pages) = -+ d->arch.paging.p2m_total_pages - 1; -+ free_domheap_page(pg); -+ } -+ else -+ { -+ printk(XENLOG_ERR -+ "Failed to free P2M pages, P2M freelist is empty.\n"); -+ return -ENOMEM; -+ } -+ } -+ else -+ break; -+ -+ /* Check to see if we need to yield and try again */ -+ if ( preempted && general_preempt_check() ) -+ { -+ *preempted = true; -+ return -ERESTART; -+ } -+ } -+ -+ return 0; -+} -+ -+int p2m_teardown_allocation(struct domain *d) -+{ -+ int ret = 0; -+ bool preempted = false; -+ -+ spin_lock(&d->arch.paging.lock); -+ if ( d->arch.paging.p2m_total_pages != 0 ) -+ { -+ ret = p2m_set_allocation(d, 0, &preempted); -+ if ( preempted ) -+ { -+ spin_unlock(&d->arch.paging.lock); -+ return -ERESTART; -+ } -+ ASSERT(d->arch.paging.p2m_total_pages == 0); -+ } -+ spin_unlock(&d->arch.paging.lock); -+ -+ return ret; -+} -+ - /* Unlock the flush and do a P2M TLB flush if necessary */ - void p2m_write_unlock(struct p2m_domain *p2m) - { -@@ -1599,7 +1685,9 @@ int p2m_init(struct domain *d) - unsigned int cpu; - - rwlock_init(&p2m->lock); -+ spin_lock_init(&d->arch.paging.lock); - INIT_PAGE_LIST_HEAD(&p2m->pages); -+ INIT_PAGE_LIST_HEAD(&d->arch.paging.p2m_freelist); - - p2m->vmid = INVALID_VMID; - -diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h -index 9b3647587a04..c90daa65afa7 100644 ---- a/xen/include/asm-arm/domain.h -+++ b/xen/include/asm-arm/domain.h -@@ -40,6 +40,14 @@ struct vtimer { - uint64_t cval; - }; - -+struct paging_domain { -+ spinlock_t lock; -+ /* Free P2M pages from the pre-allocated P2M pool */ -+ struct page_list_head p2m_freelist; -+ /* Number of pages from the pre-allocated P2M pool */ -+ unsigned long p2m_total_pages; -+}; -+ - struct arch_domain - { - #ifdef CONFIG_ARM_64 -@@ -51,6 +59,8 @@ struct arch_domain - - struct hvm_domain hvm; - -+ struct paging_domain paging; -+ - struct vmmio vmmio; - - /* Continuable domain_relinquish_resources(). */ -diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h -index b3ba83283e11..c9598740bd02 100644 ---- a/xen/include/asm-arm/p2m.h -+++ b/xen/include/asm-arm/p2m.h -@@ -218,6 +218,10 @@ void p2m_restore_state(struct vcpu *n); - /* Print debugging/statistial info about a domain's p2m */ - void p2m_dump_info(struct domain *d); - -+unsigned int p2m_get_allocation(struct domain *d); -+int p2m_set_allocation(struct domain *d, unsigned long pages, bool *preempted); -+int p2m_teardown_allocation(struct domain *d); -+ - static inline void p2m_write_lock(struct p2m_domain *p2m) - { - write_lock(&p2m->lock); --- -2.37.1 - diff --git a/xsa409-4.13-0003-xen-arm-libxl-Implement-XEN_DOMCTL_shadow_op-for-Arm.patch b/xsa409-4.13-0003-xen-arm-libxl-Implement-XEN_DOMCTL_shadow_op-for-Arm.patch deleted file mode 100644 index bb638e1..0000000 --- a/xsa409-4.13-0003-xen-arm-libxl-Implement-XEN_DOMCTL_shadow_op-for-Arm.patch +++ /dev/null @@ -1,106 +0,0 @@ -From 332a9979d4dd0b047aa16db201c50fcedbd56743 Mon Sep 17 00:00:00 2001 -From: Henry Wang -Date: Mon, 6 Jun 2022 06:17:29 +0000 -Subject: [PATCH 3/4] xen/arm, libxl: Implement XEN_DOMCTL_shadow_op for Arm - -This commit implements the `XEN_DOMCTL_shadow_op` support in Xen -for Arm. The p2m pages pool size for xl guests is supposed to be -determined by `XEN_DOMCTL_shadow_op`. Hence, this commit: - -- Introduces a function `p2m_domctl` and implements the subops -`XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION` and -`XEN_DOMCTL_SHADOW_OP_GET_ALLOCATION` of `XEN_DOMCTL_shadow_op`. - -- Adds the `XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION` support in libxl. - -Therefore enabling the setting of shadow memory pool size -when creating a guest from xl and getting shadow memory pool size -from Xen. - -Note that the `XEN_DOMCTL_shadow_op` added in this commit is only -a dummy op, and the functionality of setting/getting p2m memory pool -size for xl guests will be added in following commits. - -This is part of CVE-2022-33747 / XSA-409. - -Signed-off-by: Henry Wang -Reviewed-by: Stefano Stabellini ---- - tools/libs/light/libxl_arm.c | 12 ++++++++++++ - xen/arch/arm/domctl.c | 32 ++++++++++++++++++++++++++++++++ - 2 files changed, 44 insertions(+) - -diff --git a/tools/libs/light/libxl_arm.c b/tools/libs/light/libxl_arm.c -index 73a95e83af24..22a0c561bbc6 100644 ---- a/tools/libs/light/libxl_arm.c -+++ b/tools/libs/light/libxl_arm.c -@@ -131,6 +131,18 @@ int libxl__arch_domain_create(libxl__gc *gc, - libxl__domain_build_state *state, - uint32_t domid) - { -+ libxl_ctx *ctx = libxl__gc_owner(gc); -+ unsigned int shadow_mb = DIV_ROUNDUP(d_config->b_info.shadow_memkb, 1024); -+ -+ int r = xc_shadow_control(ctx->xch, domid, -+ XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION, -+ &shadow_mb, 0); -+ if (r) { -+ LOGED(ERROR, domid, -+ "Failed to set %u MiB shadow allocation", shadow_mb); -+ return ERROR_FAIL; -+ } -+ - return 0; - } - -diff --git a/xen/arch/arm/domctl.c b/xen/arch/arm/domctl.c -index 1baf25c3d98b..9bf72e693019 100644 ---- a/xen/arch/arm/domctl.c -+++ b/xen/arch/arm/domctl.c -@@ -47,11 +47,43 @@ static int handle_vuart_init(struct domain *d, - return rc; - } - -+static long p2m_domctl(struct domain *d, struct xen_domctl_shadow_op *sc, -+ XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) -+{ -+ if ( unlikely(d == current->domain) ) -+ { -+ printk(XENLOG_ERR "Tried to do a p2m domctl op on itself.\n"); -+ return -EINVAL; -+ } -+ -+ if ( unlikely(d->is_dying) ) -+ { -+ printk(XENLOG_ERR "Tried to do a p2m domctl op on dying domain %u\n", -+ d->domain_id); -+ return -EINVAL; -+ } -+ -+ switch ( sc->op ) -+ { -+ case XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION: -+ return 0; -+ case XEN_DOMCTL_SHADOW_OP_GET_ALLOCATION: -+ return 0; -+ default: -+ { -+ printk(XENLOG_ERR "Bad p2m domctl op %u\n", sc->op); -+ return -EINVAL; -+ } -+ } -+} -+ - long arch_do_domctl(struct xen_domctl *domctl, struct domain *d, - XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) - { - switch ( domctl->cmd ) - { -+ case XEN_DOMCTL_shadow_op: -+ return p2m_domctl(d, &domctl->u.shadow_op, u_domctl); - case XEN_DOMCTL_cacheflush: - { - gfn_t s = _gfn(domctl->u.cacheflush.start_pfn); --- -2.37.1 - diff --git a/xsa409-4.13-0004-xen-arm-Allocate-and-free-P2M-pages-from-the-P2M-poo.patch b/xsa409-4.13-0004-xen-arm-Allocate-and-free-P2M-pages-from-the-P2M-poo.patch deleted file mode 100644 index 5366c8f..0000000 --- a/xsa409-4.13-0004-xen-arm-Allocate-and-free-P2M-pages-from-the-P2M-poo.patch +++ /dev/null @@ -1,287 +0,0 @@ -From 39664d9ee041f96e9c7ee131ed8ef72a4d19c9f8 Mon Sep 17 00:00:00 2001 -From: Henry Wang -Date: Mon, 6 Jun 2022 06:17:30 +0000 -Subject: [PATCH 4/4] xen/arm: Allocate and free P2M pages from the P2M pool - -This commit sets/tearsdown of p2m pages pool for non-privileged Arm -guests by calling `p2m_set_allocation` and `p2m_teardown_allocation`. - -- For dom0, P2M pages should come from heap directly instead of p2m -pool, so that the kernel may take advantage of the extended regions. - -- For xl guests, the setting of the p2m pool is called in -`XEN_DOMCTL_shadow_op` and the p2m pool is destroyed in -`domain_relinquish_resources`. Note that domctl->u.shadow_op.mb is -updated with the new size when setting the p2m pool. - -- For dom0less domUs, the setting of the p2m pool is called before -allocating memory during domain creation. Users can specify the p2m -pool size by `xen,domain-p2m-mem-mb` dts property. - -To actually allocate/free pages from the p2m pool, this commit adds -two helper functions namely `p2m_alloc_page` and `p2m_free_page` to -`struct p2m_domain`. By replacing the `alloc_domheap_page` and -`free_domheap_page` with these two helper functions, p2m pages can -be added/removed from the list of p2m pool rather than from the heap. - -Since page from `p2m_alloc_page` is cleaned, take the opportunity -to remove the redundant `clean_page` in `p2m_create_table`. - -This is part of CVE-2022-33747 / XSA-409. - -Signed-off-by: Henry Wang -Reviewed-by: Stefano Stabellini ---- - docs/misc/arm/device-tree/booting.txt | 8 ++++ - xen/arch/arm/domain.c | 6 +++ - xen/arch/arm/domain_build.c | 29 ++++++++++++++ - xen/arch/arm/domctl.c | 23 ++++++++++- - xen/arch/arm/p2m.c | 57 +++++++++++++++++++++++++-- - 5 files changed, 118 insertions(+), 5 deletions(-) - -diff --git a/docs/misc/arm/device-tree/booting.txt b/docs/misc/arm/device-tree/booting.txt -index 71895663a4de..d92ccc56ffe0 100644 ---- a/docs/misc/arm/device-tree/booting.txt -+++ b/docs/misc/arm/device-tree/booting.txt -@@ -182,6 +182,14 @@ with the following properties: - Both #address-cells and #size-cells need to be specified because - both sub-nodes (described shortly) have reg properties. - -+- xen,domain-p2m-mem-mb -+ -+ Optional. A 32-bit integer specifying the amount of megabytes of RAM -+ used for the domain P2M pool. This is in-sync with the shadow_memory -+ option in xl.cfg. Leaving this field empty in device tree will lead to -+ the default size of domain P2M pool, i.e. 1MB per guest vCPU plus 4KB -+ per MB of guest RAM plus 512KB for guest extended regions. -+ - Under the "xen,domain" compatible node, one or more sub-nodes are present - for the DomU kernel and ramdisk. - -diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c -index 2694c39127c5..a818f33a1afa 100644 ---- a/xen/arch/arm/domain.c -+++ b/xen/arch/arm/domain.c -@@ -997,6 +997,7 @@ enum { - PROG_page, - PROG_mapping, - PROG_p2m, -+ PROG_p2m_pool, - PROG_done, - }; - -@@ -1062,6 +1063,11 @@ int domain_relinquish_resources(struct domain *d) - if ( ret ) - return ret; - -+ PROGRESS(p2m_pool): -+ ret = p2m_teardown_allocation(d); -+ if( ret ) -+ return ret; -+ - PROGRESS(done): - break; - -diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c -index d02bacbcd1ed..8aec3755ca5d 100644 ---- a/xen/arch/arm/domain_build.c -+++ b/xen/arch/arm/domain_build.c -@@ -2833,6 +2833,21 @@ static void __init find_gnttab_region(struct domain *d, - kinfo->gnttab_start, kinfo->gnttab_start + kinfo->gnttab_size); - } - -+static unsigned long __init domain_p2m_pages(unsigned long maxmem_kb, -+ unsigned int smp_cpus) -+{ -+ /* -+ * Keep in sync with libxl__get_required_paging_memory(). -+ * 256 pages (1MB) per vcpu, plus 1 page per MiB of RAM for the P2M map, -+ * plus 128 pages to cover extended regions. -+ */ -+ unsigned long memkb = 4 * (256 * smp_cpus + (maxmem_kb / 1024) + 128); -+ -+ BUILD_BUG_ON(PAGE_SIZE != SZ_4K); -+ -+ return DIV_ROUND_UP(memkb, 1024) << (20 - PAGE_SHIFT); -+} -+ - static int __init construct_domain(struct domain *d, struct kernel_info *kinfo) - { - unsigned int i; -@@ -2924,6 +2939,8 @@ static int __init construct_domU(struct domain *d, - struct kernel_info kinfo = {}; - int rc; - u64 mem; -+ u32 p2m_mem_mb; -+ unsigned long p2m_pages; - - rc = dt_property_read_u64(node, "memory", &mem); - if ( !rc ) -@@ -2933,6 +2950,18 @@ static int __init construct_domU(struct domain *d, - } - kinfo.unassigned_mem = (paddr_t)mem * SZ_1K; - -+ rc = dt_property_read_u32(node, "xen,domain-p2m-mem-mb", &p2m_mem_mb); -+ /* If xen,domain-p2m-mem-mb is not specified, use the default value. */ -+ p2m_pages = rc ? -+ p2m_mem_mb << (20 - PAGE_SHIFT) : -+ domain_p2m_pages(mem, d->max_vcpus); -+ -+ spin_lock(&d->arch.paging.lock); -+ rc = p2m_set_allocation(d, p2m_pages, NULL); -+ spin_unlock(&d->arch.paging.lock); -+ if ( rc != 0 ) -+ return rc; -+ - printk("*** LOADING DOMU cpus=%u memory=%"PRIx64"KB ***\n", d->max_vcpus, mem); - - kinfo.vpl011 = dt_property_read_bool(node, "vpl011"); -diff --git a/xen/arch/arm/domctl.c b/xen/arch/arm/domctl.c -index 9bf72e693019..c8fdeb124084 100644 ---- a/xen/arch/arm/domctl.c -+++ b/xen/arch/arm/domctl.c -@@ -50,6 +50,9 @@ static int handle_vuart_init(struct domain *d, - static long p2m_domctl(struct domain *d, struct xen_domctl_shadow_op *sc, - XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) - { -+ long rc; -+ bool preempted = false; -+ - if ( unlikely(d == current->domain) ) - { - printk(XENLOG_ERR "Tried to do a p2m domctl op on itself.\n"); -@@ -66,9 +69,27 @@ static long p2m_domctl(struct domain *d, struct xen_domctl_shadow_op *sc, - switch ( sc->op ) - { - case XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION: -- return 0; -+ { -+ /* Allow and handle preemption */ -+ spin_lock(&d->arch.paging.lock); -+ rc = p2m_set_allocation(d, sc->mb << (20 - PAGE_SHIFT), &preempted); -+ spin_unlock(&d->arch.paging.lock); -+ -+ if ( preempted ) -+ /* Not finished. Set up to re-run the call. */ -+ rc = hypercall_create_continuation(__HYPERVISOR_domctl, "h", -+ u_domctl); -+ else -+ /* Finished. Return the new allocation. */ -+ sc->mb = p2m_get_allocation(d); -+ -+ return rc; -+ } - case XEN_DOMCTL_SHADOW_OP_GET_ALLOCATION: -+ { -+ sc->mb = p2m_get_allocation(d); - return 0; -+ } - default: - { - printk(XENLOG_ERR "Bad p2m domctl op %u\n", sc->op); -diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c -index 79f3d37f5230..1bf9cbeb53cf 100644 ---- a/xen/arch/arm/p2m.c -+++ b/xen/arch/arm/p2m.c -@@ -50,6 +50,54 @@ static uint64_t generate_vttbr(uint16_t vmid, mfn_t root_mfn) - return (mfn_to_maddr(root_mfn) | ((uint64_t)vmid << 48)); - } - -+static struct page_info *p2m_alloc_page(struct domain *d) -+{ -+ struct page_info *pg; -+ -+ spin_lock(&d->arch.paging.lock); -+ /* -+ * For hardware domain, there should be no limit in the number of pages that -+ * can be allocated, so that the kernel may take advantage of the extended -+ * regions. Hence, allocate p2m pages for hardware domains from heap. -+ */ -+ if ( is_hardware_domain(d) ) -+ { -+ pg = alloc_domheap_page(NULL, 0); -+ if ( pg == NULL ) -+ { -+ printk(XENLOG_G_ERR "Failed to allocate P2M pages for hwdom.\n"); -+ spin_unlock(&d->arch.paging.lock); -+ return NULL; -+ } -+ } -+ else -+ { -+ pg = page_list_remove_head(&d->arch.paging.p2m_freelist); -+ if ( unlikely(!pg) ) -+ { -+ spin_unlock(&d->arch.paging.lock); -+ return NULL; -+ } -+ d->arch.paging.p2m_total_pages--; -+ } -+ spin_unlock(&d->arch.paging.lock); -+ -+ return pg; -+} -+ -+static void p2m_free_page(struct domain *d, struct page_info *pg) -+{ -+ spin_lock(&d->arch.paging.lock); -+ if ( is_hardware_domain(d) ) -+ free_domheap_page(pg); -+ else -+ { -+ d->arch.paging.p2m_total_pages++; -+ page_list_add_tail(pg, &d->arch.paging.p2m_freelist); -+ } -+ spin_unlock(&d->arch.paging.lock); -+} -+ - /* Return the size of the pool, rounded up to the nearest MB */ - unsigned int p2m_get_allocation(struct domain *d) - { -@@ -751,7 +799,7 @@ static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry) - - ASSERT(!p2m_is_valid(*entry)); - -- page = alloc_domheap_page(NULL, 0); -+ page = p2m_alloc_page(p2m->domain); - if ( page == NULL ) - return -ENOMEM; - -@@ -878,7 +926,7 @@ static void p2m_free_entry(struct p2m_domain *p2m, - pg = mfn_to_page(mfn); - - page_list_del(pg, &p2m->pages); -- free_domheap_page(pg); -+ p2m_free_page(p2m->domain, pg); - } - - static bool p2m_split_superpage(struct p2m_domain *p2m, lpae_t *entry, -@@ -902,7 +950,7 @@ static bool p2m_split_superpage(struct p2m_domain *p2m, lpae_t *entry, - ASSERT(level < target); - ASSERT(p2m_is_superpage(*entry, level)); - -- page = alloc_domheap_page(NULL, 0); -+ page = p2m_alloc_page(p2m->domain); - if ( !page ) - return false; - -@@ -1641,7 +1689,7 @@ int p2m_teardown(struct domain *d) - - while ( (pg = page_list_remove_head(&p2m->pages)) ) - { -- free_domheap_page(pg); -+ p2m_free_page(p2m->domain, pg); - count++; - /* Arbitrarily preempt every 512 iterations */ - if ( !(count % 512) && hypercall_preempt_check() ) -@@ -1665,6 +1713,7 @@ void p2m_final_teardown(struct domain *d) - return; - - ASSERT(page_list_empty(&p2m->pages)); -+ ASSERT(page_list_empty(&d->arch.paging.p2m_freelist)); - - if ( p2m->root ) - free_domheap_pages(p2m->root, P2M_ROOT_ORDER); --- -2.37.1 - diff --git a/xsa410-4.16-01.patch b/xsa410-4.16-01.patch deleted file mode 100644 index 8f352ee..0000000 --- a/xsa410-4.16-01.patch +++ /dev/null @@ -1,59 +0,0 @@ -From 4b4359122a414cc15156e13e3805988b71ff9da0 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 6 Jun 2022 06:17:25 +0000 -Subject: [PATCH 1/2] xen/arm: p2m: Prevent adding mapping when domain is dying - -During the domain destroy process, the domain will still be accessible -until it is fully destroyed. So does the P2M because we don't bail -out early if is_dying is non-zero. If a domain has permission to -modify the other domain's P2M (i.e. dom0, or a stubdomain), then -foreign mapping can be added past relinquish_p2m_mapping(). - -Therefore, we need to prevent mapping to be added when the domain -is dying. This commit prevents such adding of mapping by adding the -d->is_dying check to p2m_set_entry(). Also this commit enhances the -check in relinquish_p2m_mapping() to make sure that no mappings can -be added in the P2M after the P2M lock is released. - -This is part of CVE-2022-33746 / XSA-410. - -Signed-off-by: Julien Grall -Signed-off-by: Henry Wang -Tested-by: Henry Wang -Reviewed-by: Stefano Stabellini ---- - xen/arch/arm/p2m.c | 11 +++++++++++ - 1 file changed, 11 insertions(+) - -diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c -index fb71fa4c1c90..cbeff90f4371 100644 ---- a/xen/arch/arm/p2m.c -+++ b/xen/arch/arm/p2m.c -@@ -1093,6 +1093,15 @@ int p2m_set_entry(struct p2m_domain *p2m, - { - int rc = 0; - -+ /* -+ * Any reference taken by the P2M mappings (e.g. foreign mapping) will -+ * be dropped in relinquish_p2m_mapping(). As the P2M will still -+ * be accessible after, we need to prevent mapping to be added when the -+ * domain is dying. -+ */ -+ if ( unlikely(p2m->domain->is_dying) ) -+ return -ENOMEM; -+ - while ( nr ) - { - unsigned long mask; -@@ -1610,6 +1619,8 @@ int relinquish_p2m_mapping(struct domain *d) - unsigned int order; - gfn_t start, end; - -+ BUG_ON(!d->is_dying); -+ /* No mappings can be added in the P2M after the P2M lock is released. */ - p2m_write_lock(p2m); - - start = p2m->lowest_mapped_gfn; --- -2.37.1 - diff --git a/xsa410-4.16-02.patch b/xsa410-4.16-02.patch deleted file mode 100644 index 7599a47..0000000 --- a/xsa410-4.16-02.patch +++ /dev/null @@ -1,165 +0,0 @@ -From 0d5846490348fa09a0d0915d7c795685a016ce10 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 6 Jun 2022 06:17:26 +0000 -Subject: [PATCH 2/2] xen/arm: p2m: Handle preemption when freeing intermediate - page tables - -At the moment the P2M page tables will be freed when the domain structure -is freed without any preemption. As the P2M is quite large, iterating -through this may take more time than it is reasonable without intermediate -preemption (to run softirqs and perhaps scheduler). - -Split p2m_teardown() in two parts: one preemptible and called when -relinquishing the resources, the other one non-preemptible and called -when freeing the domain structure. - -As we are now freeing the P2M pages early, we also need to prevent -further allocation if someone call p2m_set_entry() past p2m_teardown() -(I wasn't able to prove this will never happen). This is done by -the checking domain->is_dying from previous patch in p2m_set_entry(). - -Similarly, we want to make sure that no-one can accessed the free -pages. Therefore the root is cleared before freeing pages. - -This is part of CVE-2022-33746 / XSA-410. - -Signed-off-by: Julien Grall -Signed-off-by: Henry Wang -Tested-by: Henry Wang -Reviewed-by: Stefano Stabellini ---- - xen/arch/arm/domain.c | 10 +++++++-- - xen/arch/arm/p2m.c | 47 ++++++++++++++++++++++++++++++++++++--- - xen/include/asm-arm/p2m.h | 13 +++++++++-- - 3 files changed, 63 insertions(+), 7 deletions(-) - -diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c -index 96e1b235501d..2694c39127c5 100644 ---- a/xen/arch/arm/domain.c -+++ b/xen/arch/arm/domain.c -@@ -789,10 +789,10 @@ fail: - void arch_domain_destroy(struct domain *d) - { - /* IOMMU page table is shared with P2M, always call -- * iommu_domain_destroy() before p2m_teardown(). -+ * iommu_domain_destroy() before p2m_final_teardown(). - */ - iommu_domain_destroy(d); -- p2m_teardown(d); -+ p2m_final_teardown(d); - domain_vgic_free(d); - domain_vuart_free(d); - free_xenheap_page(d->shared_info); -@@ -996,6 +996,7 @@ enum { - PROG_xen, - PROG_page, - PROG_mapping, -+ PROG_p2m, - PROG_done, - }; - -@@ -1056,6 +1057,11 @@ int domain_relinquish_resources(struct domain *d) - if ( ret ) - return ret; - -+ PROGRESS(p2m): -+ ret = p2m_teardown(d); -+ if ( ret ) -+ return ret; -+ - PROGRESS(done): - break; - -diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c -index cbeff90f4371..3bcd1e897e88 100644 ---- a/xen/arch/arm/p2m.c -+++ b/xen/arch/arm/p2m.c -@@ -1527,17 +1527,58 @@ static void p2m_free_vmid(struct domain *d) - spin_unlock(&vmid_alloc_lock); - } - --void p2m_teardown(struct domain *d) -+int p2m_teardown(struct domain *d) - { - struct p2m_domain *p2m = p2m_get_hostp2m(d); -+ unsigned long count = 0; - struct page_info *pg; -+ unsigned int i; -+ int rc = 0; -+ -+ p2m_write_lock(p2m); -+ -+ /* -+ * We are about to free the intermediate page-tables, so clear the -+ * root to prevent any walk to use them. -+ */ -+ for ( i = 0; i < P2M_ROOT_PAGES; i++ ) -+ clear_and_clean_page(p2m->root + i); -+ -+ /* -+ * The domain will not be scheduled anymore, so in theory we should -+ * not need to flush the TLBs. Do it for safety purpose. -+ * -+ * Note that all the devices have already been de-assigned. So we don't -+ * need to flush the IOMMU TLB here. -+ */ -+ p2m_force_tlb_flush_sync(p2m); -+ -+ while ( (pg = page_list_remove_head(&p2m->pages)) ) -+ { -+ free_domheap_page(pg); -+ count++; -+ /* Arbitrarily preempt every 512 iterations */ -+ if ( !(count % 512) && hypercall_preempt_check() ) -+ { -+ rc = -ERESTART; -+ break; -+ } -+ } -+ -+ p2m_write_unlock(p2m); -+ -+ return rc; -+} -+ -+void p2m_final_teardown(struct domain *d) -+{ -+ struct p2m_domain *p2m = p2m_get_hostp2m(d); - - /* p2m not actually initialized */ - if ( !p2m->domain ) - return; - -- while ( (pg = page_list_remove_head(&p2m->pages)) ) -- free_domheap_page(pg); -+ ASSERT(page_list_empty(&p2m->pages)); - - if ( p2m->root ) - free_domheap_pages(p2m->root, P2M_ROOT_ORDER); -diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h -index 8f11d9c97b5d..b3ba83283e11 100644 ---- a/xen/include/asm-arm/p2m.h -+++ b/xen/include/asm-arm/p2m.h -@@ -192,8 +192,17 @@ void setup_virt_paging(void); - /* Init the datastructures for later use by the p2m code */ - int p2m_init(struct domain *d); - --/* Return all the p2m resources to Xen. */ --void p2m_teardown(struct domain *d); -+/* -+ * The P2M resources are freed in two parts: -+ * - p2m_teardown() will be called when relinquish the resources. It -+ * will free large resources (e.g. intermediate page-tables) that -+ * requires preemption. -+ * - p2m_final_teardown() will be called when domain struct is been -+ * freed. This *cannot* be preempted and therefore one small -+ * resources should be freed here. -+ */ -+int p2m_teardown(struct domain *d); -+void p2m_final_teardown(struct domain *d); - - /* - * Remove mapping refcount on each mapping page in the p2m --- -2.37.1 - diff --git a/xsa410-4.16-03.patch b/xsa410-4.16-03.patch deleted file mode 100644 index 7411d90..0000000 --- a/xsa410-4.16-03.patch +++ /dev/null @@ -1,113 +0,0 @@ -From: Roger Pau Monné -Subject: x86/p2m: add option to skip root pagetable removal in p2m_teardown() - -Add a new parameter to p2m_teardown() in order to select whether the -root page table should also be freed. Note that all users are -adjusted to pass the parameter to remove the root page tables, so -behavior is not modified. - -No functional change intended. - -This is part of CVE-2022-33746 / XSA-410. - -Suggested-by: Julien Grall -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich -Acked-by: Tim Deegan - ---- a/xen/include/asm-x86/p2m.h -+++ b/xen/include/asm-x86/p2m.h -@@ -574,7 +574,7 @@ int p2m_init(struct domain *d); - int p2m_alloc_table(struct p2m_domain *p2m); - - /* Return all the p2m resources to Xen. */ --void p2m_teardown(struct p2m_domain *p2m); -+void p2m_teardown(struct p2m_domain *p2m, bool remove_root); - void p2m_final_teardown(struct domain *d); - - /* Add a page to a domain's p2m table */ ---- a/xen/arch/x86/mm/hap/hap.c -+++ b/xen/arch/x86/mm/hap/hap.c -@@ -541,18 +541,18 @@ void hap_final_teardown(struct domain *d - } - - for ( i = 0; i < MAX_ALTP2M; i++ ) -- p2m_teardown(d->arch.altp2m_p2m[i]); -+ p2m_teardown(d->arch.altp2m_p2m[i], true); - } - - /* Destroy nestedp2m's first */ - for (i = 0; i < MAX_NESTEDP2M; i++) { -- p2m_teardown(d->arch.nested_p2m[i]); -+ p2m_teardown(d->arch.nested_p2m[i], true); - } - - if ( d->arch.paging.hap.total_pages != 0 ) - hap_teardown(d, NULL); - -- p2m_teardown(p2m_get_hostp2m(d)); -+ p2m_teardown(p2m_get_hostp2m(d), true); - /* Free any memory that the p2m teardown released */ - paging_lock(d); - hap_set_allocation(d, 0, NULL); ---- a/xen/arch/x86/mm/p2m.c -+++ b/xen/arch/x86/mm/p2m.c -@@ -749,11 +749,11 @@ int p2m_alloc_table(struct p2m_domain *p - * hvm fixme: when adding support for pvh non-hardware domains, this path must - * cleanup any foreign p2m types (release refcnts on them). - */ --void p2m_teardown(struct p2m_domain *p2m) -+void p2m_teardown(struct p2m_domain *p2m, bool remove_root) - /* Return all the p2m pages to Xen. - * We know we don't have any extra mappings to these pages */ - { -- struct page_info *pg; -+ struct page_info *pg, *root_pg = NULL; - struct domain *d; - - if (p2m == NULL) -@@ -763,10 +763,22 @@ void p2m_teardown(struct p2m_domain *p2m - - p2m_lock(p2m); - ASSERT(atomic_read(&d->shr_pages) == 0); -- p2m->phys_table = pagetable_null(); -+ -+ if ( remove_root ) -+ p2m->phys_table = pagetable_null(); -+ else if ( !pagetable_is_null(p2m->phys_table) ) -+ { -+ root_pg = pagetable_get_page(p2m->phys_table); -+ clear_domain_page(pagetable_get_mfn(p2m->phys_table)); -+ } - - while ( (pg = page_list_remove_head(&p2m->pages)) ) -- d->arch.paging.free_page(d, pg); -+ if ( pg != root_pg ) -+ d->arch.paging.free_page(d, pg); -+ -+ if ( root_pg ) -+ page_list_add(root_pg, &p2m->pages); -+ - p2m_unlock(p2m); - } - ---- a/xen/arch/x86/mm/shadow/common.c -+++ b/xen/arch/x86/mm/shadow/common.c -@@ -2701,7 +2701,7 @@ int shadow_enable(struct domain *d, u32 - paging_unlock(d); - out_unlocked: - if ( rv != 0 && !pagetable_is_null(p2m_get_pagetable(p2m)) ) -- p2m_teardown(p2m); -+ p2m_teardown(p2m, true); - if ( rv != 0 && pg != NULL ) - { - pg->count_info &= ~PGC_count_mask; -@@ -2866,7 +2866,7 @@ void shadow_final_teardown(struct domain - shadow_teardown(d, NULL); - - /* It is now safe to pull down the p2m map. */ -- p2m_teardown(p2m_get_hostp2m(d)); -+ p2m_teardown(p2m_get_hostp2m(d), true); - /* Free any shadow memory that the p2m teardown released */ - paging_lock(d); - shadow_set_allocation(d, 0, NULL); diff --git a/xsa410-4.16-04.patch b/xsa410-4.16-04.patch deleted file mode 100644 index 2170b8e..0000000 --- a/xsa410-4.16-04.patch +++ /dev/null @@ -1,62 +0,0 @@ -From: Jan Beulich -Subject: x86/HAP: adjust monitor table related error handling - -hap_make_monitor_table() will return INVALID_MFN if it encounters an -error condition, but hap_update_paging_modes() wasn’t handling this -value, resulting in an inappropriate value being stored in -monitor_table. This would subsequently misguide at least -hap_vcpu_teardown(). Avoid this by bailing early. - -Further, when a domain has/was already crashed or (perhaps less -important as there's no such path known to lead here) is already dying, -avoid calling domain_crash() on it again - that's at best confusing. - -This is part of CVE-2022-33746 / XSA-410. - -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné - ---- a/xen/arch/x86/mm/hap/hap.c -+++ b/xen/arch/x86/mm/hap/hap.c -@@ -39,6 +39,7 @@ - #include - #include - #include -+#include - - #include "private.h" - -@@ -405,8 +406,13 @@ static mfn_t hap_make_monitor_table(stru - return m4mfn; - - oom: -- printk(XENLOG_G_ERR "out of memory building monitor pagetable\n"); -- domain_crash(d); -+ if ( !d->is_dying && -+ (!d->is_shutting_down || d->shutdown_code != SHUTDOWN_crash) ) -+ { -+ printk(XENLOG_G_ERR "%pd: out of memory building monitor pagetable\n", -+ d); -+ domain_crash(d); -+ } - return INVALID_MFN; - } - -@@ -766,6 +772,9 @@ static void hap_update_paging_modes(stru - if ( pagetable_is_null(v->arch.hvm.monitor_table) ) - { - mfn_t mmfn = hap_make_monitor_table(v); -+ -+ if ( mfn_eq(mmfn, INVALID_MFN) ) -+ goto unlock; - v->arch.hvm.monitor_table = pagetable_from_mfn(mmfn); - make_cr3(v, mmfn); - hvm_update_host_cr3(v); -@@ -774,6 +783,7 @@ static void hap_update_paging_modes(stru - /* CR3 is effectively updated by a mode change. Flush ASIDs, etc. */ - hap_update_cr3(v, 0, false); - -+ unlock: - paging_unlock(d); - put_gfn(d, cr3_gfn); - } diff --git a/xsa410-4.16-05.patch b/xsa410-4.16-05.patch deleted file mode 100644 index dc626c7..0000000 --- a/xsa410-4.16-05.patch +++ /dev/null @@ -1,60 +0,0 @@ -From: Jan Beulich -Subject: x86/shadow: tolerate failure of sh_set_toplevel_shadow() - -Subsequently sh_set_toplevel_shadow() will be adjusted to install a -blank entry in case prealloc fails. There are, in fact, pre-existing -error paths which would put in place a blank entry. The 4- and 2-level -code in sh_update_cr3(), however, assume the top level entry to be -valid. - -Hence bail from the function in the unlikely event that it's not. Note -that 3-level logic works differently: In particular a guest is free to -supply a PDPTR pointing at 4 non-present (or otherwise deemed invalid) -entries. The guest will crash, but we already cope with that. - -Really mfn_valid() is likely wrong to use in sh_set_toplevel_shadow(), -and it should instead be !mfn_eq(gmfn, INVALID_MFN). Avoid such a change -in security context, but add a respective assertion. - -This is part of CVE-2022-33746 / XSA-410. - -Signed-off-by: Jan Beulich -Acked-by: Tim Deegan -Reviewed-by: Andrew Cooper - ---- a/xen/arch/x86/mm/shadow/common.c -+++ b/xen/arch/x86/mm/shadow/common.c -@@ -2516,6 +2516,7 @@ void sh_set_toplevel_shadow(struct vcpu - /* Now figure out the new contents: is this a valid guest MFN? */ - if ( !mfn_valid(gmfn) ) - { -+ ASSERT(mfn_eq(gmfn, INVALID_MFN)); - new_entry = pagetable_null(); - goto install_new_entry; - } ---- a/xen/arch/x86/mm/shadow/multi.c -+++ b/xen/arch/x86/mm/shadow/multi.c -@@ -3312,6 +3312,11 @@ sh_update_cr3(struct vcpu *v, int do_loc - if ( sh_remove_write_access(d, gmfn, 4, 0) != 0 ) - guest_flush_tlb_mask(d, d->dirty_cpumask); - sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l4_shadow, sh_make_shadow); -+ if ( unlikely(pagetable_is_null(v->arch.paging.shadow.shadow_table[0])) ) -+ { -+ ASSERT(d->is_dying || d->is_shutting_down); -+ return; -+ } - if ( !shadow_mode_external(d) && !is_pv_32bit_domain(d) ) - { - mfn_t smfn = pagetable_get_mfn(v->arch.paging.shadow.shadow_table[0]); -@@ -3370,6 +3375,11 @@ sh_update_cr3(struct vcpu *v, int do_loc - if ( sh_remove_write_access(d, gmfn, 2, 0) != 0 ) - guest_flush_tlb_mask(d, d->dirty_cpumask); - sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l2_shadow, sh_make_shadow); -+ if ( unlikely(pagetable_is_null(v->arch.paging.shadow.shadow_table[0])) ) -+ { -+ ASSERT(d->is_dying || d->is_shutting_down); -+ return; -+ } - #else - #error This should never happen - #endif diff --git a/xsa410-4.16-06.patch b/xsa410-4.16-06.patch deleted file mode 100644 index 8c89eb3..0000000 --- a/xsa410-4.16-06.patch +++ /dev/null @@ -1,255 +0,0 @@ -From: Roger Pau Monné -Subject: x86/shadow: tolerate failure in shadow_prealloc() - -Prevent _shadow_prealloc() from calling BUG() when unable to fulfill -the pre-allocation and instead return true/false. Modify -shadow_prealloc() to crash the domain on allocation failure (if the -domain is not already dying), as shadow cannot operate normally after -that. Modify callers to also gracefully handle {_,}shadow_prealloc() -failing to fulfill the request. - -Note this in turn requires adjusting the callers of -sh_make_monitor_table() also to handle it returning INVALID_MFN. -sh_update_paging_modes() is also modified to add additional error -paths in case of allocation failure, some of those will return with -null monitor page tables (and the domain likely crashed). This is no -different that current error paths, but the newly introduced ones are -more likely to trigger. - -The now added failure points in sh_update_paging_modes() also require -that on some error return paths the previous structures are cleared, -and thus monitor table is null. - -While there adjust the 'type' parameter type of shadow_prealloc() to -unsigned int rather than u32. - -This is part of CVE-2022-33746 / XSA-410. - -Signed-off-by: Roger Pau Monné -Signed-off-by: Jan Beulich -Acked-by: Tim Deegan - ---- a/xen/arch/x86/mm/shadow/common.c -+++ b/xen/arch/x86/mm/shadow/common.c -@@ -36,6 +36,7 @@ - #include - #include - #include -+#include - #include "private.h" - - DEFINE_PER_CPU(uint32_t,trace_shadow_path_flags); -@@ -928,14 +929,15 @@ static inline void trace_shadow_prealloc - - /* Make sure there are at least count order-sized pages - * available in the shadow page pool. */ --static void _shadow_prealloc(struct domain *d, unsigned int pages) -+static bool __must_check _shadow_prealloc(struct domain *d, unsigned int pages) - { - struct vcpu *v; - struct page_info *sp, *t; - mfn_t smfn; - int i; - -- if ( d->arch.paging.shadow.free_pages >= pages ) return; -+ if ( d->arch.paging.shadow.free_pages >= pages ) -+ return true; - - /* Shouldn't have enabled shadows if we've no vcpus. */ - ASSERT(d->vcpu && d->vcpu[0]); -@@ -951,7 +953,8 @@ static void _shadow_prealloc(struct doma - sh_unpin(d, smfn); - - /* See if that freed up enough space */ -- if ( d->arch.paging.shadow.free_pages >= pages ) return; -+ if ( d->arch.paging.shadow.free_pages >= pages ) -+ return true; - } - - /* Stage two: all shadow pages are in use in hierarchies that are -@@ -974,7 +977,7 @@ static void _shadow_prealloc(struct doma - if ( d->arch.paging.shadow.free_pages >= pages ) - { - guest_flush_tlb_mask(d, d->dirty_cpumask); -- return; -+ return true; - } - } - } -@@ -987,7 +990,12 @@ static void _shadow_prealloc(struct doma - d->arch.paging.shadow.total_pages, - d->arch.paging.shadow.free_pages, - d->arch.paging.shadow.p2m_pages); -- BUG(); -+ -+ ASSERT(d->is_dying); -+ -+ guest_flush_tlb_mask(d, d->dirty_cpumask); -+ -+ return false; - } - - /* Make sure there are at least count pages of the order according to -@@ -995,9 +1003,19 @@ static void _shadow_prealloc(struct doma - * This must be called before any calls to shadow_alloc(). Since this - * will free existing shadows to make room, it must be called early enough - * to avoid freeing shadows that the caller is currently working on. */ --void shadow_prealloc(struct domain *d, u32 type, unsigned int count) -+bool shadow_prealloc(struct domain *d, unsigned int type, unsigned int count) - { -- return _shadow_prealloc(d, shadow_size(type) * count); -+ bool ret = _shadow_prealloc(d, shadow_size(type) * count); -+ -+ if ( !ret && !d->is_dying && -+ (!d->is_shutting_down || d->shutdown_code != SHUTDOWN_crash) ) -+ /* -+ * Failing to allocate memory required for shadow usage can only result in -+ * a domain crash, do it here rather that relying on every caller to do it. -+ */ -+ domain_crash(d); -+ -+ return ret; - } - - /* Deliberately free all the memory we can: this will tear down all of -@@ -1218,7 +1236,7 @@ void shadow_free(struct domain *d, mfn_t - static struct page_info * - shadow_alloc_p2m_page(struct domain *d) - { -- struct page_info *pg; -+ struct page_info *pg = NULL; - - /* This is called both from the p2m code (which never holds the - * paging lock) and the log-dirty code (which always does). */ -@@ -1236,16 +1254,18 @@ shadow_alloc_p2m_page(struct domain *d) - d->arch.paging.shadow.p2m_pages, - shadow_min_acceptable_pages(d)); - } -- paging_unlock(d); -- return NULL; -+ goto out; - } - -- shadow_prealloc(d, SH_type_p2m_table, 1); -+ if ( !shadow_prealloc(d, SH_type_p2m_table, 1) ) -+ goto out; -+ - pg = mfn_to_page(shadow_alloc(d, SH_type_p2m_table, 0)); - d->arch.paging.shadow.p2m_pages++; - d->arch.paging.shadow.total_pages--; - ASSERT(!page_get_owner(pg) && !(pg->count_info & PGC_count_mask)); - -+ out: - paging_unlock(d); - - return pg; -@@ -1336,7 +1356,9 @@ int shadow_set_allocation(struct domain - else if ( d->arch.paging.shadow.total_pages > pages ) - { - /* Need to return memory to domheap */ -- _shadow_prealloc(d, 1); -+ if ( !_shadow_prealloc(d, 1) ) -+ return -ENOMEM; -+ - sp = page_list_remove_head(&d->arch.paging.shadow.freelist); - ASSERT(sp); - /* -@@ -2334,12 +2356,13 @@ static void sh_update_paging_modes(struc - if ( mfn_eq(v->arch.paging.shadow.oos_snapshot[0], INVALID_MFN) ) - { - int i; -+ -+ if ( !shadow_prealloc(d, SH_type_oos_snapshot, SHADOW_OOS_PAGES) ) -+ return; -+ - for(i = 0; i < SHADOW_OOS_PAGES; i++) -- { -- shadow_prealloc(d, SH_type_oos_snapshot, 1); - v->arch.paging.shadow.oos_snapshot[i] = - shadow_alloc(d, SH_type_oos_snapshot, 0); -- } - } - #endif /* OOS */ - -@@ -2403,6 +2426,9 @@ static void sh_update_paging_modes(struc - mfn_t mmfn = sh_make_monitor_table( - v, v->arch.paging.mode->shadow.shadow_levels); - -+ if ( mfn_eq(mmfn, INVALID_MFN) ) -+ return; -+ - v->arch.hvm.monitor_table = pagetable_from_mfn(mmfn); - make_cr3(v, mmfn); - hvm_update_host_cr3(v); -@@ -2441,6 +2467,12 @@ static void sh_update_paging_modes(struc - v->arch.hvm.monitor_table = pagetable_null(); - new_mfn = sh_make_monitor_table( - v, v->arch.paging.mode->shadow.shadow_levels); -+ if ( mfn_eq(new_mfn, INVALID_MFN) ) -+ { -+ sh_destroy_monitor_table(v, old_mfn, -+ old_mode->shadow.shadow_levels); -+ return; -+ } - v->arch.hvm.monitor_table = pagetable_from_mfn(new_mfn); - SHADOW_PRINTK("new monitor table %"PRI_mfn "\n", - mfn_x(new_mfn)); -@@ -2526,7 +2558,12 @@ void sh_set_toplevel_shadow(struct vcpu - if ( !mfn_valid(smfn) ) - { - /* Make sure there's enough free shadow memory. */ -- shadow_prealloc(d, root_type, 1); -+ if ( !shadow_prealloc(d, root_type, 1) ) -+ { -+ new_entry = pagetable_null(); -+ goto install_new_entry; -+ } -+ - /* Shadow the page. */ - smfn = make_shadow(v, gmfn, root_type); - } ---- a/xen/arch/x86/mm/shadow/hvm.c -+++ b/xen/arch/x86/mm/shadow/hvm.c -@@ -700,7 +700,9 @@ mfn_t sh_make_monitor_table(const struct - ASSERT(!pagetable_get_pfn(v->arch.hvm.monitor_table)); - - /* Guarantee we can get the memory we need */ -- shadow_prealloc(d, SH_type_monitor_table, CONFIG_PAGING_LEVELS); -+ if ( !shadow_prealloc(d, SH_type_monitor_table, CONFIG_PAGING_LEVELS) ) -+ return INVALID_MFN; -+ - m4mfn = shadow_alloc(d, SH_type_monitor_table, 0); - mfn_to_page(m4mfn)->shadow_flags = 4; - ---- a/xen/arch/x86/mm/shadow/multi.c -+++ b/xen/arch/x86/mm/shadow/multi.c -@@ -2440,9 +2440,14 @@ static int sh_page_fault(struct vcpu *v, - * Preallocate shadow pages *before* removing writable accesses - * otherwhise an OOS L1 might be demoted and promoted again with - * writable mappings. */ -- shadow_prealloc(d, -- SH_type_l1_shadow, -- GUEST_PAGING_LEVELS < 4 ? 1 : GUEST_PAGING_LEVELS - 1); -+ if ( !shadow_prealloc(d, SH_type_l1_shadow, -+ GUEST_PAGING_LEVELS < 4 -+ ? 1 : GUEST_PAGING_LEVELS - 1) ) -+ { -+ paging_unlock(d); -+ put_gfn(d, gfn_x(gfn)); -+ return 0; -+ } - - rc = gw_remove_write_accesses(v, va, &gw); - ---- a/xen/arch/x86/mm/shadow/private.h -+++ b/xen/arch/x86/mm/shadow/private.h -@@ -383,7 +383,8 @@ void shadow_promote(struct domain *d, mf - void shadow_demote(struct domain *d, mfn_t gmfn, u32 type); - - /* Shadow page allocation functions */ --void shadow_prealloc(struct domain *d, u32 shadow_type, unsigned int count); -+bool __must_check shadow_prealloc(struct domain *d, unsigned int shadow_type, -+ unsigned int count); - mfn_t shadow_alloc(struct domain *d, - u32 shadow_type, - unsigned long backpointer); diff --git a/xsa410-4.16-07.patch b/xsa410-4.16-07.patch deleted file mode 100644 index 9eea91c..0000000 --- a/xsa410-4.16-07.patch +++ /dev/null @@ -1,82 +0,0 @@ -From: Roger Pau Monné -Subject: x86/p2m: refuse new allocations for dying domains - -This will in particular prevent any attempts to add entries to the p2m, -once - in a subsequent change - non-root entries have been removed. - -This is part of CVE-2022-33746 / XSA-410. - -Signed-off-by: Roger Pau Monné -Signed-off-by: Jan Beulich -Acked-by: Tim Deegan - ---- a/xen/arch/x86/mm/hap/hap.c -+++ b/xen/arch/x86/mm/hap/hap.c -@@ -245,6 +245,9 @@ static struct page_info *hap_alloc(struc - - ASSERT(paging_locked_by_me(d)); - -+ if ( unlikely(d->is_dying) ) -+ return NULL; -+ - pg = page_list_remove_head(&d->arch.paging.hap.freelist); - if ( unlikely(!pg) ) - return NULL; -@@ -281,7 +284,7 @@ static struct page_info *hap_alloc_p2m_p - d->arch.paging.hap.p2m_pages++; - ASSERT(!page_get_owner(pg) && !(pg->count_info & PGC_count_mask)); - } -- else if ( !d->arch.paging.p2m_alloc_failed ) -+ else if ( !d->arch.paging.p2m_alloc_failed && !d->is_dying ) - { - d->arch.paging.p2m_alloc_failed = 1; - dprintk(XENLOG_ERR, "d%i failed to allocate from HAP pool\n", ---- a/xen/arch/x86/mm/shadow/common.c -+++ b/xen/arch/x86/mm/shadow/common.c -@@ -939,6 +939,10 @@ static bool __must_check _shadow_preallo - if ( d->arch.paging.shadow.free_pages >= pages ) - return true; - -+ if ( unlikely(d->is_dying) ) -+ /* No reclaim when the domain is dying, teardown will take care of it. */ -+ return false; -+ - /* Shouldn't have enabled shadows if we've no vcpus. */ - ASSERT(d->vcpu && d->vcpu[0]); - -@@ -991,7 +995,7 @@ static bool __must_check _shadow_preallo - d->arch.paging.shadow.free_pages, - d->arch.paging.shadow.p2m_pages); - -- ASSERT(d->is_dying); -+ ASSERT_UNREACHABLE(); - - guest_flush_tlb_mask(d, d->dirty_cpumask); - -@@ -1005,10 +1009,13 @@ static bool __must_check _shadow_preallo - * to avoid freeing shadows that the caller is currently working on. */ - bool shadow_prealloc(struct domain *d, unsigned int type, unsigned int count) - { -- bool ret = _shadow_prealloc(d, shadow_size(type) * count); -+ bool ret; -+ -+ if ( unlikely(d->is_dying) ) -+ return false; - -- if ( !ret && !d->is_dying && -- (!d->is_shutting_down || d->shutdown_code != SHUTDOWN_crash) ) -+ ret = _shadow_prealloc(d, shadow_size(type) * count); -+ if ( !ret && (!d->is_shutting_down || d->shutdown_code != SHUTDOWN_crash) ) - /* - * Failing to allocate memory required for shadow usage can only result in - * a domain crash, do it here rather that relying on every caller to do it. -@@ -1238,6 +1245,9 @@ shadow_alloc_p2m_page(struct domain *d) - { - struct page_info *pg = NULL; - -+ if ( unlikely(d->is_dying) ) -+ return NULL; -+ - /* This is called both from the p2m code (which never holds the - * paging lock) and the log-dirty code (which always does). */ - paging_lock_recursive(d); diff --git a/xsa410-4.16-08.patch b/xsa410-4.16-08.patch deleted file mode 100644 index 92cdb49..0000000 --- a/xsa410-4.16-08.patch +++ /dev/null @@ -1,96 +0,0 @@ -From: Roger Pau Monné -Subject: x86/p2m: truly free paging pool memory for dying domains - -Modify {hap,shadow}_free to free the page immediately if the domain is -dying, so that pages don't accumulate in the pool when -{shadow,hap}_final_teardown() get called. This is to limit the amount of -work which needs to be done there (in a non-preemptable manner). - -Note the call to shadow_free() in shadow_free_p2m_page() is moved after -increasing total_pages, so that the decrease done in shadow_free() in -case the domain is dying doesn't underflow the counter, even if just for -a short interval. - -This is part of CVE-2022-33746 / XSA-410. - -Signed-off-by: Roger Pau Monné -Signed-off-by: Jan Beulich -Acked-by: Tim Deegan - ---- a/xen/arch/x86/mm/hap/hap.c -+++ b/xen/arch/x86/mm/hap/hap.c -@@ -265,6 +265,18 @@ static void hap_free(struct domain *d, m - - ASSERT(paging_locked_by_me(d)); - -+ /* -+ * For dying domains, actually free the memory here. This way less work is -+ * left to hap_final_teardown(), which cannot easily have preemption checks -+ * added. -+ */ -+ if ( unlikely(d->is_dying) ) -+ { -+ free_domheap_page(pg); -+ d->arch.paging.hap.total_pages--; -+ return; -+ } -+ - d->arch.paging.hap.free_pages++; - page_list_add_tail(pg, &d->arch.paging.hap.freelist); - } ---- a/xen/arch/x86/mm/shadow/common.c -+++ b/xen/arch/x86/mm/shadow/common.c -@@ -1187,6 +1187,7 @@ mfn_t shadow_alloc(struct domain *d, - void shadow_free(struct domain *d, mfn_t smfn) - { - struct page_info *next = NULL, *sp = mfn_to_page(smfn); -+ bool dying = ACCESS_ONCE(d->is_dying); - struct page_list_head *pin_list; - unsigned int pages; - u32 shadow_type; -@@ -1229,11 +1230,32 @@ void shadow_free(struct domain *d, mfn_t - * just before the allocator hands the page out again. */ - page_set_tlbflush_timestamp(sp); - perfc_decr(shadow_alloc_count); -- page_list_add_tail(sp, &d->arch.paging.shadow.freelist); -+ -+ /* -+ * For dying domains, actually free the memory here. This way less -+ * work is left to shadow_final_teardown(), which cannot easily have -+ * preemption checks added. -+ */ -+ if ( unlikely(dying) ) -+ { -+ /* -+ * The backpointer field (sh.back) used by shadow code aliases the -+ * domain owner field, unconditionally clear it here to avoid -+ * free_domheap_page() attempting to parse it. -+ */ -+ page_set_owner(sp, NULL); -+ free_domheap_page(sp); -+ } -+ else -+ page_list_add_tail(sp, &d->arch.paging.shadow.freelist); -+ - sp = next; - } - -- d->arch.paging.shadow.free_pages += pages; -+ if ( unlikely(dying) ) -+ d->arch.paging.shadow.total_pages -= pages; -+ else -+ d->arch.paging.shadow.free_pages += pages; - } - - /* Divert a page from the pool to be used by the p2m mapping. -@@ -1303,9 +1325,9 @@ shadow_free_p2m_page(struct domain *d, s - * paging lock) and the log-dirty code (which always does). */ - paging_lock_recursive(d); - -- shadow_free(d, page_to_mfn(pg)); - d->arch.paging.shadow.p2m_pages--; - d->arch.paging.shadow.total_pages++; -+ shadow_free(d, page_to_mfn(pg)); - - paging_unlock(d); - } diff --git a/xsa410-4.16-09.patch b/xsa410-4.16-09.patch deleted file mode 100644 index 60259e1..0000000 --- a/xsa410-4.16-09.patch +++ /dev/null @@ -1,159 +0,0 @@ -From: Roger Pau Monné -Subject: x86/p2m: free the paging memory pool preemptively - -The paging memory pool is currently freed in two different places: -from {shadow,hap}_teardown() via domain_relinquish_resources() and -from {shadow,hap}_final_teardown() via complete_domain_destroy(). -While the former does handle preemption, the later doesn't. - -Attempt to move as much p2m related freeing as possible to happen -before the call to {shadow,hap}_teardown(), so that most memory can be -freed in a preemptive way. In order to avoid causing issues to -existing callers leave the root p2m page tables set and free them in -{hap,shadow}_final_teardown(). Also modify {hap,shadow}_free to free -the page immediately if the domain is dying, so that pages don't -accumulate in the pool when {shadow,hap}_final_teardown() get called. - -Move altp2m_vcpu_disable_ve() to be done in hap_teardown(), as that's -the place where altp2m_active gets disabled now. - -This is part of CVE-2022-33746 / XSA-410. - -Signed-off-by: Roger Pau Monné -Signed-off-by: Jan Beulich -Acked-by: Tim Deegan - ---- a/xen/arch/x86/domain.c -+++ b/xen/arch/x86/domain.c -@@ -38,7 +38,6 @@ - #include - #include - #include --#include - #include - #include - #include -@@ -2381,12 +2380,6 @@ int domain_relinquish_resources(struct d - vpmu_destroy(v); - } - -- if ( altp2m_active(d) ) -- { -- for_each_vcpu ( d, v ) -- altp2m_vcpu_disable_ve(v); -- } -- - if ( is_pv_domain(d) ) - { - for_each_vcpu ( d, v ) ---- a/xen/arch/x86/mm/hap/hap.c -+++ b/xen/arch/x86/mm/hap/hap.c -@@ -28,6 +28,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -546,24 +547,8 @@ void hap_final_teardown(struct domain *d - unsigned int i; - - if ( hvm_altp2m_supported() ) -- { -- d->arch.altp2m_active = 0; -- -- if ( d->arch.altp2m_eptp ) -- { -- free_xenheap_page(d->arch.altp2m_eptp); -- d->arch.altp2m_eptp = NULL; -- } -- -- if ( d->arch.altp2m_visible_eptp ) -- { -- free_xenheap_page(d->arch.altp2m_visible_eptp); -- d->arch.altp2m_visible_eptp = NULL; -- } -- - for ( i = 0; i < MAX_ALTP2M; i++ ) - p2m_teardown(d->arch.altp2m_p2m[i], true); -- } - - /* Destroy nestedp2m's first */ - for (i = 0; i < MAX_NESTEDP2M; i++) { -@@ -578,6 +563,8 @@ void hap_final_teardown(struct domain *d - paging_lock(d); - hap_set_allocation(d, 0, NULL); - ASSERT(d->arch.paging.hap.p2m_pages == 0); -+ ASSERT(d->arch.paging.hap.free_pages == 0); -+ ASSERT(d->arch.paging.hap.total_pages == 0); - paging_unlock(d); - } - -@@ -603,6 +590,7 @@ void hap_vcpu_teardown(struct vcpu *v) - void hap_teardown(struct domain *d, bool *preempted) - { - struct vcpu *v; -+ unsigned int i; - - ASSERT(d->is_dying); - ASSERT(d != current->domain); -@@ -611,6 +599,28 @@ void hap_teardown(struct domain *d, bool - for_each_vcpu ( d, v ) - hap_vcpu_teardown(v); - -+ /* Leave the root pt in case we get further attempts to modify the p2m. */ -+ if ( hvm_altp2m_supported() ) -+ { -+ if ( altp2m_active(d) ) -+ for_each_vcpu ( d, v ) -+ altp2m_vcpu_disable_ve(v); -+ -+ d->arch.altp2m_active = 0; -+ -+ FREE_XENHEAP_PAGE(d->arch.altp2m_eptp); -+ FREE_XENHEAP_PAGE(d->arch.altp2m_visible_eptp); -+ -+ for ( i = 0; i < MAX_ALTP2M; i++ ) -+ p2m_teardown(d->arch.altp2m_p2m[i], false); -+ } -+ -+ /* Destroy nestedp2m's after altp2m. */ -+ for ( i = 0; i < MAX_NESTEDP2M; i++ ) -+ p2m_teardown(d->arch.nested_p2m[i], false); -+ -+ p2m_teardown(p2m_get_hostp2m(d), false); -+ - paging_lock(d); /* Keep various asserts happy */ - - if ( d->arch.paging.hap.total_pages != 0 ) ---- a/xen/arch/x86/mm/shadow/common.c -+++ b/xen/arch/x86/mm/shadow/common.c -@@ -2824,8 +2824,17 @@ void shadow_teardown(struct domain *d, b - for_each_vcpu ( d, v ) - shadow_vcpu_teardown(v); - -+ p2m_teardown(p2m_get_hostp2m(d), false); -+ - paging_lock(d); - -+ /* -+ * Reclaim all shadow memory so that shadow_set_allocation() doesn't find -+ * in-use pages, as _shadow_prealloc() will no longer try to reclaim pages -+ * because the domain is dying. -+ */ -+ shadow_blow_tables(d); -+ - #if (SHADOW_OPTIMIZATIONS & (SHOPT_VIRTUAL_TLB|SHOPT_OUT_OF_SYNC)) - /* Free the virtual-TLB array attached to each vcpu */ - for_each_vcpu(d, v) -@@ -2946,6 +2955,9 @@ void shadow_final_teardown(struct domain - d->arch.paging.shadow.total_pages, - d->arch.paging.shadow.free_pages, - d->arch.paging.shadow.p2m_pages); -+ ASSERT(!d->arch.paging.shadow.total_pages); -+ ASSERT(!d->arch.paging.shadow.free_pages); -+ ASSERT(!d->arch.paging.shadow.p2m_pages); - paging_unlock(d); - } - diff --git a/xsa410-4.16-10.patch b/xsa410-4.16-10.patch deleted file mode 100644 index 258b7ff..0000000 --- a/xsa410-4.16-10.patch +++ /dev/null @@ -1,171 +0,0 @@ -From: Julien Grall -Subject: xen/x86: p2m: Add preemption in p2m_teardown() - -The list p2m->pages contain all the pages used by the P2M. On large -instance this can be quite large and the time spent to call -d->arch.paging.free_page() will take more than 1ms for a 80GB guest -on a Xen running in nested environment on a c5.metal. - -By extrapolation, it would take > 100ms for a 8TB guest (what we -current security support). So add some preemption in p2m_teardown() -and propagate to the callers. Note there are 3 places where -the preemption is not enabled: - - hap_final_teardown()/shadow_final_teardown(): We are - preventing update the P2M once the domain is dying (so - no more pages could be allocated) and most of the P2M pages - will be freed in preemptive manneer when relinquishing the - resources. So this is fine to disable preemption. - - shadow_enable(): This is fine because it will undo the allocation - that may have been made by p2m_alloc_table() (so only the root - page table). - -The preemption is arbitrarily checked every 1024 iterations. - -Note that with the current approach, Xen doesn't keep track on whether -the alt/nested P2Ms have been cleared. So there are some redundant work. -However, this is not expected to incurr too much overhead (the P2M lock -shouldn't be contended during teardown). So this is optimization is -left outside of the security event. - -This is part of CVE-2022-33746 / XSA-410. - -Signed-off-by: Julien Grall -Signed-off-by: Jan Beulich - ---- a/xen/include/asm-x86/p2m.h -+++ b/xen/include/asm-x86/p2m.h -@@ -574,7 +574,7 @@ int p2m_init(struct domain *d); - int p2m_alloc_table(struct p2m_domain *p2m); - - /* Return all the p2m resources to Xen. */ --void p2m_teardown(struct p2m_domain *p2m, bool remove_root); -+void p2m_teardown(struct p2m_domain *p2m, bool remove_root, bool *preempted); - void p2m_final_teardown(struct domain *d); - - /* Add a page to a domain's p2m table */ ---- a/xen/arch/x86/mm/hap/hap.c -+++ b/xen/arch/x86/mm/hap/hap.c -@@ -548,17 +548,17 @@ void hap_final_teardown(struct domain *d - - if ( hvm_altp2m_supported() ) - for ( i = 0; i < MAX_ALTP2M; i++ ) -- p2m_teardown(d->arch.altp2m_p2m[i], true); -+ p2m_teardown(d->arch.altp2m_p2m[i], true, NULL); - - /* Destroy nestedp2m's first */ - for (i = 0; i < MAX_NESTEDP2M; i++) { -- p2m_teardown(d->arch.nested_p2m[i], true); -+ p2m_teardown(d->arch.nested_p2m[i], true, NULL); - } - - if ( d->arch.paging.hap.total_pages != 0 ) - hap_teardown(d, NULL); - -- p2m_teardown(p2m_get_hostp2m(d), true); -+ p2m_teardown(p2m_get_hostp2m(d), true, NULL); - /* Free any memory that the p2m teardown released */ - paging_lock(d); - hap_set_allocation(d, 0, NULL); -@@ -612,14 +612,24 @@ void hap_teardown(struct domain *d, bool - FREE_XENHEAP_PAGE(d->arch.altp2m_visible_eptp); - - for ( i = 0; i < MAX_ALTP2M; i++ ) -- p2m_teardown(d->arch.altp2m_p2m[i], false); -+ { -+ p2m_teardown(d->arch.altp2m_p2m[i], false, preempted); -+ if ( preempted && *preempted ) -+ return; -+ } - } - - /* Destroy nestedp2m's after altp2m. */ - for ( i = 0; i < MAX_NESTEDP2M; i++ ) -- p2m_teardown(d->arch.nested_p2m[i], false); -+ { -+ p2m_teardown(d->arch.nested_p2m[i], false, preempted); -+ if ( preempted && *preempted ) -+ return; -+ } - -- p2m_teardown(p2m_get_hostp2m(d), false); -+ p2m_teardown(p2m_get_hostp2m(d), false, preempted); -+ if ( preempted && *preempted ) -+ return; - - paging_lock(d); /* Keep various asserts happy */ - ---- a/xen/arch/x86/mm/p2m.c -+++ b/xen/arch/x86/mm/p2m.c -@@ -749,12 +749,13 @@ int p2m_alloc_table(struct p2m_domain *p - * hvm fixme: when adding support for pvh non-hardware domains, this path must - * cleanup any foreign p2m types (release refcnts on them). - */ --void p2m_teardown(struct p2m_domain *p2m, bool remove_root) -+void p2m_teardown(struct p2m_domain *p2m, bool remove_root, bool *preempted) - /* Return all the p2m pages to Xen. - * We know we don't have any extra mappings to these pages */ - { - struct page_info *pg, *root_pg = NULL; - struct domain *d; -+ unsigned int i = 0; - - if (p2m == NULL) - return; -@@ -773,8 +774,19 @@ void p2m_teardown(struct p2m_domain *p2m - } - - while ( (pg = page_list_remove_head(&p2m->pages)) ) -- if ( pg != root_pg ) -- d->arch.paging.free_page(d, pg); -+ { -+ if ( pg == root_pg ) -+ continue; -+ -+ d->arch.paging.free_page(d, pg); -+ -+ /* Arbitrarily check preemption every 1024 iterations */ -+ if ( preempted && !(++i % 1024) && general_preempt_check() ) -+ { -+ *preempted = true; -+ break; -+ } -+ } - - if ( root_pg ) - page_list_add(root_pg, &p2m->pages); ---- a/xen/arch/x86/mm/shadow/common.c -+++ b/xen/arch/x86/mm/shadow/common.c -@@ -2770,8 +2770,12 @@ int shadow_enable(struct domain *d, u32 - out_locked: - paging_unlock(d); - out_unlocked: -+ /* -+ * This is fine to ignore the preemption here because only the root -+ * will be allocated by p2m_alloc_table(). -+ */ - if ( rv != 0 && !pagetable_is_null(p2m_get_pagetable(p2m)) ) -- p2m_teardown(p2m, true); -+ p2m_teardown(p2m, true, NULL); - if ( rv != 0 && pg != NULL ) - { - pg->count_info &= ~PGC_count_mask; -@@ -2824,7 +2828,9 @@ void shadow_teardown(struct domain *d, b - for_each_vcpu ( d, v ) - shadow_vcpu_teardown(v); - -- p2m_teardown(p2m_get_hostp2m(d), false); -+ p2m_teardown(p2m_get_hostp2m(d), false, preempted); -+ if ( preempted && *preempted ) -+ return; - - paging_lock(d); - -@@ -2945,7 +2951,7 @@ void shadow_final_teardown(struct domain - shadow_teardown(d, NULL); - - /* It is now safe to pull down the p2m map. */ -- p2m_teardown(p2m_get_hostp2m(d), true); -+ p2m_teardown(p2m_get_hostp2m(d), true, NULL); - /* Free any shadow memory that the p2m teardown released */ - paging_lock(d); - shadow_set_allocation(d, 0, NULL); diff --git a/xsa411.patch b/xsa411.patch deleted file mode 100644 index 50dcae4..0000000 --- a/xsa411.patch +++ /dev/null @@ -1,55 +0,0 @@ -From: Jan Beulich -Subject: gnttab: correct locking on transitive grant copy error path - -While the comment next to the lock dropping in preparation of -recursively calling acquire_grant_for_copy() mistakenly talks about the -rd == td case (excluded a few lines further up), the same concerns apply -to the calling of release_grant_for_copy() on a subsequent error path. - -This is CVE-2022-33748 / XSA-411. - -Fixes: ad48fb963dbf ("gnttab: fix transitive grant handling") -Signed-off-by: Jan Beulich ---- -v2: Extend code comment. - ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -2622,9 +2622,8 @@ acquire_grant_for_copy( - trans_domid); - - /* -- * acquire_grant_for_copy() could take the lock on the -- * remote table (if rd == td), so we have to drop the lock -- * here and reacquire. -+ * acquire_grant_for_copy() will take the lock on the remote table, -+ * so we have to drop the lock here and reacquire. - */ - active_entry_release(act); - grant_read_unlock(rgt); -@@ -2661,11 +2660,25 @@ acquire_grant_for_copy( - act->trans_gref != trans_gref || - !act->is_sub_page)) ) - { -+ /* -+ * Like above for acquire_grant_for_copy() we need to drop and then -+ * re-acquire the locks here to prevent lock order inversion issues. -+ * Unlike for acquire_grant_for_copy() we don't need to re-check -+ * anything, as release_grant_for_copy() doesn't depend on the grant -+ * table entry: It only updates internal state and the status flags. -+ */ -+ active_entry_release(act); -+ grant_read_unlock(rgt); -+ - release_grant_for_copy(td, trans_gref, readonly); - rcu_unlock_domain(td); -+ -+ grant_read_lock(rgt); -+ act = active_entry_acquire(rgt, gref); - reduce_status_for_pin(rd, act, status, readonly); - active_entry_release(act); - grant_read_unlock(rgt); -+ - put_page(*page); - *page = NULL; - return ERESTART; diff --git a/xsa412-4.16.patch b/xsa412-4.16.patch deleted file mode 100644 index f37fc21..0000000 --- a/xsa412-4.16.patch +++ /dev/null @@ -1,245 +0,0 @@ -From: Andrew Cooper -Subject: x86/vmx: Revert "VMX: use a single, global APIC access page" - -The claim "No accesses would ever go to this page." is false. A consequence -of how Intel's APIC Acceleration works, and Xen's choice to have per-domain -P2Ms (rather than per-vCPU P2Ms) means that the APIC page is fully read-write -to any vCPU which is not in xAPIC mode. - -This reverts commit 58850b9074d3e7affdf3bc94c84e417ecfa4d165. - -This is XSA-412 / CVE-2022-42327. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c -index d429d76c18c9..3f4276531322 100644 ---- a/xen/arch/x86/hvm/vmx/vmx.c -+++ b/xen/arch/x86/hvm/vmx/vmx.c -@@ -66,7 +66,8 @@ boolean_param("force-ept", opt_force_ept); - static void vmx_ctxt_switch_from(struct vcpu *v); - static void vmx_ctxt_switch_to(struct vcpu *v); - --static int alloc_vlapic_mapping(void); -+static int vmx_alloc_vlapic_mapping(struct domain *d); -+static void vmx_free_vlapic_mapping(struct domain *d); - static void vmx_install_vlapic_mapping(struct vcpu *v); - static void vmx_update_guest_cr(struct vcpu *v, unsigned int cr, - unsigned int flags); -@@ -77,8 +78,6 @@ static int vmx_msr_read_intercept(unsigned int msr, uint64_t *msr_content); - static int vmx_msr_write_intercept(unsigned int msr, uint64_t msr_content); - static void vmx_invlpg(struct vcpu *v, unsigned long linear); - --static mfn_t __read_mostly apic_access_mfn = INVALID_MFN_INITIALIZER; -- - /* Values for domain's ->arch.hvm_domain.pi_ops.flags. */ - #define PI_CSW_FROM (1u << 0) - #define PI_CSW_TO (1u << 1) -@@ -402,6 +401,7 @@ static int vmx_domain_initialise(struct domain *d) - .to = vmx_ctxt_switch_to, - .tail = vmx_do_resume, - }; -+ int rc; - - d->arch.ctxt_switch = &csw; - -@@ -411,15 +411,24 @@ static int vmx_domain_initialise(struct domain *d) - */ - d->arch.hvm.vmx.exec_sp = is_hardware_domain(d) || opt_ept_exec_sp; - -+ if ( (rc = vmx_alloc_vlapic_mapping(d)) != 0 ) -+ return rc; -+ - return 0; - } - -+static void vmx_domain_relinquish_resources(struct domain *d) -+{ -+ vmx_free_vlapic_mapping(d); -+} -+ - static void domain_creation_finished(struct domain *d) - { - gfn_t gfn = gaddr_to_gfn(APIC_DEFAULT_PHYS_BASE); -+ mfn_t apic_access_mfn = d->arch.hvm.vmx.apic_access_mfn; - bool ipat; - -- if ( !has_vlapic(d) || mfn_eq(apic_access_mfn, INVALID_MFN) ) -+ if ( mfn_eq(apic_access_mfn, _mfn(0)) ) - return; - - ASSERT(epte_get_entry_emt(d, gfn, apic_access_mfn, 0, &ipat, -@@ -2481,6 +2490,7 @@ static struct hvm_function_table __initdata vmx_function_table = { - .cpu_up_prepare = vmx_cpu_up_prepare, - .cpu_dead = vmx_cpu_dead, - .domain_initialise = vmx_domain_initialise, -+ .domain_relinquish_resources = vmx_domain_relinquish_resources, - .domain_creation_finished = domain_creation_finished, - .vcpu_initialise = vmx_vcpu_initialise, - .vcpu_destroy = vmx_vcpu_destroy, -@@ -2731,7 +2741,7 @@ const struct hvm_function_table * __init start_vmx(void) - { - set_in_cr4(X86_CR4_VMXE); - -- if ( vmx_vmcs_init() || alloc_vlapic_mapping() ) -+ if ( vmx_vmcs_init() ) - { - printk("VMX: failed to initialise.\n"); - return NULL; -@@ -3305,36 +3315,55 @@ static int vmx_msr_read_intercept(unsigned int msr, uint64_t *msr_content) - return X86EMUL_EXCEPTION; - } - --static int __init alloc_vlapic_mapping(void) -+static int vmx_alloc_vlapic_mapping(struct domain *d) - { - struct page_info *pg; - mfn_t mfn; - -- if ( !cpu_has_vmx_virtualize_apic_accesses ) -+ if ( !has_vlapic(d) || !cpu_has_vmx_virtualize_apic_accesses ) - return 0; - -- pg = alloc_domheap_page(NULL, 0); -+ pg = alloc_domheap_page(d, MEMF_no_refcount); - if ( !pg ) - return -ENOMEM; - -- /* -- * Signal to shadow code that this page cannot be refcounted. This also -- * makes epte_get_entry_emt() recognize this page as "special". -- */ -- page_suppress_refcounting(pg); -+ if ( !get_page_and_type(pg, d, PGT_writable_page) ) -+ { -+ /* -+ * The domain can't possibly know about this page yet, so failure -+ * here is a clear indication of something fishy going on. -+ */ -+ domain_crash(d); -+ return -ENODATA; -+ } - - mfn = page_to_mfn(pg); - clear_domain_page(mfn); -- apic_access_mfn = mfn; -+ d->arch.hvm.vmx.apic_access_mfn = mfn; - - return 0; - } - -+static void vmx_free_vlapic_mapping(struct domain *d) -+{ -+ mfn_t mfn = d->arch.hvm.vmx.apic_access_mfn; -+ -+ d->arch.hvm.vmx.apic_access_mfn = _mfn(0); -+ if ( !mfn_eq(mfn, _mfn(0)) ) -+ { -+ struct page_info *pg = mfn_to_page(mfn); -+ -+ put_page_alloc_ref(pg); -+ put_page_and_type(pg); -+ } -+} -+ - static void vmx_install_vlapic_mapping(struct vcpu *v) - { -+ mfn_t apic_access_mfn = v->domain->arch.hvm.vmx.apic_access_mfn; - paddr_t virt_page_ma, apic_page_ma; - -- if ( !has_vlapic(v->domain) || mfn_eq(apic_access_mfn, INVALID_MFN) ) -+ if ( mfn_eq(apic_access_mfn, _mfn(0)) ) - return; - - ASSERT(cpu_has_vmx_virtualize_apic_accesses); -diff --git a/xen/arch/x86/mm/shadow/set.c b/xen/arch/x86/mm/shadow/set.c -index 87e9c6eeb219..bd6c68b547c9 100644 ---- a/xen/arch/x86/mm/shadow/set.c -+++ b/xen/arch/x86/mm/shadow/set.c -@@ -101,14 +101,6 @@ shadow_get_page_from_l1e(shadow_l1e_t sl1e, struct domain *d, p2m_type_t type) - owner = page_get_owner(pg); - } - -- /* -- * Check whether refcounting is suppressed on this page. For example, -- * VMX'es APIC access MFN is just a surrogate page. It doesn't actually -- * get accessed, and hence there's no need to refcount it. -- */ -- if ( pg && page_refcounting_suppressed(pg) ) -- return 0; -- - if ( owner == dom_io ) - owner = NULL; - -diff --git a/xen/arch/x86/mm/shadow/types.h b/xen/arch/x86/mm/shadow/types.h -index 6970e7d6ea4a..814a4018535a 100644 ---- a/xen/arch/x86/mm/shadow/types.h -+++ b/xen/arch/x86/mm/shadow/types.h -@@ -276,16 +276,9 @@ int shadow_set_l4e(struct domain *d, shadow_l4e_t *sl4e, - static void inline - shadow_put_page_from_l1e(shadow_l1e_t sl1e, struct domain *d) - { -- mfn_t mfn = shadow_l1e_get_mfn(sl1e); -- - if ( !shadow_mode_refcounts(d) ) - return; - -- if ( mfn_valid(mfn) && -- /* See the respective comment in shadow_get_page_from_l1e(). */ -- page_refcounting_suppressed(mfn_to_page(mfn)) ) -- return; -- - put_page_from_l1e(sl1e, d); - } - -diff --git a/xen/include/asm-x86/hvm/vmx/vmcs.h b/xen/include/asm-x86/hvm/vmx/vmcs.h -index 03c9ccf627ab..8073af323b96 100644 ---- a/xen/include/asm-x86/hvm/vmx/vmcs.h -+++ b/xen/include/asm-x86/hvm/vmx/vmcs.h -@@ -58,6 +58,7 @@ struct ept_data { - #define _VMX_DOMAIN_PML_ENABLED 0 - #define VMX_DOMAIN_PML_ENABLED (1ul << _VMX_DOMAIN_PML_ENABLED) - struct vmx_domain { -+ mfn_t apic_access_mfn; - /* VMX_DOMAIN_* */ - unsigned int status; - -diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h -index 7bdf9c2290d8..e1bcea57a8f5 100644 ---- a/xen/include/asm-x86/mm.h -+++ b/xen/include/asm-x86/mm.h -@@ -83,7 +83,7 @@ - #define PGC_state_offlined PG_mask(2, 6) - #define PGC_state_free PG_mask(3, 6) - #define page_state_is(pg, st) (((pg)->count_info&PGC_state) == PGC_state_##st) --/* Page is not reference counted (see below for caveats) */ -+/* Page is not reference counted */ - #define _PGC_extra PG_shift(7) - #define PGC_extra PG_mask(1, 7) - -@@ -375,24 +375,6 @@ void zap_ro_mpt(mfn_t mfn); - - bool is_iomem_page(mfn_t mfn); - --/* -- * Pages with no owner which may get passed to functions wanting to -- * refcount them can be marked PGC_extra to bypass this refcounting (which -- * would fail due to the lack of an owner). -- * -- * (For pages with owner PGC_extra has different meaning.) -- */ --static inline void page_suppress_refcounting(struct page_info *pg) --{ -- ASSERT(!page_get_owner(pg)); -- pg->count_info |= PGC_extra; --} -- --static inline bool page_refcounting_suppressed(const struct page_info *pg) --{ -- return !page_get_owner(pg) && (pg->count_info & PGC_extra); --} -- - struct platform_bad_page { - unsigned long mfn; - unsigned int order; diff --git a/xsa414.patch b/xsa414.patch deleted file mode 100644 index 27ab0c2..0000000 --- a/xsa414.patch +++ /dev/null @@ -1,112 +0,0 @@ -From: Julien Grall -Subject: tools/xenstore: create_node: Don't defer work to undo any changes on - failure - -XSA-115 extended destroy_node() to update the node accounting for the -connection. The implementation is assuming the connection is the parent -of the node, however all the nodes are allocated using a separate context -(see process_message()). This will result to crash (or corrupt) xenstored -as the pointer is wrongly used. - -In case of an error, any changes to the database or update to the -accounting will now be reverted in create_node() by calling directly -destroy_node(). This has the nice advantage to remove the loop to unset -the destructors in case of success. - -Take the opportunity to free the nodes right now as they are not -going to be reachable (the function returns NULL) and are just wasting -resources. - -This is XSA-414 / CVE-2022-42309. - -Reported-by: Julien Grall -Fixes: 0bfb2101f243 ("tools/xenstore: fix node accounting after failed node creation") -Signed-off-by: Julien Grall -Reviewed-by: Juergen Gross - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 8867f93431d4..c30d14cbf2ab 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1084,9 +1084,8 @@ static struct node *construct_node(struct connection *conn, const void *ctx, - return NULL; - } - --static int destroy_node(void *_node) -+static int destroy_node(struct connection *conn, struct node *node) - { -- struct node *node = _node; - TDB_DATA key; - - if (streq(node->name, "/")) -@@ -1095,7 +1094,7 @@ static int destroy_node(void *_node) - set_tdb_key(node->name, &key); - tdb_delete(tdb_ctx, key); - -- domain_entry_dec(talloc_parent(node), node); -+ domain_entry_dec(conn, node); - - return 0; - } -@@ -1104,7 +1103,8 @@ static struct node *create_node(struct connection *conn, const void *ctx, - const char *name, - void *data, unsigned int datalen) - { -- struct node *node, *i; -+ struct node *node, *i, *j; -+ int ret; - - node = construct_node(conn, ctx, name); - if (!node) -@@ -1126,23 +1126,40 @@ static struct node *create_node(struct connection *conn, const void *ctx, - /* i->parent is set for each new node, so check quota. */ - if (i->parent && - domain_entry(conn) >= quota_nb_entry_per_domain) { -- errno = ENOSPC; -- return NULL; -+ ret = ENOSPC; -+ goto err; - } -- if (write_node(conn, i, false)) -- return NULL; - -- /* Account for new node, set destructor for error case. */ -- if (i->parent) { -+ ret = write_node(conn, i, false); -+ if (ret) -+ goto err; -+ -+ /* Account for new node */ -+ if (i->parent) - domain_entry_inc(conn, i); -- talloc_set_destructor(i, destroy_node); -- } - } - -- /* OK, now remove destructors so they stay around */ -- for (i = node; i->parent; i = i->parent) -- talloc_set_destructor(i, NULL); - return node; -+ -+err: -+ /* -+ * We failed to update TDB for some of the nodes. Undo any work that -+ * have already been done. -+ */ -+ for (j = node; j != i; j = j->parent) -+ destroy_node(conn, j); -+ -+ /* We don't need to keep the nodes around, so free them. */ -+ i = node; -+ while (i) { -+ j = i; -+ i = i->parent; -+ talloc_free(j); -+ } -+ -+ errno = ret; -+ -+ return NULL; - } - - /* path, data... */ diff --git a/xsa415.patch b/xsa415.patch deleted file mode 100644 index b6f6971..0000000 --- a/xsa415.patch +++ /dev/null @@ -1,134 +0,0 @@ -From: Julien Grall -Subject: tools/xenstore: Fail a transaction if it is not possible to create a - node - -Commit f2bebf72c4d5 "xenstore: rework of transaction handling" moved -out from copying the entire database everytime a new transaction is -opened to track the list of nodes changed. - -The content of all the nodes accessed during a transaction will be -temporarily stored in TDB using a different key. - -The function create_node() may write/update multiple nodes if the child -doesn't exist. In case of a failure, the function will revert any -changes (this include any update to TDB). Unfortunately, the function -which reverts the changes (i.e. destroy_node()) will not use the correct -key to delete any update or even request the transaction to fail. - -This means that if a client decide to go ahead with committing the -transaction, orphan nodes will be created because they were not linked -to an existing node (create_node() will write the nodes backwards). - -Once some nodes have been partially updated in a transaction, it is not -easily possible to undo any changes. So rather than continuing and hit -weird issue while committing, it is much saner to fail the transaction. - -This will have an impact on any client that decides to commit even if it -can't write a node. Although, it is not clear why a normal client would -want to do that... - -Lastly, update destroy_node() to use the correct key for deleting the -node. Rather than recreating it (this will allocate memory and -therefore fail), stash the key in the structure node. - -This is XSA-415 / CVE-2022-42310. - -Reported-by: Julien Grall -Signed-off-by: Julien Grall -Reviewed-by: Juergen Gross - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index c30d14cbf2ab..55b79e4c032e 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -562,15 +562,17 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, - return 0; - } - -+/* -+ * Write the node. If the node is written, caller can find the key used in -+ * node->key. This can later be used if the change needs to be reverted. -+ */ - static int write_node(struct connection *conn, struct node *node, - bool no_quota_check) - { -- TDB_DATA key; -- -- if (access_node(conn, node, NODE_ACCESS_WRITE, &key)) -+ if (access_node(conn, node, NODE_ACCESS_WRITE, &node->key)) - return errno; - -- return write_node_raw(conn, &key, node, no_quota_check); -+ return write_node_raw(conn, &node->key, node, no_quota_check); - } - - unsigned int perm_for_conn(struct connection *conn, -@@ -1086,16 +1088,21 @@ static struct node *construct_node(struct connection *conn, const void *ctx, - - static int destroy_node(struct connection *conn, struct node *node) - { -- TDB_DATA key; -- - if (streq(node->name, "/")) - corrupt(NULL, "Destroying root node!"); - -- set_tdb_key(node->name, &key); -- tdb_delete(tdb_ctx, key); -+ tdb_delete(tdb_ctx, node->key); - - domain_entry_dec(conn, node); - -+ /* -+ * It is not possible to easily revert the changes in a transaction. -+ * So if the failure happens in a transaction, mark it as fail to -+ * prevent any commit. -+ */ -+ if ( conn->transaction ) -+ fail_transaction(conn->transaction); -+ - return 0; - } - -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index 742812a97469..7d0fe77e7989 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -155,6 +155,8 @@ struct node_perms { - - struct node { - const char *name; -+ /* Key used to update TDB */ -+ TDB_DATA key; - - /* Parent (optional) */ - struct node *parent; -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index cd07fb0f218b..faf6c930e42a 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -580,6 +580,11 @@ void transaction_entry_dec(struct transaction *trans, unsigned int domid) - list_add_tail(&d->list, &trans->changed_domains); - } - -+void fail_transaction(struct transaction *trans) -+{ -+ trans->fail = true; -+} -+ - void conn_delete_all_transactions(struct connection *conn) - { - struct transaction *trans; -diff --git a/tools/xenstore/xenstored_transaction.h b/tools/xenstore/xenstored_transaction.h -index 43a162bea3f3..14062730e3c9 100644 ---- a/tools/xenstore/xenstored_transaction.h -+++ b/tools/xenstore/xenstored_transaction.h -@@ -46,6 +46,9 @@ int access_node(struct connection *conn, struct node *node, - int transaction_prepend(struct connection *conn, const char *name, - TDB_DATA *key); - -+/* Mark the transaction as failed. This will prevent it to be committed. */ -+void fail_transaction(struct transaction *trans); -+ - void conn_delete_all_transactions(struct connection *conn); - int check_transactions(struct hashtable *hash); - diff --git a/xsa416-4.16.patch b/xsa416-4.16.patch deleted file mode 100644 index 0df409a..0000000 --- a/xsa416-4.16.patch +++ /dev/null @@ -1,704 +0,0 @@ -From 80d128b14482d2e9342184d2d9949367851c4d14 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:10 +0200 -Subject: tools/xenstore: don't use conn->in as context for temporary - allocations - -Using the struct buffered data pointer of the current processed request -for temporary data allocations has a major drawback: the used area (and -with that the temporary data) is freed only after the response of the -request has been written to the ring page or has been read via the -socket. This can happen much later in case a guest isn't reading its -responses fast enough. - -As the temporary data can be safely freed after creating the response, -add a temporary context for that purpose and use that for allocating -the temporary memory, as it was already the case before commit -cc0612464896 ("xenstore: add small default data buffer to internal -struct"). - -Some sub-functions need to gain the "const" attribute for the talloc -context. - -This is XSA-416 / CVE-2022-42319. - -Reported-by: Julien Grall -Fixes: cc0612464896 ("xenstore: add small default data buffer to internal struct") -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_control.c b/tools/xenstore/xenstored_control.c -index 1031a81c3874..d0350c6ad861 100644 ---- a/tools/xenstore/xenstored_control.c -+++ b/tools/xenstore/xenstored_control.c -@@ -155,7 +155,7 @@ bool lu_is_pending(void) - - struct cmd_s { - char *cmd; -- int (*func)(void *, struct connection *, char **, int); -+ int (*func)(const void *, struct connection *, char **, int); - char *pars; - /* - * max_pars can be used to limit the size of the parameter vector, -@@ -167,7 +167,7 @@ struct cmd_s { - unsigned int max_pars; - }; - --static int do_control_check(void *ctx, struct connection *conn, -+static int do_control_check(const void *ctx, struct connection *conn, - char **vec, int num) - { - if (num) -@@ -179,7 +179,7 @@ static int do_control_check(void *ctx, struct connection *conn, - return 0; - } - --static int do_control_log(void *ctx, struct connection *conn, -+static int do_control_log(const void *ctx, struct connection *conn, - char **vec, int num) - { - if (num != 1) -@@ -281,7 +281,7 @@ static int quota_get(const void *ctx, struct connection *conn, - return domain_get_quota(ctx, conn, atoi(vec[0])); - } - --static int do_control_quota(void *ctx, struct connection *conn, -+static int do_control_quota(const void *ctx, struct connection *conn, - char **vec, int num) - { - if (num == 0) -@@ -293,7 +293,7 @@ static int do_control_quota(void *ctx, struct connection *conn, - return quota_get(ctx, conn, vec, num); - } - --static int do_control_quota_s(void *ctx, struct connection *conn, -+static int do_control_quota_s(const void *ctx, struct connection *conn, - char **vec, int num) - { - if (num == 0) -@@ -306,7 +306,7 @@ static int do_control_quota_s(void *ctx, struct connection *conn, - } - - #ifdef __MINIOS__ --static int do_control_memreport(void *ctx, struct connection *conn, -+static int do_control_memreport(const void *ctx, struct connection *conn, - char **vec, int num) - { - if (num) -@@ -318,7 +318,7 @@ static int do_control_memreport(void *ctx, struct connection *conn, - return 0; - } - #else --static int do_control_logfile(void *ctx, struct connection *conn, -+static int do_control_logfile(const void *ctx, struct connection *conn, - char **vec, int num) - { - if (num != 1) -@@ -333,7 +333,7 @@ static int do_control_logfile(void *ctx, struct connection *conn, - return 0; - } - --static int do_control_memreport(void *ctx, struct connection *conn, -+static int do_control_memreport(const void *ctx, struct connection *conn, - char **vec, int num) - { - FILE *fp; -@@ -373,7 +373,7 @@ static int do_control_memreport(void *ctx, struct connection *conn, - } - #endif - --static int do_control_print(void *ctx, struct connection *conn, -+static int do_control_print(const void *ctx, struct connection *conn, - char **vec, int num) - { - if (num != 1) -@@ -875,7 +875,7 @@ static const char *lu_start(const void *ctx, struct connection *conn, - return NULL; - } - --static int do_control_lu(void *ctx, struct connection *conn, -+static int do_control_lu(const void *ctx, struct connection *conn, - char **vec, int num) - { - const char *ret = NULL; -@@ -922,7 +922,7 @@ static int do_control_lu(void *ctx, struct connection *conn, - } - #endif - --static int do_control_help(void *, struct connection *, char **, int); -+static int do_control_help(const void *, struct connection *, char **, int); - - static struct cmd_s cmds[] = { - { "check", do_control_check, "" }, -@@ -961,7 +961,7 @@ static struct cmd_s cmds[] = { - { "help", do_control_help, "" }, - }; - --static int do_control_help(void *ctx, struct connection *conn, -+static int do_control_help(const void *ctx, struct connection *conn, - char **vec, int num) - { - int cmd, len = 0; -@@ -997,7 +997,8 @@ static int do_control_help(void *ctx, struct connection *conn, - return 0; - } - --int do_control(struct connection *conn, struct buffered_data *in) -+int do_control(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - unsigned int cmd, num, off; - char **vec = NULL; -@@ -1017,11 +1018,11 @@ int do_control(struct connection *conn, struct buffered_data *in) - num = xs_count_strings(in->buffer, in->used); - if (cmds[cmd].max_pars) - num = min(num, cmds[cmd].max_pars); -- vec = talloc_array(in, char *, num); -+ vec = talloc_array(ctx, char *, num); - if (!vec) - return ENOMEM; - if (get_strings(in, vec, num) < num) - return EIO; - -- return cmds[cmd].func(in, conn, vec + 1, num - 1); -+ return cmds[cmd].func(ctx, conn, vec + 1, num - 1); - } -diff --git a/tools/xenstore/xenstored_control.h b/tools/xenstore/xenstored_control.h -index 98b6fbcea2b1..a8cb76559ba1 100644 ---- a/tools/xenstore/xenstored_control.h -+++ b/tools/xenstore/xenstored_control.h -@@ -16,7 +16,8 @@ - along with this program; If not, see . - */ - --int do_control(struct connection *conn, struct buffered_data *in); -+int do_control(const void *ctx, struct connection *conn, -+ struct buffered_data *in); - void lu_read_state(void); - - struct connection *lu_get_connection(void); -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 16504de42017..411cc0e44714 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1248,11 +1248,13 @@ static struct node *get_node_canonicalized(struct connection *conn, - return get_node(conn, ctx, *canonical_name, perm); - } - --static int send_directory(struct connection *conn, struct buffered_data *in) -+static int send_directory(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - struct node *node; - -- node = get_node_canonicalized(conn, in, onearg(in), NULL, XS_PERM_READ); -+ node = get_node_canonicalized(conn, ctx, onearg(in), NULL, -+ XS_PERM_READ); - if (!node) - return errno; - -@@ -1261,7 +1263,7 @@ static int send_directory(struct connection *conn, struct buffered_data *in) - return 0; - } - --static int send_directory_part(struct connection *conn, -+static int send_directory_part(const void *ctx, struct connection *conn, - struct buffered_data *in) - { - unsigned int off, len, maxlen, genlen; -@@ -1273,7 +1275,8 @@ static int send_directory_part(struct connection *conn, - return EINVAL; - - /* First arg is node name. */ -- node = get_node_canonicalized(conn, in, in->buffer, NULL, XS_PERM_READ); -+ node = get_node_canonicalized(conn, ctx, in->buffer, NULL, -+ XS_PERM_READ); - if (!node) - return errno; - -@@ -1300,7 +1303,7 @@ static int send_directory_part(struct connection *conn, - break; - } - -- data = talloc_array(in, char, genlen + len + 1); -+ data = talloc_array(ctx, char, genlen + len + 1); - if (!data) - return ENOMEM; - -@@ -1316,11 +1319,13 @@ static int send_directory_part(struct connection *conn, - return 0; - } - --static int do_read(struct connection *conn, struct buffered_data *in) -+static int do_read(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - struct node *node; - -- node = get_node_canonicalized(conn, in, onearg(in), NULL, XS_PERM_READ); -+ node = get_node_canonicalized(conn, ctx, onearg(in), NULL, -+ XS_PERM_READ); - if (!node) - return errno; - -@@ -1510,7 +1515,8 @@ static struct node *create_node(struct connection *conn, const void *ctx, - } - - /* path, data... */ --static int do_write(struct connection *conn, struct buffered_data *in) -+static int do_write(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - unsigned int offset, datalen; - struct node *node; -@@ -1524,12 +1530,12 @@ static int do_write(struct connection *conn, struct buffered_data *in) - offset = strlen(vec[0]) + 1; - datalen = in->used - offset; - -- node = get_node_canonicalized(conn, in, vec[0], &name, XS_PERM_WRITE); -+ node = get_node_canonicalized(conn, ctx, vec[0], &name, XS_PERM_WRITE); - if (!node) { - /* No permissions, invalid input? */ - if (errno != ENOENT) - return errno; -- node = create_node(conn, in, name, in->buffer + offset, -+ node = create_node(conn, ctx, name, in->buffer + offset, - datalen); - if (!node) - return errno; -@@ -1540,18 +1546,19 @@ static int do_write(struct connection *conn, struct buffered_data *in) - return errno; - } - -- fire_watches(conn, in, name, node, false, NULL); -+ fire_watches(conn, ctx, name, node, false, NULL); - send_ack(conn, XS_WRITE); - - return 0; - } - --static int do_mkdir(struct connection *conn, struct buffered_data *in) -+static int do_mkdir(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - struct node *node; - char *name; - -- node = get_node_canonicalized(conn, in, onearg(in), &name, -+ node = get_node_canonicalized(conn, ctx, onearg(in), &name, - XS_PERM_WRITE); - - /* If it already exists, fine. */ -@@ -1561,10 +1568,10 @@ static int do_mkdir(struct connection *conn, struct buffered_data *in) - return errno; - if (!name) - return ENOMEM; -- node = create_node(conn, in, name, NULL, 0); -+ node = create_node(conn, ctx, name, NULL, 0); - if (!node) - return errno; -- fire_watches(conn, in, name, node, false, NULL); -+ fire_watches(conn, ctx, name, node, false, NULL); - } - send_ack(conn, XS_MKDIR); - -@@ -1662,24 +1669,25 @@ static int _rm(struct connection *conn, const void *ctx, struct node *node, - } - - --static int do_rm(struct connection *conn, struct buffered_data *in) -+static int do_rm(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - struct node *node; - int ret; - char *name; - char *parentname; - -- node = get_node_canonicalized(conn, in, onearg(in), &name, -+ node = get_node_canonicalized(conn, ctx, onearg(in), &name, - XS_PERM_WRITE); - if (!node) { - /* Didn't exist already? Fine, if parent exists. */ - if (errno == ENOENT) { - if (!name) - return ENOMEM; -- parentname = get_parent(in, name); -+ parentname = get_parent(ctx, name); - if (!parentname) - return errno; -- node = read_node(conn, in, parentname); -+ node = read_node(conn, ctx, parentname); - if (node) { - send_ack(conn, XS_RM); - return 0; -@@ -1694,7 +1702,7 @@ static int do_rm(struct connection *conn, struct buffered_data *in) - if (streq(name, "/")) - return EINVAL; - -- ret = _rm(conn, in, node, name); -+ ret = _rm(conn, ctx, node, name); - if (ret) - return ret; - -@@ -1704,13 +1712,15 @@ static int do_rm(struct connection *conn, struct buffered_data *in) - } - - --static int do_get_perms(struct connection *conn, struct buffered_data *in) -+static int do_get_perms(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - struct node *node; - char *strings; - unsigned int len; - -- node = get_node_canonicalized(conn, in, onearg(in), NULL, XS_PERM_READ); -+ node = get_node_canonicalized(conn, ctx, onearg(in), NULL, -+ XS_PERM_READ); - if (!node) - return errno; - -@@ -1723,7 +1733,8 @@ static int do_get_perms(struct connection *conn, struct buffered_data *in) - return 0; - } - --static int do_set_perms(struct connection *conn, struct buffered_data *in) -+static int do_set_perms(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - struct node_perms perms, old_perms; - char *name, *permstr; -@@ -1740,7 +1751,7 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) - - permstr = in->buffer + strlen(in->buffer) + 1; - -- perms.p = talloc_array(in, struct xs_permissions, perms.num); -+ perms.p = talloc_array(ctx, struct xs_permissions, perms.num); - if (!perms.p) - return ENOMEM; - if (!xs_strings_to_perms(perms.p, perms.num, permstr)) -@@ -1755,7 +1766,7 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) - } - - /* We must own node to do this (tools can do this too). */ -- node = get_node_canonicalized(conn, in, in->buffer, &name, -+ node = get_node_canonicalized(conn, ctx, in->buffer, &name, - XS_PERM_WRITE | XS_PERM_OWNER); - if (!node) - return errno; -@@ -1790,7 +1801,7 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) - return errno; - } - -- fire_watches(conn, in, name, node, false, &old_perms); -+ fire_watches(conn, ctx, name, node, false, &old_perms); - send_ack(conn, XS_SET_PERMS); - - return 0; -@@ -1798,7 +1809,8 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) - - static struct { - const char *str; -- int (*func)(struct connection *conn, struct buffered_data *in); -+ int (*func)(const void *ctx, struct connection *conn, -+ struct buffered_data *in); - unsigned int flags; - #define XS_FLAG_NOTID (1U << 0) /* Ignore transaction id. */ - #define XS_FLAG_PRIV (1U << 1) /* Privileged domain only. */ -@@ -1874,6 +1886,7 @@ static void process_message(struct connection *conn, struct buffered_data *in) - struct transaction *trans; - enum xsd_sockmsg_type type = in->hdr.msg.type; - int ret; -+ void *ctx; - - /* At least send_error() and send_reply() expects conn->in == in */ - assert(conn->in == in); -@@ -1898,10 +1911,17 @@ static void process_message(struct connection *conn, struct buffered_data *in) - return; - } - -+ ctx = talloc_new(NULL); -+ if (!ctx) { -+ send_error(conn, ENOMEM); -+ return; -+ } -+ - assert(conn->transaction == NULL); - conn->transaction = trans; - -- ret = wire_funcs[type].func(conn, in); -+ ret = wire_funcs[type].func(ctx, conn, in); -+ talloc_free(ctx); - if (ret) - send_error(conn, ret); - -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index e7c6886ccf47..fb732d0a14c3 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -330,7 +330,7 @@ bool domain_is_unprivileged(struct connection *conn) - domid_is_unprivileged(conn->domain->domid); - } - --static char *talloc_domain_path(void *context, unsigned int domid) -+static char *talloc_domain_path(const void *context, unsigned int domid) - { - return talloc_asprintf(context, "/local/domain/%u", domid); - } -@@ -534,7 +534,8 @@ static struct domain *introduce_domain(const void *ctx, - } - - /* domid, gfn, evtchn, path */ --int do_introduce(struct connection *conn, struct buffered_data *in) -+int do_introduce(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - struct domain *domain; - char *vec[3]; -@@ -552,7 +553,7 @@ int do_introduce(struct connection *conn, struct buffered_data *in) - if (port <= 0) - return EINVAL; - -- domain = introduce_domain(in, domid, port, false); -+ domain = introduce_domain(ctx, domid, port, false); - if (!domain) - return errno; - -@@ -575,7 +576,8 @@ static struct domain *find_connected_domain(unsigned int domid) - return domain; - } - --int do_set_target(struct connection *conn, struct buffered_data *in) -+int do_set_target(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - char *vec[2]; - unsigned int domid, tdomid; -@@ -619,7 +621,8 @@ static struct domain *onearg_domain(struct connection *conn, - } - - /* domid */ --int do_release(struct connection *conn, struct buffered_data *in) -+int do_release(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - struct domain *domain; - -@@ -634,7 +637,8 @@ int do_release(struct connection *conn, struct buffered_data *in) - return 0; - } - --int do_resume(struct connection *conn, struct buffered_data *in) -+int do_resume(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - struct domain *domain; - -@@ -649,7 +653,8 @@ int do_resume(struct connection *conn, struct buffered_data *in) - return 0; - } - --int do_get_domain_path(struct connection *conn, struct buffered_data *in) -+int do_get_domain_path(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - char *path; - const char *domid_str = onearg(in); -@@ -657,18 +662,17 @@ int do_get_domain_path(struct connection *conn, struct buffered_data *in) - if (!domid_str) - return EINVAL; - -- path = talloc_domain_path(conn, atoi(domid_str)); -+ path = talloc_domain_path(ctx, atoi(domid_str)); - if (!path) - return errno; - - send_reply(conn, XS_GET_DOMAIN_PATH, path, strlen(path) + 1); - -- talloc_free(path); -- - return 0; - } - --int do_is_domain_introduced(struct connection *conn, struct buffered_data *in) -+int do_is_domain_introduced(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - int result; - unsigned int domid; -@@ -689,7 +693,8 @@ int do_is_domain_introduced(struct connection *conn, struct buffered_data *in) - } - - /* Allow guest to reset all watches */ --int do_reset_watches(struct connection *conn, struct buffered_data *in) -+int do_reset_watches(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - conn_delete_all_watches(conn); - conn_delete_all_transactions(conn); -diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h -index 904faa923afb..b9e152890149 100644 ---- a/tools/xenstore/xenstored_domain.h -+++ b/tools/xenstore/xenstored_domain.h -@@ -24,25 +24,32 @@ void handle_event(void); - void check_domains(void); - - /* domid, mfn, eventchn, path */ --int do_introduce(struct connection *conn, struct buffered_data *in); -+int do_introduce(const void *ctx, struct connection *conn, -+ struct buffered_data *in); - - /* domid */ --int do_is_domain_introduced(struct connection *conn, struct buffered_data *in); -+int do_is_domain_introduced(const void *ctx, struct connection *conn, -+ struct buffered_data *in); - - /* domid */ --int do_release(struct connection *conn, struct buffered_data *in); -+int do_release(const void *ctx, struct connection *conn, -+ struct buffered_data *in); - - /* domid */ --int do_resume(struct connection *conn, struct buffered_data *in); -+int do_resume(const void *ctx, struct connection *conn, -+ struct buffered_data *in); - - /* domid, target */ --int do_set_target(struct connection *conn, struct buffered_data *in); -+int do_set_target(const void *ctx, struct connection *conn, -+ struct buffered_data *in); - - /* domid */ --int do_get_domain_path(struct connection *conn, struct buffered_data *in); -+int do_get_domain_path(const void *ctx, struct connection *conn, -+ struct buffered_data *in); - - /* Allow guest to reset all watches */ --int do_reset_watches(struct connection *conn, struct buffered_data *in); -+int do_reset_watches(const void *ctx, struct connection *conn, -+ struct buffered_data *in); - - void domain_init(int evtfd); - void dom0_init(void); -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index 28774813de83..3e3eb47326cc 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -481,7 +481,8 @@ struct transaction *transaction_lookup(struct connection *conn, uint32_t id) - return ERR_PTR(-ENOENT); - } - --int do_transaction_start(struct connection *conn, struct buffered_data *in) -+int do_transaction_start(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - struct transaction *trans, *exists; - char id_str[20]; -@@ -494,8 +495,8 @@ int do_transaction_start(struct connection *conn, struct buffered_data *in) - conn->transaction_started > quota_max_transaction) - return ENOSPC; - -- /* Attach transaction to input for autofree until it's complete */ -- trans = talloc_zero(in, struct transaction); -+ /* Attach transaction to ctx for autofree until it's complete */ -+ trans = talloc_zero(ctx, struct transaction); - if (!trans) - return ENOMEM; - -@@ -544,7 +545,8 @@ static int transaction_fix_domains(struct transaction *trans, bool update) - return 0; - } - --int do_transaction_end(struct connection *conn, struct buffered_data *in) -+int do_transaction_end(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - const char *arg = onearg(in); - struct transaction *trans; -@@ -562,8 +564,8 @@ int do_transaction_end(struct connection *conn, struct buffered_data *in) - if (!conn->transaction_started) - conn->ta_start_time = 0; - -- /* Attach transaction to in for auto-cleanup */ -- talloc_steal(in, trans); -+ /* Attach transaction to ctx for auto-cleanup */ -+ talloc_steal(ctx, trans); - - if (streq(arg, "T")) { - if (trans->fail) -diff --git a/tools/xenstore/xenstored_transaction.h b/tools/xenstore/xenstored_transaction.h -index e3cbd6b23095..39d7f81c5127 100644 ---- a/tools/xenstore/xenstored_transaction.h -+++ b/tools/xenstore/xenstored_transaction.h -@@ -29,8 +29,10 @@ struct transaction; - - extern uint64_t generation; - --int do_transaction_start(struct connection *conn, struct buffered_data *node); --int do_transaction_end(struct connection *conn, struct buffered_data *in); -+int do_transaction_start(const void *ctx, struct connection *conn, -+ struct buffered_data *node); -+int do_transaction_end(const void *ctx, struct connection *conn, -+ struct buffered_data *in); - - struct transaction *transaction_lookup(struct connection *conn, uint32_t id); - -diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c -index 85362bcce314..316c08b7f754 100644 ---- a/tools/xenstore/xenstored_watch.c -+++ b/tools/xenstore/xenstored_watch.c -@@ -243,7 +243,7 @@ static struct watch *add_watch(struct connection *conn, char *path, char *token, - return NULL; - } - --int do_watch(struct connection *conn, struct buffered_data *in) -+int do_watch(const void *ctx, struct connection *conn, struct buffered_data *in) - { - struct watch *watch; - char *vec[2]; -@@ -252,7 +252,7 @@ int do_watch(struct connection *conn, struct buffered_data *in) - if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) - return EINVAL; - -- errno = check_watch_path(conn, in, &(vec[0]), &relative); -+ errno = check_watch_path(conn, ctx, &(vec[0]), &relative); - if (errno) - return errno; - -@@ -283,7 +283,8 @@ int do_watch(struct connection *conn, struct buffered_data *in) - return 0; - } - --int do_unwatch(struct connection *conn, struct buffered_data *in) -+int do_unwatch(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - struct watch *watch; - char *node, *vec[2]; -@@ -291,7 +292,7 @@ int do_unwatch(struct connection *conn, struct buffered_data *in) - if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) - return EINVAL; - -- node = xenstore_canonicalize(conn, in, vec[0]); -+ node = xenstore_canonicalize(conn, ctx, vec[0]); - if (!node) - return ENOMEM; - list_for_each_entry(watch, &conn->watches, list) { -diff --git a/tools/xenstore/xenstored_watch.h b/tools/xenstore/xenstored_watch.h -index 0e693f0839cd..091890edca96 100644 ---- a/tools/xenstore/xenstored_watch.h -+++ b/tools/xenstore/xenstored_watch.h -@@ -21,8 +21,10 @@ - - #include "xenstored_core.h" - --int do_watch(struct connection *conn, struct buffered_data *in); --int do_unwatch(struct connection *conn, struct buffered_data *in); -+int do_watch(const void *ctx, struct connection *conn, -+ struct buffered_data *in); -+int do_unwatch(const void *ctx, struct connection *conn, -+ struct buffered_data *in); - - /* Fire all watches: !exact means all the children are affected (ie. rm). */ - void fire_watches(struct connection *conn, const void *tmp, const char *name, diff --git a/xsa417.patch b/xsa417.patch deleted file mode 100644 index 3f10bf7..0000000 --- a/xsa417.patch +++ /dev/null @@ -1,135 +0,0 @@ -From 67d5ecd609b8f12346eadb40e547cd7e01d825dc Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:10 +0200 -Subject: tools/xenstore: fix checking node permissions - -Today chk_domain_generation() is being used to check whether a node -permission entry is still valid or whether it is referring to a domain -no longer existing. This is done by comparing the node's and the -domain's generation count. - -In case no struct domain is existing for a checked domain, but the -domain itself is valid, chk_domain_generation() assumes it is being -called due to the first node created for a new domain and it will -return success. - -This might be wrong in case the checked permission is related to an -old domain, which has just been replaced with a new domain using the -same domid. - -Fix that by letting chk_domain_generation() fail in case a struct -domain isn't found. In order to cover the case of the first node for -a new domain try to allocate the needed struct domain explicitly when -processing the related SET_PERMS command. In case a referenced domain -isn't existing, flag the related permission to be ignored right away. - -This is XSA-417 / CVE-2022-42320. - -Reported-by: Juergen Gross -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 66bbeaf6bfb0..a0c176fa203e 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1753,6 +1753,11 @@ static int do_set_perms(const void *ctx, struct connection *conn, - if (!xs_strings_to_perms(perms.p, perms.num, permstr)) - return errno; - -+ if (domain_alloc_permrefs(&perms) < 0) -+ return ENOMEM; -+ if (perms.p[0].perms & XS_PERM_IGNORE) -+ return ENOENT; -+ - /* First arg is node name. */ - if (strstarts(in->buffer, "@")) { - if (set_perms_special(conn, in->buffer, &perms)) -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index b9ff4ded8360..98b401fdec30 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -907,7 +907,6 @@ int domain_entry_inc(struct connection *conn, struct node *node) - * count (used for testing whether a node permission is older than a domain). - * - * Return values: -- * -1: error - * 0: domain has higher generation count (it is younger than a node with the - * given count), or domain isn't existing any longer - * 1: domain is older than the node -@@ -915,20 +914,38 @@ int domain_entry_inc(struct connection *conn, struct node *node) - static int chk_domain_generation(unsigned int domid, uint64_t gen) - { - struct domain *d; -- xc_dominfo_t dominfo; - - if (!xc_handle && domid == 0) - return 1; - - d = find_domain_struct(domid); -- if (d) -- return (d->generation <= gen) ? 1 : 0; - -- if (!get_domain_info(domid, &dominfo)) -- return 0; -+ return (d && d->generation <= gen) ? 1 : 0; -+} - -- d = alloc_domain(NULL, domid); -- return d ? 1 : -1; -+/* -+ * Allocate all missing struct domain referenced by a permission set. -+ * Any permission entries for not existing domains will be marked to be -+ * ignored. -+ */ -+int domain_alloc_permrefs(struct node_perms *perms) -+{ -+ unsigned int i, domid; -+ struct domain *d; -+ xc_dominfo_t dominfo; -+ -+ for (i = 0; i < perms->num; i++) { -+ domid = perms->p[i].id; -+ d = find_domain_struct(domid); -+ if (!d) { -+ if (!get_domain_info(domid, &dominfo)) -+ perms->p[i].perms |= XS_PERM_IGNORE; -+ else if (!alloc_domain(NULL, domid)) -+ return ENOMEM; -+ } -+ } -+ -+ return 0; - } - - /* -@@ -941,8 +958,6 @@ int domain_adjust_node_perms(struct connection *conn, struct node *node) - int ret; - - ret = chk_domain_generation(node->perms.p[0].id, node->generation); -- if (ret < 0) -- return errno; - - /* If the owner doesn't exist any longer give it to priv domain. */ - if (!ret) { -@@ -959,8 +974,6 @@ int domain_adjust_node_perms(struct connection *conn, struct node *node) - continue; - ret = chk_domain_generation(node->perms.p[i].id, - node->generation); -- if (ret < 0) -- return errno; - if (!ret) - node->perms.p[i].perms |= XS_PERM_IGNORE; - } -diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h -index 209442190911..7fe0a21d9e45 100644 ---- a/tools/xenstore/xenstored_domain.h -+++ b/tools/xenstore/xenstored_domain.h -@@ -63,6 +63,7 @@ bool domain_is_unprivileged(struct connection *conn); - - /* Remove node permissions for no longer existing domains. */ - int domain_adjust_node_perms(struct connection *conn, struct node *node); -+int domain_alloc_permrefs(struct node_perms *perms); - - /* Quota manipulation */ - int domain_entry_inc(struct connection *conn, struct node *); diff --git a/xsa418-4.16-01.patch b/xsa418-4.16-01.patch deleted file mode 100644 index c4a17b4..0000000 --- a/xsa418-4.16-01.patch +++ /dev/null @@ -1,119 +0,0 @@ -From d1e6dca486599ab914af7b38b3782b237d3d603b Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:11 +0200 -Subject: tools/xenstore: remove recursion from construct_node() - -In order to reduce stack usage due to recursion, switch -construct_node() to use a loop instead. - -This is part of XSA-418 / CVE-2022-42321. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index c676ee4e4e4f..3907c35643e9 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1377,45 +1377,69 @@ static int add_child(const void *ctx, struct node *parent, const char *name) - static struct node *construct_node(struct connection *conn, const void *ctx, - const char *name) - { -- struct node *parent, *node; -- char *parentname = get_parent(ctx, name); -+ const char **names = NULL; -+ unsigned int levels = 0; -+ struct node *node = NULL; -+ struct node *parent = NULL; -+ const char *parentname = talloc_strdup(ctx, name); - - if (!parentname) - return NULL; - -- /* If parent doesn't exist, create it. */ -- parent = read_node(conn, parentname, parentname); -- if (!parent && errno == ENOENT) -- parent = construct_node(conn, ctx, parentname); -- if (!parent) -- return NULL; -+ /* Walk the path up until an existing node is found. */ -+ while (!parent) { -+ names = talloc_realloc(ctx, names, const char *, levels + 1); -+ if (!names) -+ goto nomem; - -- /* Add child to parent. */ -- if (add_child(ctx, parent, name)) -- goto nomem; -+ /* -+ * names[0] is the name of the node to construct initially, -+ * names[1] is its parent, and so on. -+ */ -+ names[levels] = parentname; -+ parentname = get_parent(ctx, parentname); -+ if (!parentname) -+ return NULL; - -- /* Allocate node */ -- node = talloc(ctx, struct node); -- if (!node) -- goto nomem; -- node->name = talloc_strdup(node, name); -- if (!node->name) -- goto nomem; -+ /* Try to read parent node until we found an existing one. */ -+ parent = read_node(conn, ctx, parentname); -+ if (!parent && (errno != ENOENT || !strcmp(parentname, "/"))) -+ return NULL; - -- /* Inherit permissions, except unprivileged domains own what they create */ -- node->perms.num = parent->perms.num; -- node->perms.p = talloc_memdup(node, parent->perms.p, -- node->perms.num * sizeof(*node->perms.p)); -- if (!node->perms.p) -- goto nomem; -- if (domain_is_unprivileged(conn)) -- node->perms.p[0].id = conn->id; -+ levels++; -+ } -+ -+ /* Walk the path down again constructing the missing nodes. */ -+ for (; levels > 0; levels--) { -+ /* Add child to parent. */ -+ if (add_child(ctx, parent, names[levels - 1])) -+ goto nomem; -+ -+ /* Allocate node */ -+ node = talloc(ctx, struct node); -+ if (!node) -+ goto nomem; -+ node->name = talloc_steal(node, names[levels - 1]); -+ -+ /* Inherit permissions, unpriv domains own what they create. */ -+ node->perms.num = parent->perms.num; -+ node->perms.p = talloc_memdup(node, parent->perms.p, -+ node->perms.num * -+ sizeof(*node->perms.p)); -+ if (!node->perms.p) -+ goto nomem; -+ if (domain_is_unprivileged(conn)) -+ node->perms.p[0].id = conn->id; -+ -+ /* No children, no data */ -+ node->children = node->data = NULL; -+ node->childlen = node->datalen = 0; -+ node->acc.memory = 0; -+ node->parent = parent; -+ -+ parent = node; -+ } - -- /* No children, no data */ -- node->children = node->data = NULL; -- node->childlen = node->datalen = 0; -- node->acc.memory = 0; -- node->parent = parent; - return node; - - nomem: diff --git a/xsa418-4.16-02.patch b/xsa418-4.16-02.patch deleted file mode 100644 index 874bab2..0000000 --- a/xsa418-4.16-02.patch +++ /dev/null @@ -1,103 +0,0 @@ -From c13d85a2fe94bbf3cb8186b89324c5d1b4f9a61f Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:11 +0200 -Subject: tools/xenstore: don't let remove_child_entry() call corrupt() - -In case of write_node() returning an error, remove_child_entry() will -call corrupt() today. This could result in an endless recursion, as -remove_child_entry() is called by corrupt(), too: - -corrupt() - check_store() - check_store_() - remove_child_entry() - -Fix that by letting remove_child_entry() return an error instead and -let the caller decide what to do. - -This is part of XSA-418 / CVE-2022-42321. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 3907c35643e9..f433a45dc217 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1608,15 +1608,15 @@ static void memdel(void *mem, unsigned off, unsigned len, unsigned total) - memmove(mem + off, mem + off + len, total - off - len); - } - --static void remove_child_entry(struct connection *conn, struct node *node, -- size_t offset) -+static int remove_child_entry(struct connection *conn, struct node *node, -+ size_t offset) - { - size_t childlen = strlen(node->children + offset); - - memdel(node->children, offset, childlen + 1, node->childlen); - node->childlen -= childlen + 1; -- if (write_node(conn, node, true)) -- corrupt(conn, "Can't update parent node '%s'", node->name); -+ -+ return write_node(conn, node, true); - } - - static void delete_child(struct connection *conn, -@@ -1626,7 +1626,9 @@ static void delete_child(struct connection *conn, - - for (i = 0; i < node->childlen; i += strlen(node->children+i) + 1) { - if (streq(node->children+i, childname)) { -- remove_child_entry(conn, node, i); -+ if (remove_child_entry(conn, node, i)) -+ corrupt(conn, "Can't update parent node '%s'", -+ node->name); - return; - } - } -@@ -2325,6 +2327,17 @@ int remember_string(struct hashtable *hash, const char *str) - return hashtable_insert(hash, k, (void *)1); - } - -+static int rm_child_entry(struct node *node, size_t off, size_t len) -+{ -+ if (!recovery) -+ return off; -+ -+ if (remove_child_entry(NULL, node, off)) -+ log("check_store: child entry could not be removed from '%s'", -+ node->name); -+ -+ return off - len - 1; -+} - - /** - * A node has a children field that names the children of the node, separated -@@ -2377,12 +2390,7 @@ static int check_store_(const char *name, struct hashtable *reachable) - if (hashtable_search(children, childname)) { - log("check_store: '%s' is duplicated!", - childname); -- -- if (recovery) { -- remove_child_entry(NULL, node, -- i); -- i -= childlen + 1; -- } -+ i = rm_child_entry(node, i, childlen); - } - else { - if (!remember_string(children, -@@ -2399,11 +2407,7 @@ static int check_store_(const char *name, struct hashtable *reachable) - } else if (errno != ENOMEM) { - log("check_store: No child '%s' found!\n", - childname); -- -- if (recovery) { -- remove_child_entry(NULL, node, i); -- i -= childlen + 1; -- } -+ i = rm_child_entry(node, i, childlen); - } else { - log("check_store: ENOMEM"); - ret = ENOMEM; diff --git a/xsa418-4.16-03.patch b/xsa418-4.16-03.patch deleted file mode 100644 index 321ccf0..0000000 --- a/xsa418-4.16-03.patch +++ /dev/null @@ -1,243 +0,0 @@ -From aac9b51b6fbbbd16c910f69365345528c5bec106 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:11 +0200 -Subject: tools/xenstore: add generic treewalk function - -Add a generic function to walk the complete node tree. It will start -at "/" and descend recursively into each child, calling a function -specified by the caller. Depending on the return value of the user -specified function the walk will be aborted, continued, or the current -child will be skipped by not descending into its children. - -This is part of XSA-418 / CVE-2022-42321. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Acked-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index f433a45dc217..2cda3ee375ab 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1838,6 +1838,135 @@ static int do_set_perms(const void *ctx, struct connection *conn, - return 0; - } - -+static char *child_name(const void *ctx, const char *s1, const char *s2) -+{ -+ if (strcmp(s1, "/")) -+ return talloc_asprintf(ctx, "%s/%s", s1, s2); -+ return talloc_asprintf(ctx, "/%s", s2); -+} -+ -+static int rm_from_parent(struct connection *conn, struct node *parent, -+ const char *name) -+{ -+ size_t off; -+ -+ if (!parent) -+ return WALK_TREE_ERROR_STOP; -+ -+ for (off = parent->childoff - 1; off && parent->children[off - 1]; -+ off--); -+ if (remove_child_entry(conn, parent, off)) { -+ log("treewalk: child entry could not be removed from '%s'", -+ parent->name); -+ return WALK_TREE_ERROR_STOP; -+ } -+ parent->childoff = off; -+ -+ return WALK_TREE_OK; -+} -+ -+static int walk_call_func(const void *ctx, struct connection *conn, -+ struct node *node, struct node *parent, void *arg, -+ int (*func)(const void *ctx, struct connection *conn, -+ struct node *node, void *arg)) -+{ -+ int ret; -+ -+ if (!func) -+ return WALK_TREE_OK; -+ -+ ret = func(ctx, conn, node, arg); -+ if (ret == WALK_TREE_RM_CHILDENTRY && parent) -+ ret = rm_from_parent(conn, parent, node->name); -+ -+ return ret; -+} -+ -+int walk_node_tree(const void *ctx, struct connection *conn, const char *root, -+ struct walk_funcs *funcs, void *arg) -+{ -+ int ret = 0; -+ void *tmpctx; -+ char *name; -+ struct node *node = NULL; -+ struct node *parent = NULL; -+ -+ tmpctx = talloc_new(ctx); -+ if (!tmpctx) { -+ errno = ENOMEM; -+ return WALK_TREE_ERROR_STOP; -+ } -+ name = talloc_strdup(tmpctx, root); -+ if (!name) { -+ errno = ENOMEM; -+ talloc_free(tmpctx); -+ return WALK_TREE_ERROR_STOP; -+ } -+ -+ /* Continue the walk until an error is returned. */ -+ while (ret >= 0) { -+ /* node == NULL possible only for the initial loop iteration. */ -+ if (node) { -+ /* Go one step up if ret or if last child finished. */ -+ if (ret || node->childoff >= node->childlen) { -+ parent = node->parent; -+ /* Call function AFTER processing a node. */ -+ ret = walk_call_func(ctx, conn, node, parent, -+ arg, funcs->exit); -+ /* Last node, so exit loop. */ -+ if (!parent) -+ break; -+ talloc_free(node); -+ /* Continue with parent. */ -+ node = parent; -+ continue; -+ } -+ /* Get next child of current node. */ -+ name = child_name(tmpctx, node->name, -+ node->children + node->childoff); -+ if (!name) { -+ ret = WALK_TREE_ERROR_STOP; -+ break; -+ } -+ /* Point to next child. */ -+ node->childoff += strlen(node->children + -+ node->childoff) + 1; -+ /* Descent into children. */ -+ parent = node; -+ } -+ /* Read next node (root node or next child). */ -+ node = read_node(conn, tmpctx, name); -+ if (!node) { -+ /* Child not found - should not happen! */ -+ /* ENOENT case can be handled by supplied function. */ -+ if (errno == ENOENT && funcs->enoent) -+ ret = funcs->enoent(ctx, conn, parent, name, -+ arg); -+ else -+ ret = WALK_TREE_ERROR_STOP; -+ if (!parent) -+ break; -+ if (ret == WALK_TREE_RM_CHILDENTRY) -+ ret = rm_from_parent(conn, parent, name); -+ if (ret < 0) -+ break; -+ talloc_free(name); -+ node = parent; -+ continue; -+ } -+ talloc_free(name); -+ node->parent = parent; -+ node->childoff = 0; -+ /* Call function BEFORE processing a node. */ -+ ret = walk_call_func(ctx, conn, node, parent, arg, -+ funcs->enter); -+ } -+ -+ talloc_free(tmpctx); -+ -+ return ret < 0 ? ret : WALK_TREE_OK; -+} -+ - static struct { - const char *str; - int (*func)(const void *ctx, struct connection *conn, -@@ -2305,18 +2434,6 @@ static int keys_equal_fn(void *key1, void *key2) - return 0 == strcmp((char *)key1, (char *)key2); - } - -- --static char *child_name(const char *s1, const char *s2) --{ -- if (strcmp(s1, "/")) { -- return talloc_asprintf(NULL, "%s/%s", s1, s2); -- } -- else { -- return talloc_asprintf(NULL, "/%s", s2); -- } --} -- -- - int remember_string(struct hashtable *hash, const char *str) - { - char *k = malloc(strlen(str) + 1); -@@ -2376,7 +2493,7 @@ static int check_store_(const char *name, struct hashtable *reachable) - while (i < node->childlen && !ret) { - struct node *childnode; - size_t childlen = strlen(node->children + i); -- char * childname = child_name(node->name, -+ char * childname = child_name(NULL, node->name, - node->children + i); - - if (!childname) { -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index bfd3fc1e9df3..2d9942171d92 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -202,6 +202,7 @@ struct node { - - /* Children, each nul-terminated. */ - unsigned int childlen; -+ unsigned int childoff; /* Used by walk_node_tree() internally. */ - char *children; - - /* Allocation information for node currently in store. */ -@@ -338,6 +339,45 @@ void read_state_buffered_data(const void *ctx, struct connection *conn, - const struct xs_state_connection *sc); - void read_state_node(const void *ctx, const void *state); - -+/* -+ * Walk the node tree below root calling funcs->enter() and funcs->exit() for -+ * each node. funcs->enter() is being called when entering a node, so before -+ * any of the children of the node is processed. funcs->exit() is being -+ * called when leaving the node, so after all children have been processed. -+ * funcs->enoent() is being called when a node isn't existing. -+ * funcs->*() return values: -+ * < 0: tree walk is stopped, walk_node_tree() returns funcs->*() return value -+ * in case WALK_TREE_ERROR_STOP is returned, errno should be set -+ * WALK_TREE_OK: tree walk is continuing -+ * WALK_TREE_SKIP_CHILDREN: tree walk won't descend below current node, but -+ * walk continues -+ * WALK_TREE_RM_CHILDENTRY: Remove the child entry from its parent and write -+ * the modified parent node back to the data base, implies to not descend -+ * below the current node, but to continue the walk -+ * funcs->*() is allowed to modify the node it is called for in the data base. -+ * In case funcs->enter() is deleting the node, it must not return WALK_TREE_OK -+ * in order to avoid descending into no longer existing children. -+ */ -+/* Return values for funcs->*() and walk_node_tree(). */ -+#define WALK_TREE_SUCCESS_STOP -100 /* Stop walk early, no error. */ -+#define WALK_TREE_ERROR_STOP -1 /* Stop walk due to error. */ -+#define WALK_TREE_OK 0 /* No error. */ -+/* Return value for funcs->*() only. */ -+#define WALK_TREE_SKIP_CHILDREN 1 /* Don't recurse below current node. */ -+#define WALK_TREE_RM_CHILDENTRY 2 /* Remove child entry from parent. */ -+ -+struct walk_funcs { -+ int (*enter)(const void *ctx, struct connection *conn, -+ struct node *node, void *arg); -+ int (*exit)(const void *ctx, struct connection *conn, -+ struct node *node, void *arg); -+ int (*enoent)(const void *ctx, struct connection *conn, -+ struct node *parent, char *name, void *arg); -+}; -+ -+int walk_node_tree(const void *ctx, struct connection *conn, const char *root, -+ struct walk_funcs *funcs, void *arg); -+ - #endif /* _XENSTORED_CORE_H */ - - /* diff --git a/xsa418-4.16-04.patch b/xsa418-4.16-04.patch deleted file mode 100644 index 95de88f..0000000 --- a/xsa418-4.16-04.patch +++ /dev/null @@ -1,108 +0,0 @@ -From bdc931fb5dcebbd8d0e44b5d8bd3fb9106ee8596 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:12 +0200 -Subject: tools/xenstore: simplify check_store() - -check_store() is using a hash table for storing all node names it has -found via walking the tree. Additionally it using another hash table -for all children of a node to detect duplicate child names. - -Simplify that by dropping the second hash table as the first one is -already holding all the needed information. - -This is part of XSA-418 / CVE-2022-42321. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 2cda3ee375ab..760f3c16c794 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -2477,50 +2477,34 @@ static int check_store_(const char *name, struct hashtable *reachable) - if (node) { - size_t i = 0; - -- struct hashtable * children = -- create_hashtable(16, hash_from_key_fn, keys_equal_fn); -- if (!children) { -- log("check_store create table: ENOMEM"); -- return ENOMEM; -- } -- - if (!remember_string(reachable, name)) { -- hashtable_destroy(children, 0); - log("check_store: ENOMEM"); - return ENOMEM; - } - - while (i < node->childlen && !ret) { -- struct node *childnode; -+ struct node *childnode = NULL; - size_t childlen = strlen(node->children + i); -- char * childname = child_name(NULL, node->name, -- node->children + i); -+ char *childname = child_name(NULL, node->name, -+ node->children + i); - - if (!childname) { - log("check_store: ENOMEM"); - ret = ENOMEM; - break; - } -+ -+ if (hashtable_search(reachable, childname)) { -+ log("check_store: '%s' is duplicated!", -+ childname); -+ i = rm_child_entry(node, i, childlen); -+ goto next; -+ } -+ - childnode = read_node(NULL, childname, childname); -- -+ - if (childnode) { -- if (hashtable_search(children, childname)) { -- log("check_store: '%s' is duplicated!", -- childname); -- i = rm_child_entry(node, i, childlen); -- } -- else { -- if (!remember_string(children, -- childname)) { -- log("check_store: ENOMEM"); -- talloc_free(childnode); -- talloc_free(childname); -- ret = ENOMEM; -- break; -- } -- ret = check_store_(childname, -- reachable); -- } -+ ret = check_store_(childname, reachable); - } else if (errno != ENOMEM) { - log("check_store: No child '%s' found!\n", - childname); -@@ -2530,19 +2514,18 @@ static int check_store_(const char *name, struct hashtable *reachable) - ret = ENOMEM; - } - -+ next: - talloc_free(childnode); - talloc_free(childname); - i += childlen + 1; - } - -- hashtable_destroy(children, 0 /* Don't free values (they are -- all (void *)1) */); - talloc_free(node); - } else if (errno != ENOMEM) { - /* Impossible, because no database should ever be without the - root, and otherwise, we've just checked in our caller - (which made a recursive call to get here). */ -- -+ - log("check_store: No child '%s' found: impossible!", name); - } else { - log("check_store: ENOMEM"); diff --git a/xsa418-4.16-05.patch b/xsa418-4.16-05.patch deleted file mode 100644 index fca551e..0000000 --- a/xsa418-4.16-05.patch +++ /dev/null @@ -1,164 +0,0 @@ -From 27817f0a7d6802be04e8f43a0900b02f881b28b2 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:12 +0200 -Subject: tools/xenstore: use treewalk for check_store() - -Instead of doing an open tree walk using call recursion, use -walk_node_tree() when checking the store for inconsistencies. - -This will reduce code size and avoid many nesting levels of function -calls which could potentially exhaust the stack. - -This is part of XSA-418 / CVE-2022-42321. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 760f3c16c794..efdd1888fd78 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -2444,18 +2444,6 @@ int remember_string(struct hashtable *hash, const char *str) - return hashtable_insert(hash, k, (void *)1); - } - --static int rm_child_entry(struct node *node, size_t off, size_t len) --{ -- if (!recovery) -- return off; -- -- if (remove_child_entry(NULL, node, off)) -- log("check_store: child entry could not be removed from '%s'", -- node->name); -- -- return off - len - 1; --} -- - /** - * A node has a children field that names the children of the node, separated - * by NULs. We check whether there are entries in there that are duplicated -@@ -2469,70 +2457,29 @@ static int rm_child_entry(struct node *node, size_t off, size_t len) - * As we go, we record each node in the given reachable hashtable. These - * entries will be used later in clean_store. - */ --static int check_store_(const char *name, struct hashtable *reachable) -+static int check_store_step(const void *ctx, struct connection *conn, -+ struct node *node, void *arg) - { -- struct node *node = read_node(NULL, name, name); -- int ret = 0; -- -- if (node) { -- size_t i = 0; -- -- if (!remember_string(reachable, name)) { -- log("check_store: ENOMEM"); -- return ENOMEM; -- } -- -- while (i < node->childlen && !ret) { -- struct node *childnode = NULL; -- size_t childlen = strlen(node->children + i); -- char *childname = child_name(NULL, node->name, -- node->children + i); -- -- if (!childname) { -- log("check_store: ENOMEM"); -- ret = ENOMEM; -- break; -- } -+ struct hashtable *reachable = arg; - -- if (hashtable_search(reachable, childname)) { -- log("check_store: '%s' is duplicated!", -- childname); -- i = rm_child_entry(node, i, childlen); -- goto next; -- } -- -- childnode = read_node(NULL, childname, childname); -- -- if (childnode) { -- ret = check_store_(childname, reachable); -- } else if (errno != ENOMEM) { -- log("check_store: No child '%s' found!\n", -- childname); -- i = rm_child_entry(node, i, childlen); -- } else { -- log("check_store: ENOMEM"); -- ret = ENOMEM; -- } -+ if (hashtable_search(reachable, (void *)node->name)) { -+ log("check_store: '%s' is duplicated!", node->name); -+ return recovery ? WALK_TREE_RM_CHILDENTRY -+ : WALK_TREE_SKIP_CHILDREN; -+ } - -- next: -- talloc_free(childnode); -- talloc_free(childname); -- i += childlen + 1; -- } -+ if (!remember_string(reachable, node->name)) -+ return WALK_TREE_ERROR_STOP; - -- talloc_free(node); -- } else if (errno != ENOMEM) { -- /* Impossible, because no database should ever be without the -- root, and otherwise, we've just checked in our caller -- (which made a recursive call to get here). */ -+ return WALK_TREE_OK; -+} - -- log("check_store: No child '%s' found: impossible!", name); -- } else { -- log("check_store: ENOMEM"); -- ret = ENOMEM; -- } -+static int check_store_enoent(const void *ctx, struct connection *conn, -+ struct node *parent, char *name, void *arg) -+{ -+ log("check_store: node '%s' not found", name); - -- return ret; -+ return recovery ? WALK_TREE_RM_CHILDENTRY : WALK_TREE_OK; - } - - -@@ -2581,24 +2528,28 @@ static void clean_store(struct hashtable *reachable) - - void check_store(void) - { -- char * root = talloc_strdup(NULL, "/"); -- struct hashtable * reachable = -- create_hashtable(16, hash_from_key_fn, keys_equal_fn); -- -+ struct hashtable *reachable; -+ struct walk_funcs walkfuncs = { -+ .enter = check_store_step, -+ .enoent = check_store_enoent, -+ }; -+ -+ reachable = create_hashtable(16, hash_from_key_fn, keys_equal_fn); - if (!reachable) { - log("check_store: ENOMEM"); - return; - } - - log("Checking store ..."); -- if (!check_store_(root, reachable) && -- !check_transactions(reachable)) -+ if (walk_node_tree(NULL, NULL, "/", &walkfuncs, reachable)) { -+ if (errno == ENOMEM) -+ log("check_store: ENOMEM"); -+ } else if (!check_transactions(reachable)) - clean_store(reachable); - log("Checking store complete."); - - hashtable_destroy(reachable, 0 /* Don't free values (they are all - (void *)1) */); -- talloc_free(root); - } - - diff --git a/xsa418-4.16-06.patch b/xsa418-4.16-06.patch deleted file mode 100644 index d46c057..0000000 --- a/xsa418-4.16-06.patch +++ /dev/null @@ -1,174 +0,0 @@ -From 6ea0ffbd88b11f23779d763501ec1370b590bb2a Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:12 +0200 -Subject: tools/xenstore: use treewalk for deleting nodes - -Instead of doing an open tree walk using call recursion, use -walk_node_tree() when deleting a sub-tree of nodes. - -This will reduce code size and avoid many nesting levels of function -calls which could potentially exhaust the stack. - -This is part of XSA-418 / CVE-2022-42321. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Acked-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index efdd1888fd78..58fb651542ec 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1334,21 +1334,6 @@ static int do_read(const void *ctx, struct connection *conn, - return 0; - } - --static void delete_node_single(struct connection *conn, struct node *node) --{ -- TDB_DATA key; -- -- if (access_node(conn, node, NODE_ACCESS_DELETE, &key)) -- return; -- -- if (do_tdb_delete(conn, &key, &node->acc) != 0) { -- corrupt(conn, "Could not delete '%s'", node->name); -- return; -- } -- -- domain_entry_dec(conn, node); --} -- - /* Must not be / */ - static char *basename(const char *name) - { -@@ -1619,69 +1604,59 @@ static int remove_child_entry(struct connection *conn, struct node *node, - return write_node(conn, node, true); - } - --static void delete_child(struct connection *conn, -- struct node *node, const char *childname) -+static int delete_child(struct connection *conn, -+ struct node *node, const char *childname) - { - unsigned int i; - - for (i = 0; i < node->childlen; i += strlen(node->children+i) + 1) { - if (streq(node->children+i, childname)) { -- if (remove_child_entry(conn, node, i)) -- corrupt(conn, "Can't update parent node '%s'", -- node->name); -- return; -+ errno = remove_child_entry(conn, node, i) ? EIO : 0; -+ return errno; - } - } - corrupt(conn, "Can't find child '%s' in %s", childname, node->name); -+ -+ errno = EIO; -+ return errno; - } - --static int delete_node(struct connection *conn, const void *ctx, -- struct node *parent, struct node *node, bool watch_exact) -+static int delnode_sub(const void *ctx, struct connection *conn, -+ struct node *node, void *arg) - { -- char *name; -+ const char *root = arg; -+ bool watch_exact; -+ int ret; -+ TDB_DATA key; - -- /* Delete children. */ -- while (node->childlen) { -- struct node *child; -+ /* Any error here will probably be repeated for all following calls. */ -+ ret = access_node(conn, node, NODE_ACCESS_DELETE, &key); -+ if (ret > 0) -+ return WALK_TREE_SUCCESS_STOP; - -- name = talloc_asprintf(node, "%s/%s", node->name, -- node->children); -- child = name ? read_node(conn, node, name) : NULL; -- if (child) { -- if (delete_node(conn, ctx, node, child, true)) -- return errno; -- } else { -- trace("delete_node: Error deleting child '%s/%s'!\n", -- node->name, node->children); -- /* Quit deleting. */ -- errno = ENOMEM; -- return errno; -- } -- talloc_free(name); -- } -+ /* In case of error stop the walk. */ -+ if (!ret && do_tdb_delete(conn, &key, &node->acc)) -+ return WALK_TREE_SUCCESS_STOP; - - /* - * Fire the watches now, when we can still see the node permissions. - * This fine as we are single threaded and the next possible read will - * be handled only after the node has been really removed. -- */ -+ */ -+ watch_exact = strcmp(root, node->name); - fire_watches(conn, ctx, node->name, node, watch_exact, NULL); -- delete_node_single(conn, node); -- delete_child(conn, parent, basename(node->name)); -- talloc_free(node); - -- return 0; -+ domain_entry_dec(conn, node); -+ -+ return WALK_TREE_RM_CHILDENTRY; - } - --static int _rm(struct connection *conn, const void *ctx, struct node *node, -- const char *name) -+static int _rm(struct connection *conn, const void *ctx, const char *name) - { -- /* -- * Deleting node by node, so the result is always consistent even in -- * case of a failure. -- */ - struct node *parent; - char *parentname = get_parent(ctx, name); -+ struct walk_funcs walkfuncs = { .exit = delnode_sub }; -+ int ret; - - if (!parentname) - return errno; -@@ -1689,9 +1664,21 @@ static int _rm(struct connection *conn, const void *ctx, struct node *node, - parent = read_node(conn, ctx, parentname); - if (!parent) - return read_node_can_propagate_errno() ? errno : EINVAL; -- node->parent = parent; - -- return delete_node(conn, ctx, parent, node, false); -+ ret = walk_node_tree(ctx, conn, name, &walkfuncs, (void *)name); -+ if (ret < 0) { -+ if (ret == WALK_TREE_ERROR_STOP) { -+ corrupt(conn, "error when deleting sub-nodes of %s\n", -+ name); -+ errno = EIO; -+ } -+ return errno; -+ } -+ -+ if (delete_child(conn, parent, basename(name))) -+ return errno; -+ -+ return 0; - } - - -@@ -1728,7 +1715,7 @@ static int do_rm(const void *ctx, struct connection *conn, - if (streq(name, "/")) - return EINVAL; - -- ret = _rm(conn, ctx, node, name); -+ ret = _rm(conn, ctx, name); - if (ret) - return ret; - diff --git a/xsa418-4.16-07.patch b/xsa418-4.16-07.patch deleted file mode 100644 index 9600d8c..0000000 --- a/xsa418-4.16-07.patch +++ /dev/null @@ -1,163 +0,0 @@ -From 1ee281b18b52bec87335ea64ee74cc159e63d036 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:12 +0200 -Subject: tools/xenstore: use treewalk for creating node records - -Instead of doing an open tree walk using call recursion, use -walk_node_tree() when creating the node records during a live update. - -This will reduce code size and avoid many nesting levels of function -calls which could potentially exhaust the stack. - -This is part of XSA-418 / CVE-2022-42321. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 58fb651542ec..05d349778bb4 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -3120,101 +3120,76 @@ const char *dump_state_node_perms(FILE *fp, const struct xs_permissions *perms, - return NULL; - } - --static const char *dump_state_node_tree(FILE *fp, char *path, -- unsigned int path_max_len) -+struct dump_node_data { -+ FILE *fp; -+ const char *err; -+}; -+ -+static int dump_state_node_err(struct dump_node_data *data, const char *err) - { -- unsigned int pathlen, childlen, p = 0; -+ data->err = err; -+ return WALK_TREE_ERROR_STOP; -+} -+ -+static int dump_state_node(const void *ctx, struct connection *conn, -+ struct node *node, void *arg) -+{ -+ struct dump_node_data *data = arg; -+ FILE *fp = data->fp; -+ unsigned int pathlen; - struct xs_state_record_header head; - struct xs_state_node sn; -- TDB_DATA key, data; -- const struct xs_tdb_record_hdr *hdr; -- const char *child; - const char *ret; - -- pathlen = strlen(path) + 1; -- -- set_tdb_key(path, &key); -- data = tdb_fetch(tdb_ctx, key); -- if (data.dptr == NULL) -- return "Error reading node"; -- -- /* Clean up in case of failure. */ -- talloc_steal(path, data.dptr); -- -- hdr = (void *)data.dptr; -+ pathlen = strlen(node->name) + 1; - - head.type = XS_STATE_TYPE_NODE; - head.length = sizeof(sn); - sn.conn_id = 0; - sn.ta_id = 0; - sn.ta_access = 0; -- sn.perm_n = hdr->num_perms; -+ sn.perm_n = node->perms.num; - sn.path_len = pathlen; -- sn.data_len = hdr->datalen; -- head.length += hdr->num_perms * sizeof(*sn.perms); -+ sn.data_len = node->datalen; -+ head.length += node->perms.num * sizeof(*sn.perms); - head.length += pathlen; -- head.length += hdr->datalen; -+ head.length += node->datalen; - head.length = ROUNDUP(head.length, 3); - - if (fwrite(&head, sizeof(head), 1, fp) != 1) -- return "Dump node state error"; -+ return dump_state_node_err(data, "Dump node head error"); - if (fwrite(&sn, sizeof(sn), 1, fp) != 1) -- return "Dump node state error"; -+ return dump_state_node_err(data, "Dump node state error"); - -- ret = dump_state_node_perms(fp, hdr->perms, hdr->num_perms); -+ ret = dump_state_node_perms(fp, node->perms.p, node->perms.num); - if (ret) -- return ret; -+ return dump_state_node_err(data, ret); -+ -+ if (fwrite(node->name, pathlen, 1, fp) != 1) -+ return dump_state_node_err(data, "Dump node path error"); - -- if (fwrite(path, pathlen, 1, fp) != 1) -- return "Dump node path error"; -- if (hdr->datalen && -- fwrite(hdr->perms + hdr->num_perms, hdr->datalen, 1, fp) != 1) -- return "Dump node data error"; -+ if (node->datalen && fwrite(node->data, node->datalen, 1, fp) != 1) -+ return dump_state_node_err(data, "Dump node data error"); - - ret = dump_state_align(fp); - if (ret) -- return ret; -+ return dump_state_node_err(data, ret); - -- child = (char *)(hdr->perms + hdr->num_perms) + hdr->datalen; -- -- /* -- * Use path for constructing children paths. -- * As we don't write out nodes without having written their parent -- * already we will never clobber a part of the path we'll need later. -- */ -- pathlen--; -- if (path[pathlen - 1] != '/') { -- path[pathlen] = '/'; -- pathlen++; -- } -- while (p < hdr->childlen) { -- childlen = strlen(child) + 1; -- if (pathlen + childlen > path_max_len) -- return "Dump node path length error"; -- strcpy(path + pathlen, child); -- ret = dump_state_node_tree(fp, path, path_max_len); -- if (ret) -- return ret; -- p += childlen; -- child += childlen; -- } -- -- talloc_free(data.dptr); -- -- return NULL; -+ return WALK_TREE_OK; - } - - const char *dump_state_nodes(FILE *fp, const void *ctx) - { -- char *path; -- -- path = talloc_size(ctx, XENSTORE_ABS_PATH_MAX + 1); -- if (!path) -- return "Path buffer allocation error"; -+ struct dump_node_data data = { -+ .fp = fp, -+ .err = "Dump node walk error" -+ }; -+ struct walk_funcs walkfuncs = { .enter = dump_state_node }; - -- strcpy(path, "/"); -+ if (walk_node_tree(ctx, NULL, "/", &walkfuncs, &data)) -+ return data.err; - -- return dump_state_node_tree(fp, path, XENSTORE_ABS_PATH_MAX + 1); -+ return NULL; - } - - void read_state_global(const void *ctx, const void *state) diff --git a/xsa419-oxenstored.patch b/xsa419-oxenstored.patch deleted file mode 100644 index 0ac365d..0000000 --- a/xsa419-oxenstored.patch +++ /dev/null @@ -1,85 +0,0 @@ -From 09228369a549427294febe351372d7227e624da1 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Date: Wed, 12 Oct 2022 19:13:06 +0100 -Subject: tools/ocaml/xenstored: Fix quota bypass on domain shutdown -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -XSA-322 fixed a domid reuse vulnerability by assigning Dom0 as the owner of -any nodes left after a domain is shutdown (e.g. outside its /local/domain/N -tree). - -However Dom0 has no quota on purpose, so this opened up another potential -attack vector. Avoid it by deleting these nodes instead of assigning them to -Dom0. - -This is part of XSA-419 / CVE-2022-42323. - -Reported-by: Juergen Gross -Fixes: c46eff921209 ("tools/ocaml/xenstored: clean up permissions for dead domains") -Signed-off-by: Edwin Török -Acked-by: Christian Lindig - -diff --git a/tools/ocaml/xenstored/perms.ml b/tools/ocaml/xenstored/perms.ml -index e8a16221f8fa..84f2503e8e29 100644 ---- a/tools/ocaml/xenstored/perms.ml -+++ b/tools/ocaml/xenstored/perms.ml -@@ -64,8 +64,7 @@ let get_owner perm = perm.owner - * *) - let remove_domid ~domid perm = - let acl = List.filter (fun (acl_domid, _) -> acl_domid <> domid) perm.acl in -- let owner = if perm.owner = domid then 0 else perm.owner in -- { perm with acl; owner } -+ if perm.owner = domid then None else Some { perm with acl; owner = perm.owner } - - let default0 = create 0 NONE [] - -diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml -index 20e67b142746..70f0c83de404 100644 ---- a/tools/ocaml/xenstored/store.ml -+++ b/tools/ocaml/xenstored/store.ml -@@ -87,10 +87,21 @@ let check_owner node connection = - - let rec recurse fct node = fct node; SymbolMap.iter (fun _ -> recurse fct) node.children - --(** [recurse_map f tree] applies [f] on each node in the tree recursively *) --let recurse_map f = -+(** [recurse_filter_map f tree] applies [f] on each node in the tree recursively, -+ possibly removing some nodes. -+ Note that the nodes removed this way won't generate watch events. -+*) -+let recurse_filter_map f = -+ let invalid = -1 in -+ let is_valid _ node = node.perms.owner <> invalid in - let rec walk node = -- f { node with children = SymbolMap.map walk node.children } -+ (* Map.filter_map is Ocaml 4.11+ only *) -+ let node = -+ { node with children = -+ SymbolMap.map walk node.children |> SymbolMap.filter is_valid } in -+ match f node with -+ | Some keep -> keep -+ | None -> { node with perms = {node.perms with owner = invalid } } - in - walk - -@@ -444,11 +455,13 @@ let setperms store perm path nperms = - - let reset_permissions store domid = - Logging.info "store|node" "Cleaning up xenstore ACLs for domid %d" domid; -- store.root <- Node.recurse_map (fun node -> -- let perms = Perms.Node.remove_domid ~domid node.perms in -- if perms <> node.perms then -- Logging.debug "store|node" "Changed permissions for node %s" (Node.get_name node); -- { node with perms } -+ store.root <- Node.recurse_filter_map (fun node -> -+ match Perms.Node.remove_domid ~domid node.perms with -+ | None -> None -+ | Some perms -> -+ if perms <> node.perms then -+ Logging.debug "store|node" "Changed permissions for node %s" (Node.get_name node); -+ Some { node with perms } - ) store.root - - type ops = { diff --git a/xsa419-xenstored-01.patch b/xsa419-xenstored-01.patch deleted file mode 100644 index 3409790..0000000 --- a/xsa419-xenstored-01.patch +++ /dev/null @@ -1,289 +0,0 @@ -From e4250bf8b39ed73623c75b0f1436ac7c1d45aba7 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:12 +0200 -Subject: tools/xenstore: remove nodes owned by destroyed domain - -In case a domain is removed from Xenstore, remove all nodes owned by -it per default. - -This tackles the problem that nodes might be created by a domain -outside its home path in Xenstore, leading to Xenstore hogging more -and more memory. Domain quota don't work in this case if the guest is -rebooting in between. - -Since XSA-322 ownership of such stale nodes is transferred to dom0, -which is helping against unintended access, but not against OOM of -Xenstore. - -As a fallback for weird cases add a Xenstore start parameter for -keeping today's way to handle stale nodes, adding the risk of Xenstore -hitting an OOM situation. - -This is part of XSA-419 / CVE-2022-42322. - -Reported-by: Juergen Gross -Fixes: 496306324d8d ("tools/xenstore: revoke access rights for removed domains") -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index bdc14679adf5..13e48aaa731c 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -80,6 +80,7 @@ static bool verbose = false; - LIST_HEAD(connections); - int tracefd = -1; - static bool recovery = true; -+bool keep_orphans = false; - static int reopen_log_pipe[2]; - static int reopen_log_pipe0_pollfd_idx = -1; - char *tracefile = NULL; -@@ -753,7 +754,7 @@ struct node *read_node(struct connection *conn, const void *ctx, - node->perms.p = hdr->perms; - node->acc.domid = node->perms.p[0].id; - node->acc.memory = data.dsize; -- if (domain_adjust_node_perms(conn, node)) -+ if (domain_adjust_node_perms(node)) - goto error; - - /* If owner is gone reset currently accounted memory size. */ -@@ -796,7 +797,7 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, - void *p; - struct xs_tdb_record_hdr *hdr; - -- if (domain_adjust_node_perms(conn, node)) -+ if (domain_adjust_node_perms(node)) - return errno; - - data.dsize = sizeof(*hdr) -@@ -1647,7 +1648,7 @@ static int delnode_sub(const void *ctx, struct connection *conn, - return WALK_TREE_RM_CHILDENTRY; - } - --static int _rm(struct connection *conn, const void *ctx, const char *name) -+int rm_node(struct connection *conn, const void *ctx, const char *name) - { - struct node *parent; - char *parentname = get_parent(ctx, name); -@@ -1711,7 +1712,7 @@ static int do_rm(const void *ctx, struct connection *conn, - if (streq(name, "/")) - return EINVAL; - -- ret = _rm(conn, ctx, name); -+ ret = rm_node(conn, ctx, name); - if (ret) - return ret; - -@@ -2618,6 +2619,8 @@ static void usage(void) - " -R, --no-recovery to request that no recovery should be attempted when\n" - " the store is corrupted (debug only),\n" - " -I, --internal-db store database in memory, not on disk\n" -+" -K, --keep-orphans don't delete nodes owned by a domain when the\n" -+" domain is deleted (this is a security risk!)\n" - " -V, --verbose to request verbose execution.\n"); - } - -@@ -2642,6 +2645,7 @@ static struct option options[] = { - { "timeout", 1, NULL, 'w' }, - { "no-recovery", 0, NULL, 'R' }, - { "internal-db", 0, NULL, 'I' }, -+ { "keep-orphans", 0, NULL, 'K' }, - { "verbose", 0, NULL, 'V' }, - { "watch-nb", 1, NULL, 'W' }, - #ifndef NO_LIVE_UPDATE -@@ -2721,7 +2725,7 @@ int main(int argc, char *argv[]) - orig_argc = argc; - orig_argv = argv; - -- while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:M:Q:q:T:RVW:w:U", -+ while ((opt = getopt_long(argc, argv, "DE:F:HKNPS:t:A:M:Q:q:T:RVW:w:U", - options, NULL)) != -1) { - switch (opt) { - case 'D': -@@ -2757,6 +2761,9 @@ int main(int argc, char *argv[]) - case 'I': - tdb_flags = TDB_INTERNAL|TDB_NOLOCK; - break; -+ case 'K': -+ keep_orphans = true; -+ break; - case 'V': - verbose = true; - break; -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index acb00ad96914..37006d508dbf 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -240,6 +240,9 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, - struct node *read_node(struct connection *conn, const void *ctx, - const char *name); - -+/* Remove a node and its children. */ -+int rm_node(struct connection *conn, const void *ctx, const char *name); -+ - void setup_structure(bool live_update); - struct connection *new_connection(const struct interface_funcs *funcs); - struct connection *get_connection_by_id(unsigned int conn_id); -@@ -284,6 +287,7 @@ extern int quota_req_outstanding; - extern int quota_trans_nodes; - extern int quota_memory_per_domain_soft; - extern int quota_memory_per_domain_hard; -+extern bool keep_orphans; - - extern unsigned int timeout_watch_event_msec; - -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index 98b401fdec30..84b7817cd5e6 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -227,10 +227,64 @@ static void unmap_interface(void *interface) - xengnttab_unmap(*xgt_handle, interface, 1); - } - -+static int domain_tree_remove_sub(const void *ctx, struct connection *conn, -+ struct node *node, void *arg) -+{ -+ struct domain *domain = arg; -+ TDB_DATA key; -+ int ret = WALK_TREE_OK; -+ -+ if (node->perms.p[0].id != domain->domid) -+ return WALK_TREE_OK; -+ -+ if (keep_orphans) { -+ set_tdb_key(node->name, &key); -+ domain->nbentry--; -+ node->perms.p[0].id = priv_domid; -+ node->acc.memory = 0; -+ domain_entry_inc(NULL, node); -+ if (write_node_raw(NULL, &key, node, true)) { -+ /* That's unfortunate. We only can try to continue. */ -+ syslog(LOG_ERR, -+ "error when moving orphaned node %s to dom0\n", -+ node->name); -+ } else -+ trace("orphaned node %s moved to dom0\n", node->name); -+ } else { -+ if (rm_node(NULL, ctx, node->name)) { -+ /* That's unfortunate. We only can try to continue. */ -+ syslog(LOG_ERR, -+ "error when deleting orphaned node %s\n", -+ node->name); -+ } else -+ trace("orphaned node %s deleted\n", node->name); -+ -+ /* Skip children in all cases in order to avoid more errors. */ -+ ret = WALK_TREE_SKIP_CHILDREN; -+ } -+ -+ return domain->nbentry > 0 ? ret : WALK_TREE_SUCCESS_STOP; -+} -+ -+static void domain_tree_remove(struct domain *domain) -+{ -+ int ret; -+ struct walk_funcs walkfuncs = { .enter = domain_tree_remove_sub }; -+ -+ if (domain->nbentry > 0) { -+ ret = walk_node_tree(domain, NULL, "/", &walkfuncs, domain); -+ if (ret == WALK_TREE_ERROR_STOP) -+ syslog(LOG_ERR, -+ "error when looking for orphaned nodes\n"); -+ } -+} -+ - static int destroy_domain(void *_domain) - { - struct domain *domain = _domain; - -+ domain_tree_remove(domain); -+ - list_del(&domain->list); - - if (!domain->introduced) -@@ -883,15 +937,15 @@ int domain_entry_inc(struct connection *conn, struct node *node) - struct domain *d; - unsigned int domid; - -- if (!conn) -+ if (!node->perms.p) - return 0; - -- domid = node->perms.p ? node->perms.p[0].id : conn->id; -+ domid = node->perms.p[0].id; - -- if (conn->transaction) { -+ if (conn && conn->transaction) { - transaction_entry_inc(conn->transaction, domid); - } else { -- d = (domid == conn->id && conn->domain) ? conn->domain -+ d = (conn && domid == conn->id && conn->domain) ? conn->domain - : find_or_alloc_existing_domain(domid); - if (d) - d->nbentry++; -@@ -952,23 +1006,11 @@ int domain_alloc_permrefs(struct node_perms *perms) - * Remove permissions for no longer existing domains in order to avoid a new - * domain with the same domid inheriting the permissions. - */ --int domain_adjust_node_perms(struct connection *conn, struct node *node) -+int domain_adjust_node_perms(struct node *node) - { - unsigned int i; - int ret; - -- ret = chk_domain_generation(node->perms.p[0].id, node->generation); -- -- /* If the owner doesn't exist any longer give it to priv domain. */ -- if (!ret) { -- /* -- * In theory we'd need to update the number of dom0 nodes here, -- * but we could be called for a read of the node. So better -- * avoid the risk to overflow the node count of dom0. -- */ -- node->perms.p[0].id = priv_domid; -- } -- - for (i = 1; i < node->perms.num; i++) { - if (node->perms.p[i].perms & XS_PERM_IGNORE) - continue; -@@ -986,15 +1028,15 @@ void domain_entry_dec(struct connection *conn, struct node *node) - struct domain *d; - unsigned int domid; - -- if (!conn) -+ if (!node->perms.p) - return; - - domid = node->perms.p ? node->perms.p[0].id : conn->id; - -- if (conn->transaction) { -+ if (conn && conn->transaction) { - transaction_entry_dec(conn->transaction, domid); - } else { -- d = (domid == conn->id && conn->domain) ? conn->domain -+ d = (conn && domid == conn->id && conn->domain) ? conn->domain - : find_domain_struct(domid); - if (d) { - d->nbentry--; -@@ -1113,7 +1155,7 @@ int domain_memory_add(unsigned int domid, int mem, bool no_quota_check) - * exist, as accounting is done either for a domain related to - * the current connection, or for the domain owning a node - * (which is always existing, as the owner of the node is -- * tested to exist and replaced by domid 0 if not). -+ * tested to exist and deleted or replaced by domid 0 if not). - * So not finding the related domain MUST be an error in the - * data base. - */ -diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h -index 7fe0a21d9e45..b38c82991dc6 100644 ---- a/tools/xenstore/xenstored_domain.h -+++ b/tools/xenstore/xenstored_domain.h -@@ -62,7 +62,7 @@ const char *get_implicit_path(const struct connection *conn); - bool domain_is_unprivileged(struct connection *conn); - - /* Remove node permissions for no longer existing domains. */ --int domain_adjust_node_perms(struct connection *conn, struct node *node); -+int domain_adjust_node_perms(struct node *node); - int domain_alloc_permrefs(struct node_perms *perms); - - /* Quota manipulation */ diff --git a/xsa419-xenstored-02.patch b/xsa419-xenstored-02.patch deleted file mode 100644 index e5d46a2..0000000 --- a/xsa419-xenstored-02.patch +++ /dev/null @@ -1,93 +0,0 @@ -From 929da557efea6c7d2340467d9a7fdae7fda6d2b1 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:13 +0200 -Subject: tools/xenstore: make the internal memory data base the default - -Having a file backed data base has the only advantage of being capable -to dump the contents of it while Xenstore is running, and potentially -using less swap space in case the data base can't be kept in memory. - -It has the major disadvantage of a huge performance overhead: switching -to keep the data base in memory only speeds up live update of xenstored -with 120000 nodes from 20 minutes to 11 seconds. A complete tree walk -of this configuration will be reduced from 7 seconds to 280 msecs -(measured by "xenstore-control check"). - -So make the internal memory data base the default and enhance the -"--internal-db" command line parameter to take an optional parameter -allowing to switch the internal data base back to the file based one. - -This is part of XSA-419. - -Reported-by: Juergen Gross -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/helpers/init-xenstore-domain.c b/tools/helpers/init-xenstore-domain.c -index 2d9ab6f1c583..04e351ca29a8 100644 ---- a/tools/helpers/init-xenstore-domain.c -+++ b/tools/helpers/init-xenstore-domain.c -@@ -222,9 +222,9 @@ static int build(xc_interface *xch) - } - - if ( param ) -- snprintf(cmdline, 512, "--event %d --internal-db %s", rv, param); -+ snprintf(cmdline, 512, "--event %d %s", rv, param); - else -- snprintf(cmdline, 512, "--event %d --internal-db", rv); -+ snprintf(cmdline, 512, "--event %d", rv); - - dom->guest_domid = domid; - dom->cmdline = xc_dom_strdup(dom, cmdline); -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 13e48aaa731c..36fb4a832834 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -2308,7 +2308,7 @@ static void accept_connection(int sock) - } - #endif - --static int tdb_flags; -+static int tdb_flags = TDB_INTERNAL | TDB_NOLOCK; - - /* We create initial nodes manually. */ - static void manual_node(const char *name, const char *child) -@@ -2618,7 +2618,8 @@ static void usage(void) - " watch-event: time a watch-event is kept pending\n" - " -R, --no-recovery to request that no recovery should be attempted when\n" - " the store is corrupted (debug only),\n" --" -I, --internal-db store database in memory, not on disk\n" -+" -I, --internal-db [on|off] store database in memory, not on disk, default is\n" -+" memory, with \"--internal-db off\" it is on disk\n" - " -K, --keep-orphans don't delete nodes owned by a domain when the\n" - " domain is deleted (this is a security risk!)\n" - " -V, --verbose to request verbose execution.\n"); -@@ -2644,7 +2645,7 @@ static struct option options[] = { - { "quota-soft", 1, NULL, 'q' }, - { "timeout", 1, NULL, 'w' }, - { "no-recovery", 0, NULL, 'R' }, -- { "internal-db", 0, NULL, 'I' }, -+ { "internal-db", 2, NULL, 'I' }, - { "keep-orphans", 0, NULL, 'K' }, - { "verbose", 0, NULL, 'V' }, - { "watch-nb", 1, NULL, 'W' }, -@@ -2725,7 +2726,8 @@ int main(int argc, char *argv[]) - orig_argc = argc; - orig_argv = argv; - -- while ((opt = getopt_long(argc, argv, "DE:F:HKNPS:t:A:M:Q:q:T:RVW:w:U", -+ while ((opt = getopt_long(argc, argv, -+ "DE:F:HI::KNPS:t:A:M:Q:q:T:RVW:w:U", - options, NULL)) != -1) { - switch (opt) { - case 'D': -@@ -2759,7 +2761,8 @@ int main(int argc, char *argv[]) - tracefile = optarg; - break; - case 'I': -- tdb_flags = TDB_INTERNAL|TDB_NOLOCK; -+ if (optarg && !strcmp(optarg, "off")) -+ tdb_flags = 0; - break; - case 'K': - keep_orphans = true; diff --git a/xsa419-xenstored-03.patch b/xsa419-xenstored-03.patch deleted file mode 100644 index 38130c0..0000000 --- a/xsa419-xenstored-03.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 54e63b7e7c42e4f975163809a01574e78552a6ab Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:13 +0200 -Subject: docs: enhance xenstore.txt with permissions description -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The permission scheme of Xenstore nodes is not really covered by -docs/misc/xenstore.txt, other than referring to the Xen wiki. - -Add a paragraph explaining the permissions of nodes, and especially -mentioning removal of nodes when a domain has been removed from -Xenstore. - -This is part of XSA-419. - -Reported-by: Juergen Gross -Signed-off-by: Juergen Gross -Reviewed-by: Edwin Török -Acked-by: Julien Grall - -diff --git a/docs/misc/xenstore.txt b/docs/misc/xenstore.txt -index 988ef89cba2d..44428ae3a755 100644 ---- a/docs/misc/xenstore.txt -+++ b/docs/misc/xenstore.txt -@@ -43,6 +43,17 @@ bytes are forbidden; clients specifying relative paths should keep - them to within 2048 bytes. (See XENSTORE_*_PATH_MAX in xs_wire.h.) - - -+Each node has one or multiple permission entries. Permissions are -+granted by domain-id, the first permission entry of each node specifies -+the owner of the node. Permissions of a node can be changed by the -+owner of the node, the owner can only be modified by the control -+domain (usually domain id 0). The owner always has the right to read -+and write the node, while other permissions can be setup to allow -+read and/or write access. When a domain is being removed from Xenstore -+nodes owned by that domain will be removed together with all of those -+nodes' children. -+ -+ - Communication with xenstore is via either sockets, or event channel - and shared memory, as specified in io/xs_wire.h: each message in - either direction is a header formatted as a struct xsd_sockmsg diff --git a/xsa420.patch b/xsa420.patch deleted file mode 100644 index 5d00dc2..0000000 --- a/xsa420.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 210879456769ca211c6630f47399ca7a61a37f35 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Date: Wed, 12 Oct 2022 19:13:05 +0100 -Subject: tools/ocaml: Ensure packet size is never negative -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Integers in Ocaml have 63 or 31 bits of signed precision. - -On 64-bit builds of Ocaml, this is fine because a C uint32_t always fits -within a 63-bit signed integer. - -In 32-bit builds of Ocaml, this goes wrong. The C uint32_t is truncated -first (loses the top bit), then has a unsigned/signed mismatch. - -A "negative" value (i.e. a packet on the ring of between 1G and 2G in size) -will trigger an exception later in Bytes.make in xb.ml, and because the packet -is not removed from the ring, the exception re-triggers on every subsequent -query, creating a livelock. - -Fix both the source of the exception in Xb, and as defence in depth, mark the -domain as bad for any Invalid_argument exceptions to avoid the risk of -livelock. - -This is XSA-420 / CVE-2022-42324. - -Reported-by: Juergen Gross -Signed-off-by: Edwin Török -Acked-by: Christian Lindig - -diff --git a/tools/ocaml/libs/xb/partial.ml b/tools/ocaml/libs/xb/partial.ml -index b6e2a716e263..3aa8927eb7f0 100644 ---- a/tools/ocaml/libs/xb/partial.ml -+++ b/tools/ocaml/libs/xb/partial.ml -@@ -36,7 +36,7 @@ let of_string s = - This will leave the guest connection is a bad state and will - be hard to recover from without restarting the connection - (ie rebooting the guest) *) -- let dlen = min xenstore_payload_max dlen in -+ let dlen = max 0 (min xenstore_payload_max dlen) in - { - tid = tid; - rid = rid; -@@ -46,8 +46,8 @@ let of_string s = - } - - let append pkt s sz = -- if pkt.len > 4096 then failwith "Buffer.add: cannot grow buffer"; -- Buffer.add_string pkt.buf (String.sub s 0 sz) -+ if Buffer.length pkt.buf + sz > xenstore_payload_max then failwith "Buffer.add: cannot grow buffer"; -+ Buffer.add_substring pkt.buf s 0 sz - - let to_complete pkt = - pkt.len - (Buffer.length pkt.buf) -diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml -index 5f439fe59f47..f3a71b24ad94 100644 ---- a/tools/ocaml/xenstored/process.ml -+++ b/tools/ocaml/xenstored/process.ml -@@ -722,7 +722,7 @@ let do_input store cons doms con = - History.reconnect con; - info "%s reconnection complete" (Connection.get_domstr con); - None -- | Failure exp -> -+ | Invalid_argument exp | Failure exp -> - error "caught exception %s" exp; - error "got a bad client %s" (sprintf "%-8s" (Connection.get_domstr con)); - Connection.mark_as_bad con; diff --git a/xsa421-01.patch b/xsa421-01.patch deleted file mode 100644 index 10960ba..0000000 --- a/xsa421-01.patch +++ /dev/null @@ -1,40 +0,0 @@ -From 9166869e7e6530befddfd8bb46ff37436a38efc1 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:13 +0200 -Subject: tools/xenstore: fix deleting node in transaction - -In case a node has been created in a transaction and it is later -deleted in the same transaction, the transaction will be terminated -with an error. - -As this error is encountered only when handling the deleted node at -transaction finalization, the transaction will have been performed -partially and without updating the accounting information. This will -enable a malicious guest to create arbitrary number of nodes. - -This is part of XSA-421 / CVE-2022-42325. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Tested-by: Julien Grall -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index 3e3eb47326cc..7ffe21bb5285 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -418,7 +418,13 @@ static int finalize_transaction(struct connection *conn, - true); - talloc_free(data.dptr); - } else { -- ret = do_tdb_delete(conn, &key, NULL); -+ /* -+ * A node having been created and later deleted -+ * in this transaction will have no generation -+ * information stored. -+ */ -+ ret = (i->generation == NO_GENERATION) -+ ? 0 : do_tdb_delete(conn, &key, NULL); - } - if (ret) - goto err; diff --git a/xsa421-02.patch b/xsa421-02.patch deleted file mode 100644 index 1a0b5cd..0000000 --- a/xsa421-02.patch +++ /dev/null @@ -1,401 +0,0 @@ -From 09fc22fea8a4689c5e563ba4a2fa959282071792 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:14 +0200 -Subject: tools/xenstore: harden transaction finalization against errors - -When finalizing a transaction, any error occurring after checking for -conflicts will result in the transaction being performed only -partially today. Additionally accounting data will not be updated at -the end of the transaction, which might result in further problems -later. - -Avoid those problems by multiple modifications: - -- free any transaction specific nodes which don't need to be committed - as they haven't been written during the transaction as soon as their - generation count has been verified, this will reduce the risk of - out-of-memory situations - -- store the transaction specific node name in struct accessed_node in - order to avoid the need to allocate additional memory for it when - finalizing the transaction - -- don't stop the transaction finalization when hitting an error - condition, but try to continue to handle all modified nodes - -- in case of a detected error do the accounting update as needed and - call the data base checking only after that - -- if writing a node in a transaction is failing (e.g. due to a failed - quota check), fail the transaction, as prior changes to struct - accessed_node can't easily be undone in that case - -This is part of XSA-421 / CVE-2022-42326. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall -Tested-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 36fb4a832834..476d5c6d51bd 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -723,8 +723,7 @@ struct node *read_node(struct connection *conn, const void *ctx, - return NULL; - } - -- if (transaction_prepend(conn, name, &key)) -- return NULL; -+ transaction_prepend(conn, name, &key); - - data = tdb_fetch(tdb_ctx, key); - -@@ -842,10 +841,21 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, - static int write_node(struct connection *conn, struct node *node, - bool no_quota_check) - { -+ int ret; -+ - if (access_node(conn, node, NODE_ACCESS_WRITE, &node->key)) - return errno; - -- return write_node_raw(conn, &node->key, node, no_quota_check); -+ ret = write_node_raw(conn, &node->key, node, no_quota_check); -+ if (ret && conn && conn->transaction) { -+ /* -+ * Reverting access_node() is hard, so just fail the -+ * transaction. -+ */ -+ fail_transaction(conn->transaction); -+ } -+ -+ return ret; - } - - unsigned int perm_for_conn(struct connection *conn, -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index 7ffe21bb5285..ac854197cadb 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -114,7 +114,8 @@ struct accessed_node - struct list_head list; - - /* The name of the node. */ -- char *node; -+ char *trans_name; /* Transaction specific name. */ -+ char *node; /* Main data base name. */ - - /* Generation count (or NO_GENERATION) for conflict checking. */ - uint64_t generation; -@@ -199,25 +200,20 @@ static char *transaction_get_node_name(void *ctx, struct transaction *trans, - * Prepend the transaction to name if node has been modified in the current - * transaction. - */ --int transaction_prepend(struct connection *conn, const char *name, -- TDB_DATA *key) -+void transaction_prepend(struct connection *conn, const char *name, -+ TDB_DATA *key) - { -- char *tdb_name; -+ struct accessed_node *i; - -- if (!conn || !conn->transaction || -- !find_accessed_node(conn->transaction, name)) { -- set_tdb_key(name, key); -- return 0; -+ if (conn && conn->transaction) { -+ i = find_accessed_node(conn->transaction, name); -+ if (i) { -+ set_tdb_key(i->trans_name, key); -+ return; -+ } - } - -- tdb_name = transaction_get_node_name(conn->transaction, -- conn->transaction, name); -- if (!tdb_name) -- return errno; -- -- set_tdb_key(tdb_name, key); -- -- return 0; -+ set_tdb_key(name, key); - } - - /* -@@ -240,7 +236,6 @@ int access_node(struct connection *conn, struct node *node, - struct accessed_node *i = NULL; - struct transaction *trans; - TDB_DATA local_key; -- const char *trans_name = NULL; - int ret; - bool introduce = false; - -@@ -259,10 +254,6 @@ int access_node(struct connection *conn, struct node *node, - - trans = conn->transaction; - -- trans_name = transaction_get_node_name(node, trans, node->name); -- if (!trans_name) -- goto nomem; -- - i = find_accessed_node(trans, node->name); - if (!i) { - if (trans->nodes >= quota_trans_nodes && -@@ -273,9 +264,10 @@ int access_node(struct connection *conn, struct node *node, - i = talloc_zero(trans, struct accessed_node); - if (!i) - goto nomem; -- i->node = talloc_strdup(i, node->name); -- if (!i->node) -+ i->trans_name = transaction_get_node_name(i, trans, node->name); -+ if (!i->trans_name) - goto nomem; -+ i->node = strchr(i->trans_name, '/') + 1; - if (node->generation != NO_GENERATION && node->perms.num) { - i->perms.p = talloc_array(i, struct xs_permissions, - node->perms.num); -@@ -302,7 +294,7 @@ int access_node(struct connection *conn, struct node *node, - i->generation = node->generation; - i->check_gen = true; - if (node->generation != NO_GENERATION) { -- set_tdb_key(trans_name, &local_key); -+ set_tdb_key(i->trans_name, &local_key); - ret = write_node_raw(conn, &local_key, node, true); - if (ret) - goto err; -@@ -321,7 +313,7 @@ int access_node(struct connection *conn, struct node *node, - return -1; - - if (key) { -- set_tdb_key(trans_name, key); -+ set_tdb_key(i->trans_name, key); - if (type == NODE_ACCESS_WRITE) - i->ta_node = true; - if (type == NODE_ACCESS_DELETE) -@@ -333,7 +325,6 @@ int access_node(struct connection *conn, struct node *node, - nomem: - ret = ENOMEM; - err: -- talloc_free((void *)trans_name); - talloc_free(i); - trans->fail = true; - errno = ret; -@@ -371,100 +362,90 @@ void queue_watches(struct connection *conn, const char *name, bool watch_exact) - * base. - */ - static int finalize_transaction(struct connection *conn, -- struct transaction *trans) -+ struct transaction *trans, bool *is_corrupt) - { -- struct accessed_node *i; -+ struct accessed_node *i, *n; - TDB_DATA key, ta_key, data; - struct xs_tdb_record_hdr *hdr; - uint64_t gen; -- char *trans_name; -- int ret; - -- list_for_each_entry(i, &trans->accessed, list) { -- if (!i->check_gen) -- continue; -+ list_for_each_entry_safe(i, n, &trans->accessed, list) { -+ if (i->check_gen) { -+ set_tdb_key(i->node, &key); -+ data = tdb_fetch(tdb_ctx, key); -+ hdr = (void *)data.dptr; -+ if (!data.dptr) { -+ if (tdb_error(tdb_ctx) != TDB_ERR_NOEXIST) -+ return EIO; -+ gen = NO_GENERATION; -+ } else -+ gen = hdr->generation; -+ talloc_free(data.dptr); -+ if (i->generation != gen) -+ return EAGAIN; -+ } - -- set_tdb_key(i->node, &key); -- data = tdb_fetch(tdb_ctx, key); -- hdr = (void *)data.dptr; -- if (!data.dptr) { -- if (tdb_error(tdb_ctx) != TDB_ERR_NOEXIST) -- return EIO; -- gen = NO_GENERATION; -- } else -- gen = hdr->generation; -- talloc_free(data.dptr); -- if (i->generation != gen) -- return EAGAIN; -+ /* Entries for unmodified nodes can be removed early. */ -+ if (!i->modified) { -+ if (i->ta_node) { -+ set_tdb_key(i->trans_name, &ta_key); -+ if (do_tdb_delete(conn, &ta_key, NULL)) -+ return EIO; -+ } -+ list_del(&i->list); -+ talloc_free(i); -+ } - } - - while ((i = list_top(&trans->accessed, struct accessed_node, list))) { -- trans_name = transaction_get_node_name(i, trans, i->node); -- if (!trans_name) -- /* We are doomed: the transaction is only partial. */ -- goto err; -- -- set_tdb_key(trans_name, &ta_key); -- -- if (i->modified) { -- set_tdb_key(i->node, &key); -- if (i->ta_node) { -- data = tdb_fetch(tdb_ctx, ta_key); -- if (!data.dptr) -- goto err; -+ set_tdb_key(i->node, &key); -+ if (i->ta_node) { -+ set_tdb_key(i->trans_name, &ta_key); -+ data = tdb_fetch(tdb_ctx, ta_key); -+ if (data.dptr) { - hdr = (void *)data.dptr; - hdr->generation = ++generation; -- ret = do_tdb_write(conn, &key, &data, NULL, -- true); -+ *is_corrupt |= do_tdb_write(conn, &key, &data, -+ NULL, true); - talloc_free(data.dptr); -+ if (do_tdb_delete(conn, &ta_key, NULL)) -+ *is_corrupt = true; - } else { -- /* -- * A node having been created and later deleted -- * in this transaction will have no generation -- * information stored. -- */ -- ret = (i->generation == NO_GENERATION) -- ? 0 : do_tdb_delete(conn, &key, NULL); -- } -- if (ret) -- goto err; -- if (i->fire_watch) { -- fire_watches(conn, trans, i->node, NULL, -- i->watch_exact, -- i->perms.p ? &i->perms : NULL); -+ *is_corrupt = true; - } -+ } else { -+ /* -+ * A node having been created and later deleted -+ * in this transaction will have no generation -+ * information stored. -+ */ -+ *is_corrupt |= (i->generation == NO_GENERATION) -+ ? false -+ : do_tdb_delete(conn, &key, NULL); - } -+ if (i->fire_watch) -+ fire_watches(conn, trans, i->node, NULL, i->watch_exact, -+ i->perms.p ? &i->perms : NULL); - -- if (i->ta_node && do_tdb_delete(conn, &ta_key, NULL)) -- goto err; - list_del(&i->list); - talloc_free(i); - } - - return 0; -- --err: -- corrupt(conn, "Partial transaction"); -- return EIO; - } - - static int destroy_transaction(void *_transaction) - { - struct transaction *trans = _transaction; - struct accessed_node *i; -- char *trans_name; - TDB_DATA key; - - wrl_ntransactions--; - trace_destroy(trans, "transaction"); - while ((i = list_top(&trans->accessed, struct accessed_node, list))) { - if (i->ta_node) { -- trans_name = transaction_get_node_name(i, trans, -- i->node); -- if (trans_name) { -- set_tdb_key(trans_name, &key); -- do_tdb_delete(trans->conn, &key, NULL); -- } -+ set_tdb_key(i->trans_name, &key); -+ do_tdb_delete(trans->conn, &key, NULL); - } - list_del(&i->list); - talloc_free(i); -@@ -556,6 +537,7 @@ int do_transaction_end(const void *ctx, struct connection *conn, - { - const char *arg = onearg(in); - struct transaction *trans; -+ bool is_corrupt = false; - int ret; - - if (!arg || (!streq(arg, "T") && !streq(arg, "F"))) -@@ -579,13 +561,17 @@ int do_transaction_end(const void *ctx, struct connection *conn, - ret = transaction_fix_domains(trans, false); - if (ret) - return ret; -- if (finalize_transaction(conn, trans)) -- return EAGAIN; -+ ret = finalize_transaction(conn, trans, &is_corrupt); -+ if (ret) -+ return ret; - - wrl_apply_debit_trans_commit(conn); - - /* fix domain entry for each changed domain */ - transaction_fix_domains(trans, true); -+ -+ if (is_corrupt) -+ corrupt(conn, "transaction inconsistency"); - } - send_ack(conn, XS_TRANSACTION_END); - -@@ -660,7 +646,7 @@ int check_transactions(struct hashtable *hash) - struct connection *conn; - struct transaction *trans; - struct accessed_node *i; -- char *tname, *tnode; -+ char *tname; - - list_for_each_entry(conn, &connections, list) { - list_for_each_entry(trans, &conn->transaction_list, list) { -@@ -672,11 +658,8 @@ int check_transactions(struct hashtable *hash) - list_for_each_entry(i, &trans->accessed, list) { - if (!i->ta_node) - continue; -- tnode = transaction_get_node_name(tname, trans, -- i->node); -- if (!tnode || !remember_string(hash, tnode)) -+ if (!remember_string(hash, i->trans_name)) - goto nomem; -- talloc_free(tnode); - } - - talloc_free(tname); -diff --git a/tools/xenstore/xenstored_transaction.h b/tools/xenstore/xenstored_transaction.h -index 39d7f81c5127..3417303f9427 100644 ---- a/tools/xenstore/xenstored_transaction.h -+++ b/tools/xenstore/xenstored_transaction.h -@@ -48,8 +48,8 @@ int __must_check access_node(struct connection *conn, struct node *node, - void queue_watches(struct connection *conn, const char *name, bool watch_exact); - - /* Prepend the transaction to name if appropriate. */ --int transaction_prepend(struct connection *conn, const char *name, -- TDB_DATA *key); -+void transaction_prepend(struct connection *conn, const char *name, -+ TDB_DATA *key); - - /* Mark the transaction as failed. This will prevent it to be committed. */ - void fail_transaction(struct transaction *trans); diff --git a/xsa422-4.16-1.patch b/xsa422-4.16-1.patch deleted file mode 100644 index 1d36873..0000000 --- a/xsa422-4.16-1.patch +++ /dev/null @@ -1,70 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Enumeration for IBPB_RET - -The IBPB_RET bit indicates that the CPU's implementation of MSR_PRED_CMD.IBPB -does flush the RSB/RAS too. - -This is part of XSA-422 / CVE-2022-23824. - -Signed-off-by: Andrew Cooper -Acked-by: Jan Beulich - -diff --git a/tools/libs/light/libxl_cpuid.c b/tools/libs/light/libxl_cpuid.c -index bf6fdee360a9..691d5c6b2a68 100644 ---- a/tools/libs/light/libxl_cpuid.c -+++ b/tools/libs/light/libxl_cpuid.c -@@ -289,6 +289,7 @@ int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str) - {"ssb-no", 0x80000008, NA, CPUID_REG_EBX, 26, 1}, - {"psfd", 0x80000008, NA, CPUID_REG_EBX, 28, 1}, - {"btc-no", 0x80000008, NA, CPUID_REG_EBX, 29, 1}, -+ {"ibpb-ret", 0x80000008, NA, CPUID_REG_EBX, 30, 1}, - - {"nc", 0x80000008, NA, CPUID_REG_ECX, 0, 8}, - {"apicidsize", 0x80000008, NA, CPUID_REG_ECX, 12, 4}, -diff --git a/tools/misc/xen-cpuid.c b/tools/misc/xen-cpuid.c -index fe22f5f5b68b..cd094427dd4c 100644 ---- a/tools/misc/xen-cpuid.c -+++ b/tools/misc/xen-cpuid.c -@@ -159,6 +159,7 @@ static const char *const str_e8b[32] = - [24] = "amd-ssbd", [25] = "virt-ssbd", - [26] = "ssb-no", - [28] = "psfd", [29] = "btc-no", -+ [30] = "ibpb-ret", - }; - - static const char *const str_7d0[32] = -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 0f4bad3d3abb..16a562d3a172 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -419,7 +419,7 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - * Hardware read-only information, stating immunity to certain issues, or - * suggestions of which mitigation to use. - */ -- printk(" Hardware hints:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", -+ printk(" Hardware hints:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", - (caps & ARCH_CAPS_RDCL_NO) ? " RDCL_NO" : "", - (caps & ARCH_CAPS_IBRS_ALL) ? " IBRS_ALL" : "", - (caps & ARCH_CAPS_RSBA) ? " RSBA" : "", -@@ -436,7 +436,8 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - (e8b & cpufeat_mask(X86_FEATURE_STIBP_ALWAYS)) ? " STIBP_ALWAYS" : "", - (e8b & cpufeat_mask(X86_FEATURE_IBRS_FAST)) ? " IBRS_FAST" : "", - (e8b & cpufeat_mask(X86_FEATURE_IBRS_SAME_MODE)) ? " IBRS_SAME_MODE" : "", -- (e8b & cpufeat_mask(X86_FEATURE_BTC_NO)) ? " BTC_NO" : ""); -+ (e8b & cpufeat_mask(X86_FEATURE_BTC_NO)) ? " BTC_NO" : "", -+ (e8b & cpufeat_mask(X86_FEATURE_IBPB_RET)) ? " IBPB_RET" : ""); - - /* Hardware features which need driving to mitigate issues. */ - printk(" Hardware features:%s%s%s%s%s%s%s%s%s%s%s%s\n", -diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h -index e7b8167800a2..e0731221404c 100644 ---- a/xen/include/public/arch-x86/cpufeatureset.h -+++ b/xen/include/public/arch-x86/cpufeatureset.h -@@ -267,6 +267,7 @@ XEN_CPUFEATURE(VIRT_SSBD, 8*32+25) /* MSR_VIRT_SPEC_CTRL.SSBD */ - XEN_CPUFEATURE(SSB_NO, 8*32+26) /*A Hardware not vulnerable to SSB */ - XEN_CPUFEATURE(PSFD, 8*32+28) /*S MSR_SPEC_CTRL.PSFD */ - XEN_CPUFEATURE(BTC_NO, 8*32+29) /*A Hardware not vulnerable to Branch Type Confusion */ -+XEN_CPUFEATURE(IBPB_RET, 8*32+30) /*A IBPB clears RSB/RAS too. */ - - /* Intel-defined CPU features, CPUID level 0x00000007:0.edx, word 9 */ - XEN_CPUFEATURE(AVX512_4VNNIW, 9*32+ 2) /*A AVX512 Neural Network Instructions */ diff --git a/xsa422-4.16-2.patch b/xsa422-4.16-2.patch deleted file mode 100644 index a1a2f8d..0000000 --- a/xsa422-4.16-2.patch +++ /dev/null @@ -1,100 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Mitigate IBPB not flushing the RSB/RAS - -Introduce spec_ctrl_new_guest_context() to encapsulate all logic pertaining to -using MSR_PRED_CMD for a new guest context, even if it only has one user -presently. - -Introduce X86_BUG_IBPB_NO_RET, and use it extend spec_ctrl_new_guest_context() -with a manual fixup for hardware which mis-implements IBPB. - -This is part of XSA-422 / CVE-2022-23824. - -Signed-off-by: Andrew Cooper -Acked-by: Jan Beulich - -diff --git a/xen/arch/x86/asm-macros.c b/xen/arch/x86/asm-macros.c -index 7e536b0d82f5..891d86c7655c 100644 ---- a/xen/arch/x86/asm-macros.c -+++ b/xen/arch/x86/asm-macros.c -@@ -1,2 +1,3 @@ - #include - #include -+#include -diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c -index 3fab2364be8d..3080cde62b5b 100644 ---- a/xen/arch/x86/domain.c -+++ b/xen/arch/x86/domain.c -@@ -2092,7 +2092,7 @@ void context_switch(struct vcpu *prev, struct vcpu *next) - */ - if ( *last_id != next_id ) - { -- wrmsrl(MSR_PRED_CMD, PRED_CMD_IBPB); -+ spec_ctrl_new_guest_context(); - *last_id = next_id; - } - } -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 16a562d3a172..90d86fe5cb47 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -805,6 +805,14 @@ static void __init ibpb_calculations(void) - } - - /* -+ * AMD/Hygon CPUs to date (June 2022) don't flush the the RAS. Future -+ * CPUs are expected to enumerate IBPB_RET when this has been fixed. -+ * Until then, cover the difference with the software sequence. -+ */ -+ if ( boot_cpu_has(X86_FEATURE_IBPB) && !boot_cpu_has(X86_FEATURE_IBPB_RET) ) -+ setup_force_cpu_cap(X86_BUG_IBPB_NO_RET); -+ -+ /* - * IBPB-on-entry mitigations for Branch Type Confusion. - * - * IBPB && !BTC_NO selects all AMD/Hygon hardware, not known to be safe, -diff --git a/xen/include/asm-x86/cpufeatures.h b/xen/include/asm-x86/cpufeatures.h -index 672c9ee22ba2..ecc1bb09505a 100644 ---- a/xen/include/asm-x86/cpufeatures.h -+++ b/xen/include/asm-x86/cpufeatures.h -@@ -49,6 +49,7 @@ XEN_CPUFEATURE(IBPB_ENTRY_HVM, X86_SYNTH(29)) /* MSR_PRED_CMD used by Xen for - #define X86_BUG_FPU_PTRS X86_BUG( 0) /* (F)X{SAVE,RSTOR} doesn't save/restore FOP/FIP/FDP. */ - #define X86_BUG_NULL_SEG X86_BUG( 1) /* NULL-ing a selector preserves the base and limit. */ - #define X86_BUG_CLFLUSH_MFENCE X86_BUG( 2) /* MFENCE needed to serialise CLFLUSH */ -+#define X86_BUG_IBPB_NO_RET X86_BUG( 3) /* IBPB doesn't flush the RSB/RAS */ - - /* Total number of capability words, inc synth and bug words. */ - #define NCAPINTS (FSCAPINTS + X86_NR_SYNTH + X86_NR_BUG) /* N 32-bit words worth of info */ -diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h -index 9403b81dc7af..6a77c3937844 100644 ---- a/xen/include/asm-x86/spec_ctrl.h -+++ b/xen/include/asm-x86/spec_ctrl.h -@@ -65,6 +65,28 @@ - void init_speculation_mitigations(void); - void spec_ctrl_init_domain(struct domain *d); - -+/* -+ * Switch to a new guest prediction context. -+ * -+ * This flushes all indirect branch predictors (BTB, RSB/RAS), so guest code -+ * which has previously run on this CPU can't attack subsequent guest code. -+ * -+ * As this flushes the RSB/RAS, it destroys the predictions of the calling -+ * context. For best performace, arrange for this to be used when we're going -+ * to jump out of the current context, e.g. with reset_stack_and_jump(). -+ * -+ * For hardware which mis-implements IBPB, fix up by flushing the RSB/RAS -+ * manually. -+ */ -+static always_inline void spec_ctrl_new_guest_context(void) -+{ -+ wrmsrl(MSR_PRED_CMD, PRED_CMD_IBPB); -+ -+ /* (ab)use alternative_input() to specify clobbers. */ -+ alternative_input("", "DO_OVERWRITE_RSB", X86_BUG_IBPB_NO_RET, -+ : "rax", "rcx"); -+} -+ - extern int8_t opt_ibpb_ctxt_switch; - extern bool opt_ssbd; - extern int8_t opt_eager_fpu; From 766c47c1cba8bfbb4561927005f40fa5aeb77825 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 20 Dec 2022 19:55:18 +0000 Subject: [PATCH 052/137] python3-setuptools BuildRequires is needed for python 3.12 --- xen.spec | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/xen.spec b/xen.spec index 989eaba..1571164 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.17.0 -Release: 1%{?dist} +Release: 2%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -121,7 +121,7 @@ BuildRequires: seabios-bin ipxe-roms-qemu # for the VMX "bios" BuildRequires: dev86 %endif -BuildRequires: python3-devel ncurses-devel +BuildRequires: python3-devel ncurses-devel python3-setuptools BuildRequires: perl-interpreter perl-generators %ifarch %{ix86} x86_64 # so that x86_64 builds pick up glibc32 correctly @@ -931,6 +931,9 @@ fi %endif %changelog +* Tue Dec 20 2022 Michael Young - 4.17.0-2 +- python3-setuptools BuildRequires is needed for python 3.12 + * Tue Dec 13 2022 Michael Young - 4.17.0-1 - update to xen-4.17.0 rebase xen.fedora.systemd.patch and xen.canonicalize.patch From 8695caf85a84e6ec55e8782e754a8c86f8c14a48 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Sun, 8 Jan 2023 09:35:06 +0000 Subject: [PATCH 053/137] fix clean up of init scripts if /etc/rc.d/init.d doesn't exist --- xen.spec | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/xen.spec b/xen.spec index 1571164..91a199a 100644 --- a/xen.spec +++ b/xen.spec @@ -515,10 +515,12 @@ mkdir -p %{buildroot}%{_sysconfdir}/logrotate.d/ install -m 644 %{SOURCE2} %{buildroot}%{_sysconfdir}/logrotate.d/%{name} # init scripts -rm %{buildroot}%{_sysconfdir}/rc.d/init.d/xen-watchdog -rm %{buildroot}%{_sysconfdir}/rc.d/init.d/xencommons -rm %{buildroot}%{_sysconfdir}/rc.d/init.d/xendomains -rm %{buildroot}%{_sysconfdir}/rc.d/init.d/xendriverdomain +%define initdloc %(test -d /etc/rc.d/init.d/ && echo rc.d/init.d || echo init.d ) + +rm %{buildroot}%{_sysconfdir}/%{initdloc}/xen-watchdog +rm %{buildroot}%{_sysconfdir}/%{initdloc}/xencommons +rm %{buildroot}%{_sysconfdir}/%{initdloc}/xendomains +rm %{buildroot}%{_sysconfdir}/%{initdloc}/xendriverdomain ############ create dirs in /var ############ @@ -931,7 +933,10 @@ fi %endif %changelog -* Tue Dec 20 2022 Michael Young - 4.17.0-2 +* Sun Jan 08 2023 Michael Young - 4.17.0-2 +- fix clean up of init scripts if /etc/rc.d/init.d doesn't exist + +* Tue Dec 20 2022 Michael Young - python3-setuptools BuildRequires is needed for python 3.12 * Tue Dec 13 2022 Michael Young - 4.17.0-1 From 0e3619e0576f6c78ba7dc9787b7f0678f9105625 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 17 Jan 2023 22:07:23 +0000 Subject: [PATCH 054/137] build fix for gcc13 --- xen.gcc13.fixes.patch | 10 ++++++++++ xen.spec | 7 ++++++- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 xen.gcc13.fixes.patch diff --git a/xen.gcc13.fixes.patch b/xen.gcc13.fixes.patch new file mode 100644 index 0000000..d9800e2 --- /dev/null +++ b/xen.gcc13.fixes.patch @@ -0,0 +1,10 @@ +--- xen-4.17.0/xen/common/bunzip2.c.orig 2022-12-08 18:03:08.000000000 +0000 ++++ xen-4.17.0/xen/common/bunzip2.c 2023-01-17 21:35:39.251292015 +0000 +@@ -223,6 +223,7 @@ + for (j = 0; j < groupCount; j++) { + unsigned char length[MAX_SYMBOLS], temp[MAX_HUFCODE_BITS+1]; + int minLen, maxLen, pp; ++ length[0]=0; + /* Read Huffman code lengths for each symbol. They're + stored in a way similar to mtf; record a starting + value for the first symbol, and an offset from the diff --git a/xen.spec b/xen.spec index 91a199a..bb50fcd 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.17.0 -Release: 2%{?dist} +Release: 3%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -109,6 +109,7 @@ Patch41: xen.gcc9.fixes.patch Patch43: xen.gcc11.fixes.patch Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch +Patch47: xen.gcc13.fixes.patch %if %build_qemutrad @@ -319,6 +320,7 @@ manage Xen virtual machines. %patch43 -p1 %patch45 -p1 %patch46 -p1 +%patch47 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -933,6 +935,9 @@ fi %endif %changelog +* Tue Jan 17 2023 Michael Young - 4.17.0-3 +- build fix for gcc13 + * Sun Jan 08 2023 Michael Young - 4.17.0-2 - fix clean up of init scripts if /etc/rc.d/init.d doesn't exist From e92b341446442f013d7adf749499ffd0810879b2 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Sat, 21 Jan 2023 07:19:34 +0000 Subject: [PATCH 055/137] Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index bb50fcd..d4b1e62 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.17.0 -Release: 3%{?dist} +Release: 4%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -935,6 +935,9 @@ fi %endif %changelog +* Sat Jan 21 2023 Fedora Release Engineering - 4.17.0-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild + * Tue Jan 17 2023 Michael Young - 4.17.0-3 - build fix for gcc13 From eadd06a27e93113260ba79ab0bb67c6cac8e4703 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 24 Jan 2023 23:45:25 +0000 Subject: [PATCH 056/137] now need BuildRequires for hostname --- xen.spec | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index d4b1e62..1e797c0 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.17.0 -Release: 4%{?dist} +Release: 5%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -182,6 +182,7 @@ BuildRequires: edk2-ovmf %if %build_hyp BuildRequires: bison flex %endif +BuildRequires: hostname %description This package contains the XenD daemon and xm command line @@ -935,6 +936,9 @@ fi %endif %changelog +* Tue Jan 24 2023 Michael Young - 4.17.0-5 +- now need BuildRequires for hostname + * Sat Jan 21 2023 Fedora Release Engineering - 4.17.0-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild From d59e49c417c5589c6425dea926d226ec82a4e57b Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 25 Jan 2023 17:36:04 +0000 Subject: [PATCH 057/137] Guests can cause Xenstore crash via soft reset [XSA-425, CVE-2022-42330] --- xen.spec | 8 +++- xsa425.patch | 132 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 xsa425.patch diff --git a/xen.spec b/xen.spec index 1e797c0..a260881 100644 --- a/xen.spec +++ b/xen.spec @@ -110,6 +110,7 @@ Patch43: xen.gcc11.fixes.patch Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch Patch47: xen.gcc13.fixes.patch +Patch48: xsa425.patch %if %build_qemutrad @@ -322,6 +323,7 @@ manage Xen virtual machines. %patch45 -p1 %patch46 -p1 %patch47 -p1 +%patch48 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -936,7 +938,11 @@ fi %endif %changelog -* Tue Jan 24 2023 Michael Young - 4.17.0-5 +* Wed Jan 25 2023 Michael Young - 4.17.0-5 +- Guests can cause Xenstore crash via soft reset [XSA-425, CVE-2022-42330] + (#2164520) + +* Tue Jan 24 2023 Michael Young - now need BuildRequires for hostname * Sat Jan 21 2023 Fedora Release Engineering - 4.17.0-4 diff --git a/xsa425.patch b/xsa425.patch new file mode 100644 index 0000000..b367320 --- /dev/null +++ b/xsa425.patch @@ -0,0 +1,132 @@ +From: Jason Andryuk +Subject: Revert "tools/xenstore: simplify loop handling connection I/O" + +I'm observing guest kexec trigger xenstored to abort on a double free. + +gdb output: +Program received signal SIGABRT, Aborted. +__pthread_kill_implementation (no_tid=0, signo=6, threadid=140645614258112) at ./nptl/pthread_kill.c:44 +44 ./nptl/pthread_kill.c: No such file or directory. +(gdb) bt + at ./nptl/pthread_kill.c:44 + at ./nptl/pthread_kill.c:78 + at ./nptl/pthread_kill.c:89 + at ../sysdeps/posix/raise.c:26 + at talloc.c:119 + ptr=ptr@entry=0x559fae724290) at talloc.c:232 + at xenstored_core.c:2945 +(gdb) frame 5 + at talloc.c:119 +119 TALLOC_ABORT("Bad talloc magic value - double free"); +(gdb) frame 7 + at xenstored_core.c:2945 +2945 talloc_increase_ref_count(conn); +(gdb) p conn +$1 = (struct connection *) 0x559fae724290 + +Looking at a xenstore trace, we have: +IN 0x559fae71f250 20230120 17:40:53 READ (/local/domain/3/image/device-model-dom +id ) +wrl: dom 0 1 msec 10000 credit 1000000 reserve 100 disc +ard +wrl: dom 3 1 msec 10000 credit 1000000 reserve 100 disc +ard +wrl: dom 0 0 msec 10000 credit 1000000 reserve 0 disc +ard +wrl: dom 3 0 msec 10000 credit 1000000 reserve 0 disc +ard +OUT 0x559fae71f250 20230120 17:40:53 ERROR (ENOENT ) +wrl: dom 0 1 msec 10000 credit 1000000 reserve 100 disc +ard +wrl: dom 3 1 msec 10000 credit 1000000 reserve 100 disc +ard +IN 0x559fae71f250 20230120 17:40:53 RELEASE (3 ) +DESTROY watch 0x559fae73f630 +DESTROY watch 0x559fae75ddf0 +DESTROY watch 0x559fae75ec30 +DESTROY watch 0x559fae75ea60 +DESTROY watch 0x559fae732c00 +DESTROY watch 0x559fae72cea0 +DESTROY watch 0x559fae728fc0 +DESTROY watch 0x559fae729570 +DESTROY connection 0x559fae724290 +orphaned node /local/domain/3/device/suspend/event-channel deleted +orphaned node /local/domain/3/device/vbd/51712 deleted +orphaned node /local/domain/3/device/vkbd/0 deleted +orphaned node /local/domain/3/device/vif/0 deleted +orphaned node /local/domain/3/control/shutdown deleted +orphaned node /local/domain/3/control/feature-poweroff deleted +orphaned node /local/domain/3/control/feature-reboot deleted +orphaned node /local/domain/3/control/feature-suspend deleted +orphaned node /local/domain/3/control/feature-s3 deleted +orphaned node /local/domain/3/control/feature-s4 deleted +orphaned node /local/domain/3/control/sysrq deleted +orphaned node /local/domain/3/data deleted +orphaned node /local/domain/3/drivers deleted +orphaned node /local/domain/3/feature deleted +orphaned node /local/domain/3/attr deleted +orphaned node /local/domain/3/error deleted +orphaned node /local/domain/3/console/backend-id deleted + +and no further output. + +The trace shows that DESTROY was called for connection 0x559fae724290, +but that is the same pointer (conn) main() was looping through from +connections. So it wasn't actually removed from the connections list? + +Reverting commit e8e6e42279a5 "tools/xenstore: simplify loop handling +connection I/O" fixes the abort/double free. I think the use of +list_for_each_entry_safe is incorrect. list_for_each_entry_safe makes +traversal safe for deleting the current iterator, but RELEASE/do_release +will delete some other entry in the connections list. I think the +observed abort is because list_for_each_entry has next pointing to the +deleted connection, and it is used in the subsequent iteration. + +Add a comment explaining the unsuitability of list_for_each_entry_safe. +Also notice that the old code takes a reference on next which would +prevents a use-after-free. + +This reverts commit e8e6e42279a5723239c5c40ba4c7f579a979465d. + +This is XSA-425/CVE-2022-42330. + +Fixes: e8e6e42279a5 ("tools/xenstore: simplify loop handling connection I/O") +Signed-off-by: Jason Andryuk +Reviewed-by: Juergen Gross +Reviewed-by: Julien Grall +--- + tools/xenstore/xenstored_core.c | 19 +++++++++++++++++-- + 1 file changed, 17 insertions(+), 2 deletions(-) + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 78a3edaa4e..029e3852fc 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -2941,8 +2941,23 @@ int main(int argc, char *argv[]) + } + } + +- list_for_each_entry_safe(conn, next, &connections, list) { +- talloc_increase_ref_count(conn); ++ /* ++ * list_for_each_entry_safe is not suitable here because ++ * handle_input may delete entries besides the current one, but ++ * those may be in the temporary next which would trigger a ++ * use-after-free. list_for_each_entry_safe is only safe for ++ * deleting the current entry. ++ */ ++ next = list_entry(connections.next, typeof(*conn), list); ++ if (&next->list != &connections) ++ talloc_increase_ref_count(next); ++ while (&next->list != &connections) { ++ conn = next; ++ ++ next = list_entry(conn->list.next, ++ typeof(*conn), list); ++ if (&next->list != &connections) ++ talloc_increase_ref_count(next); + + if (conn_can_read(conn)) + handle_input(conn); +-- +2.34.1 From cef4bbf95956f16679a79e2ceb6a9f9bdfcf2153 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 14 Feb 2023 21:19:26 +0000 Subject: [PATCH 058/137] x86: Cross-Thread Return Address Predictions [XSA-426, CVE-2022-27672] --- xen.spec | 7 +++- xsa426.patch | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 xsa426.patch diff --git a/xen.spec b/xen.spec index a260881..6918277 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.17.0 -Release: 5%{?dist} +Release: 6%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -111,6 +111,7 @@ Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch Patch47: xen.gcc13.fixes.patch Patch48: xsa425.patch +Patch49: xsa426.patch %if %build_qemutrad @@ -324,6 +325,7 @@ manage Xen virtual machines. %patch46 -p1 %patch47 -p1 %patch48 -p1 +%patch49 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -938,6 +940,9 @@ fi %endif %changelog +* Tue Feb 14 2023 Michael Young - 4.17.0-6 +- x86: Cross-Thread Return Address Predictions [XSA-426, CVE-2022-27672] + * Wed Jan 25 2023 Michael Young - 4.17.0-5 - Guests can cause Xenstore crash via soft reset [XSA-425, CVE-2022-42330] (#2164520) diff --git a/xsa426.patch b/xsa426.patch new file mode 100644 index 0000000..3f5f952 --- /dev/null +++ b/xsa426.patch @@ -0,0 +1,107 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Mitigate Cross-Thread Return Address Predictions + +This is XSA-426 / CVE-2022-27672 + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc +index 923910f553c5..a2ff38cdebf2 100644 +--- a/docs/misc/xen-command-line.pandoc ++++ b/docs/misc/xen-command-line.pandoc +@@ -2355,7 +2355,7 @@ guests to use. + on entry and exit. These blocks are necessary to virtualise support for + guests and if disabled, guests will be unable to use IBRS/STIBP/SSBD/etc. + * `rsb=` offers control over whether to overwrite the Return Stack Buffer / +- Return Address Stack on entry to Xen. ++ Return Address Stack on entry to Xen and on idle. + * `md-clear=` offers control over whether to use VERW to flush + microarchitectural buffers on idle and exit from Xen. *Note: For + compatibility with development versions of this fix, `mds=` is also accepted +diff --git a/xen/arch/x86/include/asm/cpufeatures.h b/xen/arch/x86/include/asm/cpufeatures.h +index 865f1109866d..da0593de8542 100644 +--- a/xen/arch/x86/include/asm/cpufeatures.h ++++ b/xen/arch/x86/include/asm/cpufeatures.h +@@ -35,7 +35,8 @@ XEN_CPUFEATURE(SC_RSB_HVM, X86_SYNTH(19)) /* RSB overwrite needed for HVM + XEN_CPUFEATURE(XEN_SELFSNOOP, X86_SYNTH(20)) /* SELFSNOOP gets used by Xen itself */ + XEN_CPUFEATURE(SC_MSR_IDLE, X86_SYNTH(21)) /* Clear MSR_SPEC_CTRL on idle */ + XEN_CPUFEATURE(XEN_LBR, X86_SYNTH(22)) /* Xen uses MSR_DEBUGCTL.LBR */ +-/* Bits 23,24 unused. */ ++/* Bits 23 unused. */ ++XEN_CPUFEATURE(SC_RSB_IDLE, X86_SYNTH(24)) /* RSB overwrite needed for idle. */ + XEN_CPUFEATURE(SC_VERW_IDLE, X86_SYNTH(25)) /* VERW used by Xen for idle */ + XEN_CPUFEATURE(XEN_SHSTK, X86_SYNTH(26)) /* Xen uses CET Shadow Stacks */ + XEN_CPUFEATURE(XEN_IBT, X86_SYNTH(27)) /* Xen uses CET Indirect Branch Tracking */ +diff --git a/xen/arch/x86/include/asm/spec_ctrl.h b/xen/arch/x86/include/asm/spec_ctrl.h +index 6a77c3937844..391973ef6a28 100644 +--- a/xen/arch/x86/include/asm/spec_ctrl.h ++++ b/xen/arch/x86/include/asm/spec_ctrl.h +@@ -159,6 +159,21 @@ static always_inline void spec_ctrl_enter_idle(struct cpu_info *info) + */ + alternative_input("", "verw %[sel]", X86_FEATURE_SC_VERW_IDLE, + [sel] "m" (info->verw_sel)); ++ ++ /* ++ * Cross-Thread Return Address Predictions: ++ * ++ * On vulnerable systems, the return predictions (RSB/RAS) are statically ++ * partitioned between active threads. When entering idle, our entries ++ * are re-partitioned to allow the other threads to use them. ++ * ++ * In some cases, we might still have guest entries in the RAS, so flush ++ * them before injecting them sideways to our sibling thread. ++ * ++ * (ab)use alternative_input() to specify clobbers. ++ */ ++ alternative_input("", "DO_OVERWRITE_RSB", X86_FEATURE_SC_RSB_IDLE, ++ : "rax", "rcx"); + } + + /* WARNING! `ret`, `call *`, `jmp *` not safe before this call. */ +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index a320b81947c8..e80e2a5ed1a9 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -1327,13 +1327,38 @@ void __init init_speculation_mitigations(void) + * 3) Some CPUs have RSBs which are not full width, which allow the + * attacker's entries to alias Xen addresses. + * ++ * 4) Some CPUs have RSBs which are re-partitioned based on thread ++ * idleness, which allows an attacker to inject entries into the other ++ * thread. We still active the optimisation in this case, and mitigate ++ * in the idle path which has lower overhead. ++ * + * It is safe to turn off RSB stuffing when Xen is using SMEP itself, and + * 32bit PV guests are disabled, and when the RSB is full width. + */ + BUILD_BUG_ON(RO_MPT_VIRT_START != PML4_ADDR(256)); +- if ( opt_rsb_pv == -1 && boot_cpu_has(X86_FEATURE_XEN_SMEP) && +- !opt_pv32 && rsb_is_full_width() ) +- opt_rsb_pv = 0; ++ if ( opt_rsb_pv == -1 ) ++ { ++ opt_rsb_pv = (opt_pv32 || !boot_cpu_has(X86_FEATURE_XEN_SMEP) || ++ !rsb_is_full_width()); ++ ++ /* ++ * Cross-Thread Return Address Predictions. ++ * ++ * Vulnerable systems are Zen1/Zen2 uarch, which is AMD Fam17 / Hygon ++ * Fam18, when SMT is active. ++ * ++ * To mitigate, we must flush the RSB/RAS/RAP once between entering ++ * Xen and going idle. ++ * ++ * Most cases flush on entry to Xen anyway. The one case where we ++ * don't is when using the SMEP optimisation for PV guests. Flushing ++ * before going idle is less overhead than flushing on PV entry. ++ */ ++ if ( !opt_rsb_pv && hw_smt_enabled && ++ (boot_cpu_data.x86_vendor & (X86_VENDOR_AMD|X86_VENDOR_HYGON)) && ++ (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) ) ++ setup_force_cpu_cap(X86_FEATURE_SC_RSB_IDLE); ++ } + + if ( opt_rsb_pv ) + { From 04f2270c6eab29274039e79b6b268a8a09b95e95 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Sat, 18 Feb 2023 08:18:51 +0000 Subject: [PATCH 059/137] use OVMF.fd from new edk2-ovmf-xen package ovmf.bin file built from edk2-ovmf package no longer supports xen (#2170930) --- xen.spec | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/xen.spec b/xen.spec index 6918277..60e5db9 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.17.0 -Release: 6%{?dist} +Release: 7%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -178,9 +178,6 @@ BuildRequires: systemd-devel %ifarch armv7hl aarch64 BuildRequires: libfdt-devel %endif -%if %build_ovmf -BuildRequires: edk2-ovmf -%endif %if %build_hyp BuildRequires: bison flex %endif @@ -216,6 +213,9 @@ Recommends: qemu-system-x86-core # rom file for qemu-xen-traditional Recommends: ipxe-roms-qemu %endif +%if %build_ovmf +Recommends: edk2-ovmf-xen +%endif %description runtime This package contains the runtime programs and daemons which @@ -377,7 +377,7 @@ CONFIG_EXTRA="--enable-qemu-traditional" CONFIG_EXTRA="" %endif %if %build_ovmf -CONFIG_EXTRA="$CONFIG_EXTRA --with-system-ovmf=%{_libexecdir}/%{name}/boot/ovmf.bin" +CONFIG_EXTRA="$CONFIG_EXTRA --with-system-ovmf=/usr/share/edk2/xen/OVMF.fd" %endif %ifnarch armv7hl aarch64 CONFIG_EXTRA="$CONFIG_EXTRA --with-system-ipxe=/usr/share/ipxe/10ec8139.rom" @@ -511,10 +511,6 @@ strip -s %{buildroot}/%{_libdir}/efi/xen-%{version}.efi rm -rf %{buildroot}/%{_unitdir}/oxenstored.service %endif -%if %build_ovmf -cat /usr/share/OVMF/OVMF_{VARS,CODE}.fd >%{buildroot}%{_libexecdir}/%{name}/boot/ovmf.bin -%endif - ############ fixup files in /etc ############ # logrotate @@ -794,9 +790,6 @@ fi %{_libexecdir}/%{name}/boot/xen-shim /usr/lib/debug%{_libexecdir}/xen/boot/xen-shim-syms %endif -%if %build_ovmf -%{_libexecdir}/xen/boot/ovmf.bin -%endif %if %build_stubdom %if %build_qemutrad %{_libexecdir}/xen/boot/ioemu-stubdom.gz @@ -940,6 +933,10 @@ fi %endif %changelog +* Sat Feb 18 2023 Michael Young - 4.17.0-7 +- use OVMF.fd from new edk2-ovmf-xen package as ovmf.bin file + built from edk2-ovmf package no longer supports xen (#2170930) + * Tue Feb 14 2023 Michael Young - 4.17.0-6 - x86: Cross-Thread Return Address Predictions [XSA-426, CVE-2022-27672] From bf947f8f9369e91f4cc0a1f3bef036338e2eecd2 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 21 Mar 2023 17:20:08 +0000 Subject: [PATCH 060/137] 3 security issues (#2180425) x86 shadow plus log-dirty mode use-after-free [XSA-427, CVE-2022-42332] x86/HVM pinned cache attributes mis-handling [XSA-428, CVE-2022-42333, CVE-2022-42334] x86: speculative vulnerability in 32bit SYSCALL path [XSA-429, CVE-2022-42331] --- xen.spec | 18 ++++++- xsa427.patch | 76 +++++++++++++++++++++++++++++ xsa428-4.17-1.patch | 40 ++++++++++++++++ xsa428-4.17-2.patch | 114 ++++++++++++++++++++++++++++++++++++++++++++ xsa429.patch | 46 ++++++++++++++++++ 5 files changed, 293 insertions(+), 1 deletion(-) create mode 100644 xsa427.patch create mode 100644 xsa428-4.17-1.patch create mode 100644 xsa428-4.17-2.patch create mode 100644 xsa429.patch diff --git a/xen.spec b/xen.spec index 60e5db9..8ae2d41 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.17.0 -Release: 7%{?dist} +Release: 8%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -112,6 +112,10 @@ Patch46: xen.efi.build.patch Patch47: xen.gcc13.fixes.patch Patch48: xsa425.patch Patch49: xsa426.patch +Patch50: xsa427.patch +Patch51: xsa428-4.17-1.patch +Patch52: xsa428-4.17-2.patch +Patch53: xsa429.patch %if %build_qemutrad @@ -326,6 +330,10 @@ manage Xen virtual machines. %patch47 -p1 %patch48 -p1 %patch49 -p1 +%patch50 -p1 +%patch51 -p1 +%patch52 -p1 +%patch53 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -933,6 +941,14 @@ fi %endif %changelog +* Tue Mar 21 2023 Michael Young - 4.17.0-8 +- 3 security issues (#2180425) + x86 shadow plus log-dirty mode use-after-free [XSA-427, CVE-2022-42332] + x86/HVM pinned cache attributes mis-handling [XSA-428, CVE-2022-42333, + CVE-2022-42334] + x86: speculative vulnerability in 32bit SYSCALL path [XSA-429, + CVE-2022-42331] + * Sat Feb 18 2023 Michael Young - 4.17.0-7 - use OVMF.fd from new edk2-ovmf-xen package as ovmf.bin file built from edk2-ovmf package no longer supports xen (#2170930) diff --git a/xsa427.patch b/xsa427.patch new file mode 100644 index 0000000..861f81d --- /dev/null +++ b/xsa427.patch @@ -0,0 +1,76 @@ +From: Jan Beulich +Subject: x86/shadow: account for log-dirty mode when pre-allocating + +Pre-allocation is intended to ensure that in the course of constructing +or updating shadows there won't be any risk of just made shadows or +shadows being acted upon can disappear under our feet. The amount of +pages pre-allocated then, however, needs to account for all possible +subsequent allocations. While the use in sh_page_fault() accounts for +all shadows which may need making, so far it didn't account for +allocations coming from log-dirty tracking (which piggybacks onto the +P2M allocation functions). + +Since shadow_prealloc() takes a count of shadows (or other data +structures) rather than a count of pages, putting the adjustment at the +call site of this function won't work very well: We simply can't express +the correct count that way in all cases. Instead take care of this in +the function itself, by "snooping" for L1 type requests. (While not +applicable right now, future new request sites of L1 tables would then +also be covered right away.) + +It is relevant to note here that pre-allocations like the one done from +shadow_alloc_p2m_page() are benign when they fall in the "scope" of an +earlier pre-alloc which already included that count: The inner call will +simply find enough pages available then; it'll bail right away. + +This is CVE-2022-42332 / XSA-427. + +Signed-off-by: Jan Beulich +Reviewed-by: Tim Deegan +--- +v2: Entirely different approach. + +--- a/xen/arch/x86/include/asm/paging.h ++++ b/xen/arch/x86/include/asm/paging.h +@@ -189,6 +189,10 @@ bool paging_mfn_is_dirty(const struct do + #define L4_LOGDIRTY_IDX(pfn) ((pfn_x(pfn) >> (PAGE_SHIFT + 3 + PAGETABLE_ORDER * 2)) & \ + (LOGDIRTY_NODE_ENTRIES-1)) + ++#define paging_logdirty_levels() \ ++ (DIV_ROUND_UP(PADDR_BITS - PAGE_SHIFT - (PAGE_SHIFT + 3), \ ++ PAGE_SHIFT - ilog2(sizeof(mfn_t))) + 1) ++ + #ifdef CONFIG_HVM + /* VRAM dirty tracking support */ + struct sh_dirty_vram { +--- a/xen/arch/x86/mm/paging.c ++++ b/xen/arch/x86/mm/paging.c +@@ -282,6 +282,7 @@ void paging_mark_pfn_dirty(struct domain + if ( unlikely(!VALID_M2P(pfn_x(pfn))) ) + return; + ++ BUILD_BUG_ON(paging_logdirty_levels() != 4); + i1 = L1_LOGDIRTY_IDX(pfn); + i2 = L2_LOGDIRTY_IDX(pfn); + i3 = L3_LOGDIRTY_IDX(pfn); +--- a/xen/arch/x86/mm/shadow/common.c ++++ b/xen/arch/x86/mm/shadow/common.c +@@ -1011,7 +1011,17 @@ bool shadow_prealloc(struct domain *d, u + if ( unlikely(d->is_dying) ) + return false; + +- ret = _shadow_prealloc(d, shadow_size(type) * count); ++ count *= shadow_size(type); ++ /* ++ * Log-dirty handling may result in allocations when populating its ++ * tracking structures. Tie this to the caller requesting space for L1 ++ * shadows. ++ */ ++ if ( paging_mode_log_dirty(d) && ++ ((SHF_L1_ANY | SHF_FL1_ANY) & (1u << type)) ) ++ count += paging_logdirty_levels(); ++ ++ ret = _shadow_prealloc(d, count); + if ( !ret && (!d->is_shutting_down || d->shutdown_code != SHUTDOWN_crash) ) + /* + * Failing to allocate memory required for shadow usage can only result in diff --git a/xsa428-4.17-1.patch b/xsa428-4.17-1.patch new file mode 100644 index 0000000..3852f08 --- /dev/null +++ b/xsa428-4.17-1.patch @@ -0,0 +1,40 @@ +From: Jan Beulich +Subject: x86/HVM: bound number of pinned cache attribute regions + +This is exposed via DMOP, i.e. to potentially not fully privileged +device models. With that we may not permit registration of an (almost) +unbounded amount of such regions. + +This is CVE-2022-42333 / part of XSA-428. + +Reported-by: Andrew Cooper +Fixes: 642123c5123f ("x86/hvm: provide XEN_DMOP_pin_memory_cacheattr") +Signed-off-by: Jan Beulich +Reviewed-by: Andrew Cooper + +--- a/xen/arch/x86/hvm/mtrr.c ++++ b/xen/arch/x86/hvm/mtrr.c +@@ -595,6 +595,7 @@ int hvm_set_mem_pinned_cacheattr(struct + uint64_t gfn_end, uint32_t type) + { + struct hvm_mem_pinned_cacheattr_range *range; ++ unsigned int nr = 0; + int rc = 1; + + if ( !is_hvm_domain(d) ) +@@ -666,11 +667,15 @@ int hvm_set_mem_pinned_cacheattr(struct + rc = -EBUSY; + break; + } ++ ++nr; + } + rcu_read_unlock(&pinned_cacheattr_rcu_lock); + if ( rc <= 0 ) + return rc; + ++ if ( nr >= 64 /* The limit is arbitrary. */ ) ++ return -ENOSPC; ++ + range = xzalloc(struct hvm_mem_pinned_cacheattr_range); + if ( range == NULL ) + return -ENOMEM; diff --git a/xsa428-4.17-2.patch b/xsa428-4.17-2.patch new file mode 100644 index 0000000..087230a --- /dev/null +++ b/xsa428-4.17-2.patch @@ -0,0 +1,114 @@ +From: Jan Beulich +Subject: x86/HVM: serialize pinned cache attribute list manipulation + +While the RCU variants of list insertion and removal allow lockless list +traversal (with RCU just read-locked), insertions and removals still +need serializing amongst themselves. To keep things simple, use the +domain lock for this purpose. + +This is CVE-2022-42334 / part of XSA-428. + +Fixes: 642123c5123f ("x86/hvm: provide XEN_DMOP_pin_memory_cacheattr") +Signed-off-by: Jan Beulich +Reviewed-by: Julien Grall + +--- a/xen/arch/x86/hvm/mtrr.c ++++ b/xen/arch/x86/hvm/mtrr.c +@@ -594,7 +594,7 @@ static void cf_check free_pinned_cacheat + int hvm_set_mem_pinned_cacheattr(struct domain *d, uint64_t gfn_start, + uint64_t gfn_end, uint32_t type) + { +- struct hvm_mem_pinned_cacheattr_range *range; ++ struct hvm_mem_pinned_cacheattr_range *range, *newr; + unsigned int nr = 0; + int rc = 1; + +@@ -608,14 +608,15 @@ int hvm_set_mem_pinned_cacheattr(struct + { + case XEN_DOMCTL_DELETE_MEM_CACHEATTR: + /* Remove the requested range. */ +- rcu_read_lock(&pinned_cacheattr_rcu_lock); +- list_for_each_entry_rcu ( range, +- &d->arch.hvm.pinned_cacheattr_ranges, +- list ) ++ domain_lock(d); ++ list_for_each_entry ( range, ++ &d->arch.hvm.pinned_cacheattr_ranges, ++ list ) + if ( range->start == gfn_start && range->end == gfn_end ) + { +- rcu_read_unlock(&pinned_cacheattr_rcu_lock); + list_del_rcu(&range->list); ++ domain_unlock(d); ++ + type = range->type; + call_rcu(&range->rcu, free_pinned_cacheattr_entry); + p2m_memory_type_changed(d); +@@ -636,7 +637,7 @@ int hvm_set_mem_pinned_cacheattr(struct + } + return 0; + } +- rcu_read_unlock(&pinned_cacheattr_rcu_lock); ++ domain_unlock(d); + return -ENOENT; + + case PAT_TYPE_UC_MINUS: +@@ -651,7 +652,10 @@ int hvm_set_mem_pinned_cacheattr(struct + return -EINVAL; + } + +- rcu_read_lock(&pinned_cacheattr_rcu_lock); ++ newr = xzalloc(struct hvm_mem_pinned_cacheattr_range); ++ ++ domain_lock(d); ++ + list_for_each_entry_rcu ( range, + &d->arch.hvm.pinned_cacheattr_ranges, + list ) +@@ -669,27 +673,34 @@ int hvm_set_mem_pinned_cacheattr(struct + } + ++nr; + } +- rcu_read_unlock(&pinned_cacheattr_rcu_lock); ++ + if ( rc <= 0 ) +- return rc; ++ /* nothing */; ++ else if ( nr >= 64 /* The limit is arbitrary. */ ) ++ rc = -ENOSPC; ++ else if ( !newr ) ++ rc = -ENOMEM; ++ else ++ { ++ newr->start = gfn_start; ++ newr->end = gfn_end; ++ newr->type = type; + +- if ( nr >= 64 /* The limit is arbitrary. */ ) +- return -ENOSPC; ++ list_add_rcu(&newr->list, &d->arch.hvm.pinned_cacheattr_ranges); ++ ++ newr = NULL; ++ rc = 0; ++ } + +- range = xzalloc(struct hvm_mem_pinned_cacheattr_range); +- if ( range == NULL ) +- return -ENOMEM; ++ domain_unlock(d); + +- range->start = gfn_start; +- range->end = gfn_end; +- range->type = type; ++ xfree(newr); + +- list_add_rcu(&range->list, &d->arch.hvm.pinned_cacheattr_ranges); + p2m_memory_type_changed(d); + if ( type != PAT_TYPE_WRBACK ) + flush_all(FLUSH_CACHE); + +- return 0; ++ return rc; + } + + static int cf_check hvm_save_mtrr_msr(struct vcpu *v, hvm_domain_context_t *h) diff --git a/xsa429.patch b/xsa429.patch new file mode 100644 index 0000000..443869f --- /dev/null +++ b/xsa429.patch @@ -0,0 +1,46 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Defer CR4_PV32_RESTORE on the cstar_enter path + +As stated (correctly) by the comment next to SPEC_CTRL_ENTRY_FROM_PV, between +the two hunks visible in the patch, RET's are not safe prior to this point. + +CR4_PV32_RESTORE hides a CALL/RET pair in certain configurations (PV32 +compiled in, SMEP or SMAP active), and the RET can be attacked with one of +several known speculative issues. + +Furthermore, CR4_PV32_RESTORE also hides a reference to the cr4_pv32_mask +global variable, which is not safe when XPTI is active before restoring Xen's +full pagetables. + +This crash has gone unnoticed because it is only AMD CPUs which permit the +SYSCALL instruction in compatibility mode, and these are not vulnerable to +Meltdown so don't activate XPTI by default. + +This is XSA-429 / CVE-2022-42331 + +Fixes: 5e7962901131 ("x86/entry: Organise the use of MSR_SPEC_CTRL at each entry/exit point") +Fixes: 5784de3e2067 ("x86: Meltdown band-aid against malicious 64-bit PV guests") +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S +index ae012851819a..7675a59ff057 100644 +--- a/xen/arch/x86/x86_64/entry.S ++++ b/xen/arch/x86/x86_64/entry.S +@@ -288,7 +288,6 @@ ENTRY(cstar_enter) + ALTERNATIVE "", "setssbsy", X86_FEATURE_XEN_SHSTK + #endif + push %rax /* Guest %rsp */ +- CR4_PV32_RESTORE + movq 8(%rsp), %rax /* Restore guest %rax. */ + movq $FLAT_USER_SS32, 8(%rsp) /* Assume a 64bit domain. Compat handled lower. */ + pushq %r11 +@@ -312,6 +311,8 @@ ENTRY(cstar_enter) + .Lcstar_cr3_okay: + sti + ++ CR4_PV32_RESTORE ++ + movq STACK_CPUINFO_FIELD(current_vcpu)(%rbx), %rbx + + #ifdef CONFIG_PV32 From e44f0d9ede09a9851d8f2987d5ec365df5dc7b36 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 25 Apr 2023 21:24:14 +0100 Subject: [PATCH 061/137] x86 shadow paging arbitrary pointer dereference [XSA-430, CVE-2022-42335] --- xen.spec | 7 +++++- xsa430.patch | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 xsa430.patch diff --git a/xen.spec b/xen.spec index 8ae2d41..9467ab7 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.17.0 -Release: 8%{?dist} +Release: 9%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -116,6 +116,7 @@ Patch50: xsa427.patch Patch51: xsa428-4.17-1.patch Patch52: xsa428-4.17-2.patch Patch53: xsa429.patch +Patch54: xsa430.patch %if %build_qemutrad @@ -334,6 +335,7 @@ manage Xen virtual machines. %patch51 -p1 %patch52 -p1 %patch53 -p1 +%patch54 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -941,6 +943,9 @@ fi %endif %changelog +* Tue Apr 25 2023 Michael Young - 4.17.0-9 +- x86 shadow paging arbitrary pointer dereference [XSA-430, CVE-2022-42335] + * Tue Mar 21 2023 Michael Young - 4.17.0-8 - 3 security issues (#2180425) x86 shadow plus log-dirty mode use-after-free [XSA-427, CVE-2022-42332] diff --git a/xsa430.patch b/xsa430.patch new file mode 100644 index 0000000..4472e41 --- /dev/null +++ b/xsa430.patch @@ -0,0 +1,65 @@ +From 57b3a2ace5c4a78118b372c95f69af4f0585b48d Mon Sep 17 00:00:00 2001 +From: Roger Pau Monne +Date: Mon, 20 Mar 2023 12:08:52 +0100 +Subject: [PATCH] x86/shadow: restore dropped check in + sh_unshadow_for_p2m_change() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +As a result of 241702e064604dbb3e0d9b731aa8f45be448243b the +mfn_valid() check in sh_unshadow_for_p2m_change() was lost. That +allows sh_remove_shadows() to be called with gfns that have no backing +page, causing an ASSERT to trigger in debug builds or dereferencing an +arbitrary pointer partially under guest control in non-debug builds: + +RIP: e008:[] sh_remove_shadows+0x19f/0x722 +RFLAGS: 0000000000010246 CONTEXT: hypervisor (d0v2) +[...] +Xen call trace: + [] R sh_remove_shadows+0x19f/0x722 + [] F arch/x86/mm/shadow/hvm.c#sh_unshadow_for_p2m_change+0xab/0x2b7 + [] F arch/x86/mm/p2m-pt.c#write_p2m_entry+0x19b/0x4d3 + [] F arch/x86/mm/p2m-pt.c#p2m_pt_set_entry+0x67b/0xa8e + [] F p2m_set_entry+0xcc/0x149 + [] F unmap_mmio_regions+0x17b/0x2c9 + [] F do_domctl+0x11f3/0x195e + [] F hvm_hypercall+0x5b1/0xa2d + [] F vmx_vmexit_handler+0x130f/0x1cd5 + [] F vmx_asm_vmexit_handler+0xf2/0x210 + +**************************************** +Panic on CPU 1: +Assertion 'mfn_valid(gmfn)' failed at arch/x86/mm/shadow/common.c:2203 +**************************************** + +Fix this by restoring the mfn_valid() check in +sh_unshadow_for_p2m_change(), unifying it with the rest of the checks +that are done at the start of the function. + +This is XSA-430 / CVE-2022-42335 + +Fixes: 241702e064 ('x86/shadow: slightly consolidate sh_unshadow_for_p2m_change() (part II)') +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +--- + xen/arch/x86/mm/shadow/hvm.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/xen/arch/x86/mm/shadow/hvm.c b/xen/arch/x86/mm/shadow/hvm.c +index 88c3c16322..6de479c008 100644 +--- a/xen/arch/x86/mm/shadow/hvm.c ++++ b/xen/arch/x86/mm/shadow/hvm.c +@@ -814,7 +814,8 @@ static void cf_check sh_unshadow_for_p2m_change( + + /* Only previously present / valid entries need processing. */ + if ( !(oflags & _PAGE_PRESENT) || +- (!p2m_is_valid(p2mt) && !p2m_is_grant(p2mt)) ) ++ (!p2m_is_valid(p2mt) && !p2m_is_grant(p2mt)) || ++ !mfn_valid(omfn) ) + return; + + switch ( level ) +-- +2.40.0 + From d6d8d8a3933fa5df15c2511afeb7862d72e8cb22 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 2 May 2023 21:53:01 +0100 Subject: [PATCH 062/137] update to xen-4.17.1 remove patches now included upstream switch from patchN to patch N format for applying patches --- .gitignore | 2 +- sources | 2 +- xen.spec | 103 ++++++++++++++++------------------ xsa425.patch | 132 -------------------------------------------- xsa426.patch | 107 ----------------------------------- xsa427.patch | 76 ------------------------- xsa428-4.17-1.patch | 40 -------------- xsa428-4.17-2.patch | 114 -------------------------------------- xsa429.patch | 46 --------------- xsa430.patch | 65 ---------------------- 10 files changed, 49 insertions(+), 638 deletions(-) delete mode 100644 xsa425.patch delete mode 100644 xsa426.patch delete mode 100644 xsa427.patch delete mode 100644 xsa428-4.17-1.patch delete mode 100644 xsa428-4.17-2.patch delete mode 100644 xsa429.patch delete mode 100644 xsa430.patch diff --git a/.gitignore b/.gitignore index 139f059..a3ba858 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.17.0.tar.gz +/xen-4.17.1.tar.gz diff --git a/sources b/sources index 435fdc0..c81c5e2 100644 --- a/sources +++ b/sources @@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.17.0.tar.gz) = 8aa7c3025c81127a4f653411bc1cbe8fc27b767a2f96a2a454329a6773cb74d714ecc1ff011a8e7169c6b83b2cc5cb39c39798bd6b1178df75ce9ff00f5a1011 +SHA512 (xen-4.17.1.tar.gz) = bd98c5a2d75a0368ef312274b8e47f27db7f6b79302fd0c8b4975185e63640f98d9302e2834344acc1e97133e19f3993359dfab85aea5928008debada78c5f7a diff --git a/xen.spec b/xen.spec index 9467ab7..0c8c5de 100644 --- a/xen.spec +++ b/xen.spec @@ -54,8 +54,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.17.0 -Release: 9%{?dist} +Version: 4.17.1 +Release: 1%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -110,13 +110,6 @@ Patch43: xen.gcc11.fixes.patch Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch Patch47: xen.gcc13.fixes.patch -Patch48: xsa425.patch -Patch49: xsa426.patch -Patch50: xsa427.patch -Patch51: xsa428-4.17-1.patch -Patch52: xsa428-4.17-2.patch -Patch53: xsa429.patch -Patch54: xsa430.patch %if %build_qemutrad @@ -298,56 +291,49 @@ manage Xen virtual machines. %prep %setup -q -%patch4 -p1 -%patch5 -p1 -%patch6 -p1 -%patch7 -p1 -%patch8 -p1 -%patch9 -p1 -%patch10 -p1 -%patch11 -p1 -%patch12 -p1 -%patch13 -p1 -%patch14 -p1 -%patch15 -p1 -%patch16 -p1 -%patch17 -p1 -%patch18 -p1 -%patch19 -p1 -%patch20 -p1 -%patch21 -p1 -%patch22 -p1 -%patch23 -p1 -%patch24 -p1 -%patch25 -p1 -%patch33 -p1 -%patch34 -p1 -%patch37 -p1 -%patch40 -p1 -%patch41 -p1 -%patch43 -p1 -%patch45 -p1 -%patch46 -p1 -%patch47 -p1 -%patch48 -p1 -%patch49 -p1 -%patch50 -p1 -%patch51 -p1 -%patch52 -p1 -%patch53 -p1 -%patch54 -p1 +%patch 4 -p1 +%patch 5 -p1 +%patch 6 -p1 +%patch 7 -p1 +%patch 8 -p1 +%patch 9 -p1 +%patch 10 -p1 +%patch 11 -p1 +%patch 12 -p1 +%patch 13 -p1 +%patch 14 -p1 +%patch 15 -p1 +%patch 16 -p1 +%patch 17 -p1 +%patch 18 -p1 +%patch 19 -p1 +%patch 20 -p1 +%patch 21 -p1 +%patch 22 -p1 +%patch 23 -p1 +%patch 24 -p1 +%patch 25 -p1 +%patch 33 -p1 +%patch 34 -p1 +%patch 37 -p1 +%patch 40 -p1 +%patch 41 -p1 +%patch 43 -p1 +%patch 45 -p1 +%patch 46 -p1 +%patch 47 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional -%patch27 -p1 -%patch29 -p1 -%patch30 -p1 -%patch31 -p1 -%patch32 -p1 -%patch35 -p1 -%patch36 -p1 -%patch38 -p1 -%patch39 -p1 +%patch 27 -p1 +%patch 29 -p1 +%patch 30 -p1 +%patch 31 -p1 +%patch 32 -p1 +%patch 35 -p1 +%patch 36 -p1 +%patch 38 -p1 +%patch 39 -p1 popd # qemu-xen patches @@ -943,6 +929,11 @@ fi %endif %changelog +* Tue May 02 2023 Michael Young - 4.17.1-1 +- update to xen-4.17.1 + remove patches now included upstream + switch from patchN to patch N format for applying patches + * Tue Apr 25 2023 Michael Young - 4.17.0-9 - x86 shadow paging arbitrary pointer dereference [XSA-430, CVE-2022-42335] diff --git a/xsa425.patch b/xsa425.patch deleted file mode 100644 index b367320..0000000 --- a/xsa425.patch +++ /dev/null @@ -1,132 +0,0 @@ -From: Jason Andryuk -Subject: Revert "tools/xenstore: simplify loop handling connection I/O" - -I'm observing guest kexec trigger xenstored to abort on a double free. - -gdb output: -Program received signal SIGABRT, Aborted. -__pthread_kill_implementation (no_tid=0, signo=6, threadid=140645614258112) at ./nptl/pthread_kill.c:44 -44 ./nptl/pthread_kill.c: No such file or directory. -(gdb) bt - at ./nptl/pthread_kill.c:44 - at ./nptl/pthread_kill.c:78 - at ./nptl/pthread_kill.c:89 - at ../sysdeps/posix/raise.c:26 - at talloc.c:119 - ptr=ptr@entry=0x559fae724290) at talloc.c:232 - at xenstored_core.c:2945 -(gdb) frame 5 - at talloc.c:119 -119 TALLOC_ABORT("Bad talloc magic value - double free"); -(gdb) frame 7 - at xenstored_core.c:2945 -2945 talloc_increase_ref_count(conn); -(gdb) p conn -$1 = (struct connection *) 0x559fae724290 - -Looking at a xenstore trace, we have: -IN 0x559fae71f250 20230120 17:40:53 READ (/local/domain/3/image/device-model-dom -id ) -wrl: dom 0 1 msec 10000 credit 1000000 reserve 100 disc -ard -wrl: dom 3 1 msec 10000 credit 1000000 reserve 100 disc -ard -wrl: dom 0 0 msec 10000 credit 1000000 reserve 0 disc -ard -wrl: dom 3 0 msec 10000 credit 1000000 reserve 0 disc -ard -OUT 0x559fae71f250 20230120 17:40:53 ERROR (ENOENT ) -wrl: dom 0 1 msec 10000 credit 1000000 reserve 100 disc -ard -wrl: dom 3 1 msec 10000 credit 1000000 reserve 100 disc -ard -IN 0x559fae71f250 20230120 17:40:53 RELEASE (3 ) -DESTROY watch 0x559fae73f630 -DESTROY watch 0x559fae75ddf0 -DESTROY watch 0x559fae75ec30 -DESTROY watch 0x559fae75ea60 -DESTROY watch 0x559fae732c00 -DESTROY watch 0x559fae72cea0 -DESTROY watch 0x559fae728fc0 -DESTROY watch 0x559fae729570 -DESTROY connection 0x559fae724290 -orphaned node /local/domain/3/device/suspend/event-channel deleted -orphaned node /local/domain/3/device/vbd/51712 deleted -orphaned node /local/domain/3/device/vkbd/0 deleted -orphaned node /local/domain/3/device/vif/0 deleted -orphaned node /local/domain/3/control/shutdown deleted -orphaned node /local/domain/3/control/feature-poweroff deleted -orphaned node /local/domain/3/control/feature-reboot deleted -orphaned node /local/domain/3/control/feature-suspend deleted -orphaned node /local/domain/3/control/feature-s3 deleted -orphaned node /local/domain/3/control/feature-s4 deleted -orphaned node /local/domain/3/control/sysrq deleted -orphaned node /local/domain/3/data deleted -orphaned node /local/domain/3/drivers deleted -orphaned node /local/domain/3/feature deleted -orphaned node /local/domain/3/attr deleted -orphaned node /local/domain/3/error deleted -orphaned node /local/domain/3/console/backend-id deleted - -and no further output. - -The trace shows that DESTROY was called for connection 0x559fae724290, -but that is the same pointer (conn) main() was looping through from -connections. So it wasn't actually removed from the connections list? - -Reverting commit e8e6e42279a5 "tools/xenstore: simplify loop handling -connection I/O" fixes the abort/double free. I think the use of -list_for_each_entry_safe is incorrect. list_for_each_entry_safe makes -traversal safe for deleting the current iterator, but RELEASE/do_release -will delete some other entry in the connections list. I think the -observed abort is because list_for_each_entry has next pointing to the -deleted connection, and it is used in the subsequent iteration. - -Add a comment explaining the unsuitability of list_for_each_entry_safe. -Also notice that the old code takes a reference on next which would -prevents a use-after-free. - -This reverts commit e8e6e42279a5723239c5c40ba4c7f579a979465d. - -This is XSA-425/CVE-2022-42330. - -Fixes: e8e6e42279a5 ("tools/xenstore: simplify loop handling connection I/O") -Signed-off-by: Jason Andryuk -Reviewed-by: Juergen Gross -Reviewed-by: Julien Grall ---- - tools/xenstore/xenstored_core.c | 19 +++++++++++++++++-- - 1 file changed, 17 insertions(+), 2 deletions(-) - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 78a3edaa4e..029e3852fc 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -2941,8 +2941,23 @@ int main(int argc, char *argv[]) - } - } - -- list_for_each_entry_safe(conn, next, &connections, list) { -- talloc_increase_ref_count(conn); -+ /* -+ * list_for_each_entry_safe is not suitable here because -+ * handle_input may delete entries besides the current one, but -+ * those may be in the temporary next which would trigger a -+ * use-after-free. list_for_each_entry_safe is only safe for -+ * deleting the current entry. -+ */ -+ next = list_entry(connections.next, typeof(*conn), list); -+ if (&next->list != &connections) -+ talloc_increase_ref_count(next); -+ while (&next->list != &connections) { -+ conn = next; -+ -+ next = list_entry(conn->list.next, -+ typeof(*conn), list); -+ if (&next->list != &connections) -+ talloc_increase_ref_count(next); - - if (conn_can_read(conn)) - handle_input(conn); --- -2.34.1 diff --git a/xsa426.patch b/xsa426.patch deleted file mode 100644 index 3f5f952..0000000 --- a/xsa426.patch +++ /dev/null @@ -1,107 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Mitigate Cross-Thread Return Address Predictions - -This is XSA-426 / CVE-2022-27672 - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc -index 923910f553c5..a2ff38cdebf2 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -2355,7 +2355,7 @@ guests to use. - on entry and exit. These blocks are necessary to virtualise support for - guests and if disabled, guests will be unable to use IBRS/STIBP/SSBD/etc. - * `rsb=` offers control over whether to overwrite the Return Stack Buffer / -- Return Address Stack on entry to Xen. -+ Return Address Stack on entry to Xen and on idle. - * `md-clear=` offers control over whether to use VERW to flush - microarchitectural buffers on idle and exit from Xen. *Note: For - compatibility with development versions of this fix, `mds=` is also accepted -diff --git a/xen/arch/x86/include/asm/cpufeatures.h b/xen/arch/x86/include/asm/cpufeatures.h -index 865f1109866d..da0593de8542 100644 ---- a/xen/arch/x86/include/asm/cpufeatures.h -+++ b/xen/arch/x86/include/asm/cpufeatures.h -@@ -35,7 +35,8 @@ XEN_CPUFEATURE(SC_RSB_HVM, X86_SYNTH(19)) /* RSB overwrite needed for HVM - XEN_CPUFEATURE(XEN_SELFSNOOP, X86_SYNTH(20)) /* SELFSNOOP gets used by Xen itself */ - XEN_CPUFEATURE(SC_MSR_IDLE, X86_SYNTH(21)) /* Clear MSR_SPEC_CTRL on idle */ - XEN_CPUFEATURE(XEN_LBR, X86_SYNTH(22)) /* Xen uses MSR_DEBUGCTL.LBR */ --/* Bits 23,24 unused. */ -+/* Bits 23 unused. */ -+XEN_CPUFEATURE(SC_RSB_IDLE, X86_SYNTH(24)) /* RSB overwrite needed for idle. */ - XEN_CPUFEATURE(SC_VERW_IDLE, X86_SYNTH(25)) /* VERW used by Xen for idle */ - XEN_CPUFEATURE(XEN_SHSTK, X86_SYNTH(26)) /* Xen uses CET Shadow Stacks */ - XEN_CPUFEATURE(XEN_IBT, X86_SYNTH(27)) /* Xen uses CET Indirect Branch Tracking */ -diff --git a/xen/arch/x86/include/asm/spec_ctrl.h b/xen/arch/x86/include/asm/spec_ctrl.h -index 6a77c3937844..391973ef6a28 100644 ---- a/xen/arch/x86/include/asm/spec_ctrl.h -+++ b/xen/arch/x86/include/asm/spec_ctrl.h -@@ -159,6 +159,21 @@ static always_inline void spec_ctrl_enter_idle(struct cpu_info *info) - */ - alternative_input("", "verw %[sel]", X86_FEATURE_SC_VERW_IDLE, - [sel] "m" (info->verw_sel)); -+ -+ /* -+ * Cross-Thread Return Address Predictions: -+ * -+ * On vulnerable systems, the return predictions (RSB/RAS) are statically -+ * partitioned between active threads. When entering idle, our entries -+ * are re-partitioned to allow the other threads to use them. -+ * -+ * In some cases, we might still have guest entries in the RAS, so flush -+ * them before injecting them sideways to our sibling thread. -+ * -+ * (ab)use alternative_input() to specify clobbers. -+ */ -+ alternative_input("", "DO_OVERWRITE_RSB", X86_FEATURE_SC_RSB_IDLE, -+ : "rax", "rcx"); - } - - /* WARNING! `ret`, `call *`, `jmp *` not safe before this call. */ -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index a320b81947c8..e80e2a5ed1a9 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -1327,13 +1327,38 @@ void __init init_speculation_mitigations(void) - * 3) Some CPUs have RSBs which are not full width, which allow the - * attacker's entries to alias Xen addresses. - * -+ * 4) Some CPUs have RSBs which are re-partitioned based on thread -+ * idleness, which allows an attacker to inject entries into the other -+ * thread. We still active the optimisation in this case, and mitigate -+ * in the idle path which has lower overhead. -+ * - * It is safe to turn off RSB stuffing when Xen is using SMEP itself, and - * 32bit PV guests are disabled, and when the RSB is full width. - */ - BUILD_BUG_ON(RO_MPT_VIRT_START != PML4_ADDR(256)); -- if ( opt_rsb_pv == -1 && boot_cpu_has(X86_FEATURE_XEN_SMEP) && -- !opt_pv32 && rsb_is_full_width() ) -- opt_rsb_pv = 0; -+ if ( opt_rsb_pv == -1 ) -+ { -+ opt_rsb_pv = (opt_pv32 || !boot_cpu_has(X86_FEATURE_XEN_SMEP) || -+ !rsb_is_full_width()); -+ -+ /* -+ * Cross-Thread Return Address Predictions. -+ * -+ * Vulnerable systems are Zen1/Zen2 uarch, which is AMD Fam17 / Hygon -+ * Fam18, when SMT is active. -+ * -+ * To mitigate, we must flush the RSB/RAS/RAP once between entering -+ * Xen and going idle. -+ * -+ * Most cases flush on entry to Xen anyway. The one case where we -+ * don't is when using the SMEP optimisation for PV guests. Flushing -+ * before going idle is less overhead than flushing on PV entry. -+ */ -+ if ( !opt_rsb_pv && hw_smt_enabled && -+ (boot_cpu_data.x86_vendor & (X86_VENDOR_AMD|X86_VENDOR_HYGON)) && -+ (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) ) -+ setup_force_cpu_cap(X86_FEATURE_SC_RSB_IDLE); -+ } - - if ( opt_rsb_pv ) - { diff --git a/xsa427.patch b/xsa427.patch deleted file mode 100644 index 861f81d..0000000 --- a/xsa427.patch +++ /dev/null @@ -1,76 +0,0 @@ -From: Jan Beulich -Subject: x86/shadow: account for log-dirty mode when pre-allocating - -Pre-allocation is intended to ensure that in the course of constructing -or updating shadows there won't be any risk of just made shadows or -shadows being acted upon can disappear under our feet. The amount of -pages pre-allocated then, however, needs to account for all possible -subsequent allocations. While the use in sh_page_fault() accounts for -all shadows which may need making, so far it didn't account for -allocations coming from log-dirty tracking (which piggybacks onto the -P2M allocation functions). - -Since shadow_prealloc() takes a count of shadows (or other data -structures) rather than a count of pages, putting the adjustment at the -call site of this function won't work very well: We simply can't express -the correct count that way in all cases. Instead take care of this in -the function itself, by "snooping" for L1 type requests. (While not -applicable right now, future new request sites of L1 tables would then -also be covered right away.) - -It is relevant to note here that pre-allocations like the one done from -shadow_alloc_p2m_page() are benign when they fall in the "scope" of an -earlier pre-alloc which already included that count: The inner call will -simply find enough pages available then; it'll bail right away. - -This is CVE-2022-42332 / XSA-427. - -Signed-off-by: Jan Beulich -Reviewed-by: Tim Deegan ---- -v2: Entirely different approach. - ---- a/xen/arch/x86/include/asm/paging.h -+++ b/xen/arch/x86/include/asm/paging.h -@@ -189,6 +189,10 @@ bool paging_mfn_is_dirty(const struct do - #define L4_LOGDIRTY_IDX(pfn) ((pfn_x(pfn) >> (PAGE_SHIFT + 3 + PAGETABLE_ORDER * 2)) & \ - (LOGDIRTY_NODE_ENTRIES-1)) - -+#define paging_logdirty_levels() \ -+ (DIV_ROUND_UP(PADDR_BITS - PAGE_SHIFT - (PAGE_SHIFT + 3), \ -+ PAGE_SHIFT - ilog2(sizeof(mfn_t))) + 1) -+ - #ifdef CONFIG_HVM - /* VRAM dirty tracking support */ - struct sh_dirty_vram { ---- a/xen/arch/x86/mm/paging.c -+++ b/xen/arch/x86/mm/paging.c -@@ -282,6 +282,7 @@ void paging_mark_pfn_dirty(struct domain - if ( unlikely(!VALID_M2P(pfn_x(pfn))) ) - return; - -+ BUILD_BUG_ON(paging_logdirty_levels() != 4); - i1 = L1_LOGDIRTY_IDX(pfn); - i2 = L2_LOGDIRTY_IDX(pfn); - i3 = L3_LOGDIRTY_IDX(pfn); ---- a/xen/arch/x86/mm/shadow/common.c -+++ b/xen/arch/x86/mm/shadow/common.c -@@ -1011,7 +1011,17 @@ bool shadow_prealloc(struct domain *d, u - if ( unlikely(d->is_dying) ) - return false; - -- ret = _shadow_prealloc(d, shadow_size(type) * count); -+ count *= shadow_size(type); -+ /* -+ * Log-dirty handling may result in allocations when populating its -+ * tracking structures. Tie this to the caller requesting space for L1 -+ * shadows. -+ */ -+ if ( paging_mode_log_dirty(d) && -+ ((SHF_L1_ANY | SHF_FL1_ANY) & (1u << type)) ) -+ count += paging_logdirty_levels(); -+ -+ ret = _shadow_prealloc(d, count); - if ( !ret && (!d->is_shutting_down || d->shutdown_code != SHUTDOWN_crash) ) - /* - * Failing to allocate memory required for shadow usage can only result in diff --git a/xsa428-4.17-1.patch b/xsa428-4.17-1.patch deleted file mode 100644 index 3852f08..0000000 --- a/xsa428-4.17-1.patch +++ /dev/null @@ -1,40 +0,0 @@ -From: Jan Beulich -Subject: x86/HVM: bound number of pinned cache attribute regions - -This is exposed via DMOP, i.e. to potentially not fully privileged -device models. With that we may not permit registration of an (almost) -unbounded amount of such regions. - -This is CVE-2022-42333 / part of XSA-428. - -Reported-by: Andrew Cooper -Fixes: 642123c5123f ("x86/hvm: provide XEN_DMOP_pin_memory_cacheattr") -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper - ---- a/xen/arch/x86/hvm/mtrr.c -+++ b/xen/arch/x86/hvm/mtrr.c -@@ -595,6 +595,7 @@ int hvm_set_mem_pinned_cacheattr(struct - uint64_t gfn_end, uint32_t type) - { - struct hvm_mem_pinned_cacheattr_range *range; -+ unsigned int nr = 0; - int rc = 1; - - if ( !is_hvm_domain(d) ) -@@ -666,11 +667,15 @@ int hvm_set_mem_pinned_cacheattr(struct - rc = -EBUSY; - break; - } -+ ++nr; - } - rcu_read_unlock(&pinned_cacheattr_rcu_lock); - if ( rc <= 0 ) - return rc; - -+ if ( nr >= 64 /* The limit is arbitrary. */ ) -+ return -ENOSPC; -+ - range = xzalloc(struct hvm_mem_pinned_cacheattr_range); - if ( range == NULL ) - return -ENOMEM; diff --git a/xsa428-4.17-2.patch b/xsa428-4.17-2.patch deleted file mode 100644 index 087230a..0000000 --- a/xsa428-4.17-2.patch +++ /dev/null @@ -1,114 +0,0 @@ -From: Jan Beulich -Subject: x86/HVM: serialize pinned cache attribute list manipulation - -While the RCU variants of list insertion and removal allow lockless list -traversal (with RCU just read-locked), insertions and removals still -need serializing amongst themselves. To keep things simple, use the -domain lock for this purpose. - -This is CVE-2022-42334 / part of XSA-428. - -Fixes: 642123c5123f ("x86/hvm: provide XEN_DMOP_pin_memory_cacheattr") -Signed-off-by: Jan Beulich -Reviewed-by: Julien Grall - ---- a/xen/arch/x86/hvm/mtrr.c -+++ b/xen/arch/x86/hvm/mtrr.c -@@ -594,7 +594,7 @@ static void cf_check free_pinned_cacheat - int hvm_set_mem_pinned_cacheattr(struct domain *d, uint64_t gfn_start, - uint64_t gfn_end, uint32_t type) - { -- struct hvm_mem_pinned_cacheattr_range *range; -+ struct hvm_mem_pinned_cacheattr_range *range, *newr; - unsigned int nr = 0; - int rc = 1; - -@@ -608,14 +608,15 @@ int hvm_set_mem_pinned_cacheattr(struct - { - case XEN_DOMCTL_DELETE_MEM_CACHEATTR: - /* Remove the requested range. */ -- rcu_read_lock(&pinned_cacheattr_rcu_lock); -- list_for_each_entry_rcu ( range, -- &d->arch.hvm.pinned_cacheattr_ranges, -- list ) -+ domain_lock(d); -+ list_for_each_entry ( range, -+ &d->arch.hvm.pinned_cacheattr_ranges, -+ list ) - if ( range->start == gfn_start && range->end == gfn_end ) - { -- rcu_read_unlock(&pinned_cacheattr_rcu_lock); - list_del_rcu(&range->list); -+ domain_unlock(d); -+ - type = range->type; - call_rcu(&range->rcu, free_pinned_cacheattr_entry); - p2m_memory_type_changed(d); -@@ -636,7 +637,7 @@ int hvm_set_mem_pinned_cacheattr(struct - } - return 0; - } -- rcu_read_unlock(&pinned_cacheattr_rcu_lock); -+ domain_unlock(d); - return -ENOENT; - - case PAT_TYPE_UC_MINUS: -@@ -651,7 +652,10 @@ int hvm_set_mem_pinned_cacheattr(struct - return -EINVAL; - } - -- rcu_read_lock(&pinned_cacheattr_rcu_lock); -+ newr = xzalloc(struct hvm_mem_pinned_cacheattr_range); -+ -+ domain_lock(d); -+ - list_for_each_entry_rcu ( range, - &d->arch.hvm.pinned_cacheattr_ranges, - list ) -@@ -669,27 +673,34 @@ int hvm_set_mem_pinned_cacheattr(struct - } - ++nr; - } -- rcu_read_unlock(&pinned_cacheattr_rcu_lock); -+ - if ( rc <= 0 ) -- return rc; -+ /* nothing */; -+ else if ( nr >= 64 /* The limit is arbitrary. */ ) -+ rc = -ENOSPC; -+ else if ( !newr ) -+ rc = -ENOMEM; -+ else -+ { -+ newr->start = gfn_start; -+ newr->end = gfn_end; -+ newr->type = type; - -- if ( nr >= 64 /* The limit is arbitrary. */ ) -- return -ENOSPC; -+ list_add_rcu(&newr->list, &d->arch.hvm.pinned_cacheattr_ranges); -+ -+ newr = NULL; -+ rc = 0; -+ } - -- range = xzalloc(struct hvm_mem_pinned_cacheattr_range); -- if ( range == NULL ) -- return -ENOMEM; -+ domain_unlock(d); - -- range->start = gfn_start; -- range->end = gfn_end; -- range->type = type; -+ xfree(newr); - -- list_add_rcu(&range->list, &d->arch.hvm.pinned_cacheattr_ranges); - p2m_memory_type_changed(d); - if ( type != PAT_TYPE_WRBACK ) - flush_all(FLUSH_CACHE); - -- return 0; -+ return rc; - } - - static int cf_check hvm_save_mtrr_msr(struct vcpu *v, hvm_domain_context_t *h) diff --git a/xsa429.patch b/xsa429.patch deleted file mode 100644 index 443869f..0000000 --- a/xsa429.patch +++ /dev/null @@ -1,46 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Defer CR4_PV32_RESTORE on the cstar_enter path - -As stated (correctly) by the comment next to SPEC_CTRL_ENTRY_FROM_PV, between -the two hunks visible in the patch, RET's are not safe prior to this point. - -CR4_PV32_RESTORE hides a CALL/RET pair in certain configurations (PV32 -compiled in, SMEP or SMAP active), and the RET can be attacked with one of -several known speculative issues. - -Furthermore, CR4_PV32_RESTORE also hides a reference to the cr4_pv32_mask -global variable, which is not safe when XPTI is active before restoring Xen's -full pagetables. - -This crash has gone unnoticed because it is only AMD CPUs which permit the -SYSCALL instruction in compatibility mode, and these are not vulnerable to -Meltdown so don't activate XPTI by default. - -This is XSA-429 / CVE-2022-42331 - -Fixes: 5e7962901131 ("x86/entry: Organise the use of MSR_SPEC_CTRL at each entry/exit point") -Fixes: 5784de3e2067 ("x86: Meltdown band-aid against malicious 64-bit PV guests") -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S -index ae012851819a..7675a59ff057 100644 ---- a/xen/arch/x86/x86_64/entry.S -+++ b/xen/arch/x86/x86_64/entry.S -@@ -288,7 +288,6 @@ ENTRY(cstar_enter) - ALTERNATIVE "", "setssbsy", X86_FEATURE_XEN_SHSTK - #endif - push %rax /* Guest %rsp */ -- CR4_PV32_RESTORE - movq 8(%rsp), %rax /* Restore guest %rax. */ - movq $FLAT_USER_SS32, 8(%rsp) /* Assume a 64bit domain. Compat handled lower. */ - pushq %r11 -@@ -312,6 +311,8 @@ ENTRY(cstar_enter) - .Lcstar_cr3_okay: - sti - -+ CR4_PV32_RESTORE -+ - movq STACK_CPUINFO_FIELD(current_vcpu)(%rbx), %rbx - - #ifdef CONFIG_PV32 diff --git a/xsa430.patch b/xsa430.patch deleted file mode 100644 index 4472e41..0000000 --- a/xsa430.patch +++ /dev/null @@ -1,65 +0,0 @@ -From 57b3a2ace5c4a78118b372c95f69af4f0585b48d Mon Sep 17 00:00:00 2001 -From: Roger Pau Monne -Date: Mon, 20 Mar 2023 12:08:52 +0100 -Subject: [PATCH] x86/shadow: restore dropped check in - sh_unshadow_for_p2m_change() -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -As a result of 241702e064604dbb3e0d9b731aa8f45be448243b the -mfn_valid() check in sh_unshadow_for_p2m_change() was lost. That -allows sh_remove_shadows() to be called with gfns that have no backing -page, causing an ASSERT to trigger in debug builds or dereferencing an -arbitrary pointer partially under guest control in non-debug builds: - -RIP: e008:[] sh_remove_shadows+0x19f/0x722 -RFLAGS: 0000000000010246 CONTEXT: hypervisor (d0v2) -[...] -Xen call trace: - [] R sh_remove_shadows+0x19f/0x722 - [] F arch/x86/mm/shadow/hvm.c#sh_unshadow_for_p2m_change+0xab/0x2b7 - [] F arch/x86/mm/p2m-pt.c#write_p2m_entry+0x19b/0x4d3 - [] F arch/x86/mm/p2m-pt.c#p2m_pt_set_entry+0x67b/0xa8e - [] F p2m_set_entry+0xcc/0x149 - [] F unmap_mmio_regions+0x17b/0x2c9 - [] F do_domctl+0x11f3/0x195e - [] F hvm_hypercall+0x5b1/0xa2d - [] F vmx_vmexit_handler+0x130f/0x1cd5 - [] F vmx_asm_vmexit_handler+0xf2/0x210 - -**************************************** -Panic on CPU 1: -Assertion 'mfn_valid(gmfn)' failed at arch/x86/mm/shadow/common.c:2203 -**************************************** - -Fix this by restoring the mfn_valid() check in -sh_unshadow_for_p2m_change(), unifying it with the rest of the checks -that are done at the start of the function. - -This is XSA-430 / CVE-2022-42335 - -Fixes: 241702e064 ('x86/shadow: slightly consolidate sh_unshadow_for_p2m_change() (part II)') -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich ---- - xen/arch/x86/mm/shadow/hvm.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/xen/arch/x86/mm/shadow/hvm.c b/xen/arch/x86/mm/shadow/hvm.c -index 88c3c16322..6de479c008 100644 ---- a/xen/arch/x86/mm/shadow/hvm.c -+++ b/xen/arch/x86/mm/shadow/hvm.c -@@ -814,7 +814,8 @@ static void cf_check sh_unshadow_for_p2m_change( - - /* Only previously present / valid entries need processing. */ - if ( !(oflags & _PAGE_PRESENT) || -- (!p2m_is_valid(p2mt) && !p2m_is_grant(p2mt)) ) -+ (!p2m_is_valid(p2mt) && !p2m_is_grant(p2mt)) || -+ !mfn_valid(omfn) ) - return; - - switch ( level ) --- -2.40.0 - From f3cac52735cbd10e6bbd305401dd832bca994fd0 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 16 May 2023 19:21:37 +0100 Subject: [PATCH 063/137] Mishandling of guest SSBD selection on AMD hardware [XSA-431, CVE-2022-42336] --- xen.spec | 8 ++++- xsa431.patch | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 xsa431.patch diff --git a/xen.spec b/xen.spec index 0c8c5de..5f71cfc 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.17.1 -Release: 1%{?dist} +Release: 2%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -110,6 +110,7 @@ Patch43: xen.gcc11.fixes.patch Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch Patch47: xen.gcc13.fixes.patch +Patch48: xsa431.patch %if %build_qemutrad @@ -322,6 +323,7 @@ manage Xen virtual machines. %patch 45 -p1 %patch 46 -p1 %patch 47 -p1 +%patch 48 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -929,6 +931,10 @@ fi %endif %changelog +* Tue May 16 2023 Michael Young - 4.17.1-2 +- Mishandling of guest SSBD selection on AMD hardware + [XSA-431, CVE-2022-42336] + * Tue May 02 2023 Michael Young - 4.17.1-1 - update to xen-4.17.1 remove patches now included upstream diff --git a/xsa431.patch b/xsa431.patch new file mode 100644 index 0000000..b804592 --- /dev/null +++ b/xsa431.patch @@ -0,0 +1,94 @@ +From 9c03380fc9e328f0ccba860cbe09ef58ea366f71 Mon Sep 17 00:00:00 2001 +From: Roger Pau Monne +Date: Wed, 22 Mar 2023 11:52:07 +0100 +Subject: [PATCH] x86/amd: fix legacy setting of SSBD on AMD Family 17h +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The current logic to set SSBD on AMD Family 17h and Hygon Family 18h +processors requires that the setting of SSBD is coordinated at a core +level, as the setting is shared between threads. Logic was introduced +to keep track of how many threads require SSBD active in order to +coordinate it, such logic relies on using a per-core counter of +threads that have SSBD active. + +Given the current logic, it's possible for a guest to under or +overflow the thread counter, because each write to VIRT_SPEC_CTRL.SSBD +by the guest gets propagated to the helper that does the per-core +active accounting. Overflowing the counter is not so much of an +issue, as this would just make SSBD sticky. + +Underflowing however is more problematic: on non-debug Xen builds a +guest can perform empty writes to VIRT_SPEC_CTRL that would cause the +counter to underflow and thus the value gets saturated to the max +value of unsigned int. At which points attempts from any thread to +set VIRT_SPEC_CTRL.SSBD won't get propagated to the hardware anymore, +because the logic will see that the counter is greater than 1 and +assume that SSBD is already active, effectively loosing the setting +of SSBD and the protection it provides. + +Fix this by introducing a per-CPU variable that keeps track of whether +the current thread has legacy SSBD active or not, and thus only +attempt to propagate the value to the hardware once the thread +selected value changes. + +This is XSA-431 / CVE-2022-42336 + +Fixes: b2030e6730a2 ('amd/virt_ssbd: set SSBD at vCPU context switch') +Reported-by: Andrew Cooper +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +--- + xen/arch/x86/cpu/amd.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c +index caafe4474021..9a1a3858edd4 100644 +--- a/xen/arch/x86/cpu/amd.c ++++ b/xen/arch/x86/cpu/amd.c +@@ -783,12 +783,23 @@ bool __init amd_setup_legacy_ssbd(void) + return true; + } + ++/* ++ * legacy_ssbd is always initialized to false because when SSBD is set ++ * from the command line guest attempts to change it are a no-op (see ++ * amd_set_legacy_ssbd()), whereas when SSBD is inactive hardware will ++ * be forced into that mode (see amd_init_ssbd()). ++ */ ++static DEFINE_PER_CPU(bool, legacy_ssbd); ++ ++/* Must be called only when the SSBD setting needs toggling. */ + static void core_set_legacy_ssbd(bool enable) + { + const struct cpuinfo_x86 *c = ¤t_cpu_data; + struct ssbd_ls_cfg *status; + unsigned long flags; + ++ BUG_ON(this_cpu(legacy_ssbd) == enable); ++ + if ((c->x86 != 0x17 && c->x86 != 0x18) || c->x86_num_siblings <= 1) { + BUG_ON(!set_legacy_ssbd(c, enable)); + return; +@@ -816,12 +827,17 @@ void amd_set_legacy_ssbd(bool enable) + */ + return; + ++ if (this_cpu(legacy_ssbd) == enable) ++ return; ++ + if (cpu_has_virt_ssbd) + wrmsr(MSR_VIRT_SPEC_CTRL, enable ? SPEC_CTRL_SSBD : 0, 0); + else if (amd_legacy_ssbd) + core_set_legacy_ssbd(enable); + else + ASSERT_UNREACHABLE(); ++ ++ this_cpu(legacy_ssbd) = enable; + } + + /* +-- +2.40.0 + From b65002c5bff594567e4a527ef5ee3e3a0912069e Mon Sep 17 00:00:00 2001 From: Python Maint Date: Tue, 13 Jun 2023 21:39:25 +0200 Subject: [PATCH 064/137] Rebuilt for Python 3.12 --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 5f71cfc..527486d 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.17.1 -Release: 2%{?dist} +Release: 3%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -931,6 +931,9 @@ fi %endif %changelog +* Tue Jun 13 2023 Python Maint - 4.17.1-3 +- Rebuilt for Python 3.12 + * Tue May 16 2023 Michael Young - 4.17.1-2 - Mishandling of guest SSBD selection on AMD hardware [XSA-431, CVE-2022-42336] From 19ebd639fc7411f3536ce607e11fa1c99dafda7f Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 27 Jun 2023 20:32:48 +0100 Subject: [PATCH 065/137] work around a build problem with python 3.12 --- xen.python3.12.patch | 22 ++++++++++++++++++++++ xen.spec | 7 ++++++- 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 xen.python3.12.patch diff --git a/xen.python3.12.patch b/xen.python3.12.patch new file mode 100644 index 0000000..a6539c2 --- /dev/null +++ b/xen.python3.12.patch @@ -0,0 +1,22 @@ +--- xen-4.17.1/tools/python/Makefile.orig 2023-04-27 13:53:19.000000000 +0100 ++++ xen-4.17.1/tools/python/Makefile 2023-06-22 22:21:25.287486906 +0100 +@@ -4,7 +4,7 @@ + .PHONY: all + all: build + +-PY_CFLAGS = $(CFLAGS) $(PY_NOOPT_CFLAGS) ++PY_CFLAGS = $(CFLAGS) $(PY_NOOPT_CFLAGS) -Wno-error=declaration-after-statement + PY_LDFLAGS = $(SHLIB_LDFLAGS) $(APPEND_LDFLAGS) + INSTALL_LOG = build/installed_files.txt + +--- xen-4.17.1/tools/pygrub/Makefile.orig 2023-04-27 13:53:19.000000000 +0100 ++++ xen-4.17.1/tools/pygrub/Makefile 2023-06-22 22:52:52.803047401 +0100 +@@ -2,7 +2,7 @@ + XEN_ROOT = $(CURDIR)/../.. + include $(XEN_ROOT)/tools/Rules.mk + +-PY_CFLAGS = $(CFLAGS) $(PY_NOOPT_CFLAGS) ++PY_CFLAGS = $(CFLAGS) $(PY_NOOPT_CFLAGS) -Wno-error=declaration-after-statement + PY_LDFLAGS = $(SHLIB_LDFLAGS) $(APPEND_LDFLAGS) + INSTALL_LOG = build/installed_files.txt + diff --git a/xen.spec b/xen.spec index 527486d..fe57b65 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.17.1 -Release: 3%{?dist} +Release: 4%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -111,6 +111,7 @@ Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch Patch47: xen.gcc13.fixes.patch Patch48: xsa431.patch +Patch49: xen.python3.12.patch %if %build_qemutrad @@ -324,6 +325,7 @@ manage Xen virtual machines. %patch 46 -p1 %patch 47 -p1 %patch 48 -p1 +%patch 49 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -931,6 +933,9 @@ fi %endif %changelog +* Tue Jun 27 2023 Michael Young - 4.17.1-4 +- work around a build problem with python 3.12 + * Tue Jun 13 2023 Python Maint - 4.17.1-3 - Rebuilt for Python 3.12 From 70cfe51077bd939d93b6d28fe3d2fc65dbe070ff Mon Sep 17 00:00:00 2001 From: Jerry James Date: Mon, 10 Jul 2023 15:55:49 -0600 Subject: [PATCH 066/137] Add patch for OCaml 5.0.0 --- xen.ocaml5.fixes.patch | 70 ++++++++++++++++++++++++++++++++++++++++++ xen.spec | 7 ++++- 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 xen.ocaml5.fixes.patch diff --git a/xen.ocaml5.fixes.patch b/xen.ocaml5.fixes.patch new file mode 100644 index 0000000..2191334 --- /dev/null +++ b/xen.ocaml5.fixes.patch @@ -0,0 +1,70 @@ +--- xen-4.17.1/tools/ocaml/common.make.orig 2023-04-27 06:53:19.000000000 -0600 ++++ xen-4.17.1/tools/ocaml/common.make 2023-06-20 09:47:25.250969763 -0600 +@@ -11,9 +11,9 @@ OCAMLFIND ?= ocamlfind + + CFLAGS += -fPIC -I$(shell ocamlc -where) + +-OCAMLOPTFLAG_G := $(shell $(OCAMLOPT) -h 2>&1 | sed -n 's/^ *\(-g\) .*/\1/p') +-OCAMLOPTFLAGS = $(OCAMLOPTFLAG_G) -ccopt "$(LDFLAGS)" -dtypes $(OCAMLINCLUDE) -cc $(CC) -w F -warn-error F +-OCAMLCFLAGS += -g $(OCAMLINCLUDE) -w F -warn-error F ++OCAMLOPTFLAG_G := -g ++OCAMLOPTFLAGS = $(OCAMLOPTFLAG_G) -ccopt "$(LDFLAGS)" -dtypes $(OCAMLINCLUDE) -cc $(CC) -w F -warn-error F -I +unix ++OCAMLCFLAGS += -g $(OCAMLINCLUDE) -w F -warn-error F -I +unix + + VERSION := 4.1 + +--- xen-4.17.1/tools/ocaml/libs/xentoollog/xentoollog_stubs.c.orig 2023-04-27 06:53:19.000000000 -0600 ++++ xen-4.17.1/tools/ocaml/libs/xentoollog/xentoollog_stubs.c 2023-06-20 09:04:30.375263358 -0600 +@@ -33,7 +33,7 @@ + + /* The following is equal to the CAMLreturn macro, but without the return */ + #define CAMLdone do{ \ +-caml_local_roots = caml__frame; \ ++CAML_LOCAL_ROOTS = caml__frame; \ + }while (0) + + #define XTL ((xentoollog_logger *) Xtl_val(handle)) +--- xen-4.17.1/tools/ocaml/libs/xl/xenlight_stubs.c.orig 2023-04-27 06:53:19.000000000 -0600 ++++ xen-4.17.1/tools/ocaml/libs/xl/xenlight_stubs.c 2023-06-20 09:34:09.550454354 -0600 +@@ -43,14 +43,14 @@ + #ifndef CAMLreturnT + #define CAMLreturnT(type, result) do { \ + type caml__temp_result = (result); \ +- caml_local_roots = caml__frame; \ ++ CAML_LOCAL_ROOTS = caml__frame; \ + return (caml__temp_result); \ + } while (0) + #endif + + /* The following is equal to the CAMLreturn macro, but without the return */ + #define CAMLdone do{ \ +-caml_local_roots = caml__frame; \ ++CAML_LOCAL_ROOTS = caml__frame; \ + }while (0) + + #define Ctx_val(x)(*((libxl_ctx **) Data_custom_val(x))) +--- xen-4.17.1/tools/ocaml/Makefile.rules.orig 2023-04-27 06:53:19.000000000 -0600 ++++ xen-4.17.1/tools/ocaml/Makefile.rules 2023-06-20 10:00:58.769235173 -0600 +@@ -59,9 +59,9 @@ quiet-command = $(if $(V),$1,@printf " % + mk-caml-lib-native = $(call quiet-command, $(OCAMLOPT) $(OCAMLOPTFLAGS) -a -o $1 $2 $3,MLA,$1) + mk-caml-lib-bytecode = $(call quiet-command, $(OCAMLC) $(OCAMLCFLAGS) -a -o $1 $2 $3,MLA,$1) + +-mk-caml-stubs = $(call quiet-command, $(OCAMLMKLIB) -o `basename $1 .a` $2,MKLIB,$1) ++mk-caml-stubs = $(call quiet-command, $(OCAMLMKLIB) -g -o `basename $1 .a` $2,MKLIB,$1) + mk-caml-lib-stubs = \ +- $(call quiet-command, $(AR) rcs $1 $2 && $(OCAMLMKLIB) -o `basename $1 .a | sed -e 's/^lib//'` $2,MKLIB,$1) ++ $(call quiet-command, $(AR) rcs $1 $2 && $(OCAMLMKLIB) -g -o `basename $1 .a | sed -e 's/^lib//'` $2,MKLIB,$1) + + # define a library target .cmxa and .cma + define OCAML_LIBRARY_template +--- xen-4.17.1/tools/ocaml/xenstored/disk.ml.orig 2023-04-27 06:53:19.000000000 -0600 ++++ xen-4.17.1/tools/ocaml/xenstored/disk.ml 2023-06-20 09:49:44.361963710 -0600 +@@ -30,7 +30,7 @@ let undec c = + | _ -> raise (Failure "undecify") + + let unhex c = +- let c = Char.lowercase c in ++ let c = Char.lowercase_ascii c in + match c with + | '0' .. '9' -> (Char.code c) - (Char.code '0') + | 'a' .. 'f' -> (Char.code c) - (Char.code 'a') + 10 diff --git a/xen.spec b/xen.spec index fe57b65..db8ddb6 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.17.1 -Release: 4%{?dist} +Release: 5%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -112,6 +112,7 @@ Patch46: xen.efi.build.patch Patch47: xen.gcc13.fixes.patch Patch48: xsa431.patch Patch49: xen.python3.12.patch +Patch50: xen.ocaml5.fixes.patch %if %build_qemutrad @@ -326,6 +327,7 @@ manage Xen virtual machines. %patch 47 -p1 %patch 48 -p1 %patch 49 -p1 +%patch 50 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -933,6 +935,9 @@ fi %endif %changelog +* Mon Jul 10 2023 Jerry James - 4.17.1-5 +- Add patch for OCaml 5.0.0 + * Tue Jun 27 2023 Michael Young - 4.17.1-4 - work around a build problem with python 3.12 From 7669a6105566bc9fc6a2f3fa3d27e0e90665c02c Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Sat, 22 Jul 2023 18:41:52 +0000 Subject: [PATCH 067/137] Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index db8ddb6..5ae86a8 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.17.1 -Release: 5%{?dist} +Release: 6%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -935,6 +935,9 @@ fi %endif %changelog +* Sat Jul 22 2023 Fedora Release Engineering - 4.17.1-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild + * Mon Jul 10 2023 Jerry James - 4.17.1-5 - Add patch for OCaml 5.0.0 From 14c61e310509e7b9ceffadaf26722e201a1e2ace Mon Sep 17 00:00:00 2001 From: Michael Young Date: Mon, 24 Jul 2023 22:15:29 +0100 Subject: [PATCH 068/137] x86/AMD: Zenbleed [XSA-433] omit OCaml 5 patch on fc38 --- xen.spec | 10 +++- xsa433-4.17.patch | 138 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 xsa433-4.17.patch diff --git a/xen.spec b/xen.spec index 5ae86a8..8e953f9 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.17.1 -Release: 6%{?dist} +Release: 7%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -113,6 +113,7 @@ Patch47: xen.gcc13.fixes.patch Patch48: xsa431.patch Patch49: xen.python3.12.patch Patch50: xen.ocaml5.fixes.patch +Patch51: xsa433-4.17.patch %if %build_qemutrad @@ -327,7 +328,10 @@ manage Xen virtual machines. %patch 47 -p1 %patch 48 -p1 %patch 49 -p1 +%if "%dist" >= ".fc39" %patch 50 -p1 +%endif +%patch 51 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -935,6 +939,10 @@ fi %endif %changelog +* Mon Jul 24 2023 Michael Young - 4.17.1-7 +- x86/AMD: Zenbleed [XSA-433] +- omit OCaml 5 patch on fc38 + * Sat Jul 22 2023 Fedora Release Engineering - 4.17.1-6 - Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild diff --git a/xsa433-4.17.patch b/xsa433-4.17.patch new file mode 100644 index 0000000..668f556 --- /dev/null +++ b/xsa433-4.17.patch @@ -0,0 +1,138 @@ +From: Andrew Cooper +Subject: x86/amd: Mitigations for Zenbleed + +Zenbleed is a malfunction on AMD Zen2 uarch parts which results in corruption +of the vector registers. An attacker can trigger this bug deliberately in +order to access stale data in the physical vector register file. This can +include data from sibling threads, or a higher-privilege context. + +Microcode is the preferred mitigation but in the case that's not available use +the chickenbit as instructed by AMD. Re-evaluate the mitigation on late +microcode load too. + +This is XSA-433 / CVE-2023-20593. + +Signed-off-by: Andrew Cooper +Acked-by: Roger Pau Monné + +diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c +index b6a20d375ad1..8d23a5be0c5f 100644 +--- a/xen/arch/x86/cpu/amd.c ++++ b/xen/arch/x86/cpu/amd.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + + #include "cpu.h" + +@@ -878,6 +879,72 @@ void __init detect_zen2_null_seg_behaviour(void) + + } + ++void amd_check_zenbleed(void) ++{ ++ const struct cpu_signature *sig = &this_cpu(cpu_sig); ++ unsigned int good_rev, chickenbit = (1 << 9); ++ uint64_t val, old_val; ++ ++ /* ++ * If we're virtualised, we can't do family/model checks safely, and ++ * we likely wouldn't have access to DE_CFG even if we could see a ++ * microcode revision. ++ * ++ * A hypervisor may hide AVX as a stopgap mitigation. We're not in a ++ * position to care either way. An admin doesn't want to be disabling ++ * AVX as a mitigation on any build of Xen with this logic present. ++ */ ++ if (cpu_has_hypervisor || boot_cpu_data.x86 != 0x17) ++ return; ++ ++ switch (boot_cpu_data.x86_model) { ++ case 0x30 ... 0x3f: good_rev = 0x0830107a; break; ++ case 0x60 ... 0x67: good_rev = 0x0860010b; break; ++ case 0x68 ... 0x6f: good_rev = 0x08608105; break; ++ case 0x70 ... 0x7f: good_rev = 0x08701032; break; ++ case 0xa0 ... 0xaf: good_rev = 0x08a00008; break; ++ default: ++ /* ++ * With the Fam17h check above, parts getting here are Zen1. ++ * They're not affected. ++ */ ++ return; ++ } ++ ++ rdmsrl(MSR_AMD64_DE_CFG, val); ++ old_val = val; ++ ++ /* ++ * Microcode is the preferred mitigation, in terms of performance. ++ * However, without microcode, this chickenbit (specific to the Zen2 ++ * uarch) disables Floating Point Mov-Elimination to mitigate the ++ * issue. ++ */ ++ val &= ~chickenbit; ++ if (sig->rev < good_rev) ++ val |= chickenbit; ++ ++ if (val == old_val) ++ /* Nothing to change. */ ++ return; ++ ++ /* ++ * DE_CFG is a Core-scoped MSR, and this write is racy during late ++ * microcode load. However, both threads calculate the new value from ++ * state which is shared, and unrelated to the old value, so the ++ * result should be consistent. ++ */ ++ wrmsrl(MSR_AMD64_DE_CFG, val); ++ ++ /* ++ * Inform the admin that we changed something, but don't spam, ++ * especially during a late microcode load. ++ */ ++ if (smp_processor_id() == 0) ++ printk(XENLOG_INFO "Zenbleed mitigation - using %s\n", ++ val & chickenbit ? "chickenbit" : "microcode"); ++} ++ + static void cf_check init_amd(struct cpuinfo_x86 *c) + { + u32 l, h; +@@ -1150,6 +1217,8 @@ static void cf_check init_amd(struct cpuinfo_x86 *c) + if ((smp_processor_id() == 1) && !cpu_has(c, X86_FEATURE_ITSC)) + disable_c1_ramping(); + ++ amd_check_zenbleed(); ++ + check_syscfg_dram_mod_en(); + + amd_log_freq(c); +diff --git a/xen/arch/x86/cpu/microcode/amd.c b/xen/arch/x86/cpu/microcode/amd.c +index ded8fe90e650..c6d13f3fb35f 100644 +--- a/xen/arch/x86/cpu/microcode/amd.c ++++ b/xen/arch/x86/cpu/microcode/amd.c +@@ -262,6 +262,8 @@ static int cf_check apply_microcode(const struct microcode_patch *patch) + "microcode: CPU%u updated from revision %#x to %#x, date = %04x-%02x-%02x\n", + cpu, old_rev, rev, patch->year, patch->month, patch->day); + ++ amd_check_zenbleed(); ++ + return 0; + } + +diff --git a/xen/arch/x86/include/asm/processor.h b/xen/arch/x86/include/asm/processor.h +index 8e2816fae9b9..66611df6efc1 100644 +--- a/xen/arch/x86/include/asm/processor.h ++++ b/xen/arch/x86/include/asm/processor.h +@@ -637,6 +637,8 @@ enum ap_boot_method { + }; + extern enum ap_boot_method ap_boot_method; + ++void amd_check_zenbleed(void); ++ + #endif /* !__ASSEMBLY__ */ + + #endif /* __ASM_X86_PROCESSOR_H */ + From baa6ba2e5a0739576d01a71e01c25b7e0cacac07 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 26 Jul 2023 00:01:08 +0100 Subject: [PATCH 069/137] adjust OCaml patch condition so eln builds work --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 8e953f9..9883325 100644 --- a/xen.spec +++ b/xen.spec @@ -328,7 +328,7 @@ manage Xen virtual machines. %patch 47 -p1 %patch 48 -p1 %patch 49 -p1 -%if "%dist" >= ".fc39" +%if "%dist" != ".fc38" %patch 50 -p1 %endif %patch 51 -p1 @@ -939,6 +939,9 @@ fi %endif %changelog +* Tue Jul 25 2023 Michael Young +- adjust OCaml patch condition so eln builds work + * Mon Jul 24 2023 Michael Young - 4.17.1-7 - x86/AMD: Zenbleed [XSA-433] - omit OCaml 5 patch on fc38 From c09e550528d079432c9502ca08aeacdc2b2db5ab Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 26 Jul 2023 19:46:52 +0100 Subject: [PATCH 070/137] add CVE reference --- xen.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 9883325..3c8922b 100644 --- a/xen.spec +++ b/xen.spec @@ -943,7 +943,7 @@ fi - adjust OCaml patch condition so eln builds work * Mon Jul 24 2023 Michael Young - 4.17.1-7 -- x86/AMD: Zenbleed [XSA-433] +- x86/AMD: Zenbleed [XSA-433, CVE-2023-20593] - omit OCaml 5 patch on fc38 * Sat Jul 22 2023 Fedora Release Engineering - 4.17.1-6 From eb85956ccc59f95e1ff982c0a491b6dca8b9237a Mon Sep 17 00:00:00 2001 From: Michael Young Date: Mon, 31 Jul 2023 20:47:37 +0100 Subject: [PATCH 071/137] bugfix for x86/AMD: Zenbleed [XSA-433, CVE-2023-20593] --- xen.spec | 5 +++++ xsa433-bugfix.patch | 29 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 xsa433-bugfix.patch diff --git a/xen.spec b/xen.spec index 3c8922b..844667f 100644 --- a/xen.spec +++ b/xen.spec @@ -114,6 +114,7 @@ Patch48: xsa431.patch Patch49: xen.python3.12.patch Patch50: xen.ocaml5.fixes.patch Patch51: xsa433-4.17.patch +Patch52: xsa433-bugfix.patch %if %build_qemutrad @@ -332,6 +333,7 @@ manage Xen virtual machines. %patch 50 -p1 %endif %patch 51 -p1 +%patch 52 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -939,6 +941,9 @@ fi %endif %changelog +* Mon Jul 31 2023 Michael Young - 4.17.1-8 +- bugfix for x86/AMD: Zenbleed [XSA-433, CVE-2023-20593] + * Tue Jul 25 2023 Michael Young - adjust OCaml patch condition so eln builds work diff --git a/xsa433-bugfix.patch b/xsa433-bugfix.patch new file mode 100644 index 0000000..8ad4eda --- /dev/null +++ b/xsa433-bugfix.patch @@ -0,0 +1,29 @@ +From: Andrew Cooper +Subject: x86/amd: Fix DE_CFG truncation in amd_check_zenbleed() + +This line: + + val &= ~chickenbit; + +ends up truncating val to 32 bits, and turning off various errata workarounds +in Zen2 systems. + +Fixes: f91c5ea97067 ("x86/amd: Mitigations for Zenbleed") +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c +index 3ed06f670491..df2681b7c455 100644 +--- a/xen/arch/x86/cpu/amd.c ++++ b/xen/arch/x86/cpu/amd.c +@@ -909,8 +909,8 @@ void __init detect_zen2_null_seg_behaviour(void) + void amd_check_zenbleed(void) + { + const struct cpu_signature *sig = &this_cpu(cpu_sig); +- unsigned int good_rev, chickenbit = (1 << 9); +- uint64_t val, old_val; ++ unsigned int good_rev; ++ uint64_t val, old_val, chickenbit = (1 << 9); + + /* + * If we're virtualised, we can't do family/model checks safely, and From ca3f59d0e3666693e753bdb4fdb181338eae9f15 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Mon, 31 Jul 2023 20:53:11 +0100 Subject: [PATCH 072/137] increment Release --- xen.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 844667f..13c1487 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.17.1 -Release: 7%{?dist} +Release: 8%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz From c0ff82bee4c64c2bbe125d5a9cad4b7dd5febc97 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 1 Aug 2023 21:16:33 +0100 Subject: [PATCH 073/137] arm: Guests can trigger a deadlock on Cortex-A77 [XSA-436, CVE-2023-34320] --- xen.spec | 8 +- xsa436.patch | 288 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 295 insertions(+), 1 deletion(-) create mode 100644 xsa436.patch diff --git a/xen.spec b/xen.spec index 13c1487..dfe9536 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.17.1 -Release: 8%{?dist} +Release: 9%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -115,6 +115,7 @@ Patch49: xen.python3.12.patch Patch50: xen.ocaml5.fixes.patch Patch51: xsa433-4.17.patch Patch52: xsa433-bugfix.patch +Patch53: xsa436.patch %if %build_qemutrad @@ -334,6 +335,7 @@ manage Xen virtual machines. %endif %patch 51 -p1 %patch 52 -p1 +%patch 53 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -941,6 +943,10 @@ fi %endif %changelog +* Tue Aug 01 2023 Michael Young - 4.17.1-9 +- arm: Guests can trigger a deadlock on Cortex-A77 [XSA-436, CVE-2023-34320] + (#2228238) + * Mon Jul 31 2023 Michael Young - 4.17.1-8 - bugfix for x86/AMD: Zenbleed [XSA-433, CVE-2023-20593] diff --git a/xsa436.patch b/xsa436.patch new file mode 100644 index 0000000..02c04f2 --- /dev/null +++ b/xsa436.patch @@ -0,0 +1,288 @@ +From b6c28b0a7fa91e9c92caf388ac875639c424abce Mon Sep 17 00:00:00 2001 +From: Luca Fancellu +Date: Mon, 17 Jul 2023 13:25:46 +0100 +Subject: [PATCH] xen/arm: Add Cortex-A77 erratum 1508412 handling + +Cortex-A77 cores (r0p0, r1p0) could deadlock on a sequence of a +store-exclusive or read of PAR_EL1 and a load with device or non-cacheable +memory attributes. +A workaround is available, but it depends on a firmware counterpart. + +The proposed workaround from the errata document is to modify the software +running at EL1 and above to include a DMB SY before and after accessing +PAR_EL1. + +In conjunction to the above, the firmware needs to use a specific write +sequence to several IMPLEMENTATION DEFINED registers to have the hardware +insert a DMB SY after all load-exclusive and store-exclusive instructions. + +Apply the workaround to Xen where PAR_EL1 is read, implementing an helper +function to do that. +Since Xen can be interrupted by irqs in any moment, add a barrier on +entry/exit when we are running on the affected cores. + +A guest without the workaround can deadlock the system, so warn the users +of Xen with the above type of cores to use only trusted guests, by +printing a message on Xen startup. + +Signed-off-by: Luca Fancellu +Reviewed-by: Bertrand Marquis +Reviewed-by: Julien Grall +--- + SUPPORT.md | 2 ++ + docs/misc/arm/silicon-errata.txt | 1 + + xen/arch/arm/Kconfig | 21 +++++++++++++++++++++ + xen/arch/arm/arm64/entry.S | 19 +++++++++++++++++++ + xen/arch/arm/cpuerrata.c | 16 ++++++++++++---- + xen/arch/arm/domain.c | 2 +- + xen/arch/arm/include/asm/arm64/page.h | 12 ++++++------ + xen/arch/arm/include/asm/cpufeature.h | 3 ++- + xen/arch/arm/include/asm/sysregs.h | 24 ++++++++++++++++++++++++ + 9 files changed, 88 insertions(+), 12 deletions(-) + +diff --git a/SUPPORT.md b/SUPPORT.md +index 8e040d1c1ef5..b63376f4d8d2 100644 +--- a/SUPPORT.md ++++ b/SUPPORT.md +@@ -39,8 +39,10 @@ supported in this document. + + Status: Supported + Status, Cortex A57 r0p0-r1p1: Supported, not security supported ++ Status, Cortex A77 r0p0-r1p0: Supported, not security supported + + For the Cortex A57 r0p0 - r1p1, see Errata 832075. ++For the Cortex A77 r0p0 - r1p0, see Errata 1508412. + + ## Host hardware support + +diff --git a/docs/misc/arm/silicon-errata.txt b/docs/misc/arm/silicon-errata.txt +index 1925d8fd4ee0..c4e82df53566 100644 +--- a/docs/misc/arm/silicon-errata.txt ++++ b/docs/misc/arm/silicon-errata.txt +@@ -58,4 +58,5 @@ stable hypervisors. + | ARM | Cortex-A76 | #1286807 | ARM64_ERRATUM_1286807 | + | ARM | Neoverse-N1 | #1165522 | N/A + | ARM | Neoverse-N1 | #1286807 | ARM64_ERRATUM_1286807 | ++| ARM | Cortex-A77 | #1508412 | ARM64_ERRATUM_1508412 | + | ARM | MMU-500 | #842869 | N/A | +diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig +index 33e004d702bf..6ed1d51791e1 100644 +--- a/xen/arch/arm/Kconfig ++++ b/xen/arch/arm/Kconfig +@@ -312,6 +312,27 @@ config ARM64_ERRATUM_1286807 + + If unsure, say Y. + ++config ARM64_ERRATUM_1508412 ++ bool "Cortex-A77: 1508412: possible deadlock on sequence of NC/Device load and store exclusive or PAR read" ++ default y ++ depends on ARM_64 ++ help ++ This option adds a workaround for Arm Cortex-A77 erratum 1508412. ++ ++ Affected Cortex-A77 cores (r0p0, r1p0) could deadlock on a sequence ++ of a store-exclusive or read of PAR_EL1 and a load with device or ++ non-cacheable memory attributes. The workaround depends on a firmware ++ counterpart. ++ ++ Xen guests must also have the workaround implemented or they can ++ deadlock the system. ++ ++ Work around the issue by inserting DMB SY barriers around PAR_EL1 ++ register reads and warning Xen users. The DMB barrier is sufficient ++ to prevent a speculative PAR_EL1 read. ++ ++ If unsure, say Y. ++ + endmenu + + config ARM64_HARDEN_BRANCH_PREDICTOR +diff --git a/xen/arch/arm/arm64/entry.S b/xen/arch/arm/arm64/entry.S +index 95f1a9268419..95ff4e3e0517 100644 +--- a/xen/arch/arm/arm64/entry.S ++++ b/xen/arch/arm/arm64/entry.S +@@ -134,6 +134,16 @@ + * position on the stack before. + */ + .macro entry, hyp, compat, save_x0_x1=1 ++ ++ /* ++ * Ensure any PAR_EL1 reads complete, in case we were interrupted ++ * between the PAR_EL1 read and the memory barrier for the erratum ++ * 1508412 workaround. ++ */ ++ alternative_if ARM64_WORKAROUND_1508412 ++ dmb sy ++ alternative_else_nop_endif ++ + sub sp, sp, #(UREGS_SPSR_el1 - UREGS_LR) /* CPSR, PC, SP, LR */ + + .if \hyp == 0 /* Guest mode */ +@@ -492,6 +502,15 @@ return_from_trap: + + ldr lr, [sp], #(UREGS_SPSR_el1 - UREGS_LR) /* CPSR, PC, SP, LR */ + ++ /* ++ * Ensure any device/NC reads complete, in case we were interrupted ++ * between the memory barrier for the erratum 1508412 workaround and ++ * any PAR_EL1 read. ++ */ ++ alternative_if ARM64_WORKAROUND_1508412 ++ dmb sy ++ alternative_else_nop_endif ++ + eret + sb + +diff --git a/xen/arch/arm/cpuerrata.c b/xen/arch/arm/cpuerrata.c +index ae649d16ef02..ea680fac2e44 100644 +--- a/xen/arch/arm/cpuerrata.c ++++ b/xen/arch/arm/cpuerrata.c +@@ -668,6 +668,14 @@ static const struct arm_cpu_capabilities arm_errata[] = { + .capability = ARM64_WORKAROUND_AT_SPECULATE, + MIDR_ALL_VERSIONS(MIDR_CORTEX_A57), + }, ++#ifdef CONFIG_ARM64_ERRATUM_1508412 ++ { ++ /* Cortex-A77 r0p0 - r1p0 */ ++ .desc = "ARM erratum 1508412 (hypervisor portion)", ++ .capability = ARM64_WORKAROUND_1508412, ++ MIDR_RANGE(MIDR_CORTEX_A77, 0, 1), ++ }, ++#endif + { + /* Cortex-A55 (All versions as erratum is open in SDEN v14) */ + .desc = "ARM erratum 1530923", +@@ -686,11 +694,11 @@ void __init enable_errata_workarounds(void) + { + enable_cpu_capabilities(arm_errata); + +-#ifdef CONFIG_ARM64_ERRATUM_832075 +- if ( cpus_have_cap(ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE) ) ++#if defined(CONFIG_ARM64_ERRATUM_832075) || defined(CONFIG_ARM64_ERRATUM_1508412) ++ if ( cpus_have_cap(ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE) || ++ cpus_have_cap(ARM64_WORKAROUND_1508412) ) + { +- printk_once("**** This CPU is affected by the errata 832075. ****\n" +- "**** Guests without CPU erratum workarounds can deadlock the system! ****\n" ++ printk_once("**** Guests without CPU erratum workarounds can deadlock the system! ****\n" + "**** Only trusted guests should be used. ****\n"); + + /* Taint the machine has being insecure */ +diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c +index 2cd481979cf1..3e372fb70b99 100644 +--- a/xen/arch/arm/domain.c ++++ b/xen/arch/arm/domain.c +@@ -137,7 +137,7 @@ static void ctxt_switch_from(struct vcpu *p) + p->arch.ttbr1 = READ_SYSREG64(TTBR1_EL1); + if ( is_32bit_domain(p->domain) ) + p->arch.dacr = READ_SYSREG(DACR32_EL2); +- p->arch.par = READ_SYSREG64(PAR_EL1); ++ p->arch.par = read_sysreg_par(); + #if defined(CONFIG_ARM_32) + p->arch.mair0 = READ_CP32(MAIR0); + p->arch.mair1 = READ_CP32(MAIR1); +diff --git a/xen/arch/arm/include/asm/arm64/page.h b/xen/arch/arm/include/asm/arm64/page.h +index 0cba2663733b..fbfe67bf8951 100644 +--- a/xen/arch/arm/include/asm/arm64/page.h ++++ b/xen/arch/arm/include/asm/arm64/page.h +@@ -48,11 +48,11 @@ static inline void invalidate_icache_local(void) + /* Ask the MMU to translate a VA for us */ + static inline uint64_t __va_to_par(vaddr_t va) + { +- uint64_t par, tmp = READ_SYSREG64(PAR_EL1); ++ uint64_t par, tmp = read_sysreg_par(); + + asm volatile ("at s1e2r, %0;" : : "r" (va)); + isb(); +- par = READ_SYSREG64(PAR_EL1); ++ par = read_sysreg_par(); + WRITE_SYSREG64(tmp, PAR_EL1); + return par; + } +@@ -60,28 +60,28 @@ static inline uint64_t __va_to_par(vaddr_t va) + /* Ask the MMU to translate a Guest VA for us */ + static inline uint64_t gva_to_ma_par(vaddr_t va, unsigned int flags) + { +- uint64_t par, tmp = READ_SYSREG64(PAR_EL1); ++ uint64_t par, tmp = read_sysreg_par(); + + if ( (flags & GV2M_WRITE) == GV2M_WRITE ) + asm volatile ("at s12e1w, %0;" : : "r" (va)); + else + asm volatile ("at s12e1r, %0;" : : "r" (va)); + isb(); +- par = READ_SYSREG64(PAR_EL1); ++ par = read_sysreg_par(); + WRITE_SYSREG64(tmp, PAR_EL1); + return par; + } + + static inline uint64_t gva_to_ipa_par(vaddr_t va, unsigned int flags) + { +- uint64_t par, tmp = READ_SYSREG64(PAR_EL1); ++ uint64_t par, tmp = read_sysreg_par(); + + if ( (flags & GV2M_WRITE) == GV2M_WRITE ) + asm volatile ("at s1e1w, %0;" : : "r" (va)); + else + asm volatile ("at s1e1r, %0;" : : "r" (va)); + isb(); +- par = READ_SYSREG64(PAR_EL1); ++ par = read_sysreg_par(); + WRITE_SYSREG64(tmp, PAR_EL1); + return par; + } +diff --git a/xen/arch/arm/include/asm/cpufeature.h b/xen/arch/arm/include/asm/cpufeature.h +index c86a2e7f291c..3a39fe4b5afe 100644 +--- a/xen/arch/arm/include/asm/cpufeature.h ++++ b/xen/arch/arm/include/asm/cpufeature.h +@@ -68,8 +68,9 @@ + #define ARM_WORKAROUND_BHB_LOOP_32 14 + #define ARM_WORKAROUND_BHB_SMCC_3 15 + #define ARM_HAS_SB 16 ++#define ARM64_WORKAROUND_1508412 17 + +-#define ARM_NCAPS 17 ++#define ARM_NCAPS 18 + + #ifndef __ASSEMBLY__ + +diff --git a/xen/arch/arm/include/asm/sysregs.h b/xen/arch/arm/include/asm/sysregs.h +index 5c5c51bbcdbf..61e30c9e517c 100644 +--- a/xen/arch/arm/include/asm/sysregs.h ++++ b/xen/arch/arm/include/asm/sysregs.h +@@ -9,6 +9,30 @@ + # error "unknown ARM variant" + #endif + ++#ifndef __ASSEMBLY__ ++ ++#include ++ ++static inline register_t read_sysreg_par(void) ++{ ++ register_t par_el1; ++ ++ /* ++ * On Cortex-A77 r0p0 and r1p0, read access to PAR_EL1 shall include a ++ * DMB SY before and after accessing it, as part of the workaround for the ++ * errata 1508412. ++ */ ++ asm volatile(ALTERNATIVE("nop", "dmb sy", ARM64_WORKAROUND_1508412, ++ CONFIG_ARM64_ERRATUM_1508412)); ++ par_el1 = READ_SYSREG64(PAR_EL1); ++ asm volatile(ALTERNATIVE("nop", "dmb sy", ARM64_WORKAROUND_1508412, ++ CONFIG_ARM64_ERRATUM_1508412)); ++ ++ return par_el1; ++} ++ ++#endif /* !__ASSEMBLY__ */ ++ + #endif /* __ASM_ARM_SYSREGS_H */ + /* + * Local variables: +-- +2.40.1 + From 9f61265760472d58d2c71ca9a2770538144a4be5 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 10 Aug 2023 19:07:43 +0100 Subject: [PATCH 074/137] update to xen-4.17.2 which includes security updates x86/AMD: Speculative Return Stack Overflow [XSA-434, CVE-2023-20569] x86/Intel: Gather Data Sampling [XSA-435, CVE-2022-40982] remove patches now included upstream --- .gitignore | 2 +- sources | 2 +- xen.spec | 18 ++- xsa431.patch | 94 --------------- xsa433-4.17.patch | 138 --------------------- xsa433-bugfix.patch | 29 ----- xsa436.patch | 288 -------------------------------------------- 7 files changed, 10 insertions(+), 561 deletions(-) delete mode 100644 xsa431.patch delete mode 100644 xsa433-4.17.patch delete mode 100644 xsa433-bugfix.patch delete mode 100644 xsa436.patch diff --git a/.gitignore b/.gitignore index a3ba858..e0e02a4 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.17.1.tar.gz +/xen-4.17.2.tar.gz diff --git a/sources b/sources index c81c5e2..730c9d1 100644 --- a/sources +++ b/sources @@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.17.1.tar.gz) = bd98c5a2d75a0368ef312274b8e47f27db7f6b79302fd0c8b4975185e63640f98d9302e2834344acc1e97133e19f3993359dfab85aea5928008debada78c5f7a +SHA512 (xen-4.17.2.tar.gz) = 0bc475483676e4aa27735695f9a8d2821059e7a55984adb8a29badb5c09a4e7cf8ea29cbc9691be616cc0d7a5ee6b6dacc59ba29c2b16e0919ebdf7dfc54201a diff --git a/xen.spec b/xen.spec index dfe9536..dda5785 100644 --- a/xen.spec +++ b/xen.spec @@ -54,8 +54,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.17.1 -Release: 9%{?dist} +Version: 4.17.2 +Release: 1%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -110,12 +110,8 @@ Patch43: xen.gcc11.fixes.patch Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch Patch47: xen.gcc13.fixes.patch -Patch48: xsa431.patch Patch49: xen.python3.12.patch Patch50: xen.ocaml5.fixes.patch -Patch51: xsa433-4.17.patch -Patch52: xsa433-bugfix.patch -Patch53: xsa436.patch %if %build_qemutrad @@ -328,14 +324,10 @@ manage Xen virtual machines. %patch 45 -p1 %patch 46 -p1 %patch 47 -p1 -%patch 48 -p1 %patch 49 -p1 %if "%dist" != ".fc38" %patch 50 -p1 %endif -%patch 51 -p1 -%patch 52 -p1 -%patch 53 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -943,6 +935,12 @@ fi %endif %changelog +* Thu Aug 10 2023 Michael Young - 4.17.2-1 +- update to xen-4.17.2 which includes + x86/AMD: Speculative Return Stack Overflow [XSA-434, CVE-2023-20569] + x86/Intel: Gather Data Sampling [XSA-435, CVE-2022-40982] +- remove patches now included upstream + * Tue Aug 01 2023 Michael Young - 4.17.1-9 - arm: Guests can trigger a deadlock on Cortex-A77 [XSA-436, CVE-2023-34320] (#2228238) diff --git a/xsa431.patch b/xsa431.patch deleted file mode 100644 index b804592..0000000 --- a/xsa431.patch +++ /dev/null @@ -1,94 +0,0 @@ -From 9c03380fc9e328f0ccba860cbe09ef58ea366f71 Mon Sep 17 00:00:00 2001 -From: Roger Pau Monne -Date: Wed, 22 Mar 2023 11:52:07 +0100 -Subject: [PATCH] x86/amd: fix legacy setting of SSBD on AMD Family 17h -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The current logic to set SSBD on AMD Family 17h and Hygon Family 18h -processors requires that the setting of SSBD is coordinated at a core -level, as the setting is shared between threads. Logic was introduced -to keep track of how many threads require SSBD active in order to -coordinate it, such logic relies on using a per-core counter of -threads that have SSBD active. - -Given the current logic, it's possible for a guest to under or -overflow the thread counter, because each write to VIRT_SPEC_CTRL.SSBD -by the guest gets propagated to the helper that does the per-core -active accounting. Overflowing the counter is not so much of an -issue, as this would just make SSBD sticky. - -Underflowing however is more problematic: on non-debug Xen builds a -guest can perform empty writes to VIRT_SPEC_CTRL that would cause the -counter to underflow and thus the value gets saturated to the max -value of unsigned int. At which points attempts from any thread to -set VIRT_SPEC_CTRL.SSBD won't get propagated to the hardware anymore, -because the logic will see that the counter is greater than 1 and -assume that SSBD is already active, effectively loosing the setting -of SSBD and the protection it provides. - -Fix this by introducing a per-CPU variable that keeps track of whether -the current thread has legacy SSBD active or not, and thus only -attempt to propagate the value to the hardware once the thread -selected value changes. - -This is XSA-431 / CVE-2022-42336 - -Fixes: b2030e6730a2 ('amd/virt_ssbd: set SSBD at vCPU context switch') -Reported-by: Andrew Cooper -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich ---- - xen/arch/x86/cpu/amd.c | 16 ++++++++++++++++ - 1 file changed, 16 insertions(+) - -diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c -index caafe4474021..9a1a3858edd4 100644 ---- a/xen/arch/x86/cpu/amd.c -+++ b/xen/arch/x86/cpu/amd.c -@@ -783,12 +783,23 @@ bool __init amd_setup_legacy_ssbd(void) - return true; - } - -+/* -+ * legacy_ssbd is always initialized to false because when SSBD is set -+ * from the command line guest attempts to change it are a no-op (see -+ * amd_set_legacy_ssbd()), whereas when SSBD is inactive hardware will -+ * be forced into that mode (see amd_init_ssbd()). -+ */ -+static DEFINE_PER_CPU(bool, legacy_ssbd); -+ -+/* Must be called only when the SSBD setting needs toggling. */ - static void core_set_legacy_ssbd(bool enable) - { - const struct cpuinfo_x86 *c = ¤t_cpu_data; - struct ssbd_ls_cfg *status; - unsigned long flags; - -+ BUG_ON(this_cpu(legacy_ssbd) == enable); -+ - if ((c->x86 != 0x17 && c->x86 != 0x18) || c->x86_num_siblings <= 1) { - BUG_ON(!set_legacy_ssbd(c, enable)); - return; -@@ -816,12 +827,17 @@ void amd_set_legacy_ssbd(bool enable) - */ - return; - -+ if (this_cpu(legacy_ssbd) == enable) -+ return; -+ - if (cpu_has_virt_ssbd) - wrmsr(MSR_VIRT_SPEC_CTRL, enable ? SPEC_CTRL_SSBD : 0, 0); - else if (amd_legacy_ssbd) - core_set_legacy_ssbd(enable); - else - ASSERT_UNREACHABLE(); -+ -+ this_cpu(legacy_ssbd) = enable; - } - - /* --- -2.40.0 - diff --git a/xsa433-4.17.patch b/xsa433-4.17.patch deleted file mode 100644 index 668f556..0000000 --- a/xsa433-4.17.patch +++ /dev/null @@ -1,138 +0,0 @@ -From: Andrew Cooper -Subject: x86/amd: Mitigations for Zenbleed - -Zenbleed is a malfunction on AMD Zen2 uarch parts which results in corruption -of the vector registers. An attacker can trigger this bug deliberately in -order to access stale data in the physical vector register file. This can -include data from sibling threads, or a higher-privilege context. - -Microcode is the preferred mitigation but in the case that's not available use -the chickenbit as instructed by AMD. Re-evaluate the mitigation on late -microcode load too. - -This is XSA-433 / CVE-2023-20593. - -Signed-off-by: Andrew Cooper -Acked-by: Roger Pau Monné - -diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c -index b6a20d375ad1..8d23a5be0c5f 100644 ---- a/xen/arch/x86/cpu/amd.c -+++ b/xen/arch/x86/cpu/amd.c -@@ -13,6 +13,7 @@ - #include - #include - #include -+#include - - #include "cpu.h" - -@@ -878,6 +879,72 @@ void __init detect_zen2_null_seg_behaviour(void) - - } - -+void amd_check_zenbleed(void) -+{ -+ const struct cpu_signature *sig = &this_cpu(cpu_sig); -+ unsigned int good_rev, chickenbit = (1 << 9); -+ uint64_t val, old_val; -+ -+ /* -+ * If we're virtualised, we can't do family/model checks safely, and -+ * we likely wouldn't have access to DE_CFG even if we could see a -+ * microcode revision. -+ * -+ * A hypervisor may hide AVX as a stopgap mitigation. We're not in a -+ * position to care either way. An admin doesn't want to be disabling -+ * AVX as a mitigation on any build of Xen with this logic present. -+ */ -+ if (cpu_has_hypervisor || boot_cpu_data.x86 != 0x17) -+ return; -+ -+ switch (boot_cpu_data.x86_model) { -+ case 0x30 ... 0x3f: good_rev = 0x0830107a; break; -+ case 0x60 ... 0x67: good_rev = 0x0860010b; break; -+ case 0x68 ... 0x6f: good_rev = 0x08608105; break; -+ case 0x70 ... 0x7f: good_rev = 0x08701032; break; -+ case 0xa0 ... 0xaf: good_rev = 0x08a00008; break; -+ default: -+ /* -+ * With the Fam17h check above, parts getting here are Zen1. -+ * They're not affected. -+ */ -+ return; -+ } -+ -+ rdmsrl(MSR_AMD64_DE_CFG, val); -+ old_val = val; -+ -+ /* -+ * Microcode is the preferred mitigation, in terms of performance. -+ * However, without microcode, this chickenbit (specific to the Zen2 -+ * uarch) disables Floating Point Mov-Elimination to mitigate the -+ * issue. -+ */ -+ val &= ~chickenbit; -+ if (sig->rev < good_rev) -+ val |= chickenbit; -+ -+ if (val == old_val) -+ /* Nothing to change. */ -+ return; -+ -+ /* -+ * DE_CFG is a Core-scoped MSR, and this write is racy during late -+ * microcode load. However, both threads calculate the new value from -+ * state which is shared, and unrelated to the old value, so the -+ * result should be consistent. -+ */ -+ wrmsrl(MSR_AMD64_DE_CFG, val); -+ -+ /* -+ * Inform the admin that we changed something, but don't spam, -+ * especially during a late microcode load. -+ */ -+ if (smp_processor_id() == 0) -+ printk(XENLOG_INFO "Zenbleed mitigation - using %s\n", -+ val & chickenbit ? "chickenbit" : "microcode"); -+} -+ - static void cf_check init_amd(struct cpuinfo_x86 *c) - { - u32 l, h; -@@ -1150,6 +1217,8 @@ static void cf_check init_amd(struct cpuinfo_x86 *c) - if ((smp_processor_id() == 1) && !cpu_has(c, X86_FEATURE_ITSC)) - disable_c1_ramping(); - -+ amd_check_zenbleed(); -+ - check_syscfg_dram_mod_en(); - - amd_log_freq(c); -diff --git a/xen/arch/x86/cpu/microcode/amd.c b/xen/arch/x86/cpu/microcode/amd.c -index ded8fe90e650..c6d13f3fb35f 100644 ---- a/xen/arch/x86/cpu/microcode/amd.c -+++ b/xen/arch/x86/cpu/microcode/amd.c -@@ -262,6 +262,8 @@ static int cf_check apply_microcode(const struct microcode_patch *patch) - "microcode: CPU%u updated from revision %#x to %#x, date = %04x-%02x-%02x\n", - cpu, old_rev, rev, patch->year, patch->month, patch->day); - -+ amd_check_zenbleed(); -+ - return 0; - } - -diff --git a/xen/arch/x86/include/asm/processor.h b/xen/arch/x86/include/asm/processor.h -index 8e2816fae9b9..66611df6efc1 100644 ---- a/xen/arch/x86/include/asm/processor.h -+++ b/xen/arch/x86/include/asm/processor.h -@@ -637,6 +637,8 @@ enum ap_boot_method { - }; - extern enum ap_boot_method ap_boot_method; - -+void amd_check_zenbleed(void); -+ - #endif /* !__ASSEMBLY__ */ - - #endif /* __ASM_X86_PROCESSOR_H */ - diff --git a/xsa433-bugfix.patch b/xsa433-bugfix.patch deleted file mode 100644 index 8ad4eda..0000000 --- a/xsa433-bugfix.patch +++ /dev/null @@ -1,29 +0,0 @@ -From: Andrew Cooper -Subject: x86/amd: Fix DE_CFG truncation in amd_check_zenbleed() - -This line: - - val &= ~chickenbit; - -ends up truncating val to 32 bits, and turning off various errata workarounds -in Zen2 systems. - -Fixes: f91c5ea97067 ("x86/amd: Mitigations for Zenbleed") -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c -index 3ed06f670491..df2681b7c455 100644 ---- a/xen/arch/x86/cpu/amd.c -+++ b/xen/arch/x86/cpu/amd.c -@@ -909,8 +909,8 @@ void __init detect_zen2_null_seg_behaviour(void) - void amd_check_zenbleed(void) - { - const struct cpu_signature *sig = &this_cpu(cpu_sig); -- unsigned int good_rev, chickenbit = (1 << 9); -- uint64_t val, old_val; -+ unsigned int good_rev; -+ uint64_t val, old_val, chickenbit = (1 << 9); - - /* - * If we're virtualised, we can't do family/model checks safely, and diff --git a/xsa436.patch b/xsa436.patch deleted file mode 100644 index 02c04f2..0000000 --- a/xsa436.patch +++ /dev/null @@ -1,288 +0,0 @@ -From b6c28b0a7fa91e9c92caf388ac875639c424abce Mon Sep 17 00:00:00 2001 -From: Luca Fancellu -Date: Mon, 17 Jul 2023 13:25:46 +0100 -Subject: [PATCH] xen/arm: Add Cortex-A77 erratum 1508412 handling - -Cortex-A77 cores (r0p0, r1p0) could deadlock on a sequence of a -store-exclusive or read of PAR_EL1 and a load with device or non-cacheable -memory attributes. -A workaround is available, but it depends on a firmware counterpart. - -The proposed workaround from the errata document is to modify the software -running at EL1 and above to include a DMB SY before and after accessing -PAR_EL1. - -In conjunction to the above, the firmware needs to use a specific write -sequence to several IMPLEMENTATION DEFINED registers to have the hardware -insert a DMB SY after all load-exclusive and store-exclusive instructions. - -Apply the workaround to Xen where PAR_EL1 is read, implementing an helper -function to do that. -Since Xen can be interrupted by irqs in any moment, add a barrier on -entry/exit when we are running on the affected cores. - -A guest without the workaround can deadlock the system, so warn the users -of Xen with the above type of cores to use only trusted guests, by -printing a message on Xen startup. - -Signed-off-by: Luca Fancellu -Reviewed-by: Bertrand Marquis -Reviewed-by: Julien Grall ---- - SUPPORT.md | 2 ++ - docs/misc/arm/silicon-errata.txt | 1 + - xen/arch/arm/Kconfig | 21 +++++++++++++++++++++ - xen/arch/arm/arm64/entry.S | 19 +++++++++++++++++++ - xen/arch/arm/cpuerrata.c | 16 ++++++++++++---- - xen/arch/arm/domain.c | 2 +- - xen/arch/arm/include/asm/arm64/page.h | 12 ++++++------ - xen/arch/arm/include/asm/cpufeature.h | 3 ++- - xen/arch/arm/include/asm/sysregs.h | 24 ++++++++++++++++++++++++ - 9 files changed, 88 insertions(+), 12 deletions(-) - -diff --git a/SUPPORT.md b/SUPPORT.md -index 8e040d1c1ef5..b63376f4d8d2 100644 ---- a/SUPPORT.md -+++ b/SUPPORT.md -@@ -39,8 +39,10 @@ supported in this document. - - Status: Supported - Status, Cortex A57 r0p0-r1p1: Supported, not security supported -+ Status, Cortex A77 r0p0-r1p0: Supported, not security supported - - For the Cortex A57 r0p0 - r1p1, see Errata 832075. -+For the Cortex A77 r0p0 - r1p0, see Errata 1508412. - - ## Host hardware support - -diff --git a/docs/misc/arm/silicon-errata.txt b/docs/misc/arm/silicon-errata.txt -index 1925d8fd4ee0..c4e82df53566 100644 ---- a/docs/misc/arm/silicon-errata.txt -+++ b/docs/misc/arm/silicon-errata.txt -@@ -58,4 +58,5 @@ stable hypervisors. - | ARM | Cortex-A76 | #1286807 | ARM64_ERRATUM_1286807 | - | ARM | Neoverse-N1 | #1165522 | N/A - | ARM | Neoverse-N1 | #1286807 | ARM64_ERRATUM_1286807 | -+| ARM | Cortex-A77 | #1508412 | ARM64_ERRATUM_1508412 | - | ARM | MMU-500 | #842869 | N/A | -diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig -index 33e004d702bf..6ed1d51791e1 100644 ---- a/xen/arch/arm/Kconfig -+++ b/xen/arch/arm/Kconfig -@@ -312,6 +312,27 @@ config ARM64_ERRATUM_1286807 - - If unsure, say Y. - -+config ARM64_ERRATUM_1508412 -+ bool "Cortex-A77: 1508412: possible deadlock on sequence of NC/Device load and store exclusive or PAR read" -+ default y -+ depends on ARM_64 -+ help -+ This option adds a workaround for Arm Cortex-A77 erratum 1508412. -+ -+ Affected Cortex-A77 cores (r0p0, r1p0) could deadlock on a sequence -+ of a store-exclusive or read of PAR_EL1 and a load with device or -+ non-cacheable memory attributes. The workaround depends on a firmware -+ counterpart. -+ -+ Xen guests must also have the workaround implemented or they can -+ deadlock the system. -+ -+ Work around the issue by inserting DMB SY barriers around PAR_EL1 -+ register reads and warning Xen users. The DMB barrier is sufficient -+ to prevent a speculative PAR_EL1 read. -+ -+ If unsure, say Y. -+ - endmenu - - config ARM64_HARDEN_BRANCH_PREDICTOR -diff --git a/xen/arch/arm/arm64/entry.S b/xen/arch/arm/arm64/entry.S -index 95f1a9268419..95ff4e3e0517 100644 ---- a/xen/arch/arm/arm64/entry.S -+++ b/xen/arch/arm/arm64/entry.S -@@ -134,6 +134,16 @@ - * position on the stack before. - */ - .macro entry, hyp, compat, save_x0_x1=1 -+ -+ /* -+ * Ensure any PAR_EL1 reads complete, in case we were interrupted -+ * between the PAR_EL1 read and the memory barrier for the erratum -+ * 1508412 workaround. -+ */ -+ alternative_if ARM64_WORKAROUND_1508412 -+ dmb sy -+ alternative_else_nop_endif -+ - sub sp, sp, #(UREGS_SPSR_el1 - UREGS_LR) /* CPSR, PC, SP, LR */ - - .if \hyp == 0 /* Guest mode */ -@@ -492,6 +502,15 @@ return_from_trap: - - ldr lr, [sp], #(UREGS_SPSR_el1 - UREGS_LR) /* CPSR, PC, SP, LR */ - -+ /* -+ * Ensure any device/NC reads complete, in case we were interrupted -+ * between the memory barrier for the erratum 1508412 workaround and -+ * any PAR_EL1 read. -+ */ -+ alternative_if ARM64_WORKAROUND_1508412 -+ dmb sy -+ alternative_else_nop_endif -+ - eret - sb - -diff --git a/xen/arch/arm/cpuerrata.c b/xen/arch/arm/cpuerrata.c -index ae649d16ef02..ea680fac2e44 100644 ---- a/xen/arch/arm/cpuerrata.c -+++ b/xen/arch/arm/cpuerrata.c -@@ -668,6 +668,14 @@ static const struct arm_cpu_capabilities arm_errata[] = { - .capability = ARM64_WORKAROUND_AT_SPECULATE, - MIDR_ALL_VERSIONS(MIDR_CORTEX_A57), - }, -+#ifdef CONFIG_ARM64_ERRATUM_1508412 -+ { -+ /* Cortex-A77 r0p0 - r1p0 */ -+ .desc = "ARM erratum 1508412 (hypervisor portion)", -+ .capability = ARM64_WORKAROUND_1508412, -+ MIDR_RANGE(MIDR_CORTEX_A77, 0, 1), -+ }, -+#endif - { - /* Cortex-A55 (All versions as erratum is open in SDEN v14) */ - .desc = "ARM erratum 1530923", -@@ -686,11 +694,11 @@ void __init enable_errata_workarounds(void) - { - enable_cpu_capabilities(arm_errata); - --#ifdef CONFIG_ARM64_ERRATUM_832075 -- if ( cpus_have_cap(ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE) ) -+#if defined(CONFIG_ARM64_ERRATUM_832075) || defined(CONFIG_ARM64_ERRATUM_1508412) -+ if ( cpus_have_cap(ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE) || -+ cpus_have_cap(ARM64_WORKAROUND_1508412) ) - { -- printk_once("**** This CPU is affected by the errata 832075. ****\n" -- "**** Guests without CPU erratum workarounds can deadlock the system! ****\n" -+ printk_once("**** Guests without CPU erratum workarounds can deadlock the system! ****\n" - "**** Only trusted guests should be used. ****\n"); - - /* Taint the machine has being insecure */ -diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c -index 2cd481979cf1..3e372fb70b99 100644 ---- a/xen/arch/arm/domain.c -+++ b/xen/arch/arm/domain.c -@@ -137,7 +137,7 @@ static void ctxt_switch_from(struct vcpu *p) - p->arch.ttbr1 = READ_SYSREG64(TTBR1_EL1); - if ( is_32bit_domain(p->domain) ) - p->arch.dacr = READ_SYSREG(DACR32_EL2); -- p->arch.par = READ_SYSREG64(PAR_EL1); -+ p->arch.par = read_sysreg_par(); - #if defined(CONFIG_ARM_32) - p->arch.mair0 = READ_CP32(MAIR0); - p->arch.mair1 = READ_CP32(MAIR1); -diff --git a/xen/arch/arm/include/asm/arm64/page.h b/xen/arch/arm/include/asm/arm64/page.h -index 0cba2663733b..fbfe67bf8951 100644 ---- a/xen/arch/arm/include/asm/arm64/page.h -+++ b/xen/arch/arm/include/asm/arm64/page.h -@@ -48,11 +48,11 @@ static inline void invalidate_icache_local(void) - /* Ask the MMU to translate a VA for us */ - static inline uint64_t __va_to_par(vaddr_t va) - { -- uint64_t par, tmp = READ_SYSREG64(PAR_EL1); -+ uint64_t par, tmp = read_sysreg_par(); - - asm volatile ("at s1e2r, %0;" : : "r" (va)); - isb(); -- par = READ_SYSREG64(PAR_EL1); -+ par = read_sysreg_par(); - WRITE_SYSREG64(tmp, PAR_EL1); - return par; - } -@@ -60,28 +60,28 @@ static inline uint64_t __va_to_par(vaddr_t va) - /* Ask the MMU to translate a Guest VA for us */ - static inline uint64_t gva_to_ma_par(vaddr_t va, unsigned int flags) - { -- uint64_t par, tmp = READ_SYSREG64(PAR_EL1); -+ uint64_t par, tmp = read_sysreg_par(); - - if ( (flags & GV2M_WRITE) == GV2M_WRITE ) - asm volatile ("at s12e1w, %0;" : : "r" (va)); - else - asm volatile ("at s12e1r, %0;" : : "r" (va)); - isb(); -- par = READ_SYSREG64(PAR_EL1); -+ par = read_sysreg_par(); - WRITE_SYSREG64(tmp, PAR_EL1); - return par; - } - - static inline uint64_t gva_to_ipa_par(vaddr_t va, unsigned int flags) - { -- uint64_t par, tmp = READ_SYSREG64(PAR_EL1); -+ uint64_t par, tmp = read_sysreg_par(); - - if ( (flags & GV2M_WRITE) == GV2M_WRITE ) - asm volatile ("at s1e1w, %0;" : : "r" (va)); - else - asm volatile ("at s1e1r, %0;" : : "r" (va)); - isb(); -- par = READ_SYSREG64(PAR_EL1); -+ par = read_sysreg_par(); - WRITE_SYSREG64(tmp, PAR_EL1); - return par; - } -diff --git a/xen/arch/arm/include/asm/cpufeature.h b/xen/arch/arm/include/asm/cpufeature.h -index c86a2e7f291c..3a39fe4b5afe 100644 ---- a/xen/arch/arm/include/asm/cpufeature.h -+++ b/xen/arch/arm/include/asm/cpufeature.h -@@ -68,8 +68,9 @@ - #define ARM_WORKAROUND_BHB_LOOP_32 14 - #define ARM_WORKAROUND_BHB_SMCC_3 15 - #define ARM_HAS_SB 16 -+#define ARM64_WORKAROUND_1508412 17 - --#define ARM_NCAPS 17 -+#define ARM_NCAPS 18 - - #ifndef __ASSEMBLY__ - -diff --git a/xen/arch/arm/include/asm/sysregs.h b/xen/arch/arm/include/asm/sysregs.h -index 5c5c51bbcdbf..61e30c9e517c 100644 ---- a/xen/arch/arm/include/asm/sysregs.h -+++ b/xen/arch/arm/include/asm/sysregs.h -@@ -9,6 +9,30 @@ - # error "unknown ARM variant" - #endif - -+#ifndef __ASSEMBLY__ -+ -+#include -+ -+static inline register_t read_sysreg_par(void) -+{ -+ register_t par_el1; -+ -+ /* -+ * On Cortex-A77 r0p0 and r1p0, read access to PAR_EL1 shall include a -+ * DMB SY before and after accessing it, as part of the workaround for the -+ * errata 1508412. -+ */ -+ asm volatile(ALTERNATIVE("nop", "dmb sy", ARM64_WORKAROUND_1508412, -+ CONFIG_ARM64_ERRATUM_1508412)); -+ par_el1 = READ_SYSREG64(PAR_EL1); -+ asm volatile(ALTERNATIVE("nop", "dmb sy", ARM64_WORKAROUND_1508412, -+ CONFIG_ARM64_ERRATUM_1508412)); -+ -+ return par_el1; -+} -+ -+#endif /* !__ASSEMBLY__ */ -+ - #endif /* __ASM_ARM_SYSREGS_H */ - /* - * Local variables: --- -2.40.1 - From afce7b2845e4f1fee296daa141c72e9a194554bd Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 26 Sep 2023 15:20:03 +0100 Subject: [PATCH 075/137] 3 security issues - arm32: The cache may not be properly cleaned/invalidated [XSA-437, CVE-2023-34321] - top-level shadow reference dropped too early for 64-bit PV guests [XSA-438, CVE-2023-34322] - x86/AMD: Divide speculative information leak [XSA-439, CVE-2023-20588] --- xen.spec | 33 +- xsa437.patch | 110 +++++ xsa438-4.17.patch | 416 ++++++++++++++++++ ...enbleed-check-to-models-good-ucode-i.patch | 49 +++ ...x-confusion-between-SPEC_CTRL_EXIT_T.patch | 77 ++++ ...ld-DO_SPEC_CTRL_EXIT_TO_XEN-into-it-.patch | 88 ++++ ...rn-the-remaining-SPEC_CTRL_-ENTRY-EX.patch | 86 ++++ ...prove-all-SPEC_CTRL_-ENTER-EXIT-_-co.patch | 109 +++++ ...-restore_all_xen-to-hold-stack_end-i.patch | 77 ++++ ...the-IST-ness-of-an-entry-for-the-exi.patch | 112 +++++ ...rl-Issue-VERW-during-IST-exit-to-Xen.patch | 92 ++++ ...troduce-is_zen-1-2-_uarch-predicates.patch | 94 ++++ ...c-ctrl-Mitigate-the-Zen1-DIV-leakage.patch | 231 ++++++++++ 13 files changed, 1573 insertions(+), 1 deletion(-) create mode 100644 xsa437.patch create mode 100644 xsa438-4.17.patch create mode 100644 xsa439-0001-x86-AMD-extend-Zenbleed-check-to-models-good-ucode-i.patch create mode 100644 xsa439-0002-x86-spec-ctrl-Fix-confusion-between-SPEC_CTRL_EXIT_T.patch create mode 100644 xsa439-0003-x86-spec-ctrl-Fold-DO_SPEC_CTRL_EXIT_TO_XEN-into-it-.patch create mode 100644 xsa439-0004-x86-spec-ctrl-Turn-the-remaining-SPEC_CTRL_-ENTRY-EX.patch create mode 100644 xsa439-0005-x86-spec-ctrl-Improve-all-SPEC_CTRL_-ENTER-EXIT-_-co.patch create mode 100644 xsa439-0006-x86-entry-Adjust-restore_all_xen-to-hold-stack_end-i.patch create mode 100644 xsa439-0007-x86-entry-Track-the-IST-ness-of-an-entry-for-the-exi.patch create mode 100644 xsa439-0008-x86-spec-ctrl-Issue-VERW-during-IST-exit-to-Xen.patch create mode 100644 xsa439-0009-x86-amd-Introduce-is_zen-1-2-_uarch-predicates.patch create mode 100644 xsa439-0010-x86-spec-ctrl-Mitigate-the-Zen1-DIV-leakage.patch diff --git a/xen.spec b/xen.spec index dda5785..778a2e5 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.17.2 -Release: 1%{?dist} +Release: 2%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -112,6 +112,18 @@ Patch46: xen.efi.build.patch Patch47: xen.gcc13.fixes.patch Patch49: xen.python3.12.patch Patch50: xen.ocaml5.fixes.patch +Patch51: xsa437.patch +Patch52: xsa438-4.17.patch +Patch53: xsa439-0001-x86-AMD-extend-Zenbleed-check-to-models-good-ucode-i.patch +Patch54: xsa439-0002-x86-spec-ctrl-Fix-confusion-between-SPEC_CTRL_EXIT_T.patch +Patch55: xsa439-0003-x86-spec-ctrl-Fold-DO_SPEC_CTRL_EXIT_TO_XEN-into-it-.patch +Patch56: xsa439-0004-x86-spec-ctrl-Turn-the-remaining-SPEC_CTRL_-ENTRY-EX.patch +Patch57: xsa439-0005-x86-spec-ctrl-Improve-all-SPEC_CTRL_-ENTER-EXIT-_-co.patch +Patch58: xsa439-0006-x86-entry-Adjust-restore_all_xen-to-hold-stack_end-i.patch +Patch59: xsa439-0007-x86-entry-Track-the-IST-ness-of-an-entry-for-the-exi.patch +Patch60: xsa439-0008-x86-spec-ctrl-Issue-VERW-during-IST-exit-to-Xen.patch +Patch61: xsa439-0009-x86-amd-Introduce-is_zen-1-2-_uarch-predicates.patch +Patch62: xsa439-0010-x86-spec-ctrl-Mitigate-the-Zen1-DIV-leakage.patch %if %build_qemutrad @@ -328,6 +340,18 @@ manage Xen virtual machines. %if "%dist" != ".fc38" %patch 50 -p1 %endif +%patch 51 -p1 +%patch 52 -p1 +%patch 53 -p1 +%patch 54 -p1 +%patch 55 -p1 +%patch 56 -p1 +%patch 57 -p1 +%patch 58 -p1 +%patch 59 -p1 +%patch 60 -p1 +%patch 61 -p1 +%patch 62 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -935,6 +959,13 @@ fi %endif %changelog +* Tue Sep 26 2023 Michael Young - 4.17.2-2 +- arm32: The cache may not be properly cleaned/invalidated [XSA-437, + CVE-2023-34321] +- top-level shadow reference dropped too early for 64-bit PV guests + [XSA-438, CVE-2023-34322] +- x86/AMD: Divide speculative information leak [XSA-439, CVE-2023-20588] + * Thu Aug 10 2023 Michael Young - 4.17.2-1 - update to xen-4.17.2 which includes x86/AMD: Speculative Return Stack Overflow [XSA-434, CVE-2023-20569] diff --git a/xsa437.patch b/xsa437.patch new file mode 100644 index 0000000..18c9f8f --- /dev/null +++ b/xsa437.patch @@ -0,0 +1,110 @@ +From 7fac5971340a13ca9458195305bcfe14df2e52d2 Mon Sep 17 00:00:00 2001 +From: Stefano Stabellini +Date: Thu, 17 Aug 2023 13:41:35 +0100 +Subject: [PATCH] xen/arm: page: Handle cache flush of an element at the top of + the address space + +The region that needs to be cleaned/invalidated may be at the top +of the address space. This means that 'end' (i.e. 'p + size') will +be 0 and therefore nothing will be cleaned/invalidated as the check +in the loop will always be false. + +On Arm64, we only support we only support up to 48-bit Virtual +address space. So this is not a concern there. However, for 32-bit, +the mapcache is using the last 2GB of the address space. Therefore +we may not clean/invalidate properly some pages. This could lead +to memory corruption or data leakage (the scrubbed value may +still sit in the cache when the guest could read directly the memory +and therefore read the old content). + +Rework invalidate_dcache_va_range(), clean_dcache_va_range(), +clean_and_invalidate_dcache_va_range() to handle a cache flush +with an element at the top of the address space. + +This is CVE-2023-34321 / XSA-437. + +Reported-by: Julien Grall +Signed-off-by: Stefano Stabellini +Signed-off-by: Julien Grall +Acked-by: Bertrand Marquis + +--- + xen/arch/arm/include/asm/page.h | 33 ++++++++++++++++++++------------- + 1 file changed, 20 insertions(+), 13 deletions(-) + +diff --git a/xen/arch/arm/include/asm/page.h b/xen/arch/arm/include/asm/page.h +index e7cd62190c7f..d7fe770a5e49 100644 +--- a/xen/arch/arm/include/asm/page.h ++++ b/xen/arch/arm/include/asm/page.h +@@ -160,26 +160,25 @@ static inline size_t read_dcache_line_bytes(void) + + static inline int invalidate_dcache_va_range(const void *p, unsigned long size) + { +- const void *end = p + size; + size_t cacheline_mask = dcache_line_bytes - 1; + + dsb(sy); /* So the CPU issues all writes to the range */ + + if ( (uintptr_t)p & cacheline_mask ) + { ++ size -= dcache_line_bytes - ((uintptr_t)p & cacheline_mask); + p = (void *)((uintptr_t)p & ~cacheline_mask); + asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (p)); + p += dcache_line_bytes; + } +- if ( (uintptr_t)end & cacheline_mask ) +- { +- end = (void *)((uintptr_t)end & ~cacheline_mask); +- asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (end)); +- } + +- for ( ; p < end; p += dcache_line_bytes ) ++ for ( ; size >= dcache_line_bytes; ++ p += dcache_line_bytes, size -= dcache_line_bytes ) + asm volatile (__invalidate_dcache_one(0) : : "r" (p)); + ++ if ( size > 0 ) ++ asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (p)); ++ + dsb(sy); /* So we know the flushes happen before continuing */ + + return 0; +@@ -187,10 +186,14 @@ static inline int invalidate_dcache_va_range(const void *p, unsigned long size) + + static inline int clean_dcache_va_range(const void *p, unsigned long size) + { +- const void *end = p + size; ++ size_t cacheline_mask = dcache_line_bytes - 1; ++ + dsb(sy); /* So the CPU issues all writes to the range */ +- p = (void *)((uintptr_t)p & ~(dcache_line_bytes - 1)); +- for ( ; p < end; p += dcache_line_bytes ) ++ size += (uintptr_t)p & cacheline_mask; ++ size = (size + cacheline_mask) & ~cacheline_mask; ++ p = (void *)((uintptr_t)p & ~cacheline_mask); ++ for ( ; size >= dcache_line_bytes; ++ p += dcache_line_bytes, size -= dcache_line_bytes ) + asm volatile (__clean_dcache_one(0) : : "r" (p)); + dsb(sy); /* So we know the flushes happen before continuing */ + /* ARM callers assume that dcache_* functions cannot fail. */ +@@ -200,10 +203,14 @@ static inline int clean_dcache_va_range(const void *p, unsigned long size) + static inline int clean_and_invalidate_dcache_va_range + (const void *p, unsigned long size) + { +- const void *end = p + size; ++ size_t cacheline_mask = dcache_line_bytes - 1; ++ + dsb(sy); /* So the CPU issues all writes to the range */ +- p = (void *)((uintptr_t)p & ~(dcache_line_bytes - 1)); +- for ( ; p < end; p += dcache_line_bytes ) ++ size += (uintptr_t)p & cacheline_mask; ++ size = (size + cacheline_mask) & ~cacheline_mask; ++ p = (void *)((uintptr_t)p & ~cacheline_mask); ++ for ( ; size >= dcache_line_bytes; ++ p += dcache_line_bytes, size -= dcache_line_bytes ) + asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (p)); + dsb(sy); /* So we know the flushes happen before continuing */ + /* ARM callers assume that dcache_* functions cannot fail. */ +-- +2.40.1 + diff --git a/xsa438-4.17.patch b/xsa438-4.17.patch new file mode 100644 index 0000000..12d6ec7 --- /dev/null +++ b/xsa438-4.17.patch @@ -0,0 +1,416 @@ +From: Jan Beulich +Subject: x86/shadow: defer releasing of PV's top-level shadow reference + +sh_set_toplevel_shadow() re-pinning the top-level shadow we may be +running on is not enough (and at the same time unnecessary when the +shadow isn't what we're running on): That shadow becomes eligible for +blowing away (from e.g. shadow_prealloc()) immediately after the +paging lock was dropped. Yet it needs to remain valid until the actual +page table switch occurred. + +Propagate up the call chain the shadow entry that needs releasing +eventually, and carry out the release immediately after switching page +tables. Handle update_cr3() failures by switching to idle pagetables. +Note that various further uses of update_cr3() are HVM-only or only act +on paused vCPU-s, in which case sh_set_toplevel_shadow() will not defer +releasing of the reference. + +While changing the update_cr3() hook, also convert the "do_locking" +parameter to boolean. + +This is CVE-2023-34322 / XSA-438. + +Reported-by: Tim Deegan +Signed-off-by: Jan Beulich +Reviewed-by: George Dunlap + +--- a/xen/arch/x86/include/asm/mm.h ++++ b/xen/arch/x86/include/asm/mm.h +@@ -552,7 +552,7 @@ void audit_domains(void); + #endif + + void make_cr3(struct vcpu *v, mfn_t mfn); +-void update_cr3(struct vcpu *v); ++pagetable_t update_cr3(struct vcpu *v); + int vcpu_destroy_pagetables(struct vcpu *); + void *do_page_walk(struct vcpu *v, unsigned long addr); + +--- a/xen/arch/x86/include/asm/paging.h ++++ b/xen/arch/x86/include/asm/paging.h +@@ -138,7 +138,7 @@ struct paging_mode { + paddr_t ga, uint32_t *pfec, + unsigned int *page_order); + #endif +- void (*update_cr3 )(struct vcpu *v, int do_locking, ++ pagetable_t (*update_cr3 )(struct vcpu *v, bool do_locking, + bool noflush); + void (*update_paging_modes )(struct vcpu *v); + bool (*flush_tlb )(const unsigned long *vcpu_bitmap); +@@ -310,9 +310,9 @@ static inline unsigned long paging_ga_to + /* Update all the things that are derived from the guest's CR3. + * Called when the guest changes CR3; the caller can then use v->arch.cr3 + * as the value to load into the host CR3 to schedule this vcpu */ +-static inline void paging_update_cr3(struct vcpu *v, bool noflush) ++static inline pagetable_t paging_update_cr3(struct vcpu *v, bool noflush) + { +- paging_get_hostmode(v)->update_cr3(v, 1, noflush); ++ return paging_get_hostmode(v)->update_cr3(v, 1, noflush); + } + + /* Update all the things that are derived from the guest's CR0/CR3/CR4. +--- a/xen/arch/x86/include/asm/shadow.h ++++ b/xen/arch/x86/include/asm/shadow.h +@@ -99,6 +99,9 @@ int shadow_set_allocation(struct domain + + int shadow_get_allocation_bytes(struct domain *d, uint64_t *size); + ++/* Helper to invoke for deferred releasing of a top-level shadow's reference. */ ++void shadow_put_top_level(struct domain *d, pagetable_t old); ++ + #else /* !CONFIG_SHADOW_PAGING */ + + #define shadow_vcpu_teardown(v) ASSERT(is_pv_vcpu(v)) +@@ -121,6 +124,11 @@ static inline void shadow_prepare_page_t + + static inline void shadow_blow_tables_per_domain(struct domain *d) {} + ++static inline void shadow_put_top_level(struct domain *d, pagetable_t old) ++{ ++ ASSERT_UNREACHABLE(); ++} ++ + static inline int shadow_domctl(struct domain *d, + struct xen_domctl_shadow_op *sc, + XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) +--- a/xen/arch/x86/mm/hap/hap.c ++++ b/xen/arch/x86/mm/hap/hap.c +@@ -739,11 +739,13 @@ static bool cf_check hap_invlpg(struct v + return 1; + } + +-static void cf_check hap_update_cr3( +- struct vcpu *v, int do_locking, bool noflush) ++static pagetable_t cf_check hap_update_cr3( ++ struct vcpu *v, bool do_locking, bool noflush) + { + v->arch.hvm.hw_cr[3] = v->arch.hvm.guest_cr[3]; + hvm_update_guest_cr3(v, noflush); ++ ++ return pagetable_null(); + } + + static bool flush_vcpu(const struct vcpu *v, const unsigned long *vcpu_bitmap) +--- a/xen/arch/x86/mm/shadow/common.c ++++ b/xen/arch/x86/mm/shadow/common.c +@@ -2590,13 +2590,13 @@ void cf_check shadow_update_paging_modes + } + + /* Set up the top-level shadow and install it in slot 'slot' of shadow_table */ +-void sh_set_toplevel_shadow(struct vcpu *v, +- unsigned int slot, +- mfn_t gmfn, +- unsigned int root_type, +- mfn_t (*make_shadow)(struct vcpu *v, +- mfn_t gmfn, +- uint32_t shadow_type)) ++pagetable_t sh_set_toplevel_shadow(struct vcpu *v, ++ unsigned int slot, ++ mfn_t gmfn, ++ unsigned int root_type, ++ mfn_t (*make_shadow)(struct vcpu *v, ++ mfn_t gmfn, ++ uint32_t shadow_type)) + { + mfn_t smfn; + pagetable_t old_entry, new_entry; +@@ -2653,20 +2653,37 @@ void sh_set_toplevel_shadow(struct vcpu + mfn_x(gmfn), mfn_x(pagetable_get_mfn(new_entry))); + v->arch.paging.shadow.shadow_table[slot] = new_entry; + +- /* Decrement the refcount of the old contents of this slot */ +- if ( !pagetable_is_null(old_entry) ) ++ /* ++ * Decrement the refcount of the old contents of this slot, unless ++ * we're still running on that shadow - in that case it'll need holding ++ * on to until the actual page table switch did occur. ++ */ ++ if ( !pagetable_is_null(old_entry) && (v != current || !is_pv_domain(d)) ) + { +- mfn_t old_smfn = pagetable_get_mfn(old_entry); +- /* Need to repin the old toplevel shadow if it's been unpinned +- * by shadow_prealloc(): in PV mode we're still running on this +- * shadow and it's not safe to free it yet. */ +- if ( !mfn_to_page(old_smfn)->u.sh.pinned && !sh_pin(d, old_smfn) ) +- { +- printk(XENLOG_G_ERR "can't re-pin %"PRI_mfn"\n", mfn_x(old_smfn)); +- domain_crash(d); +- } +- sh_put_ref(d, old_smfn, 0); ++ sh_put_ref(d, pagetable_get_mfn(old_entry), 0); ++ old_entry = pagetable_null(); + } ++ ++ /* ++ * 2- and 3-level shadow mode is used for HVM only. Therefore we never run ++ * on such a shadow, so only call sites requesting an L4 shadow need to pay ++ * attention to the returned value. ++ */ ++ ASSERT(pagetable_is_null(old_entry) || root_type == SH_type_l4_64_shadow); ++ ++ return old_entry; ++} ++ ++/* ++ * Helper invoked when releasing of a top-level shadow's reference was ++ * deferred in sh_set_toplevel_shadow() above. ++ */ ++void shadow_put_top_level(struct domain *d, pagetable_t old_entry) ++{ ++ ASSERT(!pagetable_is_null(old_entry)); ++ paging_lock(d); ++ sh_put_ref(d, pagetable_get_mfn(old_entry), 0); ++ paging_unlock(d); + } + + /**************************************************************************/ +--- a/xen/arch/x86/mm/shadow/multi.c ++++ b/xen/arch/x86/mm/shadow/multi.c +@@ -3224,7 +3224,8 @@ static void cf_check sh_detach_old_table + } + } + +-static void cf_check sh_update_cr3(struct vcpu *v, int do_locking, bool noflush) ++static pagetable_t cf_check sh_update_cr3(struct vcpu *v, bool do_locking, ++ bool noflush) + /* Updates vcpu->arch.cr3 after the guest has changed CR3. + * Paravirtual guests should set v->arch.guest_table (and guest_table_user, + * if appropriate). +@@ -3238,6 +3239,7 @@ static void cf_check sh_update_cr3(struc + { + struct domain *d = v->domain; + mfn_t gmfn; ++ pagetable_t old_entry = pagetable_null(); + #if GUEST_PAGING_LEVELS == 3 + const guest_l3e_t *gl3e; + unsigned int i, guest_idx; +@@ -3247,7 +3249,7 @@ static void cf_check sh_update_cr3(struc + if ( !is_hvm_domain(d) && !v->is_initialised ) + { + ASSERT(v->arch.cr3 == 0); +- return; ++ return old_entry; + } + + if ( do_locking ) paging_lock(v->domain); +@@ -3320,11 +3322,12 @@ static void cf_check sh_update_cr3(struc + #if GUEST_PAGING_LEVELS == 4 + if ( sh_remove_write_access(d, gmfn, 4, 0) != 0 ) + guest_flush_tlb_mask(d, d->dirty_cpumask); +- sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l4_shadow, sh_make_shadow); ++ old_entry = sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l4_shadow, ++ sh_make_shadow); + if ( unlikely(pagetable_is_null(v->arch.paging.shadow.shadow_table[0])) ) + { + ASSERT(d->is_dying || d->is_shutting_down); +- return; ++ return old_entry; + } + if ( !shadow_mode_external(d) && !is_pv_32bit_domain(d) ) + { +@@ -3368,24 +3371,30 @@ static void cf_check sh_update_cr3(struc + gl2gfn = guest_l3e_get_gfn(gl3e[i]); + gl2mfn = get_gfn_query_unlocked(d, gfn_x(gl2gfn), &p2mt); + if ( p2m_is_ram(p2mt) ) +- sh_set_toplevel_shadow(v, i, gl2mfn, SH_type_l2_shadow, +- sh_make_shadow); ++ old_entry = sh_set_toplevel_shadow(v, i, gl2mfn, ++ SH_type_l2_shadow, ++ sh_make_shadow); + else +- sh_set_toplevel_shadow(v, i, INVALID_MFN, 0, +- sh_make_shadow); ++ old_entry = sh_set_toplevel_shadow(v, i, INVALID_MFN, 0, ++ sh_make_shadow); + } + else +- sh_set_toplevel_shadow(v, i, INVALID_MFN, 0, sh_make_shadow); ++ old_entry = sh_set_toplevel_shadow(v, i, INVALID_MFN, 0, ++ sh_make_shadow); ++ ++ ASSERT(pagetable_is_null(old_entry)); + } + } + #elif GUEST_PAGING_LEVELS == 2 + if ( sh_remove_write_access(d, gmfn, 2, 0) != 0 ) + guest_flush_tlb_mask(d, d->dirty_cpumask); +- sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l2_shadow, sh_make_shadow); ++ old_entry = sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l2_shadow, ++ sh_make_shadow); ++ ASSERT(pagetable_is_null(old_entry)); + if ( unlikely(pagetable_is_null(v->arch.paging.shadow.shadow_table[0])) ) + { + ASSERT(d->is_dying || d->is_shutting_down); +- return; ++ return old_entry; + } + #else + #error This should never happen +@@ -3473,6 +3482,8 @@ static void cf_check sh_update_cr3(struc + + /* Release the lock, if we took it (otherwise it's the caller's problem) */ + if ( do_locking ) paging_unlock(v->domain); ++ ++ return old_entry; + } + + +--- a/xen/arch/x86/mm/shadow/none.c ++++ b/xen/arch/x86/mm/shadow/none.c +@@ -52,9 +52,11 @@ static unsigned long cf_check _gva_to_gf + } + #endif + +-static void cf_check _update_cr3(struct vcpu *v, int do_locking, bool noflush) ++static pagetable_t cf_check _update_cr3(struct vcpu *v, bool do_locking, ++ bool noflush) + { + ASSERT_UNREACHABLE(); ++ return pagetable_null(); + } + + static void cf_check _update_paging_modes(struct vcpu *v) +--- a/xen/arch/x86/mm/shadow/private.h ++++ b/xen/arch/x86/mm/shadow/private.h +@@ -391,13 +391,13 @@ mfn_t shadow_alloc(struct domain *d, + void shadow_free(struct domain *d, mfn_t smfn); + + /* Set up the top-level shadow and install it in slot 'slot' of shadow_table */ +-void sh_set_toplevel_shadow(struct vcpu *v, +- unsigned int slot, +- mfn_t gmfn, +- unsigned int root_type, +- mfn_t (*make_shadow)(struct vcpu *v, +- mfn_t gmfn, +- uint32_t shadow_type)); ++pagetable_t sh_set_toplevel_shadow(struct vcpu *v, ++ unsigned int slot, ++ mfn_t gmfn, ++ unsigned int root_type, ++ mfn_t (*make_shadow)(struct vcpu *v, ++ mfn_t gmfn, ++ uint32_t shadow_type)); + + /* Update the shadows in response to a pagetable write from Xen */ + int sh_validate_guest_entry(struct vcpu *v, mfn_t gmfn, void *entry, u32 size); +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -567,15 +567,12 @@ void write_ptbase(struct vcpu *v) + * + * Update ref counts to shadow tables appropriately. + */ +-void update_cr3(struct vcpu *v) ++pagetable_t update_cr3(struct vcpu *v) + { + mfn_t cr3_mfn; + + if ( paging_mode_enabled(v->domain) ) +- { +- paging_update_cr3(v, false); +- return; +- } ++ return paging_update_cr3(v, false); + + if ( !(v->arch.flags & TF_kernel_mode) ) + cr3_mfn = pagetable_get_mfn(v->arch.guest_table_user); +@@ -583,6 +580,8 @@ void update_cr3(struct vcpu *v) + cr3_mfn = pagetable_get_mfn(v->arch.guest_table); + + make_cr3(v, cr3_mfn); ++ ++ return pagetable_null(); + } + + static inline void set_tlbflush_timestamp(struct page_info *page) +@@ -3285,6 +3284,7 @@ int new_guest_cr3(mfn_t mfn) + struct domain *d = curr->domain; + int rc; + mfn_t old_base_mfn; ++ pagetable_t old_shadow; + + if ( is_pv_32bit_domain(d) ) + { +@@ -3352,9 +3352,22 @@ int new_guest_cr3(mfn_t mfn) + if ( !VM_ASSIST(d, m2p_strict) ) + fill_ro_mpt(mfn); + curr->arch.guest_table = pagetable_from_mfn(mfn); +- update_cr3(curr); ++ old_shadow = update_cr3(curr); ++ ++ /* ++ * In shadow mode update_cr3() can fail, in which case here we're still ++ * running on the prior top-level shadow (which we're about to release). ++ * Switch to the idle page tables in such an event; the guest will have ++ * been crashed already. ++ */ ++ if ( likely(!mfn_eq(pagetable_get_mfn(old_shadow), ++ maddr_to_mfn(curr->arch.cr3 & ~X86_CR3_NOFLUSH))) ) ++ write_ptbase(curr); ++ else ++ write_ptbase(idle_vcpu[curr->processor]); + +- write_ptbase(curr); ++ if ( !pagetable_is_null(old_shadow) ) ++ shadow_put_top_level(d, old_shadow); + + if ( likely(mfn_x(old_base_mfn) != 0) ) + { +--- a/xen/arch/x86/pv/domain.c ++++ b/xen/arch/x86/pv/domain.c +@@ -424,10 +424,13 @@ bool __init xpti_pcid_enabled(void) + + static void _toggle_guest_pt(struct vcpu *v) + { ++ bool guest_update; ++ pagetable_t old_shadow; + unsigned long cr3; + + v->arch.flags ^= TF_kernel_mode; +- update_cr3(v); ++ guest_update = v->arch.flags & TF_kernel_mode; ++ old_shadow = update_cr3(v); + + /* + * Don't flush user global mappings from the TLB. Don't tick TLB clock. +@@ -436,13 +439,31 @@ static void _toggle_guest_pt(struct vcpu + * TLB flush (for just the incoming PCID), as the top level page table may + * have changed behind our backs. To be on the safe side, suppress the + * no-flush unconditionally in this case. ++ * ++ * Furthermore in shadow mode update_cr3() can fail, in which case here ++ * we're still running on the prior top-level shadow (which we're about ++ * to release). Switch to the idle page tables in such an event; the ++ * guest will have been crashed already. + */ + cr3 = v->arch.cr3; + if ( shadow_mode_enabled(v->domain) ) ++ { + cr3 &= ~X86_CR3_NOFLUSH; ++ ++ if ( unlikely(mfn_eq(pagetable_get_mfn(old_shadow), ++ maddr_to_mfn(cr3))) ) ++ { ++ cr3 = idle_vcpu[v->processor]->arch.cr3; ++ /* Also suppress runstate/time area updates below. */ ++ guest_update = false; ++ } ++ } + write_cr3(cr3); + +- if ( !(v->arch.flags & TF_kernel_mode) ) ++ if ( !pagetable_is_null(old_shadow) ) ++ shadow_put_top_level(v->domain, old_shadow); ++ ++ if ( !guest_update ) + return; + + if ( v->arch.pv.need_update_runstate_area && update_runstate_area(v) ) diff --git a/xsa439-0001-x86-AMD-extend-Zenbleed-check-to-models-good-ucode-i.patch b/xsa439-0001-x86-AMD-extend-Zenbleed-check-to-models-good-ucode-i.patch new file mode 100644 index 0000000..96e56ec --- /dev/null +++ b/xsa439-0001-x86-AMD-extend-Zenbleed-check-to-models-good-ucode-i.patch @@ -0,0 +1,49 @@ +From d2d2dcae879c6cc05227c9620f0a772f35fe6886 Mon Sep 17 00:00:00 2001 +Message-ID: +From: Jan Beulich +Date: Wed, 23 Aug 2023 09:26:36 +0200 +Subject: [XEN PATCH 01/10] x86/AMD: extend Zenbleed check to models "good" + ucode isn't known for + +Reportedly the AMD Custom APU 0405 found on SteamDeck, models 0x90 and +0x91, (quoting the respective Linux commit) is similarly affected. Put +another instance of our Zen1 vs Zen2 distinction checks in +amd_check_zenbleed(), forcing use of the chickenbit irrespective of +ucode version (building upon real hardware never surfacing a version of +0xffffffff). + +Signed-off-by: Jan Beulich +Reviewed-by: Andrew Cooper +(cherry picked from commit 145a69c0944ac70cfcf9d247c85dee9e99d9d302) +--- + xen/arch/x86/cpu/amd.c | 13 ++++++++++--- + 1 file changed, 10 insertions(+), 3 deletions(-) + +diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c +index 3ea214fc2e..1bb3044be1 100644 +--- a/xen/arch/x86/cpu/amd.c ++++ b/xen/arch/x86/cpu/amd.c +@@ -909,10 +909,17 @@ void amd_check_zenbleed(void) + case 0xa0 ... 0xaf: good_rev = 0x08a00008; break; + default: + /* +- * With the Fam17h check above, parts getting here are Zen1. +- * They're not affected. ++ * With the Fam17h check above, most parts getting here are ++ * Zen1. They're not affected. Assume Zen2 ones making it ++ * here are affected regardless of microcode version. ++ * ++ * Zen1 vs Zen2 isn't a simple model number comparison, so use ++ * STIBP as a heuristic to distinguish. + */ +- return; ++ if (!boot_cpu_has(X86_FEATURE_AMD_STIBP)) ++ return; ++ good_rev = ~0U; ++ break; + } + + rdmsrl(MSR_AMD64_DE_CFG, val); +-- +2.41.0 + diff --git a/xsa439-0002-x86-spec-ctrl-Fix-confusion-between-SPEC_CTRL_EXIT_T.patch b/xsa439-0002-x86-spec-ctrl-Fix-confusion-between-SPEC_CTRL_EXIT_T.patch new file mode 100644 index 0000000..8b8e30a --- /dev/null +++ b/xsa439-0002-x86-spec-ctrl-Fix-confusion-between-SPEC_CTRL_EXIT_T.patch @@ -0,0 +1,77 @@ +From dc28aba565f226f9bec24cfde993e78478acfb4e Mon Sep 17 00:00:00 2001 +Message-ID: +In-Reply-To: +References: +From: Andrew Cooper +Date: Tue, 12 Sep 2023 15:06:49 +0100 +Subject: [XEN PATCH 02/10] x86/spec-ctrl: Fix confusion between + SPEC_CTRL_EXIT_TO_XEN{,_IST} + +c/s 3fffaf9c13e9 ("x86/entry: Avoid using alternatives in NMI/#MC paths") +dropped the only user, leaving behind the (incorrect) implication that Xen had +split exit paths. + +Delete the unused SPEC_CTRL_EXIT_TO_XEN and rename SPEC_CTRL_EXIT_TO_XEN_IST +to SPEC_CTRL_EXIT_TO_XEN for consistency. + +No functional change. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 1c18d73774533a55ba9d1cbee8bdace03efdb5e7) +--- + xen/arch/x86/include/asm/spec_ctrl_asm.h | 10 ++-------- + xen/arch/x86/x86_64/entry.S | 2 +- + 2 files changed, 3 insertions(+), 9 deletions(-) + +diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h +index f23bb105c5..e8fd01243c 100644 +--- a/xen/arch/x86/include/asm/spec_ctrl_asm.h ++++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h +@@ -79,7 +79,6 @@ + * - SPEC_CTRL_ENTRY_FROM_PV + * - SPEC_CTRL_ENTRY_FROM_INTR + * - SPEC_CTRL_ENTRY_FROM_INTR_IST +- * - SPEC_CTRL_EXIT_TO_XEN_IST + * - SPEC_CTRL_EXIT_TO_XEN + * - SPEC_CTRL_EXIT_TO_PV + * +@@ -268,11 +267,6 @@ + ALTERNATIVE "", __stringify(DO_SPEC_CTRL_ENTRY maybexen=1), \ + X86_FEATURE_SC_MSR_PV + +-/* Use when exiting to Xen context. */ +-#define SPEC_CTRL_EXIT_TO_XEN \ +- ALTERNATIVE "", \ +- DO_SPEC_CTRL_EXIT_TO_XEN, X86_FEATURE_SC_MSR_PV +- + /* Use when exiting to PV guest context. */ + #define SPEC_CTRL_EXIT_TO_PV \ + ALTERNATIVE "", \ +@@ -339,8 +333,8 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): + UNLIKELY_END(\@_serialise) + .endm + +-/* Use when exiting to Xen in IST context. */ +-.macro SPEC_CTRL_EXIT_TO_XEN_IST ++/* Use when exiting to Xen context. */ ++.macro SPEC_CTRL_EXIT_TO_XEN + /* + * Requires %rbx=stack_end + * Clobbers %rax, %rcx, %rdx +diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S +index 7675a59ff0..b45a09823a 100644 +--- a/xen/arch/x86/x86_64/entry.S ++++ b/xen/arch/x86/x86_64/entry.S +@@ -673,7 +673,7 @@ UNLIKELY_START(ne, exit_cr3) + UNLIKELY_END(exit_cr3) + + /* WARNING! `ret`, `call *`, `jmp *` not safe beyond this point. */ +- SPEC_CTRL_EXIT_TO_XEN_IST /* Req: %rbx=end, Clob: acd */ ++ SPEC_CTRL_EXIT_TO_XEN /* Req: %rbx=end, Clob: acd */ + + RESTORE_ALL adj=8 + iretq +-- +2.41.0 + diff --git a/xsa439-0003-x86-spec-ctrl-Fold-DO_SPEC_CTRL_EXIT_TO_XEN-into-it-.patch b/xsa439-0003-x86-spec-ctrl-Fold-DO_SPEC_CTRL_EXIT_TO_XEN-into-it-.patch new file mode 100644 index 0000000..547032e --- /dev/null +++ b/xsa439-0003-x86-spec-ctrl-Fold-DO_SPEC_CTRL_EXIT_TO_XEN-into-it-.patch @@ -0,0 +1,88 @@ +From 84690fb82c4f4aecb72a6789d8994efa74841e09 Mon Sep 17 00:00:00 2001 +Message-ID: <84690fb82c4f4aecb72a6789d8994efa74841e09.1695733540.git.m.a.young@durham.ac.uk> +In-Reply-To: +References: +From: Andrew Cooper +Date: Tue, 12 Sep 2023 17:03:16 +0100 +Subject: [XEN PATCH 03/10] x86/spec-ctrl: Fold DO_SPEC_CTRL_EXIT_TO_XEN into + it's single user + +With the SPEC_CTRL_EXIT_TO_XEN{,_IST} confusion fixed, it's now obvious that +there's only a single EXIT_TO_XEN path. Fold DO_SPEC_CTRL_EXIT_TO_XEN into +SPEC_CTRL_EXIT_TO_XEN to simplify further fixes. + +When merging labels, switch the name to .L\@_skip_sc_msr as "skip" on its own +is going to be too generic shortly. + +No functional change. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 694bb0f280fd08a4377e36e32b84b5062def4de2) +--- + xen/arch/x86/include/asm/spec_ctrl_asm.h | 40 ++++++++++-------------- + 1 file changed, 16 insertions(+), 24 deletions(-) + +diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h +index e8fd01243c..d5f65d80ea 100644 +--- a/xen/arch/x86/include/asm/spec_ctrl_asm.h ++++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h +@@ -211,27 +211,6 @@ + wrmsr + .endm + +-.macro DO_SPEC_CTRL_EXIT_TO_XEN +-/* +- * Requires %rbx=stack_end +- * Clobbers %rax, %rcx, %rdx +- * +- * When returning to Xen context, look to see whether SPEC_CTRL shadowing is +- * in effect, and reload the shadow value. This covers race conditions which +- * exist with an NMI/MCE/etc hitting late in the return-to-guest path. +- */ +- xor %edx, %edx +- +- testb $SCF_use_shadow, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%rbx) +- jz .L\@_skip +- +- mov STACK_CPUINFO_FIELD(shadow_spec_ctrl)(%rbx), %eax +- mov $MSR_SPEC_CTRL, %ecx +- wrmsr +- +-.L\@_skip: +-.endm +- + .macro DO_SPEC_CTRL_EXIT_TO_GUEST + /* + * Requires %eax=spec_ctrl, %rsp=regs/cpuinfo +@@ -340,11 +319,24 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): + * Clobbers %rax, %rcx, %rdx + */ + testb $SCF_ist_sc_msr, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%rbx) +- jz .L\@_skip ++ jz .L\@_skip_sc_msr + +- DO_SPEC_CTRL_EXIT_TO_XEN ++ /* ++ * When returning to Xen context, look to see whether SPEC_CTRL shadowing ++ * is in effect, and reload the shadow value. This covers race conditions ++ * which exist with an NMI/MCE/etc hitting late in the return-to-guest ++ * path. ++ */ ++ xor %edx, %edx + +-.L\@_skip: ++ testb $SCF_use_shadow, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%rbx) ++ jz .L\@_skip_sc_msr ++ ++ mov STACK_CPUINFO_FIELD(shadow_spec_ctrl)(%rbx), %eax ++ mov $MSR_SPEC_CTRL, %ecx ++ wrmsr ++ ++.L\@_skip_sc_msr: + .endm + + #endif /* __ASSEMBLY__ */ +-- +2.41.0 + diff --git a/xsa439-0004-x86-spec-ctrl-Turn-the-remaining-SPEC_CTRL_-ENTRY-EX.patch b/xsa439-0004-x86-spec-ctrl-Turn-the-remaining-SPEC_CTRL_-ENTRY-EX.patch new file mode 100644 index 0000000..3350750 --- /dev/null +++ b/xsa439-0004-x86-spec-ctrl-Turn-the-remaining-SPEC_CTRL_-ENTRY-EX.patch @@ -0,0 +1,86 @@ +From 3952c73bdbd05f0e666986fce633a591237b3c88 Mon Sep 17 00:00:00 2001 +Message-ID: <3952c73bdbd05f0e666986fce633a591237b3c88.1695733540.git.m.a.young@durham.ac.uk> +In-Reply-To: +References: +From: Andrew Cooper +Date: Fri, 1 Sep 2023 11:38:44 +0100 +Subject: [XEN PATCH 04/10] x86/spec-ctrl: Turn the remaining + SPEC_CTRL_{ENTRY,EXIT}_* into asm macros + +These have grown more complex over time, with some already having been +converted. + +Provide full Requires/Clobbers comments, otherwise missing at this level of +indirection. + +No functional change. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 7125429aafb9e3c9c88fc93001fc2300e0ac2cc8) +--- + xen/arch/x86/include/asm/spec_ctrl_asm.h | 37 ++++++++++++++++++------ + 1 file changed, 28 insertions(+), 9 deletions(-) + +diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h +index d5f65d80ea..c6d5f2ad01 100644 +--- a/xen/arch/x86/include/asm/spec_ctrl_asm.h ++++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h +@@ -231,26 +231,45 @@ + .endm + + /* Use after an entry from PV context (syscall/sysenter/int80/int82/etc). */ +-#define SPEC_CTRL_ENTRY_FROM_PV \ ++.macro SPEC_CTRL_ENTRY_FROM_PV ++/* ++ * Requires %rsp=regs/cpuinfo, %rdx=0 ++ * Clobbers %rax, %rcx, %rdx ++ */ + ALTERNATIVE "", __stringify(DO_SPEC_CTRL_COND_IBPB maybexen=0), \ +- X86_FEATURE_IBPB_ENTRY_PV; \ +- ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_PV; \ ++ X86_FEATURE_IBPB_ENTRY_PV ++ ++ ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_PV ++ + ALTERNATIVE "", __stringify(DO_SPEC_CTRL_ENTRY maybexen=0), \ + X86_FEATURE_SC_MSR_PV ++.endm + + /* Use in interrupt/exception context. May interrupt Xen or PV context. */ +-#define SPEC_CTRL_ENTRY_FROM_INTR \ ++.macro SPEC_CTRL_ENTRY_FROM_INTR ++/* ++ * Requires %rsp=regs, %r14=stack_end, %rdx=0 ++ * Clobbers %rax, %rcx, %rdx ++ */ + ALTERNATIVE "", __stringify(DO_SPEC_CTRL_COND_IBPB maybexen=1), \ +- X86_FEATURE_IBPB_ENTRY_PV; \ +- ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_PV; \ ++ X86_FEATURE_IBPB_ENTRY_PV ++ ++ ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_PV ++ + ALTERNATIVE "", __stringify(DO_SPEC_CTRL_ENTRY maybexen=1), \ + X86_FEATURE_SC_MSR_PV ++.endm + + /* Use when exiting to PV guest context. */ +-#define SPEC_CTRL_EXIT_TO_PV \ +- ALTERNATIVE "", \ +- DO_SPEC_CTRL_EXIT_TO_GUEST, X86_FEATURE_SC_MSR_PV; \ ++.macro SPEC_CTRL_EXIT_TO_PV ++/* ++ * Requires %rax=spec_ctrl, %rsp=regs/info ++ * Clobbers %rcx, %rdx ++ */ ++ ALTERNATIVE "", DO_SPEC_CTRL_EXIT_TO_GUEST, X86_FEATURE_SC_MSR_PV ++ + DO_SPEC_CTRL_COND_VERW ++.endm + + /* + * Use in IST interrupt/exception context. May interrupt Xen or PV context. +-- +2.41.0 + diff --git a/xsa439-0005-x86-spec-ctrl-Improve-all-SPEC_CTRL_-ENTER-EXIT-_-co.patch b/xsa439-0005-x86-spec-ctrl-Improve-all-SPEC_CTRL_-ENTER-EXIT-_-co.patch new file mode 100644 index 0000000..dda088a --- /dev/null +++ b/xsa439-0005-x86-spec-ctrl-Improve-all-SPEC_CTRL_-ENTER-EXIT-_-co.patch @@ -0,0 +1,109 @@ +From ba023e93d0b1e60b80251bf080bab694efb9f8e3 Mon Sep 17 00:00:00 2001 +Message-ID: +In-Reply-To: +References: +From: Andrew Cooper +Date: Wed, 30 Aug 2023 20:11:50 +0100 +Subject: [XEN PATCH 05/10] x86/spec-ctrl: Improve all SPEC_CTRL_{ENTER,EXIT}_* + comments + +... to better explain how they're used. + +Doing so highlights that SPEC_CTRL_EXIT_TO_XEN is missing a VERW flush for the +corner case when e.g. an NMI hits late in an exit-to-guest path. + +Leave a TODO, which will be addressed in subsequent patches which arrange for +VERW flushing to be safe within SPEC_CTRL_EXIT_TO_XEN. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 45f00557350dc7d0756551069803fc49c29184ca) +--- + xen/arch/x86/include/asm/spec_ctrl_asm.h | 36 ++++++++++++++++++++---- + 1 file changed, 31 insertions(+), 5 deletions(-) + +diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h +index c6d5f2ad01..97c4db31cd 100644 +--- a/xen/arch/x86/include/asm/spec_ctrl_asm.h ++++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h +@@ -230,7 +230,10 @@ + wrmsr + .endm + +-/* Use after an entry from PV context (syscall/sysenter/int80/int82/etc). */ ++/* ++ * Used after an entry from PV context: SYSCALL, SYSENTER, INT, ++ * etc. There is always a guest speculation state in context. ++ */ + .macro SPEC_CTRL_ENTRY_FROM_PV + /* + * Requires %rsp=regs/cpuinfo, %rdx=0 +@@ -245,7 +248,11 @@ + X86_FEATURE_SC_MSR_PV + .endm + +-/* Use in interrupt/exception context. May interrupt Xen or PV context. */ ++/* ++ * Used after an exception or maskable interrupt, hitting Xen or PV context. ++ * There will either be a guest speculation context, or (barring fatal ++ * exceptions) a well-formed Xen speculation context. ++ */ + .macro SPEC_CTRL_ENTRY_FROM_INTR + /* + * Requires %rsp=regs, %r14=stack_end, %rdx=0 +@@ -260,7 +267,10 @@ + X86_FEATURE_SC_MSR_PV + .endm + +-/* Use when exiting to PV guest context. */ ++/* ++ * Used when exiting from any entry context, back to PV context. This ++ * includes from an IST entry which moved onto the primary stack. ++ */ + .macro SPEC_CTRL_EXIT_TO_PV + /* + * Requires %rax=spec_ctrl, %rsp=regs/info +@@ -272,7 +282,13 @@ + .endm + + /* +- * Use in IST interrupt/exception context. May interrupt Xen or PV context. ++ * Used after an IST entry hitting Xen or PV context. Special care is needed, ++ * because when hitting Xen context, there may not be a well-formed ++ * speculation context. (i.e. it can hit in the middle of ++ * SPEC_CTRL_{ENTRY,EXIT}_* regions.) ++ * ++ * An IST entry which hits PV context moves onto the primary stack and leaves ++ * via SPEC_CTRL_EXIT_TO_PV, *not* SPEC_CTRL_EXIT_TO_XEN. + */ + .macro SPEC_CTRL_ENTRY_FROM_INTR_IST + /* +@@ -331,7 +347,14 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): + UNLIKELY_END(\@_serialise) + .endm + +-/* Use when exiting to Xen context. */ ++/* ++ * Use when exiting from any entry context, back to Xen context. This ++ * includes returning to other SPEC_CTRL_{ENTRY,EXIT}_* regions with an ++ * incomplete speculation context. ++ * ++ * Because we might have interrupted Xen beyond SPEC_CTRL_EXIT_TO_$GUEST, we ++ * need to treat this as if it were an EXIT_TO_$GUEST case too. ++ */ + .macro SPEC_CTRL_EXIT_TO_XEN + /* + * Requires %rbx=stack_end +@@ -356,6 +379,9 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): + wrmsr + + .L\@_skip_sc_msr: ++ ++ /* TODO VERW */ ++ + .endm + + #endif /* __ASSEMBLY__ */ +-- +2.41.0 + diff --git a/xsa439-0006-x86-entry-Adjust-restore_all_xen-to-hold-stack_end-i.patch b/xsa439-0006-x86-entry-Adjust-restore_all_xen-to-hold-stack_end-i.patch new file mode 100644 index 0000000..e44998e --- /dev/null +++ b/xsa439-0006-x86-entry-Adjust-restore_all_xen-to-hold-stack_end-i.patch @@ -0,0 +1,77 @@ +From 5f7efd47c8273fde972637d0360851802f76eca9 Mon Sep 17 00:00:00 2001 +Message-ID: <5f7efd47c8273fde972637d0360851802f76eca9.1695733540.git.m.a.young@durham.ac.uk> +In-Reply-To: +References: +From: Andrew Cooper +Date: Wed, 13 Sep 2023 13:48:16 +0100 +Subject: [XEN PATCH 06/10] x86/entry: Adjust restore_all_xen to hold stack_end + in %r14 + +All other SPEC_CTRL_{ENTRY,EXIT}_* helpers hold stack_end in %r14. Adjust it +for consistency. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 7aa28849a1155d856e214e9a80a7e65fffdc3e58) +--- + xen/arch/x86/include/asm/spec_ctrl_asm.h | 8 ++++---- + xen/arch/x86/x86_64/entry.S | 8 ++++---- + 2 files changed, 8 insertions(+), 8 deletions(-) + +diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h +index 97c4db31cd..66c706496f 100644 +--- a/xen/arch/x86/include/asm/spec_ctrl_asm.h ++++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h +@@ -357,10 +357,10 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): + */ + .macro SPEC_CTRL_EXIT_TO_XEN + /* +- * Requires %rbx=stack_end ++ * Requires %r14=stack_end + * Clobbers %rax, %rcx, %rdx + */ +- testb $SCF_ist_sc_msr, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%rbx) ++ testb $SCF_ist_sc_msr, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14) + jz .L\@_skip_sc_msr + + /* +@@ -371,10 +371,10 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): + */ + xor %edx, %edx + +- testb $SCF_use_shadow, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%rbx) ++ testb $SCF_use_shadow, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14) + jz .L\@_skip_sc_msr + +- mov STACK_CPUINFO_FIELD(shadow_spec_ctrl)(%rbx), %eax ++ mov STACK_CPUINFO_FIELD(shadow_spec_ctrl)(%r14), %eax + mov $MSR_SPEC_CTRL, %ecx + wrmsr + +diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S +index b45a09823a..92279a225d 100644 +--- a/xen/arch/x86/x86_64/entry.S ++++ b/xen/arch/x86/x86_64/entry.S +@@ -665,15 +665,15 @@ restore_all_xen: + * Check whether we need to switch to the per-CPU page tables, in + * case we return to late PV exit code (from an NMI or #MC). + */ +- GET_STACK_END(bx) +- cmpb $0, STACK_CPUINFO_FIELD(use_pv_cr3)(%rbx) ++ GET_STACK_END(14) ++ cmpb $0, STACK_CPUINFO_FIELD(use_pv_cr3)(%r14) + UNLIKELY_START(ne, exit_cr3) +- mov STACK_CPUINFO_FIELD(pv_cr3)(%rbx), %rax ++ mov STACK_CPUINFO_FIELD(pv_cr3)(%r14), %rax + mov %rax, %cr3 + UNLIKELY_END(exit_cr3) + + /* WARNING! `ret`, `call *`, `jmp *` not safe beyond this point. */ +- SPEC_CTRL_EXIT_TO_XEN /* Req: %rbx=end, Clob: acd */ ++ SPEC_CTRL_EXIT_TO_XEN /* Req: %r14=end, Clob: acd */ + + RESTORE_ALL adj=8 + iretq +-- +2.41.0 + diff --git a/xsa439-0007-x86-entry-Track-the-IST-ness-of-an-entry-for-the-exi.patch b/xsa439-0007-x86-entry-Track-the-IST-ness-of-an-entry-for-the-exi.patch new file mode 100644 index 0000000..2e36bcc --- /dev/null +++ b/xsa439-0007-x86-entry-Track-the-IST-ness-of-an-entry-for-the-exi.patch @@ -0,0 +1,112 @@ +From e4a71bc0da0baf7464bb0d8e33053f330e5ea366 Mon Sep 17 00:00:00 2001 +Message-ID: +In-Reply-To: +References: +From: Andrew Cooper +Date: Wed, 13 Sep 2023 12:20:12 +0100 +Subject: [XEN PATCH 07/10] x86/entry: Track the IST-ness of an entry for the + exit paths + +Use %r12 to hold an ist_exit boolean. This register is zero elsewhere in the +entry/exit asm, so it only needs setting in the IST path. + +As this is subtle and fragile, add check_ist_exit() to be used in debugging +builds to cross-check that the ist_exit boolean matches the entry vector. + +Write check_ist_exit() it in C, because it's debug only and the logic more +complicated than I care to maintain in asm. + +For now, we only need to use this signal in the exit-to-Xen path, but some +exit-to-guest paths happen in IST context too. Check the correctness in all +exit paths to avoid the logic bit-rotting. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 21bdc25b05a0f8ab6bc73520a9ca01327360732c) + +x86/entry: Partially revert IST-exit checks + +The patch adding check_ist_exit() didn't account for the fact that +reset_stack_and_jump() is not an ABI-preserving boundary. The IST-ness in +%r12 doesn't survive into the next context, and is a stale value C. + +This shows up in Gitlab CI for the Clang build: + + https://gitlab.com/xen-project/people/andyhhp/xen/-/jobs/5112783827 + +and in OSSTest for GCC 8: + + http://logs.test-lab.xenproject.org/osstest/logs/183045/test-amd64-amd64-xl-qemuu-debianhvm-amd64/serial-pinot0.log + +There's no straightforward way to reconstruct the IST-exit-ness on the +exit-to-guest path after a context switch. For now, we only need IST-exit on +the return-to-Xen path. + +Fixes: 21bdc25b05a0 ("x86/entry: Track the IST-ness of an entry for the exit paths") +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 9b57c800b79b96769ea3dcd6468578fa664d19f9) +--- + xen/arch/x86/traps.c | 13 +++++++++++++ + xen/arch/x86/x86_64/entry.S | 13 ++++++++++++- + 2 files changed, 25 insertions(+), 1 deletion(-) + +diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c +index d12004b1c6..e65cc60041 100644 +--- a/xen/arch/x86/traps.c ++++ b/xen/arch/x86/traps.c +@@ -2315,6 +2315,19 @@ void asm_domain_crash_synchronous(unsigned long addr) + do_softirq(); + } + ++#ifdef CONFIG_DEBUG ++void check_ist_exit(const struct cpu_user_regs *regs, bool ist_exit) ++{ ++ const unsigned int ist_mask = ++ (1U << X86_EXC_NMI) | (1U << X86_EXC_DB) | ++ (1U << X86_EXC_DF) | (1U << X86_EXC_MC); ++ uint8_t ev = regs->entry_vector; ++ bool is_ist = (ev < TRAP_nr) && ((1U << ev) & ist_mask); ++ ++ ASSERT(is_ist == ist_exit); ++} ++#endif ++ + /* + * Local variables: + * mode: C +diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S +index 92279a225d..4cebc4fbe3 100644 +--- a/xen/arch/x86/x86_64/entry.S ++++ b/xen/arch/x86/x86_64/entry.S +@@ -659,8 +659,15 @@ ENTRY(early_page_fault) + .section .text.entry, "ax", @progbits + + ALIGN +-/* No special register assumptions. */ ++/* %r12=ist_exit */ + restore_all_xen: ++ ++#ifdef CONFIG_DEBUG ++ mov %rsp, %rdi ++ mov %r12, %rsi ++ call check_ist_exit ++#endif ++ + /* + * Check whether we need to switch to the per-CPU page tables, in + * case we return to late PV exit code (from an NMI or #MC). +@@ -1091,6 +1098,10 @@ handle_ist_exception: + .L_ist_dispatch_done: + mov %r15, STACK_CPUINFO_FIELD(xen_cr3)(%r14) + mov %bl, STACK_CPUINFO_FIELD(use_pv_cr3)(%r14) ++ ++ /* This is an IST exit */ ++ mov $1, %r12d ++ + cmpb $TRAP_nmi,UREGS_entry_vector(%rsp) + jne ret_from_intr + +-- +2.41.0 + diff --git a/xsa439-0008-x86-spec-ctrl-Issue-VERW-during-IST-exit-to-Xen.patch b/xsa439-0008-x86-spec-ctrl-Issue-VERW-during-IST-exit-to-Xen.patch new file mode 100644 index 0000000..6e00ca6 --- /dev/null +++ b/xsa439-0008-x86-spec-ctrl-Issue-VERW-during-IST-exit-to-Xen.patch @@ -0,0 +1,92 @@ +From 2e2c3efcfc9f183674a8de6ed954ffbe7188b70d Mon Sep 17 00:00:00 2001 +Message-ID: <2e2c3efcfc9f183674a8de6ed954ffbe7188b70d.1695733540.git.m.a.young@durham.ac.uk> +In-Reply-To: +References: +From: Andrew Cooper +Date: Wed, 13 Sep 2023 13:53:33 +0100 +Subject: [XEN PATCH 08/10] x86/spec-ctrl: Issue VERW during IST exit to Xen + +There is a corner case where e.g. an NMI hitting an exit-to-guest path after +SPEC_CTRL_EXIT_TO_* would have run the entire NMI handler *after* the VERW +flush to scrub potentially sensitive data from uarch buffers. + +In order to compensate, issue VERW when exiting to Xen from an IST entry. + +SPEC_CTRL_EXIT_TO_XEN already has two reads of spec_ctrl_flags off the stack, +and we're about to add a third. Load the field into %ebx, and list the +register as clobbered. + +%r12 has been arranged to be the ist_exit signal, so add this as an input +dependency and use it to identify when to issue a VERW. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 3ee6066bcd737756b0990d417d94eddc0b0d2585) +--- + xen/arch/x86/include/asm/spec_ctrl_asm.h | 20 +++++++++++++++----- + xen/arch/x86/x86_64/entry.S | 2 +- + 2 files changed, 16 insertions(+), 6 deletions(-) + +diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h +index 66c706496f..28a75796e6 100644 +--- a/xen/arch/x86/include/asm/spec_ctrl_asm.h ++++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h +@@ -357,10 +357,12 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): + */ + .macro SPEC_CTRL_EXIT_TO_XEN + /* +- * Requires %r14=stack_end +- * Clobbers %rax, %rcx, %rdx ++ * Requires %r12=ist_exit, %r14=stack_end ++ * Clobbers %rax, %rbx, %rcx, %rdx + */ +- testb $SCF_ist_sc_msr, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14) ++ movzbl STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14), %ebx ++ ++ testb $SCF_ist_sc_msr, %bl + jz .L\@_skip_sc_msr + + /* +@@ -371,7 +373,7 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): + */ + xor %edx, %edx + +- testb $SCF_use_shadow, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14) ++ testb $SCF_use_shadow, %bl + jz .L\@_skip_sc_msr + + mov STACK_CPUINFO_FIELD(shadow_spec_ctrl)(%r14), %eax +@@ -380,8 +382,16 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): + + .L\@_skip_sc_msr: + +- /* TODO VERW */ ++ test %r12, %r12 ++ jz .L\@_skip_ist_exit ++ ++ /* Logically DO_SPEC_CTRL_COND_VERW but without the %rsp=cpuinfo dependency */ ++ testb $SCF_verw, %bl ++ jz .L\@_skip_verw ++ verw STACK_CPUINFO_FIELD(verw_sel)(%r14) ++.L\@_skip_verw: + ++.L\@_skip_ist_exit: + .endm + + #endif /* __ASSEMBLY__ */ +diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S +index 4cebc4fbe3..c12e011b4d 100644 +--- a/xen/arch/x86/x86_64/entry.S ++++ b/xen/arch/x86/x86_64/entry.S +@@ -680,7 +680,7 @@ UNLIKELY_START(ne, exit_cr3) + UNLIKELY_END(exit_cr3) + + /* WARNING! `ret`, `call *`, `jmp *` not safe beyond this point. */ +- SPEC_CTRL_EXIT_TO_XEN /* Req: %r14=end, Clob: acd */ ++ SPEC_CTRL_EXIT_TO_XEN /* Req: %r12=ist_exit %r14=end, Clob: abcd */ + + RESTORE_ALL adj=8 + iretq +-- +2.41.0 + diff --git a/xsa439-0009-x86-amd-Introduce-is_zen-1-2-_uarch-predicates.patch b/xsa439-0009-x86-amd-Introduce-is_zen-1-2-_uarch-predicates.patch new file mode 100644 index 0000000..5f063b1 --- /dev/null +++ b/xsa439-0009-x86-amd-Introduce-is_zen-1-2-_uarch-predicates.patch @@ -0,0 +1,94 @@ +From 19ee1e1faa32b79274b3484cb1170a5970f1e602 Mon Sep 17 00:00:00 2001 +Message-ID: <19ee1e1faa32b79274b3484cb1170a5970f1e602.1695733540.git.m.a.young@durham.ac.uk> +In-Reply-To: +References: +From: Andrew Cooper +Date: Fri, 15 Sep 2023 12:13:51 +0100 +Subject: [XEN PATCH 09/10] x86/amd: Introduce is_zen{1,2}_uarch() predicates + +We already have 3 cases using STIBP as a Zen1/2 heuristic, and are about to +introduce a 4th. Wrap the heuristic into a pair of predicates rather than +opencoding it, and the explanation of the heuristic, at each usage site. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit de1d265001397f308c5c3c5d3ffc30e7ef8c0705) +--- + xen/arch/x86/cpu/amd.c | 18 ++++-------------- + xen/arch/x86/include/asm/amd.h | 11 +++++++++++ + 2 files changed, 15 insertions(+), 14 deletions(-) + +diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c +index 1bb3044be1..e94ba5a0e0 100644 +--- a/xen/arch/x86/cpu/amd.c ++++ b/xen/arch/x86/cpu/amd.c +@@ -855,15 +855,13 @@ void amd_set_legacy_ssbd(bool enable) + * non-branch instructions to be ignored. It is to be set unilaterally in + * newer microcode. + * +- * This chickenbit is something unrelated on Zen1, and Zen1 vs Zen2 isn't a +- * simple model number comparison, so use STIBP as a heuristic to separate the +- * two uarches in Fam17h(AMD)/18h(Hygon). ++ * This chickenbit is something unrelated on Zen1. + */ + void amd_init_spectral_chicken(void) + { + uint64_t val, chickenbit = 1 << 1; + +- if (cpu_has_hypervisor || !boot_cpu_has(X86_FEATURE_AMD_STIBP)) ++ if (cpu_has_hypervisor || !is_zen2_uarch()) + return; + + if (rdmsr_safe(MSR_AMD64_DE_CFG2, val) == 0 && !(val & chickenbit)) +@@ -912,11 +910,8 @@ void amd_check_zenbleed(void) + * With the Fam17h check above, most parts getting here are + * Zen1. They're not affected. Assume Zen2 ones making it + * here are affected regardless of microcode version. +- * +- * Zen1 vs Zen2 isn't a simple model number comparison, so use +- * STIBP as a heuristic to distinguish. + */ +- if (!boot_cpu_has(X86_FEATURE_AMD_STIBP)) ++ if (is_zen1_uarch()) + return; + good_rev = ~0U; + break; +@@ -1277,12 +1272,7 @@ static int __init cf_check zen2_c6_errata_check(void) + */ + s_time_t delta; + +- /* +- * Zen1 vs Zen2 isn't a simple model number comparison, so use STIBP as +- * a heuristic to separate the two uarches in Fam17h. +- */ +- if (cpu_has_hypervisor || boot_cpu_data.x86 != 0x17 || +- !boot_cpu_has(X86_FEATURE_AMD_STIBP)) ++ if (cpu_has_hypervisor || boot_cpu_data.x86 != 0x17 || !is_zen2_uarch()) + return 0; + + /* +diff --git a/xen/arch/x86/include/asm/amd.h b/xen/arch/x86/include/asm/amd.h +index a975d3de26..82324110ab 100644 +--- a/xen/arch/x86/include/asm/amd.h ++++ b/xen/arch/x86/include/asm/amd.h +@@ -140,6 +140,17 @@ + AMD_MODEL_RANGE(0x11, 0x0, 0x0, 0xff, 0xf), \ + AMD_MODEL_RANGE(0x12, 0x0, 0x0, 0xff, 0xf)) + ++/* ++ * The Zen1 and Zen2 microarchitectures are implemented by AMD (Fam17h) and ++ * Hygon (Fam18h) but without simple model number rules. Instead, use STIBP ++ * as a heuristic that distinguishes the two. ++ * ++ * The caller is required to perform the appropriate vendor/family checks ++ * first. ++ */ ++#define is_zen1_uarch() (!boot_cpu_has(X86_FEATURE_AMD_STIBP)) ++#define is_zen2_uarch() boot_cpu_has(X86_FEATURE_AMD_STIBP) ++ + struct cpuinfo_x86; + int cpu_has_amd_erratum(const struct cpuinfo_x86 *, int, ...); + +-- +2.41.0 + diff --git a/xsa439-0010-x86-spec-ctrl-Mitigate-the-Zen1-DIV-leakage.patch b/xsa439-0010-x86-spec-ctrl-Mitigate-the-Zen1-DIV-leakage.patch new file mode 100644 index 0000000..0dc6780 --- /dev/null +++ b/xsa439-0010-x86-spec-ctrl-Mitigate-the-Zen1-DIV-leakage.patch @@ -0,0 +1,231 @@ +From 9ac2f49f5fa3a5159409241d4f74fb0d721dd4c5 Mon Sep 17 00:00:00 2001 +Message-ID: <9ac2f49f5fa3a5159409241d4f74fb0d721dd4c5.1695733540.git.m.a.young@durham.ac.uk> +In-Reply-To: +References: +From: Andrew Cooper +Date: Wed, 30 Aug 2023 20:24:25 +0100 +Subject: [XEN PATCH 10/10] x86/spec-ctrl: Mitigate the Zen1 DIV leakage + +In the Zen1 microarchitecure, there is one divider in the pipeline which +services uops from both threads. In the case of #DE, the latched result from +the previous DIV to execute will be forwarded speculatively. + +This is an interesting covert channel that allows two threads to communicate +without any system calls. In also allows userspace to obtain the result of +the most recent DIV instruction executed (even speculatively) in the core, +which can be from a higher privilege context. + +Scrub the result from the divider by executing a non-faulting divide. This +needs performing on the exit-to-guest paths, and ist_exit-to-Xen. + +Alternatives in IST context is believed safe now that it's done in NMI +context. + +This is XSA-439 / CVE-2023-20588. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit b5926c6ecf05c28ee99c6248c42d691ccbf0c315) +--- + docs/misc/xen-command-line.pandoc | 6 ++- + xen/arch/x86/hvm/svm/entry.S | 1 + + xen/arch/x86/include/asm/cpufeatures.h | 2 +- + xen/arch/x86/include/asm/spec_ctrl_asm.h | 17 +++++++++ + xen/arch/x86/spec_ctrl.c | 48 +++++++++++++++++++++++- + 5 files changed, 71 insertions(+), 3 deletions(-) + +diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc +index d9dae740cc..b92c8f969c 100644 +--- a/docs/misc/xen-command-line.pandoc ++++ b/docs/misc/xen-command-line.pandoc +@@ -2315,7 +2315,7 @@ By default SSBD will be mitigated at runtime (i.e `ssbd=runtime`). + > {msr-sc,rsb,md-clear,ibpb-entry}=|{pv,hvm}=, + > bti-thunk=retpoline|lfence|jmp, {ibrs,ibpb,ssbd,psfd, + > eager-fpu,l1d-flush,branch-harden,srb-lock, +-> unpriv-mmio,gds-mit}= ]` ++> unpriv-mmio,gds-mit,div-scrub}= ]` + + Controls for speculative execution sidechannel mitigations. By default, Xen + will pick the most appropriate mitigations based on compiled in support, +@@ -2437,6 +2437,10 @@ has elected not to lock the configuration, Xen will use GDS_CTRL to mitigate + GDS with. Otherwise, Xen will mitigate by disabling AVX, which blocks the use + of the AVX2 Gather instructions. + ++On all hardware, the `div-scrub=` option can be used to force or prevent Xen ++from mitigating the DIV-leakage vulnerability. By default, Xen will mitigate ++DIV-leakage on hardware believed to be vulnerable. ++ + ### sync_console + > `= ` + +diff --git a/xen/arch/x86/hvm/svm/entry.S b/xen/arch/x86/hvm/svm/entry.S +index 981cd82e7c..934f12cf5c 100644 +--- a/xen/arch/x86/hvm/svm/entry.S ++++ b/xen/arch/x86/hvm/svm/entry.S +@@ -74,6 +74,7 @@ __UNLIKELY_END(nsvm_hap) + 1: /* No Spectre v1 concerns. Execution will hit VMRUN imminently. */ + .endm + ALTERNATIVE "", svm_vmentry_spec_ctrl, X86_FEATURE_SC_MSR_HVM ++ ALTERNATIVE "", DO_SPEC_CTRL_DIV, X86_FEATURE_SC_DIV + + pop %r15 + pop %r14 +diff --git a/xen/arch/x86/include/asm/cpufeatures.h b/xen/arch/x86/include/asm/cpufeatures.h +index da0593de85..c3aad21c3b 100644 +--- a/xen/arch/x86/include/asm/cpufeatures.h ++++ b/xen/arch/x86/include/asm/cpufeatures.h +@@ -35,7 +35,7 @@ XEN_CPUFEATURE(SC_RSB_HVM, X86_SYNTH(19)) /* RSB overwrite needed for HVM + XEN_CPUFEATURE(XEN_SELFSNOOP, X86_SYNTH(20)) /* SELFSNOOP gets used by Xen itself */ + XEN_CPUFEATURE(SC_MSR_IDLE, X86_SYNTH(21)) /* Clear MSR_SPEC_CTRL on idle */ + XEN_CPUFEATURE(XEN_LBR, X86_SYNTH(22)) /* Xen uses MSR_DEBUGCTL.LBR */ +-/* Bits 23 unused. */ ++XEN_CPUFEATURE(SC_DIV, X86_SYNTH(23)) /* DIV scrub needed */ + XEN_CPUFEATURE(SC_RSB_IDLE, X86_SYNTH(24)) /* RSB overwrite needed for idle. */ + XEN_CPUFEATURE(SC_VERW_IDLE, X86_SYNTH(25)) /* VERW used by Xen for idle */ + XEN_CPUFEATURE(XEN_SHSTK, X86_SYNTH(26)) /* Xen uses CET Shadow Stacks */ +diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h +index 28a75796e6..f4b8b9d956 100644 +--- a/xen/arch/x86/include/asm/spec_ctrl_asm.h ++++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h +@@ -177,6 +177,19 @@ + .L\@_verw_skip: + .endm + ++.macro DO_SPEC_CTRL_DIV ++/* ++ * Requires nothing ++ * Clobbers %rax ++ * ++ * Issue a DIV for its flushing side effect (Zen1 uarch specific). Any ++ * non-faulting DIV will do; a byte DIV has least latency, and doesn't clobber ++ * %rdx. ++ */ ++ mov $1, %eax ++ div %al ++.endm ++ + .macro DO_SPEC_CTRL_ENTRY maybexen:req + /* + * Requires %rsp=regs (also cpuinfo if !maybexen) +@@ -279,6 +292,8 @@ + ALTERNATIVE "", DO_SPEC_CTRL_EXIT_TO_GUEST, X86_FEATURE_SC_MSR_PV + + DO_SPEC_CTRL_COND_VERW ++ ++ ALTERNATIVE "", DO_SPEC_CTRL_DIV, X86_FEATURE_SC_DIV + .endm + + /* +@@ -391,6 +406,8 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): + verw STACK_CPUINFO_FIELD(verw_sel)(%r14) + .L\@_skip_verw: + ++ ALTERNATIVE "", DO_SPEC_CTRL_DIV, X86_FEATURE_SC_DIV ++ + .L\@_skip_ist_exit: + .endm + +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 79b98f0fe7..0ff3c895ac 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -79,6 +79,7 @@ static int8_t __initdata opt_srb_lock = -1; + static bool __initdata opt_unpriv_mmio; + static bool __ro_after_init opt_fb_clear_mmio; + static int8_t __initdata opt_gds_mit = -1; ++static int8_t __initdata opt_div_scrub = -1; + + static int __init cf_check parse_spec_ctrl(const char *s) + { +@@ -133,6 +134,7 @@ static int __init cf_check parse_spec_ctrl(const char *s) + opt_srb_lock = 0; + opt_unpriv_mmio = false; + opt_gds_mit = 0; ++ opt_div_scrub = 0; + } + else if ( val > 0 ) + rc = -EINVAL; +@@ -285,6 +287,8 @@ static int __init cf_check parse_spec_ctrl(const char *s) + opt_unpriv_mmio = val; + else if ( (val = parse_boolean("gds-mit", s, ss)) >= 0 ) + opt_gds_mit = val; ++ else if ( (val = parse_boolean("div-scrub", s, ss)) >= 0 ) ++ opt_div_scrub = val; + else + rc = -EINVAL; + +@@ -485,7 +489,7 @@ static void __init print_details(enum ind_thunk thunk) + "\n"); + + /* Settings for Xen's protection, irrespective of guests. */ +- printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s%s%s, Other:%s%s%s%s%s\n", ++ printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s%s%s, Other:%s%s%s%s%s%s\n", + thunk == THUNK_NONE ? "N/A" : + thunk == THUNK_RETPOLINE ? "RETPOLINE" : + thunk == THUNK_LFENCE ? "LFENCE" : +@@ -510,6 +514,7 @@ static void __init print_details(enum ind_thunk thunk) + opt_l1d_flush ? " L1D_FLUSH" : "", + opt_md_clear_pv || opt_md_clear_hvm || + opt_fb_clear_mmio ? " VERW" : "", ++ opt_div_scrub ? " DIV" : "", + opt_branch_harden ? " BRANCH_HARDEN" : ""); + + /* L1TF diagnostics, printed if vulnerable or PV shadowing is in use. */ +@@ -967,6 +972,45 @@ static void __init srso_calculations(bool hw_smt_enabled) + setup_force_cpu_cap(X86_FEATURE_SRSO_NO); + } + ++/* ++ * The Div leakage issue is specific to the AMD Zen1 microarchitecure. ++ * ++ * However, there's no $FOO_NO bit defined, so if we're virtualised we have no ++ * hope of spotting the case where we might move to vulnerable hardware. We ++ * also can't make any useful conclusion about SMT-ness. ++ * ++ * Don't check the hypervisor bit, so at least we do the safe thing when ++ * booting on something that looks like a Zen1 CPU. ++ */ ++static bool __init has_div_vuln(void) ++{ ++ if ( !(boot_cpu_data.x86_vendor & ++ (X86_VENDOR_AMD | X86_VENDOR_HYGON)) ) ++ return false; ++ ++ if ( boot_cpu_data.x86 != 0x17 && boot_cpu_data.x86 != 0x18 ) ++ return false; ++ ++ return is_zen1_uarch(); ++} ++ ++static void __init div_calculations(bool hw_smt_enabled) ++{ ++ bool cpu_bug_div = has_div_vuln(); ++ ++ if ( opt_div_scrub == -1 ) ++ opt_div_scrub = cpu_bug_div; ++ ++ if ( opt_div_scrub ) ++ setup_force_cpu_cap(X86_FEATURE_SC_DIV); ++ ++ if ( opt_smt == -1 && !cpu_has_hypervisor && cpu_bug_div && hw_smt_enabled ) ++ warning_add( ++ "Booted on leaky-DIV hardware with SMT/Hyperthreading\n" ++ "enabled. Please assess your configuration and choose an\n" ++ "explicit 'smt=' setting. See XSA-439.\n"); ++} ++ + static void __init ibpb_calculations(void) + { + bool def_ibpb_entry = false; +@@ -1726,6 +1770,8 @@ void __init init_speculation_mitigations(void) + + ibpb_calculations(); + ++ div_calculations(hw_smt_enabled); ++ + /* Check whether Eager FPU should be enabled by default. */ + if ( opt_eager_fpu == -1 ) + opt_eager_fpu = should_use_eager_fpu(); +-- +2.41.0 + From 7fa4a3eee62bba7719bdc19908fc91822f9c5fe3 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Sun, 8 Oct 2023 12:02:22 +0100 Subject: [PATCH 076/137] rebuild for OCaml 5.1 --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 778a2e5..29c69a4 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.17.2 -Release: 2%{?dist} +Release: 3%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -959,6 +959,9 @@ fi %endif %changelog +* Sun Oct 08 2023 Michael Young - 4.17.2-3 +- rebuild (f40) for OCaml 5.1 + * Tue Sep 26 2023 Michael Young - 4.17.2-2 - arm32: The cache may not be properly cleaned/invalidated [XSA-437, CVE-2023-34321] From ad078f957fcdc8755a8585f90368a012a6e8c530 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 10 Oct 2023 21:42:36 +0100 Subject: [PATCH 077/137] 4 security issues xenstored: A transaction conflict can crash C Xenstored [XSA-440, CVE-2023-34323] x86/AMD: missing IOMMU TLB flushing [XSA-442, CVE-2023-34326] Multiple vulnerabilities in libfsimage disk handling [XSA-443, CVE-2023-34325] x86/AMD: Debug Mask handling [XSA-444, CVE-2023-34327, CVE-2023-34328] --- xen.spec | 41 +++++- xsa440-4.17.patch | 58 ++++++++ xsa442-4.17.patch | 185 ++++++++++++++++++++++++++ xsa443-4.17-01.patch | 70 ++++++++++ xsa443-4.17-02.patch | 32 +++++ xsa443-4.17-03.patch | 137 +++++++++++++++++++ xsa443-4.17-04.patch | 61 +++++++++ xsa443-4.17-05.patch | 59 +++++++++ xsa443-4.17-06.patch | 65 +++++++++ xsa443-4.17-07.patch | 105 +++++++++++++++ xsa443-4.17-08.patch | 126 ++++++++++++++++++ xsa443-4.17-09.patch | 307 +++++++++++++++++++++++++++++++++++++++++++ xsa443-4.17-10.patch | 250 +++++++++++++++++++++++++++++++++++ xsa443-4.17-11.patch | 157 ++++++++++++++++++++++ xsa444-4.17-1.patch | 93 +++++++++++++ xsa444-4.17-2.patch | 72 ++++++++++ 16 files changed, 1817 insertions(+), 1 deletion(-) create mode 100644 xsa440-4.17.patch create mode 100644 xsa442-4.17.patch create mode 100644 xsa443-4.17-01.patch create mode 100644 xsa443-4.17-02.patch create mode 100644 xsa443-4.17-03.patch create mode 100644 xsa443-4.17-04.patch create mode 100644 xsa443-4.17-05.patch create mode 100644 xsa443-4.17-06.patch create mode 100644 xsa443-4.17-07.patch create mode 100644 xsa443-4.17-08.patch create mode 100644 xsa443-4.17-09.patch create mode 100644 xsa443-4.17-10.patch create mode 100644 xsa443-4.17-11.patch create mode 100644 xsa444-4.17-1.patch create mode 100644 xsa444-4.17-2.patch diff --git a/xen.spec b/xen.spec index 29c69a4..fde47fb 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.17.2 -Release: 3%{?dist} +Release: 4%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -124,6 +124,21 @@ Patch59: xsa439-0007-x86-entry-Track-the-IST-ness-of-an-entry-for-the-exi.patch Patch60: xsa439-0008-x86-spec-ctrl-Issue-VERW-during-IST-exit-to-Xen.patch Patch61: xsa439-0009-x86-amd-Introduce-is_zen-1-2-_uarch-predicates.patch Patch62: xsa439-0010-x86-spec-ctrl-Mitigate-the-Zen1-DIV-leakage.patch +Patch63: xsa440-4.17.patch +Patch64: xsa442-4.17.patch +Patch65: xsa443-4.17-01.patch +Patch66: xsa443-4.17-02.patch +Patch67: xsa443-4.17-03.patch +Patch68: xsa443-4.17-04.patch +Patch69: xsa443-4.17-05.patch +Patch70: xsa443-4.17-06.patch +Patch71: xsa443-4.17-07.patch +Patch72: xsa443-4.17-08.patch +Patch73: xsa443-4.17-09.patch +Patch74: xsa443-4.17-10.patch +Patch75: xsa443-4.17-11.patch +Patch76: xsa444-4.17-1.patch +Patch77: xsa444-4.17-2.patch %if %build_qemutrad @@ -352,6 +367,21 @@ manage Xen virtual machines. %patch 60 -p1 %patch 61 -p1 %patch 62 -p1 +%patch 63 -p1 +%patch 64 -p1 +%patch 65 -p1 +%patch 66 -p1 +%patch 67 -p1 +%patch 68 -p1 +%patch 69 -p1 +%patch 70 -p1 +%patch 71 -p1 +%patch 72 -p1 +%patch 73 -p1 +%patch 74 -p1 +%patch 75 -p1 +%patch 76 -p1 +%patch 77 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -959,6 +989,15 @@ fi %endif %changelog +* Tue Oct 10 2023 Michael Young - 4.17.2-4 +- xenstored: A transaction conflict can crash C Xenstored [XSA-440, + CVE-2023-34323] +- x86/AMD: missing IOMMU TLB flushing [XSA-442, CVE-2023-34326] +- Multiple vulnerabilities in libfsimage disk handling [XSA-443, + CVE-2023-34325] +- x86/AMD: Debug Mask handling [XSA-444, CVE-2023-34327, + CVE-2023-34328] + * Sun Oct 08 2023 Michael Young - 4.17.2-3 - rebuild (f40) for OCaml 5.1 diff --git a/xsa440-4.17.patch b/xsa440-4.17.patch new file mode 100644 index 0000000..4941afc --- /dev/null +++ b/xsa440-4.17.patch @@ -0,0 +1,58 @@ +From 5d8b3d1ec98e56155d9650d7f4a70cd8ba9dc27d Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Fri, 22 Sep 2023 11:32:16 +0100 +Subject: tools/xenstored: domain_entry_fix(): Handle conflicting transaction + +The function domain_entry_fix() will be initially called to check if the +quota is correct before attempt to commit any nodes. So it would be +possible that accounting is temporarily negative. This is the case +in the following sequence: + + 1) Create 50 nodes + 2) Start two transactions + 3) Delete all the nodes in each transaction + 4) Commit the two transactions + +Because the first transaction will have succeed and updated the +accounting, there is no guarantee that 'd->nbentry + num' will still +be above 0. So the assert() would be triggered. +The assert() was introduced in dbef1f748289 ("tools/xenstore: simplify +and fix per domain node accounting") with the assumption that the +value can't be negative. As this is not true revert to the original +check but restricted to the path where we don't update. Take the +opportunity to explain the rationale behind the check. + +This CVE-2023-34323 / XSA-440. + +Reported-by: Stanislav Uschakow +Fixes: dbef1f748289 ("tools/xenstore: simplify and fix per domain node accounting") +Signed-off-by: Julien Grall +Reviewed-by: Juergen Gross + +diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c +index aa86892fed9e..6074df210c6e 100644 +--- a/tools/xenstore/xenstored_domain.c ++++ b/tools/xenstore/xenstored_domain.c +@@ -1094,10 +1094,20 @@ int domain_entry_fix(unsigned int domid, int num, bool update) + } + + cnt = d->nbentry + num; +- assert(cnt >= 0); + +- if (update) ++ if (update) { ++ assert(cnt >= 0); + d->nbentry = cnt; ++ } else if (cnt < 0) { ++ /* ++ * In a transaction when a node is being added/removed AND ++ * the same node has been added/removed outside the ++ * transaction in parallel, the result value may be negative. ++ * This is no problem, as the transaction will fail due to ++ * the resulting conflict. So override 'cnt'. ++ */ ++ cnt = 0; ++ } + + return domid_is_unprivileged(domid) ? cnt : 0; + } diff --git a/xsa442-4.17.patch b/xsa442-4.17.patch new file mode 100644 index 0000000..a78bfdd --- /dev/null +++ b/xsa442-4.17.patch @@ -0,0 +1,185 @@ +From 5b2ccb60ff22fbff44dd66214c2956a434ee6271 Mon Sep 17 00:00:00 2001 +From: Roger Pau Monne +Date: Tue, 13 Jun 2023 15:01:05 +0200 +Subject: [PATCH] iommu/amd-vi: flush IOMMU TLB when flushing the DTE +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The caching invalidation guidelines from the AMD-Vi specification (48882—Rev +3.07-PUB—Oct 2022) seem to be misleading on some hardware, as devices will +malfunction (see stale DMA mappings) if some fields of the DTE are updated but +the IOMMU TLB is not flushed. This has been observed in practice on AMD +systems. Due to the lack of guidance from the currently published +specification this patch aims to increase the flushing done in order to prevent +device malfunction. + +In order to fix, issue an INVALIDATE_IOMMU_PAGES command from +amd_iommu_flush_device(), flushing all the address space. Note this requires +callers to be adjusted in order to pass the DomID on the DTE previous to the +modification. + +Some call sites don't provide a valid DomID to amd_iommu_flush_device() in +order to avoid the flush. That's because the device had address translations +disabled and hence the previous DomID on the DTE is not valid. Note the +current logic relies on the entity disabling address translations to also flush +the TLB of the in use DomID. + +Device I/O TLB flushing when ATS are enabled is not covered by the current +change, as ATS usage is not security supported. + +This is XSA-442 / CVE-2023-34326 + +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +--- + xen/drivers/passthrough/amd/iommu.h | 3 ++- + xen/drivers/passthrough/amd/iommu_cmd.c | 10 +++++++++- + xen/drivers/passthrough/amd/iommu_guest.c | 5 +++-- + xen/drivers/passthrough/amd/iommu_init.c | 6 +++++- + xen/drivers/passthrough/amd/pci_amd_iommu.c | 14 ++++++++++---- + 5 files changed, 29 insertions(+), 9 deletions(-) + +diff --git a/xen/drivers/passthrough/amd/iommu.h b/xen/drivers/passthrough/amd/iommu.h +index 5429ada58ef5..a58be28bf96d 100644 +--- a/xen/drivers/passthrough/amd/iommu.h ++++ b/xen/drivers/passthrough/amd/iommu.h +@@ -283,7 +283,8 @@ void amd_iommu_flush_pages(struct domain *d, unsigned long dfn, + unsigned int order); + void amd_iommu_flush_iotlb(u8 devfn, const struct pci_dev *pdev, + uint64_t gaddr, unsigned int order); +-void amd_iommu_flush_device(struct amd_iommu *iommu, uint16_t bdf); ++void amd_iommu_flush_device(struct amd_iommu *iommu, uint16_t bdf, ++ domid_t domid); + void amd_iommu_flush_intremap(struct amd_iommu *iommu, uint16_t bdf); + void amd_iommu_flush_all_caches(struct amd_iommu *iommu); + +diff --git a/xen/drivers/passthrough/amd/iommu_cmd.c b/xen/drivers/passthrough/amd/iommu_cmd.c +index 40ddf366bb4d..cb28b36abc38 100644 +--- a/xen/drivers/passthrough/amd/iommu_cmd.c ++++ b/xen/drivers/passthrough/amd/iommu_cmd.c +@@ -363,10 +363,18 @@ void amd_iommu_flush_pages(struct domain *d, + _amd_iommu_flush_pages(d, __dfn_to_daddr(dfn), order); + } + +-void amd_iommu_flush_device(struct amd_iommu *iommu, uint16_t bdf) ++void amd_iommu_flush_device(struct amd_iommu *iommu, uint16_t bdf, ++ domid_t domid) + { + invalidate_dev_table_entry(iommu, bdf); + flush_command_buffer(iommu, 0); ++ ++ /* Also invalidate IOMMU TLB entries when flushing the DTE. */ ++ if ( domid != DOMID_INVALID ) ++ { ++ invalidate_iommu_pages(iommu, INV_IOMMU_ALL_PAGES_ADDRESS, domid, 0); ++ flush_command_buffer(iommu, 0); ++ } + } + + void amd_iommu_flush_intremap(struct amd_iommu *iommu, uint16_t bdf) +diff --git a/xen/drivers/passthrough/amd/iommu_guest.c b/xen/drivers/passthrough/amd/iommu_guest.c +index 80a331f546ed..be86bce6fb03 100644 +--- a/xen/drivers/passthrough/amd/iommu_guest.c ++++ b/xen/drivers/passthrough/amd/iommu_guest.c +@@ -385,7 +385,7 @@ static int do_completion_wait(struct domain *d, cmd_entry_t *cmd) + + static int do_invalidate_dte(struct domain *d, cmd_entry_t *cmd) + { +- uint16_t gbdf, mbdf, req_id, gdom_id, hdom_id; ++ uint16_t gbdf, mbdf, req_id, gdom_id, hdom_id, prev_domid; + struct amd_iommu_dte *gdte, *mdte, *dte_base; + struct amd_iommu *iommu = NULL; + struct guest_iommu *g_iommu; +@@ -445,13 +445,14 @@ static int do_invalidate_dte(struct domain *d, cmd_entry_t *cmd) + req_id = get_dma_requestor_id(iommu->seg, mbdf); + dte_base = iommu->dev_table.buffer; + mdte = &dte_base[req_id]; ++ prev_domid = mdte->domain_id; + + spin_lock_irqsave(&iommu->lock, flags); + dte_set_gcr3_table(mdte, hdom_id, gcr3_mfn << PAGE_SHIFT, gv, glx); + + spin_unlock_irqrestore(&iommu->lock, flags); + +- amd_iommu_flush_device(iommu, req_id); ++ amd_iommu_flush_device(iommu, req_id, prev_domid); + + return 0; + } +diff --git a/xen/drivers/passthrough/amd/iommu_init.c b/xen/drivers/passthrough/amd/iommu_init.c +index 166570648d26..101a60ce1794 100644 +--- a/xen/drivers/passthrough/amd/iommu_init.c ++++ b/xen/drivers/passthrough/amd/iommu_init.c +@@ -1547,7 +1547,11 @@ static int cf_check _invalidate_all_devices( + req_id = ivrs_mappings[bdf].dte_requestor_id; + if ( iommu ) + { +- amd_iommu_flush_device(iommu, req_id); ++ /* ++ * IOMMU TLB flush performed separately (see ++ * invalidate_all_domain_pages()). ++ */ ++ amd_iommu_flush_device(iommu, req_id, DOMID_INVALID); + amd_iommu_flush_intremap(iommu, req_id); + } + } +diff --git a/xen/drivers/passthrough/amd/pci_amd_iommu.c b/xen/drivers/passthrough/amd/pci_amd_iommu.c +index 94e37755064b..8641b84712a0 100644 +--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c ++++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c +@@ -192,10 +192,13 @@ static int __must_check amd_iommu_setup_domain_device( + + spin_unlock_irqrestore(&iommu->lock, flags); + +- amd_iommu_flush_device(iommu, req_id); ++ /* DTE didn't have DMA translations enabled, do not flush the TLB. */ ++ amd_iommu_flush_device(iommu, req_id, DOMID_INVALID); + } + else if ( dte->pt_root != mfn_x(page_to_mfn(root_pg)) ) + { ++ domid_t prev_domid = dte->domain_id; ++ + /* + * Strictly speaking if the device is the only one with this requestor + * ID, it could be allowed to be re-assigned regardless of unity map +@@ -252,7 +255,7 @@ static int __must_check amd_iommu_setup_domain_device( + + spin_unlock_irqrestore(&iommu->lock, flags); + +- amd_iommu_flush_device(iommu, req_id); ++ amd_iommu_flush_device(iommu, req_id, prev_domid); + } + else + spin_unlock_irqrestore(&iommu->lock, flags); +@@ -421,6 +424,8 @@ static void amd_iommu_disable_domain_device(const struct domain *domain, + spin_lock_irqsave(&iommu->lock, flags); + if ( dte->tv || dte->v ) + { ++ domid_t prev_domid = dte->domain_id; ++ + /* See the comment in amd_iommu_setup_device_table(). */ + dte->int_ctl = IOMMU_DEV_TABLE_INT_CONTROL_ABORTED; + smp_wmb(); +@@ -439,7 +444,7 @@ static void amd_iommu_disable_domain_device(const struct domain *domain, + + spin_unlock_irqrestore(&iommu->lock, flags); + +- amd_iommu_flush_device(iommu, req_id); ++ amd_iommu_flush_device(iommu, req_id, prev_domid); + + AMD_IOMMU_DEBUG("Disable: device id = %#x, " + "domain = %d, paging mode = %d\n", +@@ -610,7 +615,8 @@ static int cf_check amd_iommu_add_device(u8 devfn, struct pci_dev *pdev) + + spin_unlock_irqrestore(&iommu->lock, flags); + +- amd_iommu_flush_device(iommu, bdf); ++ /* DTE didn't have DMA translations enabled, do not flush the TLB. */ ++ amd_iommu_flush_device(iommu, bdf, DOMID_INVALID); + } + + if ( amd_iommu_reserve_domain_unity_map( +-- +2.42.0 + diff --git a/xsa443-4.17-01.patch b/xsa443-4.17-01.patch new file mode 100644 index 0000000..d9ca3f8 --- /dev/null +++ b/xsa443-4.17-01.patch @@ -0,0 +1,70 @@ +From 7e48562bf34e90f907491a0595782d2daa1ff3ad Mon Sep 17 00:00:00 2001 +From: Alejandro Vallejo +Date: Thu, 14 Sep 2023 13:22:50 +0100 +Subject: [PATCH 01/11] libfsimage/xfs: Remove dead code + +xfs_info.agnolog (and related code) and XFS_INO_AGBNO_BITS are dead code +that serve no purpose. + +This is part of XSA-443 / CVE-2023-34325 + +Signed-off-by: Alejandro Vallejo +Reviewed-by: Jan Beulich +--- + tools/libfsimage/xfs/fsys_xfs.c | 18 ------------------ + 1 file changed, 18 deletions(-) + +diff --git a/tools/libfsimage/xfs/fsys_xfs.c b/tools/libfsimage/xfs/fsys_xfs.c +index d735a88e55f3..2800699f5985 100644 +--- a/tools/libfsimage/xfs/fsys_xfs.c ++++ b/tools/libfsimage/xfs/fsys_xfs.c +@@ -37,7 +37,6 @@ struct xfs_info { + int blklog; + int inopblog; + int agblklog; +- int agnolog; + unsigned int nextents; + xfs_daddr_t next; + xfs_daddr_t daddr; +@@ -65,9 +64,7 @@ static struct xfs_info xfs; + + #define XFS_INO_MASK(k) ((xfs_uint32_t)((1ULL << (k)) - 1)) + #define XFS_INO_OFFSET_BITS xfs.inopblog +-#define XFS_INO_AGBNO_BITS xfs.agblklog + #define XFS_INO_AGINO_BITS (xfs.agblklog + xfs.inopblog) +-#define XFS_INO_AGNO_BITS xfs.agnolog + + static inline xfs_agblock_t + agino2agbno (xfs_agino_t agino) +@@ -149,20 +146,6 @@ xt_len (xfs_bmbt_rec_32_t *r) + return le32(r->l3) & mask32lo(21); + } + +-static inline int +-xfs_highbit32(xfs_uint32_t v) +-{ +- int i; +- +- if (--v) { +- for (i = 0; i < 31; i++, v >>= 1) { +- if (v == 0) +- return i; +- } +- } +- return 0; +-} +- + static int + isinxt (xfs_fileoff_t key, xfs_fileoff_t offset, xfs_filblks_t len) + { +@@ -472,7 +455,6 @@ xfs_mount (fsi_file_t *ffi, const char *options) + + xfs.inopblog = super.sb_inopblog; + xfs.agblklog = super.sb_agblklog; +- xfs.agnolog = xfs_highbit32 (le32(super.sb_agcount)); + + xfs.btnode_ptr0_off = + ((xfs.bsize - sizeof(xfs_btree_block_t)) / +-- +2.42.0 + diff --git a/xsa443-4.17-02.patch b/xsa443-4.17-02.patch new file mode 100644 index 0000000..0f2edaf --- /dev/null +++ b/xsa443-4.17-02.patch @@ -0,0 +1,32 @@ +From c26327795b78c93f6fa6d5d46e34f59dc4046601 Mon Sep 17 00:00:00 2001 +From: Alejandro Vallejo +Date: Thu, 14 Sep 2023 13:22:51 +0100 +Subject: [PATCH 02/11] libfsimage/xfs: Amend mask32lo() to allow the value 32 + +agblklog could plausibly be 32, but that would overflow this shift. +Perform the shift as ULL and cast to u32 at the end instead. + +This is part of XSA-443 / CVE-2023-34325 + +Signed-off-by: Alejandro Vallejo +Acked-by: Jan Beulich +--- + tools/libfsimage/xfs/fsys_xfs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/libfsimage/xfs/fsys_xfs.c b/tools/libfsimage/xfs/fsys_xfs.c +index 2800699f5985..4720bb4505c8 100644 +--- a/tools/libfsimage/xfs/fsys_xfs.c ++++ b/tools/libfsimage/xfs/fsys_xfs.c +@@ -60,7 +60,7 @@ static struct xfs_info xfs; + #define inode ((xfs_dinode_t *)((char *)FSYS_BUF + 8192)) + #define icore (inode->di_core) + +-#define mask32lo(n) (((xfs_uint32_t)1 << (n)) - 1) ++#define mask32lo(n) ((xfs_uint32_t)((1ull << (n)) - 1)) + + #define XFS_INO_MASK(k) ((xfs_uint32_t)((1ULL << (k)) - 1)) + #define XFS_INO_OFFSET_BITS xfs.inopblog +-- +2.42.0 + diff --git a/xsa443-4.17-03.patch b/xsa443-4.17-03.patch new file mode 100644 index 0000000..b89721a --- /dev/null +++ b/xsa443-4.17-03.patch @@ -0,0 +1,137 @@ +From 199f0538bbec052028679a55ea512437170854c9 Mon Sep 17 00:00:00 2001 +From: Alejandro Vallejo +Date: Thu, 14 Sep 2023 13:22:52 +0100 +Subject: [PATCH 03/11] libfsimage/xfs: Sanity-check the superblock during + mounts + +Sanity-check the XFS superblock for wellformedness at the mount handler. +This forces pygrub to abort parsing a potentially malformed filesystem and +ensures the invariants assumed throughout the rest of the code hold. + +Also, derive parameters from previously sanitized parameters where possible +(rather than reading them off the superblock) + +The code doesn't try to avoid overflowing the end of the disk, because +that's an unlikely and benign error. Parameters used in calculations of +xfs_daddr_t (like the root inode index) aren't in critical need of being +sanitized. + +The sanitization of agblklog is basically checking that no obvious +overflows happen on agblklog, and then ensuring agblocks is contained in +the range (2^(sb_agblklog-1), 2^sb_agblklog]. + +This is part of XSA-443 / CVE-2023-34325 + +Reported-by: Ferdinand Nölscher +Signed-off-by: Alejandro Vallejo +Reviewed-by: Jan Beulich +--- + tools/libfsimage/xfs/fsys_xfs.c | 48 ++++++++++++++++++++++++++------- + tools/libfsimage/xfs/xfs.h | 12 +++++++++ + 2 files changed, 50 insertions(+), 10 deletions(-) + +diff --git a/tools/libfsimage/xfs/fsys_xfs.c b/tools/libfsimage/xfs/fsys_xfs.c +index 4720bb4505c8..e4eb7e1ee26f 100644 +--- a/tools/libfsimage/xfs/fsys_xfs.c ++++ b/tools/libfsimage/xfs/fsys_xfs.c +@@ -17,6 +17,7 @@ + * along with this program; If not, see . + */ + ++#include + #include + #include "xfs.h" + +@@ -433,29 +434,56 @@ first_dentry (fsi_file_t *ffi, xfs_ino_t *ino) + return next_dentry (ffi, ino); + } + ++static bool ++xfs_sb_is_invalid (const xfs_sb_t *super) ++{ ++ return (le32(super->sb_magicnum) != XFS_SB_MAGIC) ++ || ((le16(super->sb_versionnum) & XFS_SB_VERSION_NUMBITS) != ++ XFS_SB_VERSION_4) ++ || (super->sb_inodelog < XFS_SB_INODELOG_MIN) ++ || (super->sb_inodelog > XFS_SB_INODELOG_MAX) ++ || (super->sb_blocklog < XFS_SB_BLOCKLOG_MIN) ++ || (super->sb_blocklog > XFS_SB_BLOCKLOG_MAX) ++ || (super->sb_blocklog < super->sb_inodelog) ++ || (super->sb_agblklog > XFS_SB_AGBLKLOG_MAX) ++ || ((1ull << super->sb_agblklog) < le32(super->sb_agblocks)) ++ || (((1ull << super->sb_agblklog) >> 1) >= ++ le32(super->sb_agblocks)) ++ || ((super->sb_blocklog + super->sb_dirblklog) >= ++ XFS_SB_DIRBLK_NUMBITS); ++} ++ + static int + xfs_mount (fsi_file_t *ffi, const char *options) + { + xfs_sb_t super; + + if (!devread (ffi, 0, 0, sizeof(super), (char *)&super) +- || (le32(super.sb_magicnum) != XFS_SB_MAGIC) +- || ((le16(super.sb_versionnum) +- & XFS_SB_VERSION_NUMBITS) != XFS_SB_VERSION_4) ) { ++ || xfs_sb_is_invalid(&super)) { + return 0; + } + +- xfs.bsize = le32 (super.sb_blocksize); +- xfs.blklog = super.sb_blocklog; +- xfs.bdlog = xfs.blklog - SECTOR_BITS; ++ /* ++ * Not sanitized. It's exclusively used to generate disk addresses, ++ * so it's not important from a security standpoint. ++ */ + xfs.rootino = le64 (super.sb_rootino); +- xfs.isize = le16 (super.sb_inodesize); +- xfs.agblocks = le32 (super.sb_agblocks); +- xfs.dirbsize = xfs.bsize << super.sb_dirblklog; + +- xfs.inopblog = super.sb_inopblog; ++ /* ++ * Sanitized to be consistent with each other, only used to ++ * generate disk addresses, so it's safe ++ */ ++ xfs.agblocks = le32 (super.sb_agblocks); + xfs.agblklog = super.sb_agblklog; + ++ /* Derived from sanitized parameters */ ++ xfs.bsize = 1 << super.sb_blocklog; ++ xfs.blklog = super.sb_blocklog; ++ xfs.bdlog = super.sb_blocklog - SECTOR_BITS; ++ xfs.isize = 1 << super.sb_inodelog; ++ xfs.dirbsize = 1 << (super.sb_blocklog + super.sb_dirblklog); ++ xfs.inopblog = super.sb_blocklog - super.sb_inodelog; ++ + xfs.btnode_ptr0_off = + ((xfs.bsize - sizeof(xfs_btree_block_t)) / + (sizeof (xfs_bmbt_key_t) + sizeof (xfs_bmbt_ptr_t))) +diff --git a/tools/libfsimage/xfs/xfs.h b/tools/libfsimage/xfs/xfs.h +index 40699281e44d..b87e37d3d7e9 100644 +--- a/tools/libfsimage/xfs/xfs.h ++++ b/tools/libfsimage/xfs/xfs.h +@@ -134,6 +134,18 @@ typedef struct xfs_sb + xfs_uint8_t sb_dummy[7]; /* padding */ + } xfs_sb_t; + ++/* Bound taken from xfs.c in GRUB2. It doesn't exist in the spec */ ++#define XFS_SB_DIRBLK_NUMBITS 27 ++/* Implied by the XFS specification. The minimum block size is 512 octets */ ++#define XFS_SB_BLOCKLOG_MIN 9 ++/* Implied by the XFS specification. The maximum block size is 65536 octets */ ++#define XFS_SB_BLOCKLOG_MAX 16 ++/* Implied by the XFS specification. The minimum inode size is 256 octets */ ++#define XFS_SB_INODELOG_MIN 8 ++/* Implied by the XFS specification. The maximum inode size is 2048 octets */ ++#define XFS_SB_INODELOG_MAX 11 ++/* High bound for sb_agblklog */ ++#define XFS_SB_AGBLKLOG_MAX 32 + + /* those are from xfs_btree.h */ + +-- +2.42.0 + diff --git a/xsa443-4.17-04.patch b/xsa443-4.17-04.patch new file mode 100644 index 0000000..dde095e --- /dev/null +++ b/xsa443-4.17-04.patch @@ -0,0 +1,61 @@ +From c66fd01277939634c624c8340838682d9d4fd839 Mon Sep 17 00:00:00 2001 +From: Alejandro Vallejo +Date: Thu, 14 Sep 2023 13:22:53 +0100 +Subject: [PATCH 04/11] libfsimage/xfs: Add compile-time check to libfsimage + +Adds the common tools include folder to the -I compile flags +of libfsimage. This allows us to use: + xen-tools/common-macros.h:BUILD_BUG_ON() + +With it, statically assert a sanitized "blocklog - SECTOR_BITS" cannot +underflow. + +This is part of XSA-443 / CVE-2023-34325 + +Signed-off-by: Alejandro Vallejo +Reviewed-by: Jan Beulich +--- + tools/libfsimage/common.mk | 2 +- + tools/libfsimage/xfs/fsys_xfs.c | 4 +++- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/tools/libfsimage/common.mk b/tools/libfsimage/common.mk +index 4fc8c6679599..e4336837d045 100644 +--- a/tools/libfsimage/common.mk ++++ b/tools/libfsimage/common.mk +@@ -1,7 +1,7 @@ + include $(XEN_ROOT)/tools/Rules.mk + + FSDIR := $(libdir)/xenfsimage +-CFLAGS += -Wno-unknown-pragmas -I$(XEN_ROOT)/tools/libfsimage/common/ -DFSIMAGE_FSDIR=\"$(FSDIR)\" ++CFLAGS += -Wno-unknown-pragmas -I$(XEN_ROOT)/tools/libfsimage/common/ $(CFLAGS_xeninclude) -DFSIMAGE_FSDIR=\"$(FSDIR)\" + CFLAGS += -D_GNU_SOURCE + LDFLAGS += -L../common/ + +diff --git a/tools/libfsimage/xfs/fsys_xfs.c b/tools/libfsimage/xfs/fsys_xfs.c +index e4eb7e1ee26f..4a8dd6f2397b 100644 +--- a/tools/libfsimage/xfs/fsys_xfs.c ++++ b/tools/libfsimage/xfs/fsys_xfs.c +@@ -19,6 +19,7 @@ + + #include + #include ++#include + #include "xfs.h" + + #define MAX_LINK_COUNT 8 +@@ -477,9 +478,10 @@ xfs_mount (fsi_file_t *ffi, const char *options) + xfs.agblklog = super.sb_agblklog; + + /* Derived from sanitized parameters */ ++ BUILD_BUG_ON(XFS_SB_BLOCKLOG_MIN < SECTOR_BITS); ++ xfs.bdlog = super.sb_blocklog - SECTOR_BITS; + xfs.bsize = 1 << super.sb_blocklog; + xfs.blklog = super.sb_blocklog; +- xfs.bdlog = super.sb_blocklog - SECTOR_BITS; + xfs.isize = 1 << super.sb_inodelog; + xfs.dirbsize = 1 << (super.sb_blocklog + super.sb_dirblklog); + xfs.inopblog = super.sb_blocklog - super.sb_inodelog; +-- +2.42.0 + diff --git a/xsa443-4.17-05.patch b/xsa443-4.17-05.patch new file mode 100644 index 0000000..b2f5daa --- /dev/null +++ b/xsa443-4.17-05.patch @@ -0,0 +1,59 @@ +From ad5d0db5e68e5d4e79255fa85d9cb0069bb1c5d5 Mon Sep 17 00:00:00 2001 +From: Alejandro Vallejo +Date: Mon, 25 Sep 2023 18:32:21 +0100 +Subject: [PATCH 05/11] tools/pygrub: Remove unnecessary hypercall + +There's a hypercall being issued in order to determine whether PV64 is +supported, but since Xen 4.3 that's strictly true so it's not required. + +Plus, this way we can avoid mapping the privcmd interface altogether in the +depriv pygrub. + +This is part of XSA-443 / CVE-2023-34325 + +Signed-off-by: Alejandro Vallejo +Reviewed-by: Andrew Cooper +--- + tools/pygrub/src/pygrub | 12 +----------- + 1 file changed, 1 insertion(+), 11 deletions(-) + +diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub +index ce7ab0eb8cf3..ce4e07d3e823 100755 +--- a/tools/pygrub/src/pygrub ++++ b/tools/pygrub/src/pygrub +@@ -18,7 +18,6 @@ import os, sys, string, struct, tempfile, re, traceback, stat, errno + import copy + import logging + import platform +-import xen.lowlevel.xc + + import curses, _curses, curses.textpad, curses.ascii + import getopt +@@ -668,14 +667,6 @@ def run_grub(file, entry, fs, cfg_args): + + return grubcfg + +-def supports64bitPVguest(): +- xc = xen.lowlevel.xc.xc() +- caps = xc.xeninfo()['xen_caps'].split(" ") +- for cap in caps: +- if cap == "xen-3.0-x86_64": +- return True +- return False +- + # If nothing has been specified, look for a Solaris domU. If found, perform the + # necessary tweaks. + def sniff_solaris(fs, cfg): +@@ -684,8 +675,7 @@ def sniff_solaris(fs, cfg): + return cfg + + if not cfg["kernel"]: +- if supports64bitPVguest() and \ +- fs.file_exists("/platform/i86xpv/kernel/amd64/unix"): ++ if fs.file_exists("/platform/i86xpv/kernel/amd64/unix"): + cfg["kernel"] = "/platform/i86xpv/kernel/amd64/unix" + cfg["ramdisk"] = "/platform/i86pc/amd64/boot_archive" + elif fs.file_exists("/platform/i86xpv/kernel/unix"): +-- +2.42.0 + diff --git a/xsa443-4.17-06.patch b/xsa443-4.17-06.patch new file mode 100644 index 0000000..22af109 --- /dev/null +++ b/xsa443-4.17-06.patch @@ -0,0 +1,65 @@ +From d3ceb0b314005a656dd2ca4b2821575a36f8426d Mon Sep 17 00:00:00 2001 +From: Alejandro Vallejo +Date: Mon, 25 Sep 2023 18:32:22 +0100 +Subject: [PATCH 06/11] tools/pygrub: Small refactors + +Small tidy up to ensure output_directory always has a trailing '/' to ease +concatenating paths and that `output` can only be a filename or None. + +This is part of XSA-443 / CVE-2023-34325 + +Signed-off-by: Alejandro Vallejo +Acked-by: Andrew Cooper +--- + tools/pygrub/src/pygrub | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub +index ce4e07d3e823..1042c05b8676 100755 +--- a/tools/pygrub/src/pygrub ++++ b/tools/pygrub/src/pygrub +@@ -793,7 +793,7 @@ if __name__ == "__main__": + debug = False + not_really = False + output_format = "sxp" +- output_directory = "/var/run/xen/pygrub" ++ output_directory = "/var/run/xen/pygrub/" + + # what was passed in + incfg = { "kernel": None, "ramdisk": None, "args": "" } +@@ -815,7 +815,8 @@ if __name__ == "__main__": + usage() + sys.exit() + elif o in ("--output",): +- output = a ++ if a != "-": ++ output = a + elif o in ("--kernel",): + incfg["kernel"] = a + elif o in ("--ramdisk",): +@@ -847,12 +848,11 @@ if __name__ == "__main__": + if not os.path.isdir(a): + print("%s is not an existing directory" % a) + sys.exit(1) +- output_directory = a ++ output_directory = a + '/' + + if debug: + logging.basicConfig(level=logging.DEBUG) + +- + try: + os.makedirs(output_directory, 0o700) + except OSError as e: +@@ -861,7 +861,7 @@ if __name__ == "__main__": + else: + raise + +- if output is None or output == "-": ++ if output is None: + fd = sys.stdout.fileno() + else: + fd = os.open(output, os.O_WRONLY) +-- +2.42.0 + diff --git a/xsa443-4.17-07.patch b/xsa443-4.17-07.patch new file mode 100644 index 0000000..94da883 --- /dev/null +++ b/xsa443-4.17-07.patch @@ -0,0 +1,105 @@ +From 9e80cfecde338cea0db136c2fb5ed78d6081e05f Mon Sep 17 00:00:00 2001 +From: Alejandro Vallejo +Date: Mon, 25 Sep 2023 18:32:23 +0100 +Subject: [PATCH 07/11] tools/pygrub: Open the output files earlier + +This patch allows pygrub to get ahold of every RW file descriptor it needs +early on. A later patch will clamp the filesystem it can access so it can't +obtain any others. + +This is part of XSA-443 / CVE-2023-34325 + +Signed-off-by: Alejandro Vallejo +Acked-by: Andrew Cooper +--- + tools/pygrub/src/pygrub | 37 ++++++++++++++++++++++--------------- + 1 file changed, 22 insertions(+), 15 deletions(-) + +diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub +index 1042c05b8676..91e2ec2ab105 100755 +--- a/tools/pygrub/src/pygrub ++++ b/tools/pygrub/src/pygrub +@@ -738,8 +738,7 @@ if __name__ == "__main__": + def usage(): + print("Usage: %s [-q|--quiet] [-i|--interactive] [-l|--list-entries] [-n|--not-really] [--output=] [--kernel=] [--ramdisk=] [--args=] [--entry=] [--output-directory=] [--output-format=sxp|simple|simple0] [--offset=] " %(sys.argv[0],), file=sys.stderr) + +- def copy_from_image(fs, file_to_read, file_type, output_directory, +- not_really): ++ def copy_from_image(fs, file_to_read, file_type, fd_dst, path_dst, not_really): + if not_really: + if fs.file_exists(file_to_read): + return "<%s:%s>" % (file_type, file_to_read) +@@ -750,21 +749,18 @@ if __name__ == "__main__": + except Exception as e: + print(e, file=sys.stderr) + sys.exit("Error opening %s in guest" % file_to_read) +- (tfd, ret) = tempfile.mkstemp(prefix="boot_"+file_type+".", +- dir=output_directory) + dataoff = 0 + while True: + data = datafile.read(FS_READ_MAX, dataoff) + if len(data) == 0: +- os.close(tfd) ++ os.close(fd_dst) + del datafile +- return ret ++ return + try: +- os.write(tfd, data) ++ os.write(fd_dst, data) + except Exception as e: + print(e, file=sys.stderr) +- os.close(tfd) +- os.unlink(ret) ++ os.unlink(path_dst) + del datafile + sys.exit("Error writing temporary copy of "+file_type) + dataoff += len(data) +@@ -861,6 +857,14 @@ if __name__ == "__main__": + else: + raise + ++ if not_really: ++ fd_kernel = path_kernel = fd_ramdisk = path_ramdisk = None ++ else: ++ (fd_kernel, path_kernel) = tempfile.mkstemp(prefix="boot_kernel.", ++ dir=output_directory) ++ (fd_ramdisk, path_ramdisk) = tempfile.mkstemp(prefix="boot_ramdisk.", ++ dir=output_directory) ++ + if output is None: + fd = sys.stdout.fileno() + else: +@@ -920,20 +924,23 @@ if __name__ == "__main__": + if fs is None: + raise RuntimeError("Unable to find partition containing kernel") + +- bootcfg["kernel"] = copy_from_image(fs, chosencfg["kernel"], "kernel", +- output_directory, not_really) ++ copy_from_image(fs, chosencfg["kernel"], "kernel", ++ fd_kernel, path_kernel, not_really) ++ bootcfg["kernel"] = path_kernel + + if chosencfg["ramdisk"]: + try: +- bootcfg["ramdisk"] = copy_from_image(fs, chosencfg["ramdisk"], +- "ramdisk", output_directory, +- not_really) ++ copy_from_image(fs, chosencfg["ramdisk"], "ramdisk", ++ fd_ramdisk, path_ramdisk, not_really) + except: + if not not_really: +- os.unlink(bootcfg["kernel"]) ++ os.unlink(path_kernel) + raise ++ bootcfg["ramdisk"] = path_ramdisk + else: + initrd = None ++ if not not_really: ++ os.unlink(path_ramdisk) + + args = None + if chosencfg["args"]: +-- +2.42.0 + diff --git a/xsa443-4.17-08.patch b/xsa443-4.17-08.patch new file mode 100644 index 0000000..bd7de1d --- /dev/null +++ b/xsa443-4.17-08.patch @@ -0,0 +1,126 @@ +From 2fb4cdcedd8720f78c4bd44739a5d30dd1a7d9a5 Mon Sep 17 00:00:00 2001 +From: Alejandro Vallejo +Date: Mon, 25 Sep 2023 18:32:24 +0100 +Subject: [PATCH 08/11] tools/libfsimage: Export a new function to preload all + plugins + +This is work required in order to let pygrub operate in highly deprivileged +chroot mode. This patch adds a function that preloads every plugin, hence +ensuring that a on function exit, every shared library is loaded in memory. + +The new "init" function is supposed to be used before depriv, but that's +fine because it's not acting on untrusted data. + +This is part of XSA-443 / CVE-2023-34325 + +Signed-off-by: Alejandro Vallejo +Acked-by: Andrew Cooper +--- + tools/libfsimage/common/fsimage_plugin.c | 4 ++-- + tools/libfsimage/common/mapfile-GNU | 1 + + tools/libfsimage/common/mapfile-SunOS | 1 + + tools/libfsimage/common/xenfsimage.h | 8 ++++++++ + tools/pygrub/src/fsimage/fsimage.c | 15 +++++++++++++++ + 5 files changed, 27 insertions(+), 2 deletions(-) + +diff --git a/tools/libfsimage/common/fsimage_plugin.c b/tools/libfsimage/common/fsimage_plugin.c +index de1412b4233a..d0cb9e96a654 100644 +--- a/tools/libfsimage/common/fsimage_plugin.c ++++ b/tools/libfsimage/common/fsimage_plugin.c +@@ -119,7 +119,7 @@ fail: + return (-1); + } + +-static int load_plugins(void) ++int fsi_init(void) + { + const char *fsdir = getenv("XEN_FSIMAGE_FSDIR"); + struct dirent *dp = NULL; +@@ -180,7 +180,7 @@ int find_plugin(fsi_t *fsi, const char *path, const char *options) + fsi_plugin_t *fp; + int ret = 0; + +- if (plugins == NULL && (ret = load_plugins()) != 0) ++ if (plugins == NULL && (ret = fsi_init()) != 0) + goto out; + + for (fp = plugins; fp != NULL; fp = fp->fp_next) { +diff --git a/tools/libfsimage/common/mapfile-GNU b/tools/libfsimage/common/mapfile-GNU +index 26d4d7a69ec7..2d54d527d7f5 100644 +--- a/tools/libfsimage/common/mapfile-GNU ++++ b/tools/libfsimage/common/mapfile-GNU +@@ -1,6 +1,7 @@ + VERSION { + libfsimage.so.1.0 { + global: ++ fsi_init; + fsi_open_fsimage; + fsi_close_fsimage; + fsi_file_exists; +diff --git a/tools/libfsimage/common/mapfile-SunOS b/tools/libfsimage/common/mapfile-SunOS +index e99b90b65077..48deedb4252f 100644 +--- a/tools/libfsimage/common/mapfile-SunOS ++++ b/tools/libfsimage/common/mapfile-SunOS +@@ -1,5 +1,6 @@ + libfsimage.so.1.0 { + global: ++ fsi_init; + fsi_open_fsimage; + fsi_close_fsimage; + fsi_file_exists; +diff --git a/tools/libfsimage/common/xenfsimage.h b/tools/libfsimage/common/xenfsimage.h +index 201abd54f23a..341883b2d71a 100644 +--- a/tools/libfsimage/common/xenfsimage.h ++++ b/tools/libfsimage/common/xenfsimage.h +@@ -35,6 +35,14 @@ extern C { + typedef struct fsi fsi_t; + typedef struct fsi_file fsi_file_t; + ++/* ++ * Optional initialization function. If invoked it loads the associated ++ * dynamic libraries for the backends ahead of time. This is required if ++ * the library is to run as part of a highly deprivileged executable, as ++ * the libraries may not be reachable after depriv. ++ */ ++int fsi_init(void); ++ + fsi_t *fsi_open_fsimage(const char *, uint64_t, const char *); + void fsi_close_fsimage(fsi_t *); + +diff --git a/tools/pygrub/src/fsimage/fsimage.c b/tools/pygrub/src/fsimage/fsimage.c +index 2ebbbe35df92..92fbf2851f01 100644 +--- a/tools/pygrub/src/fsimage/fsimage.c ++++ b/tools/pygrub/src/fsimage/fsimage.c +@@ -286,6 +286,15 @@ fsimage_getbootstring(PyObject *o, PyObject *args) + return Py_BuildValue("s", bootstring); + } + ++static PyObject * ++fsimage_init(PyObject *o, PyObject *args) ++{ ++ if (!PyArg_ParseTuple(args, "")) ++ return (NULL); ++ ++ return Py_BuildValue("i", fsi_init()); ++} ++ + PyDoc_STRVAR(fsimage_open__doc__, + "open(name, [offset=off]) - Open the given file as a filesystem image.\n" + "\n" +@@ -297,7 +306,13 @@ PyDoc_STRVAR(fsimage_getbootstring__doc__, + "getbootstring(fs) - Return the boot string needed for this file system " + "or NULL if none is needed.\n"); + ++PyDoc_STRVAR(fsimage_init__doc__, ++ "init() - Loads every dynamic library contained in xenfsimage " ++ "into memory so that it can be used in chrooted environments.\n"); ++ + static struct PyMethodDef fsimage_module_methods[] = { ++ { "init", (PyCFunction)fsimage_init, ++ METH_VARARGS, fsimage_init__doc__ }, + { "open", (PyCFunction)fsimage_open, + METH_VARARGS|METH_KEYWORDS, fsimage_open__doc__ }, + { "getbootstring", (PyCFunction)fsimage_getbootstring, +-- +2.42.0 + diff --git a/xsa443-4.17-09.patch b/xsa443-4.17-09.patch new file mode 100644 index 0000000..2e3ebd8 --- /dev/null +++ b/xsa443-4.17-09.patch @@ -0,0 +1,307 @@ +From 150771ce86a07e469e34941a63c56e2cf242223b Mon Sep 17 00:00:00 2001 +From: Alejandro Vallejo +Date: Mon, 25 Sep 2023 18:32:25 +0100 +Subject: [PATCH 09/11] tools/pygrub: Deprivilege pygrub + +Introduce a --runas= flag to deprivilege pygrub on Linux and *BSDs. It +also implicitly creates a chroot env where it drops a deprivileged forked +process. The chroot itself is cleaned up at the end. + +If the --runas arg is present, then pygrub forks, leaving the child to +deprivilege itself, and waiting for it to complete. When the child exists, +the parent performs cleanup and exits with the same error code. + +This is roughly what the child does: + 1. Initialize libfsimage (this loads every .so in memory so the chroot + can avoid bind-mounting /{,usr}/lib* + 2. Create a temporary empty chroot directory + 3. Mount tmpfs in it + 4. Bind mount the disk inside, because libfsimage expects a path, not a + file descriptor. + 5. Remount the root tmpfs to be stricter (ro,nosuid,nodev) + 6. Set RLIMIT_FSIZE to a sensibly high amount (128 MiB) + 7. Depriv gid, groups and uid + +With this scheme in place, the "output" files are writable (up to +RLIMIT_FSIZE octets) and the exposed filesystem is immutable and contains +the single only file we can't easily get rid of (the disk). + +If running on Linux, the child process also unshares mount, IPC, and +network namespaces before dropping its privileges. + +This is part of XSA-443 / CVE-2023-34325 + +Signed-off-by: Alejandro Vallejo +Acked-by: Andrew Cooper +--- + tools/pygrub/setup.py | 2 +- + tools/pygrub/src/pygrub | 162 +++++++++++++++++++++++++++++++++++++--- + 2 files changed, 154 insertions(+), 10 deletions(-) + +diff --git a/tools/pygrub/setup.py b/tools/pygrub/setup.py +index 0e4e3d02d372..06b96733d020 100644 +--- a/tools/pygrub/setup.py ++++ b/tools/pygrub/setup.py +@@ -17,7 +17,7 @@ xenfsimage = Extension("xenfsimage", + pkgs = [ 'grub' ] + + setup(name='pygrub', +- version='0.6', ++ version='0.7', + description='Boot loader that looks a lot like grub for Xen', + author='Jeremy Katz', + author_email='katzj@redhat.com', +diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub +index 91e2ec2ab105..7cea496ade08 100755 +--- a/tools/pygrub/src/pygrub ++++ b/tools/pygrub/src/pygrub +@@ -16,8 +16,11 @@ from __future__ import print_function + + import os, sys, string, struct, tempfile, re, traceback, stat, errno + import copy ++import ctypes, ctypes.util + import logging + import platform ++import resource ++import subprocess + + import curses, _curses, curses.textpad, curses.ascii + import getopt +@@ -27,10 +30,135 @@ import grub.GrubConf + import grub.LiloConf + import grub.ExtLinuxConf + +-PYGRUB_VER = 0.6 ++PYGRUB_VER = 0.7 + FS_READ_MAX = 1024 * 1024 + SECTOR_SIZE = 512 + ++# Unless provided through the env variable PYGRUB_MAX_FILE_SIZE_MB, then ++# this is the maximum filesize allowed for files written by the depriv ++# pygrub ++LIMIT_FSIZE = 128 << 20 ++ ++CLONE_NEWNS = 0x00020000 # mount namespace ++CLONE_NEWNET = 0x40000000 # network namespace ++CLONE_NEWIPC = 0x08000000 # IPC namespace ++ ++def unshare(flags): ++ if not sys.platform.startswith("linux"): ++ print("skip_unshare reason=not_linux platform=%s", sys.platform, file=sys.stderr) ++ return ++ ++ libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True) ++ unshare_prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, use_errno=True) ++ unshare = unshare_prototype(('unshare', libc)) ++ ++ if unshare(flags) < 0: ++ raise OSError(ctypes.get_errno(), os.strerror(ctypes.get_errno())) ++ ++def bind_mount(src, dst, options): ++ open(dst, "a").close() # touch ++ ++ rc = subprocess.call(["mount", "--bind", "-o", options, src, dst]) ++ if rc != 0: ++ raise RuntimeError("bad_mount: src=%s dst=%s opts=%s" % ++ (src, dst, options)) ++ ++def downgrade_rlimits(): ++ # Wipe the authority to use unrequired resources ++ resource.setrlimit(resource.RLIMIT_NPROC, (0, 0)) ++ resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) ++ resource.setrlimit(resource.RLIMIT_MEMLOCK, (0, 0)) ++ ++ # py2's resource module doesn't know about resource.RLIMIT_MSGQUEUE ++ # ++ # TODO: Use resource.RLIMIT_MSGQUEUE after python2 is deprecated ++ if sys.platform.startswith('linux'): ++ RLIMIT_MSGQUEUE = 12 ++ resource.setrlimit(RLIMIT_MSGQUEUE, (0, 0)) ++ ++ # The final look of the filesystem for this process is fully RO, but ++ # note we have some file descriptor already open (notably, kernel and ++ # ramdisk). In order to avoid a compromised pygrub from filling up the ++ # filesystem we set RLIMIT_FSIZE to a high bound, so that the file ++ # write permissions are bound. ++ fsize = LIMIT_FSIZE ++ if "PYGRUB_MAX_FILE_SIZE_MB" in os.environ.keys(): ++ fsize = os.environ["PYGRUB_MAX_FILE_SIZE_MB"] << 20 ++ ++ resource.setrlimit(resource.RLIMIT_FSIZE, (fsize, fsize)) ++ ++def depriv(output_directory, output, device, uid, path_kernel, path_ramdisk): ++ # The only point of this call is to force the loading of libfsimage. ++ # That way, we don't need to bind-mount it into the chroot ++ rc = xenfsimage.init() ++ if rc != 0: ++ os.unlink(path_ramdisk) ++ os.unlink(path_kernel) ++ raise RuntimeError("bad_xenfsimage: rc=%d" % rc) ++ ++ # Create a temporary directory for the chroot ++ chroot = tempfile.mkdtemp(prefix=str(uid)+'-', dir=output_directory) + '/' ++ device_path = '/device' ++ ++ pid = os.fork() ++ if pid: ++ # parent ++ _, rc = os.waitpid(pid, 0) ++ ++ for path in [path_kernel, path_ramdisk]: ++ # If the child didn't write anything, just get rid of it, ++ # otherwise we end up consuming a 0-size file when parsing ++ # systems without a ramdisk that the ultimate caller of pygrub ++ # may just be unaware of ++ if rc != 0 or os.path.getsize(path) == 0: ++ os.unlink(path) ++ ++ # Normally, unshare(CLONE_NEWNS) will ensure this is not required. ++ # However, this syscall doesn't exist in *BSD systems and doesn't ++ # auto-unmount everything on older Linux kernels (At least as of ++ # Linux 4.19, but it seems fixed in 5.15). Either way, ++ # recursively unmount everything if needed. Quietly. ++ with open('/dev/null', 'w') as devnull: ++ subprocess.call(["umount", "-f", chroot + device_path], ++ stdout=devnull, stderr=devnull) ++ subprocess.call(["umount", "-f", chroot], ++ stdout=devnull, stderr=devnull) ++ os.rmdir(chroot) ++ ++ sys.exit(rc) ++ ++ # By unsharing the namespace we're making sure it's all bulk-released ++ # at the end, when the namespaces disappear. This means the kernel does ++ # (almost) all the cleanup for us and the parent just has to remove the ++ # temporary directory. ++ unshare(CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWNET) ++ ++ # Set sensible limits using the setrlimit interface ++ downgrade_rlimits() ++ ++ # We'll mount tmpfs on the chroot to ensure the deprivileged child ++ # cannot affect the persistent state. It's RW now in order to ++ # bind-mount the device, but note it's remounted RO after that. ++ rc = subprocess.call(["mount", "-t", "tmpfs", "none", chroot]) ++ if rc != 0: ++ raise RuntimeError("mount_tmpfs rc=%d dst=\"%s\"" % (rc, chroot)) ++ ++ # Bind the untrusted device RO ++ bind_mount(device, chroot + device_path, "ro,nosuid,noexec") ++ ++ rc = subprocess.call(["mount", "-t", "tmpfs", "-o", "remount,ro,nosuid,noexec,nodev", "none", chroot]) ++ if rc != 0: ++ raise RuntimeError("remount_tmpfs rc=%d dst=\"%s\"" % (rc, chroot)) ++ ++ # Drop superpowers! ++ os.chroot(chroot) ++ os.chdir('/') ++ os.setgid(uid) ++ os.setgroups([uid]) ++ os.setuid(uid) ++ ++ return device_path ++ + def read_size_roundup(fd, size): + if platform.system() != 'FreeBSD': + return size +@@ -736,7 +864,7 @@ if __name__ == "__main__": + sel = None + + def usage(): +- print("Usage: %s [-q|--quiet] [-i|--interactive] [-l|--list-entries] [-n|--not-really] [--output=] [--kernel=] [--ramdisk=] [--args=] [--entry=] [--output-directory=] [--output-format=sxp|simple|simple0] [--offset=] " %(sys.argv[0],), file=sys.stderr) ++ print("Usage: %s [-q|--quiet] [-i|--interactive] [-l|--list-entries] [-n|--not-really] [--output=] [--kernel=] [--ramdisk=] [--args=] [--entry=] [--output-directory=] [--output-format=sxp|simple|simple0] [--runas=] [--offset=] " %(sys.argv[0],), file=sys.stderr) + + def copy_from_image(fs, file_to_read, file_type, fd_dst, path_dst, not_really): + if not_really: +@@ -760,7 +888,8 @@ if __name__ == "__main__": + os.write(fd_dst, data) + except Exception as e: + print(e, file=sys.stderr) +- os.unlink(path_dst) ++ if path_dst: ++ os.unlink(path_dst) + del datafile + sys.exit("Error writing temporary copy of "+file_type) + dataoff += len(data) +@@ -769,7 +898,7 @@ if __name__ == "__main__": + opts, args = getopt.gnu_getopt(sys.argv[1:], 'qilnh::', + ["quiet", "interactive", "list-entries", "not-really", "help", + "output=", "output-format=", "output-directory=", "offset=", +- "entry=", "kernel=", ++ "runas=", "entry=", "kernel=", + "ramdisk=", "args=", "isconfig", "debug"]) + except getopt.GetoptError: + usage() +@@ -790,6 +919,7 @@ if __name__ == "__main__": + not_really = False + output_format = "sxp" + output_directory = "/var/run/xen/pygrub/" ++ uid = None + + # what was passed in + incfg = { "kernel": None, "ramdisk": None, "args": "" } +@@ -813,6 +943,13 @@ if __name__ == "__main__": + elif o in ("--output",): + if a != "-": + output = a ++ elif o in ("--runas",): ++ try: ++ uid = int(a) ++ except ValueError: ++ print("runas value must be an integer user id") ++ usage() ++ sys.exit(1) + elif o in ("--kernel",): + incfg["kernel"] = a + elif o in ("--ramdisk",): +@@ -849,6 +986,10 @@ if __name__ == "__main__": + if debug: + logging.basicConfig(level=logging.DEBUG) + ++ if interactive and uid: ++ print("In order to use --runas, you must also set --entry or -q", file=sys.stderr) ++ sys.exit(1) ++ + try: + os.makedirs(output_directory, 0o700) + except OSError as e: +@@ -870,6 +1011,9 @@ if __name__ == "__main__": + else: + fd = os.open(output, os.O_WRONLY) + ++ if uid: ++ file = depriv(output_directory, output, file, uid, path_kernel, path_ramdisk) ++ + # debug + if isconfig: + chosencfg = run_grub(file, entry, fs, incfg["args"]) +@@ -925,21 +1069,21 @@ if __name__ == "__main__": + raise RuntimeError("Unable to find partition containing kernel") + + copy_from_image(fs, chosencfg["kernel"], "kernel", +- fd_kernel, path_kernel, not_really) ++ fd_kernel, None if uid else path_kernel, not_really) + bootcfg["kernel"] = path_kernel + + if chosencfg["ramdisk"]: + try: + copy_from_image(fs, chosencfg["ramdisk"], "ramdisk", +- fd_ramdisk, path_ramdisk, not_really) ++ fd_ramdisk, None if uid else path_ramdisk, not_really) + except: +- if not not_really: +- os.unlink(path_kernel) ++ if not uid and not not_really: ++ os.unlink(path_kernel) + raise + bootcfg["ramdisk"] = path_ramdisk + else: + initrd = None +- if not not_really: ++ if not uid and not not_really: + os.unlink(path_ramdisk) + + args = None +-- +2.42.0 + diff --git a/xsa443-4.17-10.patch b/xsa443-4.17-10.patch new file mode 100644 index 0000000..7c91f32 --- /dev/null +++ b/xsa443-4.17-10.patch @@ -0,0 +1,250 @@ +From 698b451473a6d868ca0f60a124fc4f31d81cd7b1 Mon Sep 17 00:00:00 2001 +From: Roger Pau Monne +Date: Mon, 25 Sep 2023 14:30:20 +0200 +Subject: [PATCH 10/11] libxl: add support for running bootloader in restricted + mode +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Much like the device model depriv mode, add the same kind of support for the +bootloader. Such feature allows passing a UID as a parameter for the +bootloader to run as, together with the bootloader itself taking the necessary +actions to isolate. + +Note that the user to run the bootloader as must have the right permissions to +access the guest disk image (in read mode only), and that the bootloader will +be run in non-interactive mode when restricted. + +If enabled bootloader restrict mode will attempt to re-use the user(s) from the +QEMU depriv implementation if no user is provided on the configuration file or +the environment. See docs/features/qemu-deprivilege.pandoc for more +information about how to setup those users. + +Bootloader restrict mode is not enabled by default as it requires certain +setup to be done first (setup of the user(s) to use in restrict mode). + +This is part of XSA-443 / CVE-2023-34325 + +Signed-off-by: Roger Pau Monné +Reviewed-by: Anthony PERARD +--- + docs/man/xl.1.pod.in | 33 +++++++++++ + tools/libs/light/libxl_bootloader.c | 89 ++++++++++++++++++++++++++++- + tools/libs/light/libxl_dm.c | 8 +-- + tools/libs/light/libxl_internal.h | 8 +++ + 4 files changed, 131 insertions(+), 7 deletions(-) + +diff --git a/docs/man/xl.1.pod.in b/docs/man/xl.1.pod.in +index 101e14241d1c..4831e122427d 100644 +--- a/docs/man/xl.1.pod.in ++++ b/docs/man/xl.1.pod.in +@@ -1957,6 +1957,39 @@ ignored: + + =back + ++=head1 ENVIRONMENT VARIABLES ++ ++The following environment variables shall affect the execution of xl: ++ ++=over 4 ++ ++=item LIBXL_BOOTLOADER_RESTRICT ++ ++Attempt to restrict the bootloader after startup, to limit the ++consequences of security vulnerabilities due to parsing guest ++owned image files. ++ ++See docs/features/qemu-deprivilege.pandoc for more information ++on how to setup the unprivileged users. ++ ++Note that running the bootloader in restricted mode also implies using ++non-interactive mode, and the disk image must be readable by the ++restricted user. ++ ++Having this variable set is equivalent to enabling the option, even if the ++value is 0. ++ ++=item LIBXL_BOOTLOADER_USER ++ ++When using bootloader_restrict, run the bootloader as this user. If ++not set the default QEMU restrict users will be used. ++ ++NOTE: Each domain MUST have a SEPARATE username. ++ ++See docs/features/qemu-deprivilege.pandoc for more information. ++ ++=back ++ + =head1 SEE ALSO + + The following man pages: +diff --git a/tools/libs/light/libxl_bootloader.c b/tools/libs/light/libxl_bootloader.c +index 108329b4a5bb..23c0ef3e8935 100644 +--- a/tools/libs/light/libxl_bootloader.c ++++ b/tools/libs/light/libxl_bootloader.c +@@ -14,6 +14,7 @@ + + #include "libxl_osdeps.h" /* must come before any other headers */ + ++#include + #include + #ifdef HAVE_UTMP_H + #include +@@ -42,8 +43,71 @@ static void bootloader_arg(libxl__bootloader_state *bl, const char *arg) + bl->args[bl->nargs++] = arg; + } + +-static void make_bootloader_args(libxl__gc *gc, libxl__bootloader_state *bl, +- const char *bootloader_path) ++static int bootloader_uid(libxl__gc *gc, domid_t guest_domid, ++ const char *user, uid_t *intended_uid) ++{ ++ struct passwd *user_base, user_pwbuf; ++ int rc; ++ ++ if (user) { ++ rc = userlookup_helper_getpwnam(gc, user, &user_pwbuf, &user_base); ++ if (rc) return rc; ++ ++ if (!user_base) { ++ LOGD(ERROR, guest_domid, "Couldn't find user %s", user); ++ return ERROR_INVAL; ++ } ++ ++ *intended_uid = user_base->pw_uid; ++ return 0; ++ } ++ ++ /* Re-use QEMU user range for the bootloader. */ ++ rc = userlookup_helper_getpwnam(gc, LIBXL_QEMU_USER_RANGE_BASE, ++ &user_pwbuf, &user_base); ++ if (rc) return rc; ++ ++ if (user_base) { ++ struct passwd *user_clash, user_clash_pwbuf; ++ uid_t temp_uid = user_base->pw_uid + guest_domid; ++ ++ rc = userlookup_helper_getpwuid(gc, temp_uid, &user_clash_pwbuf, ++ &user_clash); ++ if (rc) return rc; ++ ++ if (user_clash) { ++ LOGD(ERROR, guest_domid, ++ "wanted to use uid %ld (%s + %d) but that is user %s !", ++ (long)temp_uid, LIBXL_QEMU_USER_RANGE_BASE, ++ guest_domid, user_clash->pw_name); ++ return ERROR_INVAL; ++ } ++ ++ *intended_uid = temp_uid; ++ return 0; ++ } ++ ++ rc = userlookup_helper_getpwnam(gc, LIBXL_QEMU_USER_SHARED, &user_pwbuf, ++ &user_base); ++ if (rc) return rc; ++ ++ if (user_base) { ++ LOGD(WARN, guest_domid, "Could not find user %s, falling back to %s", ++ LIBXL_QEMU_USER_RANGE_BASE, LIBXL_QEMU_USER_SHARED); ++ *intended_uid = user_base->pw_uid; ++ ++ return 0; ++ } ++ ++ LOGD(ERROR, guest_domid, ++ "Could not find user %s or range base pseudo-user %s, cannot restrict", ++ LIBXL_QEMU_USER_SHARED, LIBXL_QEMU_USER_RANGE_BASE); ++ ++ return ERROR_INVAL; ++} ++ ++static int make_bootloader_args(libxl__gc *gc, libxl__bootloader_state *bl, ++ const char *bootloader_path) + { + const libxl_domain_build_info *info = bl->info; + +@@ -61,6 +125,23 @@ static void make_bootloader_args(libxl__gc *gc, libxl__bootloader_state *bl, + ARG(GCSPRINTF("--ramdisk=%s", info->ramdisk)); + if (info->cmdline && *info->cmdline != '\0') + ARG(GCSPRINTF("--args=%s", info->cmdline)); ++ if (getenv("LIBXL_BOOTLOADER_RESTRICT") || ++ getenv("LIBXL_BOOTLOADER_USER")) { ++ uid_t uid = -1; ++ int rc = bootloader_uid(gc, bl->domid, getenv("LIBXL_BOOTLOADER_USER"), ++ &uid); ++ ++ if (rc) return rc; ++ ++ assert(uid != -1); ++ if (!uid) { ++ LOGD(ERROR, bl->domid, "bootloader restrict UID is 0 (root)!"); ++ return ERROR_INVAL; ++ } ++ LOGD(DEBUG, bl->domid, "using uid %ld", (long)uid); ++ ARG(GCSPRINTF("--runas=%ld", (long)uid)); ++ ARG("--quiet"); ++ } + + ARG(GCSPRINTF("--output=%s", bl->outputpath)); + ARG("--output-format=simple0"); +@@ -79,6 +160,7 @@ static void make_bootloader_args(libxl__gc *gc, libxl__bootloader_state *bl, + /* Sentinel for execv */ + ARG(NULL); + ++ return 0; + #undef ARG + } + +@@ -443,7 +525,8 @@ static void bootloader_disk_attached_cb(libxl__egc *egc, + bootloader = bltmp; + } + +- make_bootloader_args(gc, bl, bootloader); ++ rc = make_bootloader_args(gc, bl, bootloader); ++ if (rc) goto out; + + bl->openpty.ao = ao; + bl->openpty.callback = bootloader_gotptys; +diff --git a/tools/libs/light/libxl_dm.c b/tools/libs/light/libxl_dm.c +index fc264a3a13a6..14b593110f7c 100644 +--- a/tools/libs/light/libxl_dm.c ++++ b/tools/libs/light/libxl_dm.c +@@ -80,10 +80,10 @@ static int libxl__create_qemu_logfile(libxl__gc *gc, char *name) + * On error, return a libxl-style error code. + */ + #define DEFINE_USERLOOKUP_HELPER(NAME,SPEC_TYPE,STRUCTNAME,SYSCONF) \ +- static int userlookup_helper_##NAME(libxl__gc *gc, \ +- SPEC_TYPE spec, \ +- struct STRUCTNAME *resultbuf, \ +- struct STRUCTNAME **out) \ ++ int userlookup_helper_##NAME(libxl__gc *gc, \ ++ SPEC_TYPE spec, \ ++ struct STRUCTNAME *resultbuf, \ ++ struct STRUCTNAME **out) \ + { \ + struct STRUCTNAME *resultp = NULL; \ + char *buf = NULL; \ +diff --git a/tools/libs/light/libxl_internal.h b/tools/libs/light/libxl_internal.h +index 7ad38de30e0b..f1e3a9a15b13 100644 +--- a/tools/libs/light/libxl_internal.h ++++ b/tools/libs/light/libxl_internal.h +@@ -4873,6 +4873,14 @@ struct libxl__cpu_policy { + struct xc_msr *msr; + }; + ++struct passwd; ++_hidden int userlookup_helper_getpwnam(libxl__gc*, const char *user, ++ struct passwd *res, ++ struct passwd **out); ++_hidden int userlookup_helper_getpwuid(libxl__gc*, uid_t uid, ++ struct passwd *res, ++ struct passwd **out); ++ + #endif + + /* +-- +2.42.0 + diff --git a/xsa443-4.17-11.patch b/xsa443-4.17-11.patch new file mode 100644 index 0000000..27e6f78 --- /dev/null +++ b/xsa443-4.17-11.patch @@ -0,0 +1,157 @@ +From 9d480426bfa2c68843ac8395b512e06fbdbcf53e Mon Sep 17 00:00:00 2001 +From: Roger Pau Monne +Date: Thu, 28 Sep 2023 12:22:35 +0200 +Subject: [PATCH 11/11] libxl: limit bootloader execution in restricted mode +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Introduce a timeout for bootloader execution when running in restricted mode. + +Allow overwriting the default time out with an environment provided value. + +This is part of XSA-443 / CVE-2023-34325 + +Signed-off-by: Roger Pau Monné +Reviewed-by: Anthony PERARD +--- + docs/man/xl.1.pod.in | 8 ++++++ + tools/libs/light/libxl_bootloader.c | 40 +++++++++++++++++++++++++++++ + tools/libs/light/libxl_internal.h | 2 ++ + 3 files changed, 50 insertions(+) + +diff --git a/docs/man/xl.1.pod.in b/docs/man/xl.1.pod.in +index 4831e122427d..c3eb6570ab8b 100644 +--- a/docs/man/xl.1.pod.in ++++ b/docs/man/xl.1.pod.in +@@ -1988,6 +1988,14 @@ NOTE: Each domain MUST have a SEPARATE username. + + See docs/features/qemu-deprivilege.pandoc for more information. + ++=item LIBXL_BOOTLOADER_TIMEOUT ++ ++Timeout in seconds for bootloader execution when running in restricted mode. ++Otherwise the build time default in LIBXL_BOOTLOADER_TIMEOUT will be used. ++ ++If defined the value must be an unsigned integer between 0 and INT_MAX, ++otherwise behavior is undefined. Setting to 0 disables the timeout. ++ + =back + + =head1 SEE ALSO +diff --git a/tools/libs/light/libxl_bootloader.c b/tools/libs/light/libxl_bootloader.c +index 23c0ef3e8935..ee26d08f3765 100644 +--- a/tools/libs/light/libxl_bootloader.c ++++ b/tools/libs/light/libxl_bootloader.c +@@ -30,6 +30,8 @@ static void bootloader_keystrokes_copyfail(libxl__egc *egc, + libxl__datacopier_state *dc, int rc, int onwrite, int errnoval); + static void bootloader_display_copyfail(libxl__egc *egc, + libxl__datacopier_state *dc, int rc, int onwrite, int errnoval); ++static void bootloader_timeout(libxl__egc *egc, libxl__ev_time *ev, ++ const struct timeval *requested_abs, int rc); + static void bootloader_domaindeath(libxl__egc*, libxl__domaindeathcheck *dc, + int rc); + static void bootloader_finished(libxl__egc *egc, libxl__ev_child *child, +@@ -297,6 +299,7 @@ void libxl__bootloader_init(libxl__bootloader_state *bl) + bl->ptys[0].master = bl->ptys[0].slave = 0; + bl->ptys[1].master = bl->ptys[1].slave = 0; + libxl__ev_child_init(&bl->child); ++ libxl__ev_time_init(&bl->time); + libxl__domaindeathcheck_init(&bl->deathcheck); + bl->keystrokes.ao = bl->ao; libxl__datacopier_init(&bl->keystrokes); + bl->display.ao = bl->ao; libxl__datacopier_init(&bl->display); +@@ -314,6 +317,7 @@ static void bootloader_cleanup(libxl__egc *egc, libxl__bootloader_state *bl) + libxl__domaindeathcheck_stop(gc,&bl->deathcheck); + libxl__datacopier_kill(&bl->keystrokes); + libxl__datacopier_kill(&bl->display); ++ libxl__ev_time_deregister(gc, &bl->time); + for (i=0; i<2; i++) { + libxl__carefd_close(bl->ptys[i].master); + libxl__carefd_close(bl->ptys[i].slave); +@@ -375,6 +379,7 @@ static void bootloader_stop(libxl__egc *egc, + + libxl__datacopier_kill(&bl->keystrokes); + libxl__datacopier_kill(&bl->display); ++ libxl__ev_time_deregister(gc, &bl->time); + if (libxl__ev_child_inuse(&bl->child)) { + r = kill(bl->child.pid, SIGTERM); + if (r) LOGED(WARN, bl->domid, "%sfailed to kill bootloader [%lu]", +@@ -637,6 +642,25 @@ static void bootloader_gotptys(libxl__egc *egc, libxl__openpty_state *op) + + struct termios termattr; + ++ if (getenv("LIBXL_BOOTLOADER_RESTRICT") || ++ getenv("LIBXL_BOOTLOADER_USER")) { ++ const char *timeout_env = getenv("LIBXL_BOOTLOADER_TIMEOUT"); ++ int timeout = timeout_env ? atoi(timeout_env) ++ : LIBXL_BOOTLOADER_TIMEOUT; ++ ++ if (timeout) { ++ /* Set execution timeout */ ++ rc = libxl__ev_time_register_rel(ao, &bl->time, ++ bootloader_timeout, ++ timeout * 1000); ++ if (rc) { ++ LOGED(ERROR, bl->domid, ++ "unable to register timeout for bootloader execution"); ++ goto out; ++ } ++ } ++ } ++ + pid_t pid = libxl__ev_child_fork(gc, &bl->child, bootloader_finished); + if (pid == -1) { + rc = ERROR_FAIL; +@@ -702,6 +726,21 @@ static void bootloader_display_copyfail(libxl__egc *egc, + libxl__bootloader_state *bl = CONTAINER_OF(dc, *bl, display); + bootloader_copyfail(egc, "bootloader output", bl, 1, rc,onwrite,errnoval); + } ++static void bootloader_timeout(libxl__egc *egc, libxl__ev_time *ev, ++ const struct timeval *requested_abs, int rc) ++{ ++ libxl__bootloader_state *bl = CONTAINER_OF(ev, *bl, time); ++ STATE_AO_GC(bl->ao); ++ ++ libxl__ev_time_deregister(gc, &bl->time); ++ ++ assert(libxl__ev_child_inuse(&bl->child)); ++ LOGD(ERROR, bl->domid, "killing bootloader because of timeout"); ++ ++ libxl__ev_child_kill_deregister(ao, &bl->child, SIGKILL); ++ ++ bootloader_callback(egc, bl, rc); ++} + + static void bootloader_domaindeath(libxl__egc *egc, + libxl__domaindeathcheck *dc, +@@ -718,6 +757,7 @@ static void bootloader_finished(libxl__egc *egc, libxl__ev_child *child, + STATE_AO_GC(bl->ao); + int rc; + ++ libxl__ev_time_deregister(gc, &bl->time); + libxl__datacopier_kill(&bl->keystrokes); + libxl__datacopier_kill(&bl->display); + +diff --git a/tools/libs/light/libxl_internal.h b/tools/libs/light/libxl_internal.h +index f1e3a9a15b13..d05783617ff5 100644 +--- a/tools/libs/light/libxl_internal.h ++++ b/tools/libs/light/libxl_internal.h +@@ -102,6 +102,7 @@ + #define LIBXL_QMP_CMD_TIMEOUT 10 + #define LIBXL_STUBDOM_START_TIMEOUT 30 + #define LIBXL_QEMU_BODGE_TIMEOUT 2 ++#define LIBXL_BOOTLOADER_TIMEOUT 120 + #define LIBXL_XENCONSOLE_LIMIT 1048576 + #define LIBXL_XENCONSOLE_PROTOCOL "vt100" + #define LIBXL_MAXMEM_CONSTANT 1024 +@@ -3744,6 +3745,7 @@ struct libxl__bootloader_state { + libxl__openpty_state openpty; + libxl__openpty_result ptys[2]; /* [0] is for bootloader */ + libxl__ev_child child; ++ libxl__ev_time time; + libxl__domaindeathcheck deathcheck; + int nargs, argsspace; + const char **args; +-- +2.42.0 + diff --git a/xsa444-4.17-1.patch b/xsa444-4.17-1.patch new file mode 100644 index 0000000..5a4b2e5 --- /dev/null +++ b/xsa444-4.17-1.patch @@ -0,0 +1,93 @@ +From: Andrew Cooper +Subject: x86/svm: Fix asymmetry with AMD DR MASK context switching + +The handling of MSR_DR{0..3}_MASK is asymmetric between PV and HVM guests. + +HVM guests context switch in based on the guest view of DBEXT, whereas PV +guest switch in base on the host capability. Both guest types leave the +context dirty for the next vCPU. + +This leads to the following issue: + + * PV or HVM guest has debugging active (%dr7 + mask) + * Switch-out deactivates %dr7 but leaves other state stale in hardware + * Another HVM guest with masks unavailable has debugging active + * Switch in loads %dr7 but leaves the mask MSRs alone + +Now, the second guest's vCPU is operating in the context of the prior vCPU's +mask MSR, while the environment the vCPU can see says there are no mask MSRs. + +As a stopgap, adjust the HVM path to switch in the masks based on host +capabilities rather than guest visibility (i.e. like the PV path). Adjustment +of the intercepts still needs to be dependent on the guest visibility of +DBEXT. + +This is part of XSA-444 / CVE-2023-34327 + +Fixes: c097f54912d3 ("x86/SVM: support data breakpoint extension registers") +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c +index e8f50e7c5ec7..fd32600ae364 100644 +--- a/xen/arch/x86/hvm/svm/svm.c ++++ b/xen/arch/x86/hvm/svm/svm.c +@@ -339,6 +339,10 @@ static void svm_save_dr(struct vcpu *v) + v->arch.hvm.flag_dr_dirty = 0; + vmcb_set_dr_intercepts(vmcb, ~0u); + ++ /* ++ * The guest can only have changed the mask MSRs if we previous dropped ++ * intercepts. Re-read them from hardware. ++ */ + if ( v->domain->arch.cpuid->extd.dbext ) + { + svm_intercept_msr(v, MSR_AMD64_DR0_ADDRESS_MASK, MSR_INTERCEPT_RW); +@@ -370,17 +374,25 @@ static void __restore_debug_registers(struct vmcb_struct *vmcb, struct vcpu *v) + + ASSERT(v == current); + +- if ( v->domain->arch.cpuid->extd.dbext ) ++ /* ++ * Both the PV and HVM paths leave stale DR_MASK values in hardware on ++ * context-switch-out. If we're activating %dr7 for the guest, we must ++ * sync the DR_MASKs too, whether or not the guest can see them. ++ */ ++ if ( boot_cpu_has(X86_FEATURE_DBEXT) ) + { +- svm_intercept_msr(v, MSR_AMD64_DR0_ADDRESS_MASK, MSR_INTERCEPT_NONE); +- svm_intercept_msr(v, MSR_AMD64_DR1_ADDRESS_MASK, MSR_INTERCEPT_NONE); +- svm_intercept_msr(v, MSR_AMD64_DR2_ADDRESS_MASK, MSR_INTERCEPT_NONE); +- svm_intercept_msr(v, MSR_AMD64_DR3_ADDRESS_MASK, MSR_INTERCEPT_NONE); +- + wrmsrl(MSR_AMD64_DR0_ADDRESS_MASK, v->arch.msrs->dr_mask[0]); + wrmsrl(MSR_AMD64_DR1_ADDRESS_MASK, v->arch.msrs->dr_mask[1]); + wrmsrl(MSR_AMD64_DR2_ADDRESS_MASK, v->arch.msrs->dr_mask[2]); + wrmsrl(MSR_AMD64_DR3_ADDRESS_MASK, v->arch.msrs->dr_mask[3]); ++ ++ if ( v->domain->arch.cpuid->extd.dbext ) ++ { ++ svm_intercept_msr(v, MSR_AMD64_DR0_ADDRESS_MASK, MSR_INTERCEPT_NONE); ++ svm_intercept_msr(v, MSR_AMD64_DR1_ADDRESS_MASK, MSR_INTERCEPT_NONE); ++ svm_intercept_msr(v, MSR_AMD64_DR2_ADDRESS_MASK, MSR_INTERCEPT_NONE); ++ svm_intercept_msr(v, MSR_AMD64_DR3_ADDRESS_MASK, MSR_INTERCEPT_NONE); ++ } + } + + write_debugreg(0, v->arch.dr[0]); +diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c +index e65cc6004148..06c4f3868b7a 100644 +--- a/xen/arch/x86/traps.c ++++ b/xen/arch/x86/traps.c +@@ -2281,6 +2281,11 @@ void activate_debugregs(const struct vcpu *curr) + if ( curr->arch.dr7 & DR7_ACTIVE_MASK ) + write_debugreg(7, curr->arch.dr7); + ++ /* ++ * Both the PV and HVM paths leave stale DR_MASK values in hardware on ++ * context-switch-out. If we're activating %dr7 for the guest, we must ++ * sync the DR_MASKs too, whether or not the guest can see them. ++ */ + if ( boot_cpu_has(X86_FEATURE_DBEXT) ) + { + wrmsrl(MSR_AMD64_DR0_ADDRESS_MASK, curr->arch.msrs->dr_mask[0]); diff --git a/xsa444-4.17-2.patch b/xsa444-4.17-2.patch new file mode 100644 index 0000000..2687bd1 --- /dev/null +++ b/xsa444-4.17-2.patch @@ -0,0 +1,72 @@ +From: Andrew Cooper +Subject: x86/pv: Correct the auditing of guest breakpoint addresses + +The use of access_ok() is buggy, because it permits access to the compat +translation area. 64bit PV guests don't use the XLAT area, but on AMD +hardware, the DBEXT feature allows a breakpoint to match up to a 4G aligned +region, allowing the breakpoint to reach outside of the XLAT area. + +Prior to c/s cda16c1bb223 ("x86: mirror compat argument translation area for +32-bit PV"), the live GDT was within 4G of the XLAT area. + +All together, this allowed a malicious 64bit PV guest on AMD hardware to place +a breakpoint over the live GDT, and trigger a #DB livelock (CVE-2015-8104). + +Introduce breakpoint_addr_ok() and explain why __addr_ok() happens to be an +appropriate check in this case. + +For Xen 4.14 and later, this is a latent bug because the XLAT area has moved +to be on its own with nothing interesting adjacent. For Xen 4.13 and older on +AMD hardware, this fixes a PV-trigger-able DoS. + +This is part of XSA-444 / CVE-2023-34328. + +Fixes: 65e355490817 ("x86/PV: support data breakpoint extension registers") +Signed-off-by: Andrew Cooper +Reviewed-by: Roger Pau Monné +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/include/asm/debugreg.h b/xen/arch/x86/include/asm/debugreg.h +index c57914efc6e8..cc298265244b 100644 +--- a/xen/arch/x86/include/asm/debugreg.h ++++ b/xen/arch/x86/include/asm/debugreg.h +@@ -77,6 +77,26 @@ + asm volatile ( "mov %%db" #reg ",%0" : "=r" (__val) ); \ + __val; \ + }) ++ ++/* ++ * Architecturally, %dr{0..3} can have any arbitrary value. However, Xen ++ * can't allow the guest to breakpoint the Xen address range, so we limit the ++ * guest to the lower canonical half, or above the Xen range in the higher ++ * canonical half. ++ * ++ * Breakpoint lengths are specified to mask the low order address bits, ++ * meaning all breakpoints are naturally aligned. With %dr7, the widest ++ * breakpoint is 8 bytes. With DBEXT, the widest breakpoint is 4G. Both of ++ * the Xen boundaries have >4G alignment. ++ * ++ * In principle we should account for HYPERVISOR_COMPAT_VIRT_START(d), but ++ * 64bit Xen has never enforced this for compat guests, and there's no problem ++ * (to Xen) if the guest breakpoints it's alias of the M2P. Skipping this ++ * aspect simplifies the logic, and causes us not to reject a migrating guest ++ * which operated fine on prior versions of Xen. ++ */ ++#define breakpoint_addr_ok(a) __addr_ok(a) ++ + long set_debugreg(struct vcpu *, unsigned int reg, unsigned long value); + void activate_debugregs(const struct vcpu *); + +diff --git a/xen/arch/x86/pv/misc-hypercalls.c b/xen/arch/x86/pv/misc-hypercalls.c +index aaaf70eb6330..f8636de907ae 100644 +--- a/xen/arch/x86/pv/misc-hypercalls.c ++++ b/xen/arch/x86/pv/misc-hypercalls.c +@@ -72,7 +72,7 @@ long set_debugreg(struct vcpu *v, unsigned int reg, unsigned long value) + switch ( reg ) + { + case 0 ... 3: +- if ( !access_ok(value, sizeof(long)) ) ++ if ( !breakpoint_addr_ok(value) ) + return -EPERM; + + v->arch.dr[reg] = value; From b5231bddba35ce2daf5df35532b14017b3cda8a2 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 14 Nov 2023 22:32:53 +0000 Subject: [PATCH 078/137] 2 security issues x86/AMD: mismatch in IOMMU quarantine page table levels [XSA-445, CVE-2023-46835] x86: BTC/SRSO fixes not fully effective [XSA-446, CVE-2023-46836] --- xen.spec | 11 ++++- xsa445-4.17.patch | 63 +++++++++++++++++++++++++ xsa446.patch | 115 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 xsa445-4.17.patch create mode 100644 xsa446.patch diff --git a/xen.spec b/xen.spec index fde47fb..1845570 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.17.2 -Release: 4%{?dist} +Release: 5%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -139,6 +139,8 @@ Patch74: xsa443-4.17-10.patch Patch75: xsa443-4.17-11.patch Patch76: xsa444-4.17-1.patch Patch77: xsa444-4.17-2.patch +Patch78: xsa445-4.17.patch +Patch79: xsa446.patch %if %build_qemutrad @@ -382,6 +384,8 @@ manage Xen virtual machines. %patch 75 -p1 %patch 76 -p1 %patch 77 -p1 +%patch 78 -p1 +%patch 79 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -989,6 +993,11 @@ fi %endif %changelog +* Tue Nov 14 2023 Michael Young - 4.17.2-5 +- x86/AMD: mismatch in IOMMU quarantine page table levels [XSA-445, + CVE-2023-46835] +- x86: BTC/SRSO fixes not fully effective [XSA-446, CVE-2023-46836] + * Tue Oct 10 2023 Michael Young - 4.17.2-4 - xenstored: A transaction conflict can crash C Xenstored [XSA-440, CVE-2023-34323] diff --git a/xsa445-4.17.patch b/xsa445-4.17.patch new file mode 100644 index 0000000..db66d7c --- /dev/null +++ b/xsa445-4.17.patch @@ -0,0 +1,63 @@ +From a43127d4f1f9a364334fe16b6239c211b35fd238 Mon Sep 17 00:00:00 2001 +From: Roger Pau Monne +Date: Wed, 11 Oct 2023 13:14:21 +0200 +Subject: [PATCH] iommu/amd-vi: use correct level for quarantine domain page + tables +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The current setup of the quarantine page tables assumes that the quarantine +domain (dom_io) has been initialized with an address width of +DEFAULT_DOMAIN_ADDRESS_WIDTH (48). + +However dom_io being a PV domain gets the AMD-Vi IOMMU page tables levels based +on the maximum (hot pluggable) RAM address, and hence on systems with no RAM +above the 512GB mark only 3 page-table levels are configured in the IOMMU. + +On systems without RAM above the 512GB boundary amd_iommu_quarantine_init() +will setup page tables for the scratch page with 4 levels, while the IOMMU will +be configured to use 3 levels only. The page destined to be used as level 1, +and to contain a directory of PTEs ends up being the address in a PTE itself, +and thus level 1 page becomes the leaf page. Without the level mismatch it's +level 0 page that should be the leaf page instead. + +The level 1 page won't be used as such, and hence it's not possible to use it +to gain access to other memory on the system. However that page is not cleared +in amd_iommu_quarantine_init() as part of re-initialization of the device +quarantine page tables, and hence data on the level 1 page can be leaked +between device usages. + +Fix this by making sure the paging levels setup by amd_iommu_quarantine_init() +match the number configured on the IOMMUs. + +Note that IVMD regions are not affected by this issue, as those areas are +mapped taking the configured paging levels into account. + +This is XSA-445 / CVE-2023-46835 + +Fixes: ea38867831da ('x86 / iommu: set up a scratch page in the quarantine domain') +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +--- + xen/drivers/passthrough/amd/iommu_map.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/xen/drivers/passthrough/amd/iommu_map.c b/xen/drivers/passthrough/amd/iommu_map.c +index 993bac6f8878..e0f4fe736a8d 100644 +--- a/xen/drivers/passthrough/amd/iommu_map.c ++++ b/xen/drivers/passthrough/amd/iommu_map.c +@@ -837,9 +837,7 @@ static int fill_qpt(union amd_iommu_pte *this, unsigned int level, + int cf_check amd_iommu_quarantine_init(struct pci_dev *pdev, bool scratch_page) + { + struct domain_iommu *hd = dom_iommu(dom_io); +- unsigned long end_gfn = +- 1ul << (DEFAULT_DOMAIN_ADDRESS_WIDTH - PAGE_SHIFT); +- unsigned int level = amd_iommu_get_paging_mode(end_gfn); ++ unsigned int level = hd->arch.amd.paging_mode; + unsigned int req_id = get_dma_requestor_id(pdev->seg, pdev->sbdf.bdf); + const struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(pdev->seg); + int rc; +-- +2.42.0 + diff --git a/xsa446.patch b/xsa446.patch new file mode 100644 index 0000000..acf1d0f --- /dev/null +++ b/xsa446.patch @@ -0,0 +1,115 @@ +From 80d5aada598c3a800a350003d5d582931545e13c Mon Sep 17 00:00:00 2001 +From: Andrew Cooper +Date: Thu, 26 Oct 2023 14:37:38 +0100 +Subject: [PATCH] x86/spec-ctrl: Remove conditional IRQs-on-ness for INT + $0x80/0x82 paths +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Before speculation defences, some paths in Xen could genuinely get away with +being IRQs-on at entry. But XPTI invalidated this property on most paths, and +attempting to maintain it on the remaining paths was a mistake. + +Fast forward, and DO_SPEC_CTRL_COND_IBPB (protection for AMD BTC/SRSO) is not +IRQ-safe, running with IRQs enabled in some cases. The other actions taken on +these paths happen to be IRQ-safe. + +Make entry_int82() and int80_direct_trap() unconditionally Interrupt Gates +rather than Trap Gates. Remove the conditional re-adjustment of +int80_direct_trap() in smp_prepare_cpus(), and have entry_int82() explicitly +enable interrupts when safe to do so. + +In smp_prepare_cpus(), with the conditional re-adjustment removed, the +clearing of pv_cr3 is the only remaining action gated on XPTI, and it is out +of place anyway, repeating work already done by smp_prepare_boot_cpu(). Drop +the entire if() condition to avoid leaving an incorrect vestigial remnant. + +Also drop comments which make incorrect statements about when its safe to +enable interrupts. + +This is XSA-446 / CVE-2023-46836 + +Signed-off-by: Andrew Cooper +Reviewed-by: Roger Pau Monné +--- + xen/arch/x86/pv/traps.c | 4 ++-- + xen/arch/x86/smpboot.c | 14 -------------- + xen/arch/x86/x86_64/compat/entry.S | 2 ++ + xen/arch/x86/x86_64/entry.S | 1 - + 4 files changed, 4 insertions(+), 17 deletions(-) + +diff --git a/xen/arch/x86/pv/traps.c b/xen/arch/x86/pv/traps.c +index 74f333da7e1c..240d1a2db7a3 100644 +--- a/xen/arch/x86/pv/traps.c ++++ b/xen/arch/x86/pv/traps.c +@@ -139,11 +139,11 @@ void __init pv_trap_init(void) + #ifdef CONFIG_PV32 + /* The 32-on-64 hypercall vector is only accessible from ring 1. */ + _set_gate(idt_table + HYPERCALL_VECTOR, +- SYS_DESC_trap_gate, 1, entry_int82); ++ SYS_DESC_irq_gate, 1, entry_int82); + #endif + + /* Fast trap for int80 (faster than taking the #GP-fixup path). */ +- _set_gate(idt_table + LEGACY_SYSCALL_VECTOR, SYS_DESC_trap_gate, 3, ++ _set_gate(idt_table + LEGACY_SYSCALL_VECTOR, SYS_DESC_irq_gate, 3, + &int80_direct_trap); + + open_softirq(NMI_SOFTIRQ, nmi_softirq); +diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c +index 3a1a659082c6..4c54ecbc91d7 100644 +--- a/xen/arch/x86/smpboot.c ++++ b/xen/arch/x86/smpboot.c +@@ -1158,20 +1158,6 @@ void __init smp_prepare_cpus(void) + + stack_base[0] = (void *)((unsigned long)stack_start & ~(STACK_SIZE - 1)); + +- if ( opt_xpti_hwdom || opt_xpti_domu ) +- { +- get_cpu_info()->pv_cr3 = 0; +- +-#ifdef CONFIG_PV +- /* +- * All entry points which may need to switch page tables have to start +- * with interrupts off. Re-write what pv_trap_init() has put there. +- */ +- _set_gate(idt_table + LEGACY_SYSCALL_VECTOR, SYS_DESC_irq_gate, 3, +- &int80_direct_trap); +-#endif +- } +- + set_nr_sockets(); + + socket_cpumask = xzalloc_array(cpumask_t *, nr_sockets); +diff --git a/xen/arch/x86/x86_64/compat/entry.S b/xen/arch/x86/x86_64/compat/entry.S +index bd5abd8040bd..fcc3a721f147 100644 +--- a/xen/arch/x86/x86_64/compat/entry.S ++++ b/xen/arch/x86/x86_64/compat/entry.S +@@ -21,6 +21,8 @@ ENTRY(entry_int82) + SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, %rdx=0, Clob: acd */ + /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ + ++ sti ++ + CR4_PV32_RESTORE + + GET_CURRENT(bx) +diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S +index 5ca74f5f62b2..9a7b129aa7e4 100644 +--- a/xen/arch/x86/x86_64/entry.S ++++ b/xen/arch/x86/x86_64/entry.S +@@ -327,7 +327,6 @@ ENTRY(sysenter_entry) + #ifdef CONFIG_XEN_SHSTK + ALTERNATIVE "", "setssbsy", X86_FEATURE_XEN_SHSTK + #endif +- /* sti could live here when we don't switch page tables below. */ + pushq $FLAT_USER_SS + pushq $0 + pushfq + +base-commit: 7befef87cc9b1bb8ca15d866ce1ecd9165ccb58c +prerequisite-patch-id: 142a87c707411d49e136c3fb76f1b14963ec6dc8 +-- +2.30.2 + From 99dc35fd60245cf181511307745ebb02164b6205 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 29 Nov 2023 19:15:48 +0000 Subject: [PATCH 079/137] update to xen-4.18.0 --- .gitignore | 2 +- sources | 2 +- xen.canonicalize.patch | 85 ++-- xen.ocaml5.fixes.patch | 42 +- xen.spec | 72 +-- xsa437.patch | 110 ----- xsa438-4.17.patch | 416 ------------------ ...enbleed-check-to-models-good-ucode-i.patch | 49 --- ...x-confusion-between-SPEC_CTRL_EXIT_T.patch | 77 ---- ...ld-DO_SPEC_CTRL_EXIT_TO_XEN-into-it-.patch | 88 ---- ...rn-the-remaining-SPEC_CTRL_-ENTRY-EX.patch | 86 ---- ...prove-all-SPEC_CTRL_-ENTER-EXIT-_-co.patch | 109 ----- ...-restore_all_xen-to-hold-stack_end-i.patch | 77 ---- ...the-IST-ness-of-an-entry-for-the-exi.patch | 112 ----- ...rl-Issue-VERW-during-IST-exit-to-Xen.patch | 92 ---- ...troduce-is_zen-1-2-_uarch-predicates.patch | 94 ---- ...c-ctrl-Mitigate-the-Zen1-DIV-leakage.patch | 231 ---------- xsa440-4.17.patch | 58 --- xsa442-4.17.patch | 185 -------- xsa443-4.17-01.patch | 70 --- xsa443-4.17-02.patch | 32 -- xsa443-4.17-03.patch | 137 ------ xsa443-4.17-04.patch | 61 --- xsa443-4.17-05.patch | 59 --- xsa443-4.17-06.patch | 65 --- xsa443-4.17-07.patch | 105 ----- xsa443-4.17-08.patch | 126 ------ xsa443-4.17-09.patch | 307 ------------- xsa443-4.17-10.patch | 250 ----------- xsa443-4.17-11.patch | 157 ------- xsa444-4.17-1.patch | 93 ---- xsa444-4.17-2.patch | 72 --- xsa445-4.17.patch | 63 --- xsa446.patch | 115 ----- 34 files changed, 65 insertions(+), 3634 deletions(-) delete mode 100644 xsa437.patch delete mode 100644 xsa438-4.17.patch delete mode 100644 xsa439-0001-x86-AMD-extend-Zenbleed-check-to-models-good-ucode-i.patch delete mode 100644 xsa439-0002-x86-spec-ctrl-Fix-confusion-between-SPEC_CTRL_EXIT_T.patch delete mode 100644 xsa439-0003-x86-spec-ctrl-Fold-DO_SPEC_CTRL_EXIT_TO_XEN-into-it-.patch delete mode 100644 xsa439-0004-x86-spec-ctrl-Turn-the-remaining-SPEC_CTRL_-ENTRY-EX.patch delete mode 100644 xsa439-0005-x86-spec-ctrl-Improve-all-SPEC_CTRL_-ENTER-EXIT-_-co.patch delete mode 100644 xsa439-0006-x86-entry-Adjust-restore_all_xen-to-hold-stack_end-i.patch delete mode 100644 xsa439-0007-x86-entry-Track-the-IST-ness-of-an-entry-for-the-exi.patch delete mode 100644 xsa439-0008-x86-spec-ctrl-Issue-VERW-during-IST-exit-to-Xen.patch delete mode 100644 xsa439-0009-x86-amd-Introduce-is_zen-1-2-_uarch-predicates.patch delete mode 100644 xsa439-0010-x86-spec-ctrl-Mitigate-the-Zen1-DIV-leakage.patch delete mode 100644 xsa440-4.17.patch delete mode 100644 xsa442-4.17.patch delete mode 100644 xsa443-4.17-01.patch delete mode 100644 xsa443-4.17-02.patch delete mode 100644 xsa443-4.17-03.patch delete mode 100644 xsa443-4.17-04.patch delete mode 100644 xsa443-4.17-05.patch delete mode 100644 xsa443-4.17-06.patch delete mode 100644 xsa443-4.17-07.patch delete mode 100644 xsa443-4.17-08.patch delete mode 100644 xsa443-4.17-09.patch delete mode 100644 xsa443-4.17-10.patch delete mode 100644 xsa443-4.17-11.patch delete mode 100644 xsa444-4.17-1.patch delete mode 100644 xsa444-4.17-2.patch delete mode 100644 xsa445-4.17.patch delete mode 100644 xsa446.patch diff --git a/.gitignore b/.gitignore index e0e02a4..7c63a9b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.17.2.tar.gz +/xen-4.18.0.tar.gz diff --git a/sources b/sources index 730c9d1..1aaa8a6 100644 --- a/sources +++ b/sources @@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.17.2.tar.gz) = 0bc475483676e4aa27735695f9a8d2821059e7a55984adb8a29badb5c09a4e7cf8ea29cbc9691be616cc0d7a5ee6b6dacc59ba29c2b16e0919ebdf7dfc54201a +SHA512 (xen-4.18.0.tar.gz) = 4cc9fd155144045a173c5f8ecc45f149817f1034eec618cb6f8b0494ef2fb5b95c4c60cf0bf4bec4bef8a622c35b6a3cb7dedc38e6d95e726f1611c73ddb3273 diff --git a/xen.canonicalize.patch b/xen.canonicalize.patch index f3ae37d..45fa724 100644 --- a/xen.canonicalize.patch +++ b/xen.canonicalize.patch @@ -1,45 +1,54 @@ ---- xen-4.9.0-rc1.2/tools/xenstore/xenstored_watch.c.orig 2017-04-12 16:18:57.000000000 +0100 -+++ xen-4.9.0-rc1.2/tools/xenstore/xenstored_watch.c 2017-04-13 21:17:12.255231094 +0100 -@@ -215,7 +215,7 @@ - goto inval; - } else { - *relative = !strstarts(*path, "/"); -- *path = canonicalize(conn, ctx, *path); -+ *path = xenstore_canonicalize(conn, ctx, *path); - if (!*path) - return errno; - if (!is_valid_nodename(*path)) -@@ -305,7 +305,7 @@ +--- xen-4.18.0-rc1/tools/xenstored/watch.c.orig 2023-09-29 09:09:29.000000000 +0100 ++++ xen-4.18.0-rc1/tools/xenstored/watch.c 2023-10-02 16:12:14.971264769 +0100 +@@ -164,7 +164,7 @@ + const char **path, bool *relative) + { + *relative = !strstarts(*path, "/") && !strstarts(*path, "@"); +- *path = canonicalize(conn, ctx, *path, true); ++ *path = xenstore_canonicalize(conn, ctx, *path, true); + + return *path ? 0 : errno; + } +@@ -250,7 +250,7 @@ if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) return EINVAL; -- node = canonicalize(conn, ctx, vec[0]); -+ node = xenstore_canonicalize(conn, ctx, vec[0]); +- node = canonicalize(conn, ctx, vec[0], true); ++ node = xenstore_canonicalize(conn, ctx, vec[0], true); if (!node) - return ENOMEM; + return errno; list_for_each_entry(watch, &conn->watches, list) { ---- xen-4.9.0-rc1.2/tools/xenstore/xenstored_core.c.orig 2017-04-12 16:18:57.000000000 +0100 -+++ xen-4.9.0-rc1.2/tools/xenstore/xenstored_core.c 2017-04-13 21:19:35.668429881 +0100 -@@ -777,7 +777,7 @@ +--- xen-4.18.0-rc1/tools/xenstored/core.c.orig 2023-09-29 09:09:29.000000000 +0100 ++++ xen-4.18.0-rc1/tools/xenstored/core.c 2023-10-02 16:12:14.993264626 +0100 +@@ -1249,7 +1249,7 @@ return strings; } --char *canonicalize(struct connection *conn, const void *ctx, const char *node) -+char *xenstore_canonicalize(struct connection *conn, const void *ctx, const char *node) +-const char *canonicalize(struct connection *conn, const void *ctx, ++const char *xenstore_canonicalize(struct connection *conn, const void *ctx, + const char *node, bool allow_special) { - const char *prefix; + const char *name; +@@ -1303,7 +1303,7 @@ + { + struct node *node; -@@ -874,7 +874,7 @@ - - if (!canonical_name) - canonical_name = &tmp_name; -- *canonical_name = canonicalize(conn, ctx, name); -+ *canonical_name = xenstore_canonicalize(conn, ctx, name); +- *canonical_name = canonicalize(conn, ctx, name, allow_special); ++ *canonical_name = xenstore_canonicalize(conn, ctx, name, allow_special); if (!*canonical_name) return NULL; - return get_node(conn, ctx, *canonical_name, perm); ---- xen-4.8.0/tools/console/testsuite/console-dom0.c.orig 2016-12-05 12:03:27.000000000 +0000 -+++ xen-4.8.0/tools/console/testsuite/console-dom0.c 2017-02-26 21:52:24.554678631 +0000 + +@@ -1320,7 +1320,7 @@ + const char *tmp_name; + const struct node *node; + +- tmp_name = canonicalize(conn, ctx, name, allow_special); ++ tmp_name = xenstore_canonicalize(conn, ctx, name, allow_special); + if (!tmp_name) + return NULL; + +--- xen-4.18.0-rc1/tools/console/testsuite/console-dom0.c.orig 2023-09-29 09:09:29.000000000 +0100 ++++ xen-4.18.0-rc1/tools/console/testsuite/console-dom0.c 2023-10-02 16:12:15.001264574 +0100 @@ -18,7 +18,7 @@ } } @@ -76,8 +85,8 @@ fprintf(stderr, "%s", line); } while (strcmp(line, "Okay.\n") != 0); ---- xen-4.8.0/tools/console/testsuite/console-domU.c.orig 2016-12-05 12:03:27.000000000 +0000 -+++ xen-4.8.0/tools/console/testsuite/console-domU.c 2017-02-26 21:52:50.320622804 +0000 +--- xen-4.18.0-rc1/tools/console/testsuite/console-domU.c.orig 2023-09-29 09:09:29.000000000 +0100 ++++ xen-4.18.0-rc1/tools/console/testsuite/console-domU.c 2023-10-02 16:12:15.008264528 +0100 @@ -6,7 +6,7 @@ #include #include @@ -96,14 +105,14 @@ seed = strtoul(line, 0, 0); printf("Seed Okay.\n"); fflush(stdout); ---- xen-4.14.1/tools/xenstore/xenstored_core.h.orig 2020-12-17 16:47:25.000000000 +0000 -+++ xen-4.14.1/tools/xenstore/xenstored_core.h 2020-12-17 20:13:10.806887309 +0000 -@@ -153,7 +153,7 @@ +--- xen-4.18.0-rc1/tools/xenstored/core.h.orig 2023-09-29 09:09:29.000000000 +0100 ++++ xen-4.18.0-rc1/tools/xenstored/core.h 2023-10-02 16:12:15.015264482 +0100 +@@ -240,7 +240,7 @@ void send_ack(struct connection *conn, enum xsd_sockmsg_type type); /* Canonicalize this path if possible. */ --char *canonicalize(struct connection *conn, const void *ctx, const char *node); -+char *xenstore_canonicalize(struct connection *conn, const void *ctx, const char *node); +-const char *canonicalize(struct connection *conn, const void *ctx, ++const char *xenstore_canonicalize(struct connection *conn, const void *ctx, + const char *node, bool allow_special); /* Get access permissions. */ - unsigned int perm_for_conn(struct connection *conn, diff --git a/xen.ocaml5.fixes.patch b/xen.ocaml5.fixes.patch index 2191334..bdffd4c 100644 --- a/xen.ocaml5.fixes.patch +++ b/xen.ocaml5.fixes.patch @@ -13,36 +13,6 @@ VERSION := 4.1 ---- xen-4.17.1/tools/ocaml/libs/xentoollog/xentoollog_stubs.c.orig 2023-04-27 06:53:19.000000000 -0600 -+++ xen-4.17.1/tools/ocaml/libs/xentoollog/xentoollog_stubs.c 2023-06-20 09:04:30.375263358 -0600 -@@ -33,7 +33,7 @@ - - /* The following is equal to the CAMLreturn macro, but without the return */ - #define CAMLdone do{ \ --caml_local_roots = caml__frame; \ -+CAML_LOCAL_ROOTS = caml__frame; \ - }while (0) - - #define XTL ((xentoollog_logger *) Xtl_val(handle)) ---- xen-4.17.1/tools/ocaml/libs/xl/xenlight_stubs.c.orig 2023-04-27 06:53:19.000000000 -0600 -+++ xen-4.17.1/tools/ocaml/libs/xl/xenlight_stubs.c 2023-06-20 09:34:09.550454354 -0600 -@@ -43,14 +43,14 @@ - #ifndef CAMLreturnT - #define CAMLreturnT(type, result) do { \ - type caml__temp_result = (result); \ -- caml_local_roots = caml__frame; \ -+ CAML_LOCAL_ROOTS = caml__frame; \ - return (caml__temp_result); \ - } while (0) - #endif - - /* The following is equal to the CAMLreturn macro, but without the return */ - #define CAMLdone do{ \ --caml_local_roots = caml__frame; \ -+CAML_LOCAL_ROOTS = caml__frame; \ - }while (0) - - #define Ctx_val(x)(*((libxl_ctx **) Data_custom_val(x))) --- xen-4.17.1/tools/ocaml/Makefile.rules.orig 2023-04-27 06:53:19.000000000 -0600 +++ xen-4.17.1/tools/ocaml/Makefile.rules 2023-06-20 10:00:58.769235173 -0600 @@ -59,9 +59,9 @@ quiet-command = $(if $(V),$1,@printf " % @@ -60,11 +30,11 @@ --- xen-4.17.1/tools/ocaml/xenstored/disk.ml.orig 2023-04-27 06:53:19.000000000 -0600 +++ xen-4.17.1/tools/ocaml/xenstored/disk.ml 2023-06-20 09:49:44.361963710 -0600 @@ -30,7 +30,7 @@ let undec c = - | _ -> raise (Failure "undecify") + | _ -> raise (Failure "undecify") let unhex c = -- let c = Char.lowercase c in -+ let c = Char.lowercase_ascii c in - match c with - | '0' .. '9' -> (Char.code c) - (Char.code '0') - | 'a' .. 'f' -> (Char.code c) - (Char.code 'a') + 10 +- let c = Char.lowercase c in ++ let c = Char.lowercase_ascii c in + match c with + | '0' .. '9' -> (Char.code c) - (Char.code '0') + | 'a' .. 'f' -> (Char.code c) - (Char.code 'a') + 10 diff --git a/xen.spec b/xen.spec index 1845570..07bb3d2 100644 --- a/xen.spec +++ b/xen.spec @@ -50,12 +50,12 @@ %endif # Hypervisor ABI -%define hv_abi 4.17 +%define hv_abi 4.18 Summary: Xen is a virtual machine monitor Name: xen -Version: 4.17.2 -Release: 5%{?dist} +Version: 4.18.0 +Release: 1%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -112,35 +112,6 @@ Patch46: xen.efi.build.patch Patch47: xen.gcc13.fixes.patch Patch49: xen.python3.12.patch Patch50: xen.ocaml5.fixes.patch -Patch51: xsa437.patch -Patch52: xsa438-4.17.patch -Patch53: xsa439-0001-x86-AMD-extend-Zenbleed-check-to-models-good-ucode-i.patch -Patch54: xsa439-0002-x86-spec-ctrl-Fix-confusion-between-SPEC_CTRL_EXIT_T.patch -Patch55: xsa439-0003-x86-spec-ctrl-Fold-DO_SPEC_CTRL_EXIT_TO_XEN-into-it-.patch -Patch56: xsa439-0004-x86-spec-ctrl-Turn-the-remaining-SPEC_CTRL_-ENTRY-EX.patch -Patch57: xsa439-0005-x86-spec-ctrl-Improve-all-SPEC_CTRL_-ENTER-EXIT-_-co.patch -Patch58: xsa439-0006-x86-entry-Adjust-restore_all_xen-to-hold-stack_end-i.patch -Patch59: xsa439-0007-x86-entry-Track-the-IST-ness-of-an-entry-for-the-exi.patch -Patch60: xsa439-0008-x86-spec-ctrl-Issue-VERW-during-IST-exit-to-Xen.patch -Patch61: xsa439-0009-x86-amd-Introduce-is_zen-1-2-_uarch-predicates.patch -Patch62: xsa439-0010-x86-spec-ctrl-Mitigate-the-Zen1-DIV-leakage.patch -Patch63: xsa440-4.17.patch -Patch64: xsa442-4.17.patch -Patch65: xsa443-4.17-01.patch -Patch66: xsa443-4.17-02.patch -Patch67: xsa443-4.17-03.patch -Patch68: xsa443-4.17-04.patch -Patch69: xsa443-4.17-05.patch -Patch70: xsa443-4.17-06.patch -Patch71: xsa443-4.17-07.patch -Patch72: xsa443-4.17-08.patch -Patch73: xsa443-4.17-09.patch -Patch74: xsa443-4.17-10.patch -Patch75: xsa443-4.17-11.patch -Patch76: xsa444-4.17-1.patch -Patch77: xsa444-4.17-2.patch -Patch78: xsa445-4.17.patch -Patch79: xsa446.patch %if %build_qemutrad @@ -357,35 +328,6 @@ manage Xen virtual machines. %if "%dist" != ".fc38" %patch 50 -p1 %endif -%patch 51 -p1 -%patch 52 -p1 -%patch 53 -p1 -%patch 54 -p1 -%patch 55 -p1 -%patch 56 -p1 -%patch 57 -p1 -%patch 58 -p1 -%patch 59 -p1 -%patch 60 -p1 -%patch 61 -p1 -%patch 62 -p1 -%patch 63 -p1 -%patch 64 -p1 -%patch 65 -p1 -%patch 66 -p1 -%patch 67 -p1 -%patch 68 -p1 -%patch 69 -p1 -%patch 70 -p1 -%patch 71 -p1 -%patch 72 -p1 -%patch 73 -p1 -%patch 74 -p1 -%patch 75 -p1 -%patch 76 -p1 -%patch 77 -p1 -%patch 78 -p1 -%patch 79 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -737,7 +679,6 @@ fi #files -f xen-xm.lang %files %doc COPYING README -%{_bindir}/xencons %{python3_sitearch}/%{name} %{python3_sitearch}/xen-*.egg-info @@ -993,6 +934,13 @@ fi %endif %changelog +* Wed Nov 29 2023 Michael Young - 4.18.0-1 +- update to xen-4.18.0 + rebase xen.canonicalize.patch and xen.ocaml5.fixes.patch + remove or adjust patches now included or superceded upstream +- xencons has been dropped + + * Tue Nov 14 2023 Michael Young - 4.17.2-5 - x86/AMD: mismatch in IOMMU quarantine page table levels [XSA-445, CVE-2023-46835] diff --git a/xsa437.patch b/xsa437.patch deleted file mode 100644 index 18c9f8f..0000000 --- a/xsa437.patch +++ /dev/null @@ -1,110 +0,0 @@ -From 7fac5971340a13ca9458195305bcfe14df2e52d2 Mon Sep 17 00:00:00 2001 -From: Stefano Stabellini -Date: Thu, 17 Aug 2023 13:41:35 +0100 -Subject: [PATCH] xen/arm: page: Handle cache flush of an element at the top of - the address space - -The region that needs to be cleaned/invalidated may be at the top -of the address space. This means that 'end' (i.e. 'p + size') will -be 0 and therefore nothing will be cleaned/invalidated as the check -in the loop will always be false. - -On Arm64, we only support we only support up to 48-bit Virtual -address space. So this is not a concern there. However, for 32-bit, -the mapcache is using the last 2GB of the address space. Therefore -we may not clean/invalidate properly some pages. This could lead -to memory corruption or data leakage (the scrubbed value may -still sit in the cache when the guest could read directly the memory -and therefore read the old content). - -Rework invalidate_dcache_va_range(), clean_dcache_va_range(), -clean_and_invalidate_dcache_va_range() to handle a cache flush -with an element at the top of the address space. - -This is CVE-2023-34321 / XSA-437. - -Reported-by: Julien Grall -Signed-off-by: Stefano Stabellini -Signed-off-by: Julien Grall -Acked-by: Bertrand Marquis - ---- - xen/arch/arm/include/asm/page.h | 33 ++++++++++++++++++++------------- - 1 file changed, 20 insertions(+), 13 deletions(-) - -diff --git a/xen/arch/arm/include/asm/page.h b/xen/arch/arm/include/asm/page.h -index e7cd62190c7f..d7fe770a5e49 100644 ---- a/xen/arch/arm/include/asm/page.h -+++ b/xen/arch/arm/include/asm/page.h -@@ -160,26 +160,25 @@ static inline size_t read_dcache_line_bytes(void) - - static inline int invalidate_dcache_va_range(const void *p, unsigned long size) - { -- const void *end = p + size; - size_t cacheline_mask = dcache_line_bytes - 1; - - dsb(sy); /* So the CPU issues all writes to the range */ - - if ( (uintptr_t)p & cacheline_mask ) - { -+ size -= dcache_line_bytes - ((uintptr_t)p & cacheline_mask); - p = (void *)((uintptr_t)p & ~cacheline_mask); - asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (p)); - p += dcache_line_bytes; - } -- if ( (uintptr_t)end & cacheline_mask ) -- { -- end = (void *)((uintptr_t)end & ~cacheline_mask); -- asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (end)); -- } - -- for ( ; p < end; p += dcache_line_bytes ) -+ for ( ; size >= dcache_line_bytes; -+ p += dcache_line_bytes, size -= dcache_line_bytes ) - asm volatile (__invalidate_dcache_one(0) : : "r" (p)); - -+ if ( size > 0 ) -+ asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (p)); -+ - dsb(sy); /* So we know the flushes happen before continuing */ - - return 0; -@@ -187,10 +186,14 @@ static inline int invalidate_dcache_va_range(const void *p, unsigned long size) - - static inline int clean_dcache_va_range(const void *p, unsigned long size) - { -- const void *end = p + size; -+ size_t cacheline_mask = dcache_line_bytes - 1; -+ - dsb(sy); /* So the CPU issues all writes to the range */ -- p = (void *)((uintptr_t)p & ~(dcache_line_bytes - 1)); -- for ( ; p < end; p += dcache_line_bytes ) -+ size += (uintptr_t)p & cacheline_mask; -+ size = (size + cacheline_mask) & ~cacheline_mask; -+ p = (void *)((uintptr_t)p & ~cacheline_mask); -+ for ( ; size >= dcache_line_bytes; -+ p += dcache_line_bytes, size -= dcache_line_bytes ) - asm volatile (__clean_dcache_one(0) : : "r" (p)); - dsb(sy); /* So we know the flushes happen before continuing */ - /* ARM callers assume that dcache_* functions cannot fail. */ -@@ -200,10 +203,14 @@ static inline int clean_dcache_va_range(const void *p, unsigned long size) - static inline int clean_and_invalidate_dcache_va_range - (const void *p, unsigned long size) - { -- const void *end = p + size; -+ size_t cacheline_mask = dcache_line_bytes - 1; -+ - dsb(sy); /* So the CPU issues all writes to the range */ -- p = (void *)((uintptr_t)p & ~(dcache_line_bytes - 1)); -- for ( ; p < end; p += dcache_line_bytes ) -+ size += (uintptr_t)p & cacheline_mask; -+ size = (size + cacheline_mask) & ~cacheline_mask; -+ p = (void *)((uintptr_t)p & ~cacheline_mask); -+ for ( ; size >= dcache_line_bytes; -+ p += dcache_line_bytes, size -= dcache_line_bytes ) - asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (p)); - dsb(sy); /* So we know the flushes happen before continuing */ - /* ARM callers assume that dcache_* functions cannot fail. */ --- -2.40.1 - diff --git a/xsa438-4.17.patch b/xsa438-4.17.patch deleted file mode 100644 index 12d6ec7..0000000 --- a/xsa438-4.17.patch +++ /dev/null @@ -1,416 +0,0 @@ -From: Jan Beulich -Subject: x86/shadow: defer releasing of PV's top-level shadow reference - -sh_set_toplevel_shadow() re-pinning the top-level shadow we may be -running on is not enough (and at the same time unnecessary when the -shadow isn't what we're running on): That shadow becomes eligible for -blowing away (from e.g. shadow_prealloc()) immediately after the -paging lock was dropped. Yet it needs to remain valid until the actual -page table switch occurred. - -Propagate up the call chain the shadow entry that needs releasing -eventually, and carry out the release immediately after switching page -tables. Handle update_cr3() failures by switching to idle pagetables. -Note that various further uses of update_cr3() are HVM-only or only act -on paused vCPU-s, in which case sh_set_toplevel_shadow() will not defer -releasing of the reference. - -While changing the update_cr3() hook, also convert the "do_locking" -parameter to boolean. - -This is CVE-2023-34322 / XSA-438. - -Reported-by: Tim Deegan -Signed-off-by: Jan Beulich -Reviewed-by: George Dunlap - ---- a/xen/arch/x86/include/asm/mm.h -+++ b/xen/arch/x86/include/asm/mm.h -@@ -552,7 +552,7 @@ void audit_domains(void); - #endif - - void make_cr3(struct vcpu *v, mfn_t mfn); --void update_cr3(struct vcpu *v); -+pagetable_t update_cr3(struct vcpu *v); - int vcpu_destroy_pagetables(struct vcpu *); - void *do_page_walk(struct vcpu *v, unsigned long addr); - ---- a/xen/arch/x86/include/asm/paging.h -+++ b/xen/arch/x86/include/asm/paging.h -@@ -138,7 +138,7 @@ struct paging_mode { - paddr_t ga, uint32_t *pfec, - unsigned int *page_order); - #endif -- void (*update_cr3 )(struct vcpu *v, int do_locking, -+ pagetable_t (*update_cr3 )(struct vcpu *v, bool do_locking, - bool noflush); - void (*update_paging_modes )(struct vcpu *v); - bool (*flush_tlb )(const unsigned long *vcpu_bitmap); -@@ -310,9 +310,9 @@ static inline unsigned long paging_ga_to - /* Update all the things that are derived from the guest's CR3. - * Called when the guest changes CR3; the caller can then use v->arch.cr3 - * as the value to load into the host CR3 to schedule this vcpu */ --static inline void paging_update_cr3(struct vcpu *v, bool noflush) -+static inline pagetable_t paging_update_cr3(struct vcpu *v, bool noflush) - { -- paging_get_hostmode(v)->update_cr3(v, 1, noflush); -+ return paging_get_hostmode(v)->update_cr3(v, 1, noflush); - } - - /* Update all the things that are derived from the guest's CR0/CR3/CR4. ---- a/xen/arch/x86/include/asm/shadow.h -+++ b/xen/arch/x86/include/asm/shadow.h -@@ -99,6 +99,9 @@ int shadow_set_allocation(struct domain - - int shadow_get_allocation_bytes(struct domain *d, uint64_t *size); - -+/* Helper to invoke for deferred releasing of a top-level shadow's reference. */ -+void shadow_put_top_level(struct domain *d, pagetable_t old); -+ - #else /* !CONFIG_SHADOW_PAGING */ - - #define shadow_vcpu_teardown(v) ASSERT(is_pv_vcpu(v)) -@@ -121,6 +124,11 @@ static inline void shadow_prepare_page_t - - static inline void shadow_blow_tables_per_domain(struct domain *d) {} - -+static inline void shadow_put_top_level(struct domain *d, pagetable_t old) -+{ -+ ASSERT_UNREACHABLE(); -+} -+ - static inline int shadow_domctl(struct domain *d, - struct xen_domctl_shadow_op *sc, - XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) ---- a/xen/arch/x86/mm/hap/hap.c -+++ b/xen/arch/x86/mm/hap/hap.c -@@ -739,11 +739,13 @@ static bool cf_check hap_invlpg(struct v - return 1; - } - --static void cf_check hap_update_cr3( -- struct vcpu *v, int do_locking, bool noflush) -+static pagetable_t cf_check hap_update_cr3( -+ struct vcpu *v, bool do_locking, bool noflush) - { - v->arch.hvm.hw_cr[3] = v->arch.hvm.guest_cr[3]; - hvm_update_guest_cr3(v, noflush); -+ -+ return pagetable_null(); - } - - static bool flush_vcpu(const struct vcpu *v, const unsigned long *vcpu_bitmap) ---- a/xen/arch/x86/mm/shadow/common.c -+++ b/xen/arch/x86/mm/shadow/common.c -@@ -2590,13 +2590,13 @@ void cf_check shadow_update_paging_modes - } - - /* Set up the top-level shadow and install it in slot 'slot' of shadow_table */ --void sh_set_toplevel_shadow(struct vcpu *v, -- unsigned int slot, -- mfn_t gmfn, -- unsigned int root_type, -- mfn_t (*make_shadow)(struct vcpu *v, -- mfn_t gmfn, -- uint32_t shadow_type)) -+pagetable_t sh_set_toplevel_shadow(struct vcpu *v, -+ unsigned int slot, -+ mfn_t gmfn, -+ unsigned int root_type, -+ mfn_t (*make_shadow)(struct vcpu *v, -+ mfn_t gmfn, -+ uint32_t shadow_type)) - { - mfn_t smfn; - pagetable_t old_entry, new_entry; -@@ -2653,20 +2653,37 @@ void sh_set_toplevel_shadow(struct vcpu - mfn_x(gmfn), mfn_x(pagetable_get_mfn(new_entry))); - v->arch.paging.shadow.shadow_table[slot] = new_entry; - -- /* Decrement the refcount of the old contents of this slot */ -- if ( !pagetable_is_null(old_entry) ) -+ /* -+ * Decrement the refcount of the old contents of this slot, unless -+ * we're still running on that shadow - in that case it'll need holding -+ * on to until the actual page table switch did occur. -+ */ -+ if ( !pagetable_is_null(old_entry) && (v != current || !is_pv_domain(d)) ) - { -- mfn_t old_smfn = pagetable_get_mfn(old_entry); -- /* Need to repin the old toplevel shadow if it's been unpinned -- * by shadow_prealloc(): in PV mode we're still running on this -- * shadow and it's not safe to free it yet. */ -- if ( !mfn_to_page(old_smfn)->u.sh.pinned && !sh_pin(d, old_smfn) ) -- { -- printk(XENLOG_G_ERR "can't re-pin %"PRI_mfn"\n", mfn_x(old_smfn)); -- domain_crash(d); -- } -- sh_put_ref(d, old_smfn, 0); -+ sh_put_ref(d, pagetable_get_mfn(old_entry), 0); -+ old_entry = pagetable_null(); - } -+ -+ /* -+ * 2- and 3-level shadow mode is used for HVM only. Therefore we never run -+ * on such a shadow, so only call sites requesting an L4 shadow need to pay -+ * attention to the returned value. -+ */ -+ ASSERT(pagetable_is_null(old_entry) || root_type == SH_type_l4_64_shadow); -+ -+ return old_entry; -+} -+ -+/* -+ * Helper invoked when releasing of a top-level shadow's reference was -+ * deferred in sh_set_toplevel_shadow() above. -+ */ -+void shadow_put_top_level(struct domain *d, pagetable_t old_entry) -+{ -+ ASSERT(!pagetable_is_null(old_entry)); -+ paging_lock(d); -+ sh_put_ref(d, pagetable_get_mfn(old_entry), 0); -+ paging_unlock(d); - } - - /**************************************************************************/ ---- a/xen/arch/x86/mm/shadow/multi.c -+++ b/xen/arch/x86/mm/shadow/multi.c -@@ -3224,7 +3224,8 @@ static void cf_check sh_detach_old_table - } - } - --static void cf_check sh_update_cr3(struct vcpu *v, int do_locking, bool noflush) -+static pagetable_t cf_check sh_update_cr3(struct vcpu *v, bool do_locking, -+ bool noflush) - /* Updates vcpu->arch.cr3 after the guest has changed CR3. - * Paravirtual guests should set v->arch.guest_table (and guest_table_user, - * if appropriate). -@@ -3238,6 +3239,7 @@ static void cf_check sh_update_cr3(struc - { - struct domain *d = v->domain; - mfn_t gmfn; -+ pagetable_t old_entry = pagetable_null(); - #if GUEST_PAGING_LEVELS == 3 - const guest_l3e_t *gl3e; - unsigned int i, guest_idx; -@@ -3247,7 +3249,7 @@ static void cf_check sh_update_cr3(struc - if ( !is_hvm_domain(d) && !v->is_initialised ) - { - ASSERT(v->arch.cr3 == 0); -- return; -+ return old_entry; - } - - if ( do_locking ) paging_lock(v->domain); -@@ -3320,11 +3322,12 @@ static void cf_check sh_update_cr3(struc - #if GUEST_PAGING_LEVELS == 4 - if ( sh_remove_write_access(d, gmfn, 4, 0) != 0 ) - guest_flush_tlb_mask(d, d->dirty_cpumask); -- sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l4_shadow, sh_make_shadow); -+ old_entry = sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l4_shadow, -+ sh_make_shadow); - if ( unlikely(pagetable_is_null(v->arch.paging.shadow.shadow_table[0])) ) - { - ASSERT(d->is_dying || d->is_shutting_down); -- return; -+ return old_entry; - } - if ( !shadow_mode_external(d) && !is_pv_32bit_domain(d) ) - { -@@ -3368,24 +3371,30 @@ static void cf_check sh_update_cr3(struc - gl2gfn = guest_l3e_get_gfn(gl3e[i]); - gl2mfn = get_gfn_query_unlocked(d, gfn_x(gl2gfn), &p2mt); - if ( p2m_is_ram(p2mt) ) -- sh_set_toplevel_shadow(v, i, gl2mfn, SH_type_l2_shadow, -- sh_make_shadow); -+ old_entry = sh_set_toplevel_shadow(v, i, gl2mfn, -+ SH_type_l2_shadow, -+ sh_make_shadow); - else -- sh_set_toplevel_shadow(v, i, INVALID_MFN, 0, -- sh_make_shadow); -+ old_entry = sh_set_toplevel_shadow(v, i, INVALID_MFN, 0, -+ sh_make_shadow); - } - else -- sh_set_toplevel_shadow(v, i, INVALID_MFN, 0, sh_make_shadow); -+ old_entry = sh_set_toplevel_shadow(v, i, INVALID_MFN, 0, -+ sh_make_shadow); -+ -+ ASSERT(pagetable_is_null(old_entry)); - } - } - #elif GUEST_PAGING_LEVELS == 2 - if ( sh_remove_write_access(d, gmfn, 2, 0) != 0 ) - guest_flush_tlb_mask(d, d->dirty_cpumask); -- sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l2_shadow, sh_make_shadow); -+ old_entry = sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l2_shadow, -+ sh_make_shadow); -+ ASSERT(pagetable_is_null(old_entry)); - if ( unlikely(pagetable_is_null(v->arch.paging.shadow.shadow_table[0])) ) - { - ASSERT(d->is_dying || d->is_shutting_down); -- return; -+ return old_entry; - } - #else - #error This should never happen -@@ -3473,6 +3482,8 @@ static void cf_check sh_update_cr3(struc - - /* Release the lock, if we took it (otherwise it's the caller's problem) */ - if ( do_locking ) paging_unlock(v->domain); -+ -+ return old_entry; - } - - ---- a/xen/arch/x86/mm/shadow/none.c -+++ b/xen/arch/x86/mm/shadow/none.c -@@ -52,9 +52,11 @@ static unsigned long cf_check _gva_to_gf - } - #endif - --static void cf_check _update_cr3(struct vcpu *v, int do_locking, bool noflush) -+static pagetable_t cf_check _update_cr3(struct vcpu *v, bool do_locking, -+ bool noflush) - { - ASSERT_UNREACHABLE(); -+ return pagetable_null(); - } - - static void cf_check _update_paging_modes(struct vcpu *v) ---- a/xen/arch/x86/mm/shadow/private.h -+++ b/xen/arch/x86/mm/shadow/private.h -@@ -391,13 +391,13 @@ mfn_t shadow_alloc(struct domain *d, - void shadow_free(struct domain *d, mfn_t smfn); - - /* Set up the top-level shadow and install it in slot 'slot' of shadow_table */ --void sh_set_toplevel_shadow(struct vcpu *v, -- unsigned int slot, -- mfn_t gmfn, -- unsigned int root_type, -- mfn_t (*make_shadow)(struct vcpu *v, -- mfn_t gmfn, -- uint32_t shadow_type)); -+pagetable_t sh_set_toplevel_shadow(struct vcpu *v, -+ unsigned int slot, -+ mfn_t gmfn, -+ unsigned int root_type, -+ mfn_t (*make_shadow)(struct vcpu *v, -+ mfn_t gmfn, -+ uint32_t shadow_type)); - - /* Update the shadows in response to a pagetable write from Xen */ - int sh_validate_guest_entry(struct vcpu *v, mfn_t gmfn, void *entry, u32 size); ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -567,15 +567,12 @@ void write_ptbase(struct vcpu *v) - * - * Update ref counts to shadow tables appropriately. - */ --void update_cr3(struct vcpu *v) -+pagetable_t update_cr3(struct vcpu *v) - { - mfn_t cr3_mfn; - - if ( paging_mode_enabled(v->domain) ) -- { -- paging_update_cr3(v, false); -- return; -- } -+ return paging_update_cr3(v, false); - - if ( !(v->arch.flags & TF_kernel_mode) ) - cr3_mfn = pagetable_get_mfn(v->arch.guest_table_user); -@@ -583,6 +580,8 @@ void update_cr3(struct vcpu *v) - cr3_mfn = pagetable_get_mfn(v->arch.guest_table); - - make_cr3(v, cr3_mfn); -+ -+ return pagetable_null(); - } - - static inline void set_tlbflush_timestamp(struct page_info *page) -@@ -3285,6 +3284,7 @@ int new_guest_cr3(mfn_t mfn) - struct domain *d = curr->domain; - int rc; - mfn_t old_base_mfn; -+ pagetable_t old_shadow; - - if ( is_pv_32bit_domain(d) ) - { -@@ -3352,9 +3352,22 @@ int new_guest_cr3(mfn_t mfn) - if ( !VM_ASSIST(d, m2p_strict) ) - fill_ro_mpt(mfn); - curr->arch.guest_table = pagetable_from_mfn(mfn); -- update_cr3(curr); -+ old_shadow = update_cr3(curr); -+ -+ /* -+ * In shadow mode update_cr3() can fail, in which case here we're still -+ * running on the prior top-level shadow (which we're about to release). -+ * Switch to the idle page tables in such an event; the guest will have -+ * been crashed already. -+ */ -+ if ( likely(!mfn_eq(pagetable_get_mfn(old_shadow), -+ maddr_to_mfn(curr->arch.cr3 & ~X86_CR3_NOFLUSH))) ) -+ write_ptbase(curr); -+ else -+ write_ptbase(idle_vcpu[curr->processor]); - -- write_ptbase(curr); -+ if ( !pagetable_is_null(old_shadow) ) -+ shadow_put_top_level(d, old_shadow); - - if ( likely(mfn_x(old_base_mfn) != 0) ) - { ---- a/xen/arch/x86/pv/domain.c -+++ b/xen/arch/x86/pv/domain.c -@@ -424,10 +424,13 @@ bool __init xpti_pcid_enabled(void) - - static void _toggle_guest_pt(struct vcpu *v) - { -+ bool guest_update; -+ pagetable_t old_shadow; - unsigned long cr3; - - v->arch.flags ^= TF_kernel_mode; -- update_cr3(v); -+ guest_update = v->arch.flags & TF_kernel_mode; -+ old_shadow = update_cr3(v); - - /* - * Don't flush user global mappings from the TLB. Don't tick TLB clock. -@@ -436,13 +439,31 @@ static void _toggle_guest_pt(struct vcpu - * TLB flush (for just the incoming PCID), as the top level page table may - * have changed behind our backs. To be on the safe side, suppress the - * no-flush unconditionally in this case. -+ * -+ * Furthermore in shadow mode update_cr3() can fail, in which case here -+ * we're still running on the prior top-level shadow (which we're about -+ * to release). Switch to the idle page tables in such an event; the -+ * guest will have been crashed already. - */ - cr3 = v->arch.cr3; - if ( shadow_mode_enabled(v->domain) ) -+ { - cr3 &= ~X86_CR3_NOFLUSH; -+ -+ if ( unlikely(mfn_eq(pagetable_get_mfn(old_shadow), -+ maddr_to_mfn(cr3))) ) -+ { -+ cr3 = idle_vcpu[v->processor]->arch.cr3; -+ /* Also suppress runstate/time area updates below. */ -+ guest_update = false; -+ } -+ } - write_cr3(cr3); - -- if ( !(v->arch.flags & TF_kernel_mode) ) -+ if ( !pagetable_is_null(old_shadow) ) -+ shadow_put_top_level(v->domain, old_shadow); -+ -+ if ( !guest_update ) - return; - - if ( v->arch.pv.need_update_runstate_area && update_runstate_area(v) ) diff --git a/xsa439-0001-x86-AMD-extend-Zenbleed-check-to-models-good-ucode-i.patch b/xsa439-0001-x86-AMD-extend-Zenbleed-check-to-models-good-ucode-i.patch deleted file mode 100644 index 96e56ec..0000000 --- a/xsa439-0001-x86-AMD-extend-Zenbleed-check-to-models-good-ucode-i.patch +++ /dev/null @@ -1,49 +0,0 @@ -From d2d2dcae879c6cc05227c9620f0a772f35fe6886 Mon Sep 17 00:00:00 2001 -Message-ID: -From: Jan Beulich -Date: Wed, 23 Aug 2023 09:26:36 +0200 -Subject: [XEN PATCH 01/10] x86/AMD: extend Zenbleed check to models "good" - ucode isn't known for - -Reportedly the AMD Custom APU 0405 found on SteamDeck, models 0x90 and -0x91, (quoting the respective Linux commit) is similarly affected. Put -another instance of our Zen1 vs Zen2 distinction checks in -amd_check_zenbleed(), forcing use of the chickenbit irrespective of -ucode version (building upon real hardware never surfacing a version of -0xffffffff). - -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper -(cherry picked from commit 145a69c0944ac70cfcf9d247c85dee9e99d9d302) ---- - xen/arch/x86/cpu/amd.c | 13 ++++++++++--- - 1 file changed, 10 insertions(+), 3 deletions(-) - -diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c -index 3ea214fc2e..1bb3044be1 100644 ---- a/xen/arch/x86/cpu/amd.c -+++ b/xen/arch/x86/cpu/amd.c -@@ -909,10 +909,17 @@ void amd_check_zenbleed(void) - case 0xa0 ... 0xaf: good_rev = 0x08a00008; break; - default: - /* -- * With the Fam17h check above, parts getting here are Zen1. -- * They're not affected. -+ * With the Fam17h check above, most parts getting here are -+ * Zen1. They're not affected. Assume Zen2 ones making it -+ * here are affected regardless of microcode version. -+ * -+ * Zen1 vs Zen2 isn't a simple model number comparison, so use -+ * STIBP as a heuristic to distinguish. - */ -- return; -+ if (!boot_cpu_has(X86_FEATURE_AMD_STIBP)) -+ return; -+ good_rev = ~0U; -+ break; - } - - rdmsrl(MSR_AMD64_DE_CFG, val); --- -2.41.0 - diff --git a/xsa439-0002-x86-spec-ctrl-Fix-confusion-between-SPEC_CTRL_EXIT_T.patch b/xsa439-0002-x86-spec-ctrl-Fix-confusion-between-SPEC_CTRL_EXIT_T.patch deleted file mode 100644 index 8b8e30a..0000000 --- a/xsa439-0002-x86-spec-ctrl-Fix-confusion-between-SPEC_CTRL_EXIT_T.patch +++ /dev/null @@ -1,77 +0,0 @@ -From dc28aba565f226f9bec24cfde993e78478acfb4e Mon Sep 17 00:00:00 2001 -Message-ID: -In-Reply-To: -References: -From: Andrew Cooper -Date: Tue, 12 Sep 2023 15:06:49 +0100 -Subject: [XEN PATCH 02/10] x86/spec-ctrl: Fix confusion between - SPEC_CTRL_EXIT_TO_XEN{,_IST} - -c/s 3fffaf9c13e9 ("x86/entry: Avoid using alternatives in NMI/#MC paths") -dropped the only user, leaving behind the (incorrect) implication that Xen had -split exit paths. - -Delete the unused SPEC_CTRL_EXIT_TO_XEN and rename SPEC_CTRL_EXIT_TO_XEN_IST -to SPEC_CTRL_EXIT_TO_XEN for consistency. - -No functional change. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -(cherry picked from commit 1c18d73774533a55ba9d1cbee8bdace03efdb5e7) ---- - xen/arch/x86/include/asm/spec_ctrl_asm.h | 10 ++-------- - xen/arch/x86/x86_64/entry.S | 2 +- - 2 files changed, 3 insertions(+), 9 deletions(-) - -diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h -index f23bb105c5..e8fd01243c 100644 ---- a/xen/arch/x86/include/asm/spec_ctrl_asm.h -+++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h -@@ -79,7 +79,6 @@ - * - SPEC_CTRL_ENTRY_FROM_PV - * - SPEC_CTRL_ENTRY_FROM_INTR - * - SPEC_CTRL_ENTRY_FROM_INTR_IST -- * - SPEC_CTRL_EXIT_TO_XEN_IST - * - SPEC_CTRL_EXIT_TO_XEN - * - SPEC_CTRL_EXIT_TO_PV - * -@@ -268,11 +267,6 @@ - ALTERNATIVE "", __stringify(DO_SPEC_CTRL_ENTRY maybexen=1), \ - X86_FEATURE_SC_MSR_PV - --/* Use when exiting to Xen context. */ --#define SPEC_CTRL_EXIT_TO_XEN \ -- ALTERNATIVE "", \ -- DO_SPEC_CTRL_EXIT_TO_XEN, X86_FEATURE_SC_MSR_PV -- - /* Use when exiting to PV guest context. */ - #define SPEC_CTRL_EXIT_TO_PV \ - ALTERNATIVE "", \ -@@ -339,8 +333,8 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): - UNLIKELY_END(\@_serialise) - .endm - --/* Use when exiting to Xen in IST context. */ --.macro SPEC_CTRL_EXIT_TO_XEN_IST -+/* Use when exiting to Xen context. */ -+.macro SPEC_CTRL_EXIT_TO_XEN - /* - * Requires %rbx=stack_end - * Clobbers %rax, %rcx, %rdx -diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S -index 7675a59ff0..b45a09823a 100644 ---- a/xen/arch/x86/x86_64/entry.S -+++ b/xen/arch/x86/x86_64/entry.S -@@ -673,7 +673,7 @@ UNLIKELY_START(ne, exit_cr3) - UNLIKELY_END(exit_cr3) - - /* WARNING! `ret`, `call *`, `jmp *` not safe beyond this point. */ -- SPEC_CTRL_EXIT_TO_XEN_IST /* Req: %rbx=end, Clob: acd */ -+ SPEC_CTRL_EXIT_TO_XEN /* Req: %rbx=end, Clob: acd */ - - RESTORE_ALL adj=8 - iretq --- -2.41.0 - diff --git a/xsa439-0003-x86-spec-ctrl-Fold-DO_SPEC_CTRL_EXIT_TO_XEN-into-it-.patch b/xsa439-0003-x86-spec-ctrl-Fold-DO_SPEC_CTRL_EXIT_TO_XEN-into-it-.patch deleted file mode 100644 index 547032e..0000000 --- a/xsa439-0003-x86-spec-ctrl-Fold-DO_SPEC_CTRL_EXIT_TO_XEN-into-it-.patch +++ /dev/null @@ -1,88 +0,0 @@ -From 84690fb82c4f4aecb72a6789d8994efa74841e09 Mon Sep 17 00:00:00 2001 -Message-ID: <84690fb82c4f4aecb72a6789d8994efa74841e09.1695733540.git.m.a.young@durham.ac.uk> -In-Reply-To: -References: -From: Andrew Cooper -Date: Tue, 12 Sep 2023 17:03:16 +0100 -Subject: [XEN PATCH 03/10] x86/spec-ctrl: Fold DO_SPEC_CTRL_EXIT_TO_XEN into - it's single user - -With the SPEC_CTRL_EXIT_TO_XEN{,_IST} confusion fixed, it's now obvious that -there's only a single EXIT_TO_XEN path. Fold DO_SPEC_CTRL_EXIT_TO_XEN into -SPEC_CTRL_EXIT_TO_XEN to simplify further fixes. - -When merging labels, switch the name to .L\@_skip_sc_msr as "skip" on its own -is going to be too generic shortly. - -No functional change. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -(cherry picked from commit 694bb0f280fd08a4377e36e32b84b5062def4de2) ---- - xen/arch/x86/include/asm/spec_ctrl_asm.h | 40 ++++++++++-------------- - 1 file changed, 16 insertions(+), 24 deletions(-) - -diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h -index e8fd01243c..d5f65d80ea 100644 ---- a/xen/arch/x86/include/asm/spec_ctrl_asm.h -+++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h -@@ -211,27 +211,6 @@ - wrmsr - .endm - --.macro DO_SPEC_CTRL_EXIT_TO_XEN --/* -- * Requires %rbx=stack_end -- * Clobbers %rax, %rcx, %rdx -- * -- * When returning to Xen context, look to see whether SPEC_CTRL shadowing is -- * in effect, and reload the shadow value. This covers race conditions which -- * exist with an NMI/MCE/etc hitting late in the return-to-guest path. -- */ -- xor %edx, %edx -- -- testb $SCF_use_shadow, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%rbx) -- jz .L\@_skip -- -- mov STACK_CPUINFO_FIELD(shadow_spec_ctrl)(%rbx), %eax -- mov $MSR_SPEC_CTRL, %ecx -- wrmsr -- --.L\@_skip: --.endm -- - .macro DO_SPEC_CTRL_EXIT_TO_GUEST - /* - * Requires %eax=spec_ctrl, %rsp=regs/cpuinfo -@@ -340,11 +319,24 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): - * Clobbers %rax, %rcx, %rdx - */ - testb $SCF_ist_sc_msr, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%rbx) -- jz .L\@_skip -+ jz .L\@_skip_sc_msr - -- DO_SPEC_CTRL_EXIT_TO_XEN -+ /* -+ * When returning to Xen context, look to see whether SPEC_CTRL shadowing -+ * is in effect, and reload the shadow value. This covers race conditions -+ * which exist with an NMI/MCE/etc hitting late in the return-to-guest -+ * path. -+ */ -+ xor %edx, %edx - --.L\@_skip: -+ testb $SCF_use_shadow, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%rbx) -+ jz .L\@_skip_sc_msr -+ -+ mov STACK_CPUINFO_FIELD(shadow_spec_ctrl)(%rbx), %eax -+ mov $MSR_SPEC_CTRL, %ecx -+ wrmsr -+ -+.L\@_skip_sc_msr: - .endm - - #endif /* __ASSEMBLY__ */ --- -2.41.0 - diff --git a/xsa439-0004-x86-spec-ctrl-Turn-the-remaining-SPEC_CTRL_-ENTRY-EX.patch b/xsa439-0004-x86-spec-ctrl-Turn-the-remaining-SPEC_CTRL_-ENTRY-EX.patch deleted file mode 100644 index 3350750..0000000 --- a/xsa439-0004-x86-spec-ctrl-Turn-the-remaining-SPEC_CTRL_-ENTRY-EX.patch +++ /dev/null @@ -1,86 +0,0 @@ -From 3952c73bdbd05f0e666986fce633a591237b3c88 Mon Sep 17 00:00:00 2001 -Message-ID: <3952c73bdbd05f0e666986fce633a591237b3c88.1695733540.git.m.a.young@durham.ac.uk> -In-Reply-To: -References: -From: Andrew Cooper -Date: Fri, 1 Sep 2023 11:38:44 +0100 -Subject: [XEN PATCH 04/10] x86/spec-ctrl: Turn the remaining - SPEC_CTRL_{ENTRY,EXIT}_* into asm macros - -These have grown more complex over time, with some already having been -converted. - -Provide full Requires/Clobbers comments, otherwise missing at this level of -indirection. - -No functional change. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -(cherry picked from commit 7125429aafb9e3c9c88fc93001fc2300e0ac2cc8) ---- - xen/arch/x86/include/asm/spec_ctrl_asm.h | 37 ++++++++++++++++++------ - 1 file changed, 28 insertions(+), 9 deletions(-) - -diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h -index d5f65d80ea..c6d5f2ad01 100644 ---- a/xen/arch/x86/include/asm/spec_ctrl_asm.h -+++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h -@@ -231,26 +231,45 @@ - .endm - - /* Use after an entry from PV context (syscall/sysenter/int80/int82/etc). */ --#define SPEC_CTRL_ENTRY_FROM_PV \ -+.macro SPEC_CTRL_ENTRY_FROM_PV -+/* -+ * Requires %rsp=regs/cpuinfo, %rdx=0 -+ * Clobbers %rax, %rcx, %rdx -+ */ - ALTERNATIVE "", __stringify(DO_SPEC_CTRL_COND_IBPB maybexen=0), \ -- X86_FEATURE_IBPB_ENTRY_PV; \ -- ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_PV; \ -+ X86_FEATURE_IBPB_ENTRY_PV -+ -+ ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_PV -+ - ALTERNATIVE "", __stringify(DO_SPEC_CTRL_ENTRY maybexen=0), \ - X86_FEATURE_SC_MSR_PV -+.endm - - /* Use in interrupt/exception context. May interrupt Xen or PV context. */ --#define SPEC_CTRL_ENTRY_FROM_INTR \ -+.macro SPEC_CTRL_ENTRY_FROM_INTR -+/* -+ * Requires %rsp=regs, %r14=stack_end, %rdx=0 -+ * Clobbers %rax, %rcx, %rdx -+ */ - ALTERNATIVE "", __stringify(DO_SPEC_CTRL_COND_IBPB maybexen=1), \ -- X86_FEATURE_IBPB_ENTRY_PV; \ -- ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_PV; \ -+ X86_FEATURE_IBPB_ENTRY_PV -+ -+ ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_PV -+ - ALTERNATIVE "", __stringify(DO_SPEC_CTRL_ENTRY maybexen=1), \ - X86_FEATURE_SC_MSR_PV -+.endm - - /* Use when exiting to PV guest context. */ --#define SPEC_CTRL_EXIT_TO_PV \ -- ALTERNATIVE "", \ -- DO_SPEC_CTRL_EXIT_TO_GUEST, X86_FEATURE_SC_MSR_PV; \ -+.macro SPEC_CTRL_EXIT_TO_PV -+/* -+ * Requires %rax=spec_ctrl, %rsp=regs/info -+ * Clobbers %rcx, %rdx -+ */ -+ ALTERNATIVE "", DO_SPEC_CTRL_EXIT_TO_GUEST, X86_FEATURE_SC_MSR_PV -+ - DO_SPEC_CTRL_COND_VERW -+.endm - - /* - * Use in IST interrupt/exception context. May interrupt Xen or PV context. --- -2.41.0 - diff --git a/xsa439-0005-x86-spec-ctrl-Improve-all-SPEC_CTRL_-ENTER-EXIT-_-co.patch b/xsa439-0005-x86-spec-ctrl-Improve-all-SPEC_CTRL_-ENTER-EXIT-_-co.patch deleted file mode 100644 index dda088a..0000000 --- a/xsa439-0005-x86-spec-ctrl-Improve-all-SPEC_CTRL_-ENTER-EXIT-_-co.patch +++ /dev/null @@ -1,109 +0,0 @@ -From ba023e93d0b1e60b80251bf080bab694efb9f8e3 Mon Sep 17 00:00:00 2001 -Message-ID: -In-Reply-To: -References: -From: Andrew Cooper -Date: Wed, 30 Aug 2023 20:11:50 +0100 -Subject: [XEN PATCH 05/10] x86/spec-ctrl: Improve all SPEC_CTRL_{ENTER,EXIT}_* - comments - -... to better explain how they're used. - -Doing so highlights that SPEC_CTRL_EXIT_TO_XEN is missing a VERW flush for the -corner case when e.g. an NMI hits late in an exit-to-guest path. - -Leave a TODO, which will be addressed in subsequent patches which arrange for -VERW flushing to be safe within SPEC_CTRL_EXIT_TO_XEN. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -(cherry picked from commit 45f00557350dc7d0756551069803fc49c29184ca) ---- - xen/arch/x86/include/asm/spec_ctrl_asm.h | 36 ++++++++++++++++++++---- - 1 file changed, 31 insertions(+), 5 deletions(-) - -diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h -index c6d5f2ad01..97c4db31cd 100644 ---- a/xen/arch/x86/include/asm/spec_ctrl_asm.h -+++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h -@@ -230,7 +230,10 @@ - wrmsr - .endm - --/* Use after an entry from PV context (syscall/sysenter/int80/int82/etc). */ -+/* -+ * Used after an entry from PV context: SYSCALL, SYSENTER, INT, -+ * etc. There is always a guest speculation state in context. -+ */ - .macro SPEC_CTRL_ENTRY_FROM_PV - /* - * Requires %rsp=regs/cpuinfo, %rdx=0 -@@ -245,7 +248,11 @@ - X86_FEATURE_SC_MSR_PV - .endm - --/* Use in interrupt/exception context. May interrupt Xen or PV context. */ -+/* -+ * Used after an exception or maskable interrupt, hitting Xen or PV context. -+ * There will either be a guest speculation context, or (barring fatal -+ * exceptions) a well-formed Xen speculation context. -+ */ - .macro SPEC_CTRL_ENTRY_FROM_INTR - /* - * Requires %rsp=regs, %r14=stack_end, %rdx=0 -@@ -260,7 +267,10 @@ - X86_FEATURE_SC_MSR_PV - .endm - --/* Use when exiting to PV guest context. */ -+/* -+ * Used when exiting from any entry context, back to PV context. This -+ * includes from an IST entry which moved onto the primary stack. -+ */ - .macro SPEC_CTRL_EXIT_TO_PV - /* - * Requires %rax=spec_ctrl, %rsp=regs/info -@@ -272,7 +282,13 @@ - .endm - - /* -- * Use in IST interrupt/exception context. May interrupt Xen or PV context. -+ * Used after an IST entry hitting Xen or PV context. Special care is needed, -+ * because when hitting Xen context, there may not be a well-formed -+ * speculation context. (i.e. it can hit in the middle of -+ * SPEC_CTRL_{ENTRY,EXIT}_* regions.) -+ * -+ * An IST entry which hits PV context moves onto the primary stack and leaves -+ * via SPEC_CTRL_EXIT_TO_PV, *not* SPEC_CTRL_EXIT_TO_XEN. - */ - .macro SPEC_CTRL_ENTRY_FROM_INTR_IST - /* -@@ -331,7 +347,14 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): - UNLIKELY_END(\@_serialise) - .endm - --/* Use when exiting to Xen context. */ -+/* -+ * Use when exiting from any entry context, back to Xen context. This -+ * includes returning to other SPEC_CTRL_{ENTRY,EXIT}_* regions with an -+ * incomplete speculation context. -+ * -+ * Because we might have interrupted Xen beyond SPEC_CTRL_EXIT_TO_$GUEST, we -+ * need to treat this as if it were an EXIT_TO_$GUEST case too. -+ */ - .macro SPEC_CTRL_EXIT_TO_XEN - /* - * Requires %rbx=stack_end -@@ -356,6 +379,9 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): - wrmsr - - .L\@_skip_sc_msr: -+ -+ /* TODO VERW */ -+ - .endm - - #endif /* __ASSEMBLY__ */ --- -2.41.0 - diff --git a/xsa439-0006-x86-entry-Adjust-restore_all_xen-to-hold-stack_end-i.patch b/xsa439-0006-x86-entry-Adjust-restore_all_xen-to-hold-stack_end-i.patch deleted file mode 100644 index e44998e..0000000 --- a/xsa439-0006-x86-entry-Adjust-restore_all_xen-to-hold-stack_end-i.patch +++ /dev/null @@ -1,77 +0,0 @@ -From 5f7efd47c8273fde972637d0360851802f76eca9 Mon Sep 17 00:00:00 2001 -Message-ID: <5f7efd47c8273fde972637d0360851802f76eca9.1695733540.git.m.a.young@durham.ac.uk> -In-Reply-To: -References: -From: Andrew Cooper -Date: Wed, 13 Sep 2023 13:48:16 +0100 -Subject: [XEN PATCH 06/10] x86/entry: Adjust restore_all_xen to hold stack_end - in %r14 - -All other SPEC_CTRL_{ENTRY,EXIT}_* helpers hold stack_end in %r14. Adjust it -for consistency. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -(cherry picked from commit 7aa28849a1155d856e214e9a80a7e65fffdc3e58) ---- - xen/arch/x86/include/asm/spec_ctrl_asm.h | 8 ++++---- - xen/arch/x86/x86_64/entry.S | 8 ++++---- - 2 files changed, 8 insertions(+), 8 deletions(-) - -diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h -index 97c4db31cd..66c706496f 100644 ---- a/xen/arch/x86/include/asm/spec_ctrl_asm.h -+++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h -@@ -357,10 +357,10 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): - */ - .macro SPEC_CTRL_EXIT_TO_XEN - /* -- * Requires %rbx=stack_end -+ * Requires %r14=stack_end - * Clobbers %rax, %rcx, %rdx - */ -- testb $SCF_ist_sc_msr, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%rbx) -+ testb $SCF_ist_sc_msr, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14) - jz .L\@_skip_sc_msr - - /* -@@ -371,10 +371,10 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): - */ - xor %edx, %edx - -- testb $SCF_use_shadow, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%rbx) -+ testb $SCF_use_shadow, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14) - jz .L\@_skip_sc_msr - -- mov STACK_CPUINFO_FIELD(shadow_spec_ctrl)(%rbx), %eax -+ mov STACK_CPUINFO_FIELD(shadow_spec_ctrl)(%r14), %eax - mov $MSR_SPEC_CTRL, %ecx - wrmsr - -diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S -index b45a09823a..92279a225d 100644 ---- a/xen/arch/x86/x86_64/entry.S -+++ b/xen/arch/x86/x86_64/entry.S -@@ -665,15 +665,15 @@ restore_all_xen: - * Check whether we need to switch to the per-CPU page tables, in - * case we return to late PV exit code (from an NMI or #MC). - */ -- GET_STACK_END(bx) -- cmpb $0, STACK_CPUINFO_FIELD(use_pv_cr3)(%rbx) -+ GET_STACK_END(14) -+ cmpb $0, STACK_CPUINFO_FIELD(use_pv_cr3)(%r14) - UNLIKELY_START(ne, exit_cr3) -- mov STACK_CPUINFO_FIELD(pv_cr3)(%rbx), %rax -+ mov STACK_CPUINFO_FIELD(pv_cr3)(%r14), %rax - mov %rax, %cr3 - UNLIKELY_END(exit_cr3) - - /* WARNING! `ret`, `call *`, `jmp *` not safe beyond this point. */ -- SPEC_CTRL_EXIT_TO_XEN /* Req: %rbx=end, Clob: acd */ -+ SPEC_CTRL_EXIT_TO_XEN /* Req: %r14=end, Clob: acd */ - - RESTORE_ALL adj=8 - iretq --- -2.41.0 - diff --git a/xsa439-0007-x86-entry-Track-the-IST-ness-of-an-entry-for-the-exi.patch b/xsa439-0007-x86-entry-Track-the-IST-ness-of-an-entry-for-the-exi.patch deleted file mode 100644 index 2e36bcc..0000000 --- a/xsa439-0007-x86-entry-Track-the-IST-ness-of-an-entry-for-the-exi.patch +++ /dev/null @@ -1,112 +0,0 @@ -From e4a71bc0da0baf7464bb0d8e33053f330e5ea366 Mon Sep 17 00:00:00 2001 -Message-ID: -In-Reply-To: -References: -From: Andrew Cooper -Date: Wed, 13 Sep 2023 12:20:12 +0100 -Subject: [XEN PATCH 07/10] x86/entry: Track the IST-ness of an entry for the - exit paths - -Use %r12 to hold an ist_exit boolean. This register is zero elsewhere in the -entry/exit asm, so it only needs setting in the IST path. - -As this is subtle and fragile, add check_ist_exit() to be used in debugging -builds to cross-check that the ist_exit boolean matches the entry vector. - -Write check_ist_exit() it in C, because it's debug only and the logic more -complicated than I care to maintain in asm. - -For now, we only need to use this signal in the exit-to-Xen path, but some -exit-to-guest paths happen in IST context too. Check the correctness in all -exit paths to avoid the logic bit-rotting. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -(cherry picked from commit 21bdc25b05a0f8ab6bc73520a9ca01327360732c) - -x86/entry: Partially revert IST-exit checks - -The patch adding check_ist_exit() didn't account for the fact that -reset_stack_and_jump() is not an ABI-preserving boundary. The IST-ness in -%r12 doesn't survive into the next context, and is a stale value C. - -This shows up in Gitlab CI for the Clang build: - - https://gitlab.com/xen-project/people/andyhhp/xen/-/jobs/5112783827 - -and in OSSTest for GCC 8: - - http://logs.test-lab.xenproject.org/osstest/logs/183045/test-amd64-amd64-xl-qemuu-debianhvm-amd64/serial-pinot0.log - -There's no straightforward way to reconstruct the IST-exit-ness on the -exit-to-guest path after a context switch. For now, we only need IST-exit on -the return-to-Xen path. - -Fixes: 21bdc25b05a0 ("x86/entry: Track the IST-ness of an entry for the exit paths") -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -(cherry picked from commit 9b57c800b79b96769ea3dcd6468578fa664d19f9) ---- - xen/arch/x86/traps.c | 13 +++++++++++++ - xen/arch/x86/x86_64/entry.S | 13 ++++++++++++- - 2 files changed, 25 insertions(+), 1 deletion(-) - -diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c -index d12004b1c6..e65cc60041 100644 ---- a/xen/arch/x86/traps.c -+++ b/xen/arch/x86/traps.c -@@ -2315,6 +2315,19 @@ void asm_domain_crash_synchronous(unsigned long addr) - do_softirq(); - } - -+#ifdef CONFIG_DEBUG -+void check_ist_exit(const struct cpu_user_regs *regs, bool ist_exit) -+{ -+ const unsigned int ist_mask = -+ (1U << X86_EXC_NMI) | (1U << X86_EXC_DB) | -+ (1U << X86_EXC_DF) | (1U << X86_EXC_MC); -+ uint8_t ev = regs->entry_vector; -+ bool is_ist = (ev < TRAP_nr) && ((1U << ev) & ist_mask); -+ -+ ASSERT(is_ist == ist_exit); -+} -+#endif -+ - /* - * Local variables: - * mode: C -diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S -index 92279a225d..4cebc4fbe3 100644 ---- a/xen/arch/x86/x86_64/entry.S -+++ b/xen/arch/x86/x86_64/entry.S -@@ -659,8 +659,15 @@ ENTRY(early_page_fault) - .section .text.entry, "ax", @progbits - - ALIGN --/* No special register assumptions. */ -+/* %r12=ist_exit */ - restore_all_xen: -+ -+#ifdef CONFIG_DEBUG -+ mov %rsp, %rdi -+ mov %r12, %rsi -+ call check_ist_exit -+#endif -+ - /* - * Check whether we need to switch to the per-CPU page tables, in - * case we return to late PV exit code (from an NMI or #MC). -@@ -1091,6 +1098,10 @@ handle_ist_exception: - .L_ist_dispatch_done: - mov %r15, STACK_CPUINFO_FIELD(xen_cr3)(%r14) - mov %bl, STACK_CPUINFO_FIELD(use_pv_cr3)(%r14) -+ -+ /* This is an IST exit */ -+ mov $1, %r12d -+ - cmpb $TRAP_nmi,UREGS_entry_vector(%rsp) - jne ret_from_intr - --- -2.41.0 - diff --git a/xsa439-0008-x86-spec-ctrl-Issue-VERW-during-IST-exit-to-Xen.patch b/xsa439-0008-x86-spec-ctrl-Issue-VERW-during-IST-exit-to-Xen.patch deleted file mode 100644 index 6e00ca6..0000000 --- a/xsa439-0008-x86-spec-ctrl-Issue-VERW-during-IST-exit-to-Xen.patch +++ /dev/null @@ -1,92 +0,0 @@ -From 2e2c3efcfc9f183674a8de6ed954ffbe7188b70d Mon Sep 17 00:00:00 2001 -Message-ID: <2e2c3efcfc9f183674a8de6ed954ffbe7188b70d.1695733540.git.m.a.young@durham.ac.uk> -In-Reply-To: -References: -From: Andrew Cooper -Date: Wed, 13 Sep 2023 13:53:33 +0100 -Subject: [XEN PATCH 08/10] x86/spec-ctrl: Issue VERW during IST exit to Xen - -There is a corner case where e.g. an NMI hitting an exit-to-guest path after -SPEC_CTRL_EXIT_TO_* would have run the entire NMI handler *after* the VERW -flush to scrub potentially sensitive data from uarch buffers. - -In order to compensate, issue VERW when exiting to Xen from an IST entry. - -SPEC_CTRL_EXIT_TO_XEN already has two reads of spec_ctrl_flags off the stack, -and we're about to add a third. Load the field into %ebx, and list the -register as clobbered. - -%r12 has been arranged to be the ist_exit signal, so add this as an input -dependency and use it to identify when to issue a VERW. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -(cherry picked from commit 3ee6066bcd737756b0990d417d94eddc0b0d2585) ---- - xen/arch/x86/include/asm/spec_ctrl_asm.h | 20 +++++++++++++++----- - xen/arch/x86/x86_64/entry.S | 2 +- - 2 files changed, 16 insertions(+), 6 deletions(-) - -diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h -index 66c706496f..28a75796e6 100644 ---- a/xen/arch/x86/include/asm/spec_ctrl_asm.h -+++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h -@@ -357,10 +357,12 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): - */ - .macro SPEC_CTRL_EXIT_TO_XEN - /* -- * Requires %r14=stack_end -- * Clobbers %rax, %rcx, %rdx -+ * Requires %r12=ist_exit, %r14=stack_end -+ * Clobbers %rax, %rbx, %rcx, %rdx - */ -- testb $SCF_ist_sc_msr, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14) -+ movzbl STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14), %ebx -+ -+ testb $SCF_ist_sc_msr, %bl - jz .L\@_skip_sc_msr - - /* -@@ -371,7 +373,7 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): - */ - xor %edx, %edx - -- testb $SCF_use_shadow, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14) -+ testb $SCF_use_shadow, %bl - jz .L\@_skip_sc_msr - - mov STACK_CPUINFO_FIELD(shadow_spec_ctrl)(%r14), %eax -@@ -380,8 +382,16 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): - - .L\@_skip_sc_msr: - -- /* TODO VERW */ -+ test %r12, %r12 -+ jz .L\@_skip_ist_exit -+ -+ /* Logically DO_SPEC_CTRL_COND_VERW but without the %rsp=cpuinfo dependency */ -+ testb $SCF_verw, %bl -+ jz .L\@_skip_verw -+ verw STACK_CPUINFO_FIELD(verw_sel)(%r14) -+.L\@_skip_verw: - -+.L\@_skip_ist_exit: - .endm - - #endif /* __ASSEMBLY__ */ -diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S -index 4cebc4fbe3..c12e011b4d 100644 ---- a/xen/arch/x86/x86_64/entry.S -+++ b/xen/arch/x86/x86_64/entry.S -@@ -680,7 +680,7 @@ UNLIKELY_START(ne, exit_cr3) - UNLIKELY_END(exit_cr3) - - /* WARNING! `ret`, `call *`, `jmp *` not safe beyond this point. */ -- SPEC_CTRL_EXIT_TO_XEN /* Req: %r14=end, Clob: acd */ -+ SPEC_CTRL_EXIT_TO_XEN /* Req: %r12=ist_exit %r14=end, Clob: abcd */ - - RESTORE_ALL adj=8 - iretq --- -2.41.0 - diff --git a/xsa439-0009-x86-amd-Introduce-is_zen-1-2-_uarch-predicates.patch b/xsa439-0009-x86-amd-Introduce-is_zen-1-2-_uarch-predicates.patch deleted file mode 100644 index 5f063b1..0000000 --- a/xsa439-0009-x86-amd-Introduce-is_zen-1-2-_uarch-predicates.patch +++ /dev/null @@ -1,94 +0,0 @@ -From 19ee1e1faa32b79274b3484cb1170a5970f1e602 Mon Sep 17 00:00:00 2001 -Message-ID: <19ee1e1faa32b79274b3484cb1170a5970f1e602.1695733540.git.m.a.young@durham.ac.uk> -In-Reply-To: -References: -From: Andrew Cooper -Date: Fri, 15 Sep 2023 12:13:51 +0100 -Subject: [XEN PATCH 09/10] x86/amd: Introduce is_zen{1,2}_uarch() predicates - -We already have 3 cases using STIBP as a Zen1/2 heuristic, and are about to -introduce a 4th. Wrap the heuristic into a pair of predicates rather than -opencoding it, and the explanation of the heuristic, at each usage site. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -(cherry picked from commit de1d265001397f308c5c3c5d3ffc30e7ef8c0705) ---- - xen/arch/x86/cpu/amd.c | 18 ++++-------------- - xen/arch/x86/include/asm/amd.h | 11 +++++++++++ - 2 files changed, 15 insertions(+), 14 deletions(-) - -diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c -index 1bb3044be1..e94ba5a0e0 100644 ---- a/xen/arch/x86/cpu/amd.c -+++ b/xen/arch/x86/cpu/amd.c -@@ -855,15 +855,13 @@ void amd_set_legacy_ssbd(bool enable) - * non-branch instructions to be ignored. It is to be set unilaterally in - * newer microcode. - * -- * This chickenbit is something unrelated on Zen1, and Zen1 vs Zen2 isn't a -- * simple model number comparison, so use STIBP as a heuristic to separate the -- * two uarches in Fam17h(AMD)/18h(Hygon). -+ * This chickenbit is something unrelated on Zen1. - */ - void amd_init_spectral_chicken(void) - { - uint64_t val, chickenbit = 1 << 1; - -- if (cpu_has_hypervisor || !boot_cpu_has(X86_FEATURE_AMD_STIBP)) -+ if (cpu_has_hypervisor || !is_zen2_uarch()) - return; - - if (rdmsr_safe(MSR_AMD64_DE_CFG2, val) == 0 && !(val & chickenbit)) -@@ -912,11 +910,8 @@ void amd_check_zenbleed(void) - * With the Fam17h check above, most parts getting here are - * Zen1. They're not affected. Assume Zen2 ones making it - * here are affected regardless of microcode version. -- * -- * Zen1 vs Zen2 isn't a simple model number comparison, so use -- * STIBP as a heuristic to distinguish. - */ -- if (!boot_cpu_has(X86_FEATURE_AMD_STIBP)) -+ if (is_zen1_uarch()) - return; - good_rev = ~0U; - break; -@@ -1277,12 +1272,7 @@ static int __init cf_check zen2_c6_errata_check(void) - */ - s_time_t delta; - -- /* -- * Zen1 vs Zen2 isn't a simple model number comparison, so use STIBP as -- * a heuristic to separate the two uarches in Fam17h. -- */ -- if (cpu_has_hypervisor || boot_cpu_data.x86 != 0x17 || -- !boot_cpu_has(X86_FEATURE_AMD_STIBP)) -+ if (cpu_has_hypervisor || boot_cpu_data.x86 != 0x17 || !is_zen2_uarch()) - return 0; - - /* -diff --git a/xen/arch/x86/include/asm/amd.h b/xen/arch/x86/include/asm/amd.h -index a975d3de26..82324110ab 100644 ---- a/xen/arch/x86/include/asm/amd.h -+++ b/xen/arch/x86/include/asm/amd.h -@@ -140,6 +140,17 @@ - AMD_MODEL_RANGE(0x11, 0x0, 0x0, 0xff, 0xf), \ - AMD_MODEL_RANGE(0x12, 0x0, 0x0, 0xff, 0xf)) - -+/* -+ * The Zen1 and Zen2 microarchitectures are implemented by AMD (Fam17h) and -+ * Hygon (Fam18h) but without simple model number rules. Instead, use STIBP -+ * as a heuristic that distinguishes the two. -+ * -+ * The caller is required to perform the appropriate vendor/family checks -+ * first. -+ */ -+#define is_zen1_uarch() (!boot_cpu_has(X86_FEATURE_AMD_STIBP)) -+#define is_zen2_uarch() boot_cpu_has(X86_FEATURE_AMD_STIBP) -+ - struct cpuinfo_x86; - int cpu_has_amd_erratum(const struct cpuinfo_x86 *, int, ...); - --- -2.41.0 - diff --git a/xsa439-0010-x86-spec-ctrl-Mitigate-the-Zen1-DIV-leakage.patch b/xsa439-0010-x86-spec-ctrl-Mitigate-the-Zen1-DIV-leakage.patch deleted file mode 100644 index 0dc6780..0000000 --- a/xsa439-0010-x86-spec-ctrl-Mitigate-the-Zen1-DIV-leakage.patch +++ /dev/null @@ -1,231 +0,0 @@ -From 9ac2f49f5fa3a5159409241d4f74fb0d721dd4c5 Mon Sep 17 00:00:00 2001 -Message-ID: <9ac2f49f5fa3a5159409241d4f74fb0d721dd4c5.1695733540.git.m.a.young@durham.ac.uk> -In-Reply-To: -References: -From: Andrew Cooper -Date: Wed, 30 Aug 2023 20:24:25 +0100 -Subject: [XEN PATCH 10/10] x86/spec-ctrl: Mitigate the Zen1 DIV leakage - -In the Zen1 microarchitecure, there is one divider in the pipeline which -services uops from both threads. In the case of #DE, the latched result from -the previous DIV to execute will be forwarded speculatively. - -This is an interesting covert channel that allows two threads to communicate -without any system calls. In also allows userspace to obtain the result of -the most recent DIV instruction executed (even speculatively) in the core, -which can be from a higher privilege context. - -Scrub the result from the divider by executing a non-faulting divide. This -needs performing on the exit-to-guest paths, and ist_exit-to-Xen. - -Alternatives in IST context is believed safe now that it's done in NMI -context. - -This is XSA-439 / CVE-2023-20588. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -(cherry picked from commit b5926c6ecf05c28ee99c6248c42d691ccbf0c315) ---- - docs/misc/xen-command-line.pandoc | 6 ++- - xen/arch/x86/hvm/svm/entry.S | 1 + - xen/arch/x86/include/asm/cpufeatures.h | 2 +- - xen/arch/x86/include/asm/spec_ctrl_asm.h | 17 +++++++++ - xen/arch/x86/spec_ctrl.c | 48 +++++++++++++++++++++++- - 5 files changed, 71 insertions(+), 3 deletions(-) - -diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc -index d9dae740cc..b92c8f969c 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -2315,7 +2315,7 @@ By default SSBD will be mitigated at runtime (i.e `ssbd=runtime`). - > {msr-sc,rsb,md-clear,ibpb-entry}=|{pv,hvm}=, - > bti-thunk=retpoline|lfence|jmp, {ibrs,ibpb,ssbd,psfd, - > eager-fpu,l1d-flush,branch-harden,srb-lock, --> unpriv-mmio,gds-mit}= ]` -+> unpriv-mmio,gds-mit,div-scrub}= ]` - - Controls for speculative execution sidechannel mitigations. By default, Xen - will pick the most appropriate mitigations based on compiled in support, -@@ -2437,6 +2437,10 @@ has elected not to lock the configuration, Xen will use GDS_CTRL to mitigate - GDS with. Otherwise, Xen will mitigate by disabling AVX, which blocks the use - of the AVX2 Gather instructions. - -+On all hardware, the `div-scrub=` option can be used to force or prevent Xen -+from mitigating the DIV-leakage vulnerability. By default, Xen will mitigate -+DIV-leakage on hardware believed to be vulnerable. -+ - ### sync_console - > `= ` - -diff --git a/xen/arch/x86/hvm/svm/entry.S b/xen/arch/x86/hvm/svm/entry.S -index 981cd82e7c..934f12cf5c 100644 ---- a/xen/arch/x86/hvm/svm/entry.S -+++ b/xen/arch/x86/hvm/svm/entry.S -@@ -74,6 +74,7 @@ __UNLIKELY_END(nsvm_hap) - 1: /* No Spectre v1 concerns. Execution will hit VMRUN imminently. */ - .endm - ALTERNATIVE "", svm_vmentry_spec_ctrl, X86_FEATURE_SC_MSR_HVM -+ ALTERNATIVE "", DO_SPEC_CTRL_DIV, X86_FEATURE_SC_DIV - - pop %r15 - pop %r14 -diff --git a/xen/arch/x86/include/asm/cpufeatures.h b/xen/arch/x86/include/asm/cpufeatures.h -index da0593de85..c3aad21c3b 100644 ---- a/xen/arch/x86/include/asm/cpufeatures.h -+++ b/xen/arch/x86/include/asm/cpufeatures.h -@@ -35,7 +35,7 @@ XEN_CPUFEATURE(SC_RSB_HVM, X86_SYNTH(19)) /* RSB overwrite needed for HVM - XEN_CPUFEATURE(XEN_SELFSNOOP, X86_SYNTH(20)) /* SELFSNOOP gets used by Xen itself */ - XEN_CPUFEATURE(SC_MSR_IDLE, X86_SYNTH(21)) /* Clear MSR_SPEC_CTRL on idle */ - XEN_CPUFEATURE(XEN_LBR, X86_SYNTH(22)) /* Xen uses MSR_DEBUGCTL.LBR */ --/* Bits 23 unused. */ -+XEN_CPUFEATURE(SC_DIV, X86_SYNTH(23)) /* DIV scrub needed */ - XEN_CPUFEATURE(SC_RSB_IDLE, X86_SYNTH(24)) /* RSB overwrite needed for idle. */ - XEN_CPUFEATURE(SC_VERW_IDLE, X86_SYNTH(25)) /* VERW used by Xen for idle */ - XEN_CPUFEATURE(XEN_SHSTK, X86_SYNTH(26)) /* Xen uses CET Shadow Stacks */ -diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h -index 28a75796e6..f4b8b9d956 100644 ---- a/xen/arch/x86/include/asm/spec_ctrl_asm.h -+++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h -@@ -177,6 +177,19 @@ - .L\@_verw_skip: - .endm - -+.macro DO_SPEC_CTRL_DIV -+/* -+ * Requires nothing -+ * Clobbers %rax -+ * -+ * Issue a DIV for its flushing side effect (Zen1 uarch specific). Any -+ * non-faulting DIV will do; a byte DIV has least latency, and doesn't clobber -+ * %rdx. -+ */ -+ mov $1, %eax -+ div %al -+.endm -+ - .macro DO_SPEC_CTRL_ENTRY maybexen:req - /* - * Requires %rsp=regs (also cpuinfo if !maybexen) -@@ -279,6 +292,8 @@ - ALTERNATIVE "", DO_SPEC_CTRL_EXIT_TO_GUEST, X86_FEATURE_SC_MSR_PV - - DO_SPEC_CTRL_COND_VERW -+ -+ ALTERNATIVE "", DO_SPEC_CTRL_DIV, X86_FEATURE_SC_DIV - .endm - - /* -@@ -391,6 +406,8 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): - verw STACK_CPUINFO_FIELD(verw_sel)(%r14) - .L\@_skip_verw: - -+ ALTERNATIVE "", DO_SPEC_CTRL_DIV, X86_FEATURE_SC_DIV -+ - .L\@_skip_ist_exit: - .endm - -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 79b98f0fe7..0ff3c895ac 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -79,6 +79,7 @@ static int8_t __initdata opt_srb_lock = -1; - static bool __initdata opt_unpriv_mmio; - static bool __ro_after_init opt_fb_clear_mmio; - static int8_t __initdata opt_gds_mit = -1; -+static int8_t __initdata opt_div_scrub = -1; - - static int __init cf_check parse_spec_ctrl(const char *s) - { -@@ -133,6 +134,7 @@ static int __init cf_check parse_spec_ctrl(const char *s) - opt_srb_lock = 0; - opt_unpriv_mmio = false; - opt_gds_mit = 0; -+ opt_div_scrub = 0; - } - else if ( val > 0 ) - rc = -EINVAL; -@@ -285,6 +287,8 @@ static int __init cf_check parse_spec_ctrl(const char *s) - opt_unpriv_mmio = val; - else if ( (val = parse_boolean("gds-mit", s, ss)) >= 0 ) - opt_gds_mit = val; -+ else if ( (val = parse_boolean("div-scrub", s, ss)) >= 0 ) -+ opt_div_scrub = val; - else - rc = -EINVAL; - -@@ -485,7 +489,7 @@ static void __init print_details(enum ind_thunk thunk) - "\n"); - - /* Settings for Xen's protection, irrespective of guests. */ -- printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s%s%s, Other:%s%s%s%s%s\n", -+ printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s%s%s, Other:%s%s%s%s%s%s\n", - thunk == THUNK_NONE ? "N/A" : - thunk == THUNK_RETPOLINE ? "RETPOLINE" : - thunk == THUNK_LFENCE ? "LFENCE" : -@@ -510,6 +514,7 @@ static void __init print_details(enum ind_thunk thunk) - opt_l1d_flush ? " L1D_FLUSH" : "", - opt_md_clear_pv || opt_md_clear_hvm || - opt_fb_clear_mmio ? " VERW" : "", -+ opt_div_scrub ? " DIV" : "", - opt_branch_harden ? " BRANCH_HARDEN" : ""); - - /* L1TF diagnostics, printed if vulnerable or PV shadowing is in use. */ -@@ -967,6 +972,45 @@ static void __init srso_calculations(bool hw_smt_enabled) - setup_force_cpu_cap(X86_FEATURE_SRSO_NO); - } - -+/* -+ * The Div leakage issue is specific to the AMD Zen1 microarchitecure. -+ * -+ * However, there's no $FOO_NO bit defined, so if we're virtualised we have no -+ * hope of spotting the case where we might move to vulnerable hardware. We -+ * also can't make any useful conclusion about SMT-ness. -+ * -+ * Don't check the hypervisor bit, so at least we do the safe thing when -+ * booting on something that looks like a Zen1 CPU. -+ */ -+static bool __init has_div_vuln(void) -+{ -+ if ( !(boot_cpu_data.x86_vendor & -+ (X86_VENDOR_AMD | X86_VENDOR_HYGON)) ) -+ return false; -+ -+ if ( boot_cpu_data.x86 != 0x17 && boot_cpu_data.x86 != 0x18 ) -+ return false; -+ -+ return is_zen1_uarch(); -+} -+ -+static void __init div_calculations(bool hw_smt_enabled) -+{ -+ bool cpu_bug_div = has_div_vuln(); -+ -+ if ( opt_div_scrub == -1 ) -+ opt_div_scrub = cpu_bug_div; -+ -+ if ( opt_div_scrub ) -+ setup_force_cpu_cap(X86_FEATURE_SC_DIV); -+ -+ if ( opt_smt == -1 && !cpu_has_hypervisor && cpu_bug_div && hw_smt_enabled ) -+ warning_add( -+ "Booted on leaky-DIV hardware with SMT/Hyperthreading\n" -+ "enabled. Please assess your configuration and choose an\n" -+ "explicit 'smt=' setting. See XSA-439.\n"); -+} -+ - static void __init ibpb_calculations(void) - { - bool def_ibpb_entry = false; -@@ -1726,6 +1770,8 @@ void __init init_speculation_mitigations(void) - - ibpb_calculations(); - -+ div_calculations(hw_smt_enabled); -+ - /* Check whether Eager FPU should be enabled by default. */ - if ( opt_eager_fpu == -1 ) - opt_eager_fpu = should_use_eager_fpu(); --- -2.41.0 - diff --git a/xsa440-4.17.patch b/xsa440-4.17.patch deleted file mode 100644 index 4941afc..0000000 --- a/xsa440-4.17.patch +++ /dev/null @@ -1,58 +0,0 @@ -From 5d8b3d1ec98e56155d9650d7f4a70cd8ba9dc27d Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Fri, 22 Sep 2023 11:32:16 +0100 -Subject: tools/xenstored: domain_entry_fix(): Handle conflicting transaction - -The function domain_entry_fix() will be initially called to check if the -quota is correct before attempt to commit any nodes. So it would be -possible that accounting is temporarily negative. This is the case -in the following sequence: - - 1) Create 50 nodes - 2) Start two transactions - 3) Delete all the nodes in each transaction - 4) Commit the two transactions - -Because the first transaction will have succeed and updated the -accounting, there is no guarantee that 'd->nbentry + num' will still -be above 0. So the assert() would be triggered. -The assert() was introduced in dbef1f748289 ("tools/xenstore: simplify -and fix per domain node accounting") with the assumption that the -value can't be negative. As this is not true revert to the original -check but restricted to the path where we don't update. Take the -opportunity to explain the rationale behind the check. - -This CVE-2023-34323 / XSA-440. - -Reported-by: Stanislav Uschakow -Fixes: dbef1f748289 ("tools/xenstore: simplify and fix per domain node accounting") -Signed-off-by: Julien Grall -Reviewed-by: Juergen Gross - -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index aa86892fed9e..6074df210c6e 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -1094,10 +1094,20 @@ int domain_entry_fix(unsigned int domid, int num, bool update) - } - - cnt = d->nbentry + num; -- assert(cnt >= 0); - -- if (update) -+ if (update) { -+ assert(cnt >= 0); - d->nbentry = cnt; -+ } else if (cnt < 0) { -+ /* -+ * In a transaction when a node is being added/removed AND -+ * the same node has been added/removed outside the -+ * transaction in parallel, the result value may be negative. -+ * This is no problem, as the transaction will fail due to -+ * the resulting conflict. So override 'cnt'. -+ */ -+ cnt = 0; -+ } - - return domid_is_unprivileged(domid) ? cnt : 0; - } diff --git a/xsa442-4.17.patch b/xsa442-4.17.patch deleted file mode 100644 index a78bfdd..0000000 --- a/xsa442-4.17.patch +++ /dev/null @@ -1,185 +0,0 @@ -From 5b2ccb60ff22fbff44dd66214c2956a434ee6271 Mon Sep 17 00:00:00 2001 -From: Roger Pau Monne -Date: Tue, 13 Jun 2023 15:01:05 +0200 -Subject: [PATCH] iommu/amd-vi: flush IOMMU TLB when flushing the DTE -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The caching invalidation guidelines from the AMD-Vi specification (48882—Rev -3.07-PUB—Oct 2022) seem to be misleading on some hardware, as devices will -malfunction (see stale DMA mappings) if some fields of the DTE are updated but -the IOMMU TLB is not flushed. This has been observed in practice on AMD -systems. Due to the lack of guidance from the currently published -specification this patch aims to increase the flushing done in order to prevent -device malfunction. - -In order to fix, issue an INVALIDATE_IOMMU_PAGES command from -amd_iommu_flush_device(), flushing all the address space. Note this requires -callers to be adjusted in order to pass the DomID on the DTE previous to the -modification. - -Some call sites don't provide a valid DomID to amd_iommu_flush_device() in -order to avoid the flush. That's because the device had address translations -disabled and hence the previous DomID on the DTE is not valid. Note the -current logic relies on the entity disabling address translations to also flush -the TLB of the in use DomID. - -Device I/O TLB flushing when ATS are enabled is not covered by the current -change, as ATS usage is not security supported. - -This is XSA-442 / CVE-2023-34326 - -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich ---- - xen/drivers/passthrough/amd/iommu.h | 3 ++- - xen/drivers/passthrough/amd/iommu_cmd.c | 10 +++++++++- - xen/drivers/passthrough/amd/iommu_guest.c | 5 +++-- - xen/drivers/passthrough/amd/iommu_init.c | 6 +++++- - xen/drivers/passthrough/amd/pci_amd_iommu.c | 14 ++++++++++---- - 5 files changed, 29 insertions(+), 9 deletions(-) - -diff --git a/xen/drivers/passthrough/amd/iommu.h b/xen/drivers/passthrough/amd/iommu.h -index 5429ada58ef5..a58be28bf96d 100644 ---- a/xen/drivers/passthrough/amd/iommu.h -+++ b/xen/drivers/passthrough/amd/iommu.h -@@ -283,7 +283,8 @@ void amd_iommu_flush_pages(struct domain *d, unsigned long dfn, - unsigned int order); - void amd_iommu_flush_iotlb(u8 devfn, const struct pci_dev *pdev, - uint64_t gaddr, unsigned int order); --void amd_iommu_flush_device(struct amd_iommu *iommu, uint16_t bdf); -+void amd_iommu_flush_device(struct amd_iommu *iommu, uint16_t bdf, -+ domid_t domid); - void amd_iommu_flush_intremap(struct amd_iommu *iommu, uint16_t bdf); - void amd_iommu_flush_all_caches(struct amd_iommu *iommu); - -diff --git a/xen/drivers/passthrough/amd/iommu_cmd.c b/xen/drivers/passthrough/amd/iommu_cmd.c -index 40ddf366bb4d..cb28b36abc38 100644 ---- a/xen/drivers/passthrough/amd/iommu_cmd.c -+++ b/xen/drivers/passthrough/amd/iommu_cmd.c -@@ -363,10 +363,18 @@ void amd_iommu_flush_pages(struct domain *d, - _amd_iommu_flush_pages(d, __dfn_to_daddr(dfn), order); - } - --void amd_iommu_flush_device(struct amd_iommu *iommu, uint16_t bdf) -+void amd_iommu_flush_device(struct amd_iommu *iommu, uint16_t bdf, -+ domid_t domid) - { - invalidate_dev_table_entry(iommu, bdf); - flush_command_buffer(iommu, 0); -+ -+ /* Also invalidate IOMMU TLB entries when flushing the DTE. */ -+ if ( domid != DOMID_INVALID ) -+ { -+ invalidate_iommu_pages(iommu, INV_IOMMU_ALL_PAGES_ADDRESS, domid, 0); -+ flush_command_buffer(iommu, 0); -+ } - } - - void amd_iommu_flush_intremap(struct amd_iommu *iommu, uint16_t bdf) -diff --git a/xen/drivers/passthrough/amd/iommu_guest.c b/xen/drivers/passthrough/amd/iommu_guest.c -index 80a331f546ed..be86bce6fb03 100644 ---- a/xen/drivers/passthrough/amd/iommu_guest.c -+++ b/xen/drivers/passthrough/amd/iommu_guest.c -@@ -385,7 +385,7 @@ static int do_completion_wait(struct domain *d, cmd_entry_t *cmd) - - static int do_invalidate_dte(struct domain *d, cmd_entry_t *cmd) - { -- uint16_t gbdf, mbdf, req_id, gdom_id, hdom_id; -+ uint16_t gbdf, mbdf, req_id, gdom_id, hdom_id, prev_domid; - struct amd_iommu_dte *gdte, *mdte, *dte_base; - struct amd_iommu *iommu = NULL; - struct guest_iommu *g_iommu; -@@ -445,13 +445,14 @@ static int do_invalidate_dte(struct domain *d, cmd_entry_t *cmd) - req_id = get_dma_requestor_id(iommu->seg, mbdf); - dte_base = iommu->dev_table.buffer; - mdte = &dte_base[req_id]; -+ prev_domid = mdte->domain_id; - - spin_lock_irqsave(&iommu->lock, flags); - dte_set_gcr3_table(mdte, hdom_id, gcr3_mfn << PAGE_SHIFT, gv, glx); - - spin_unlock_irqrestore(&iommu->lock, flags); - -- amd_iommu_flush_device(iommu, req_id); -+ amd_iommu_flush_device(iommu, req_id, prev_domid); - - return 0; - } -diff --git a/xen/drivers/passthrough/amd/iommu_init.c b/xen/drivers/passthrough/amd/iommu_init.c -index 166570648d26..101a60ce1794 100644 ---- a/xen/drivers/passthrough/amd/iommu_init.c -+++ b/xen/drivers/passthrough/amd/iommu_init.c -@@ -1547,7 +1547,11 @@ static int cf_check _invalidate_all_devices( - req_id = ivrs_mappings[bdf].dte_requestor_id; - if ( iommu ) - { -- amd_iommu_flush_device(iommu, req_id); -+ /* -+ * IOMMU TLB flush performed separately (see -+ * invalidate_all_domain_pages()). -+ */ -+ amd_iommu_flush_device(iommu, req_id, DOMID_INVALID); - amd_iommu_flush_intremap(iommu, req_id); - } - } -diff --git a/xen/drivers/passthrough/amd/pci_amd_iommu.c b/xen/drivers/passthrough/amd/pci_amd_iommu.c -index 94e37755064b..8641b84712a0 100644 ---- a/xen/drivers/passthrough/amd/pci_amd_iommu.c -+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c -@@ -192,10 +192,13 @@ static int __must_check amd_iommu_setup_domain_device( - - spin_unlock_irqrestore(&iommu->lock, flags); - -- amd_iommu_flush_device(iommu, req_id); -+ /* DTE didn't have DMA translations enabled, do not flush the TLB. */ -+ amd_iommu_flush_device(iommu, req_id, DOMID_INVALID); - } - else if ( dte->pt_root != mfn_x(page_to_mfn(root_pg)) ) - { -+ domid_t prev_domid = dte->domain_id; -+ - /* - * Strictly speaking if the device is the only one with this requestor - * ID, it could be allowed to be re-assigned regardless of unity map -@@ -252,7 +255,7 @@ static int __must_check amd_iommu_setup_domain_device( - - spin_unlock_irqrestore(&iommu->lock, flags); - -- amd_iommu_flush_device(iommu, req_id); -+ amd_iommu_flush_device(iommu, req_id, prev_domid); - } - else - spin_unlock_irqrestore(&iommu->lock, flags); -@@ -421,6 +424,8 @@ static void amd_iommu_disable_domain_device(const struct domain *domain, - spin_lock_irqsave(&iommu->lock, flags); - if ( dte->tv || dte->v ) - { -+ domid_t prev_domid = dte->domain_id; -+ - /* See the comment in amd_iommu_setup_device_table(). */ - dte->int_ctl = IOMMU_DEV_TABLE_INT_CONTROL_ABORTED; - smp_wmb(); -@@ -439,7 +444,7 @@ static void amd_iommu_disable_domain_device(const struct domain *domain, - - spin_unlock_irqrestore(&iommu->lock, flags); - -- amd_iommu_flush_device(iommu, req_id); -+ amd_iommu_flush_device(iommu, req_id, prev_domid); - - AMD_IOMMU_DEBUG("Disable: device id = %#x, " - "domain = %d, paging mode = %d\n", -@@ -610,7 +615,8 @@ static int cf_check amd_iommu_add_device(u8 devfn, struct pci_dev *pdev) - - spin_unlock_irqrestore(&iommu->lock, flags); - -- amd_iommu_flush_device(iommu, bdf); -+ /* DTE didn't have DMA translations enabled, do not flush the TLB. */ -+ amd_iommu_flush_device(iommu, bdf, DOMID_INVALID); - } - - if ( amd_iommu_reserve_domain_unity_map( --- -2.42.0 - diff --git a/xsa443-4.17-01.patch b/xsa443-4.17-01.patch deleted file mode 100644 index d9ca3f8..0000000 --- a/xsa443-4.17-01.patch +++ /dev/null @@ -1,70 +0,0 @@ -From 7e48562bf34e90f907491a0595782d2daa1ff3ad Mon Sep 17 00:00:00 2001 -From: Alejandro Vallejo -Date: Thu, 14 Sep 2023 13:22:50 +0100 -Subject: [PATCH 01/11] libfsimage/xfs: Remove dead code - -xfs_info.agnolog (and related code) and XFS_INO_AGBNO_BITS are dead code -that serve no purpose. - -This is part of XSA-443 / CVE-2023-34325 - -Signed-off-by: Alejandro Vallejo -Reviewed-by: Jan Beulich ---- - tools/libfsimage/xfs/fsys_xfs.c | 18 ------------------ - 1 file changed, 18 deletions(-) - -diff --git a/tools/libfsimage/xfs/fsys_xfs.c b/tools/libfsimage/xfs/fsys_xfs.c -index d735a88e55f3..2800699f5985 100644 ---- a/tools/libfsimage/xfs/fsys_xfs.c -+++ b/tools/libfsimage/xfs/fsys_xfs.c -@@ -37,7 +37,6 @@ struct xfs_info { - int blklog; - int inopblog; - int agblklog; -- int agnolog; - unsigned int nextents; - xfs_daddr_t next; - xfs_daddr_t daddr; -@@ -65,9 +64,7 @@ static struct xfs_info xfs; - - #define XFS_INO_MASK(k) ((xfs_uint32_t)((1ULL << (k)) - 1)) - #define XFS_INO_OFFSET_BITS xfs.inopblog --#define XFS_INO_AGBNO_BITS xfs.agblklog - #define XFS_INO_AGINO_BITS (xfs.agblklog + xfs.inopblog) --#define XFS_INO_AGNO_BITS xfs.agnolog - - static inline xfs_agblock_t - agino2agbno (xfs_agino_t agino) -@@ -149,20 +146,6 @@ xt_len (xfs_bmbt_rec_32_t *r) - return le32(r->l3) & mask32lo(21); - } - --static inline int --xfs_highbit32(xfs_uint32_t v) --{ -- int i; -- -- if (--v) { -- for (i = 0; i < 31; i++, v >>= 1) { -- if (v == 0) -- return i; -- } -- } -- return 0; --} -- - static int - isinxt (xfs_fileoff_t key, xfs_fileoff_t offset, xfs_filblks_t len) - { -@@ -472,7 +455,6 @@ xfs_mount (fsi_file_t *ffi, const char *options) - - xfs.inopblog = super.sb_inopblog; - xfs.agblklog = super.sb_agblklog; -- xfs.agnolog = xfs_highbit32 (le32(super.sb_agcount)); - - xfs.btnode_ptr0_off = - ((xfs.bsize - sizeof(xfs_btree_block_t)) / --- -2.42.0 - diff --git a/xsa443-4.17-02.patch b/xsa443-4.17-02.patch deleted file mode 100644 index 0f2edaf..0000000 --- a/xsa443-4.17-02.patch +++ /dev/null @@ -1,32 +0,0 @@ -From c26327795b78c93f6fa6d5d46e34f59dc4046601 Mon Sep 17 00:00:00 2001 -From: Alejandro Vallejo -Date: Thu, 14 Sep 2023 13:22:51 +0100 -Subject: [PATCH 02/11] libfsimage/xfs: Amend mask32lo() to allow the value 32 - -agblklog could plausibly be 32, but that would overflow this shift. -Perform the shift as ULL and cast to u32 at the end instead. - -This is part of XSA-443 / CVE-2023-34325 - -Signed-off-by: Alejandro Vallejo -Acked-by: Jan Beulich ---- - tools/libfsimage/xfs/fsys_xfs.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tools/libfsimage/xfs/fsys_xfs.c b/tools/libfsimage/xfs/fsys_xfs.c -index 2800699f5985..4720bb4505c8 100644 ---- a/tools/libfsimage/xfs/fsys_xfs.c -+++ b/tools/libfsimage/xfs/fsys_xfs.c -@@ -60,7 +60,7 @@ static struct xfs_info xfs; - #define inode ((xfs_dinode_t *)((char *)FSYS_BUF + 8192)) - #define icore (inode->di_core) - --#define mask32lo(n) (((xfs_uint32_t)1 << (n)) - 1) -+#define mask32lo(n) ((xfs_uint32_t)((1ull << (n)) - 1)) - - #define XFS_INO_MASK(k) ((xfs_uint32_t)((1ULL << (k)) - 1)) - #define XFS_INO_OFFSET_BITS xfs.inopblog --- -2.42.0 - diff --git a/xsa443-4.17-03.patch b/xsa443-4.17-03.patch deleted file mode 100644 index b89721a..0000000 --- a/xsa443-4.17-03.patch +++ /dev/null @@ -1,137 +0,0 @@ -From 199f0538bbec052028679a55ea512437170854c9 Mon Sep 17 00:00:00 2001 -From: Alejandro Vallejo -Date: Thu, 14 Sep 2023 13:22:52 +0100 -Subject: [PATCH 03/11] libfsimage/xfs: Sanity-check the superblock during - mounts - -Sanity-check the XFS superblock for wellformedness at the mount handler. -This forces pygrub to abort parsing a potentially malformed filesystem and -ensures the invariants assumed throughout the rest of the code hold. - -Also, derive parameters from previously sanitized parameters where possible -(rather than reading them off the superblock) - -The code doesn't try to avoid overflowing the end of the disk, because -that's an unlikely and benign error. Parameters used in calculations of -xfs_daddr_t (like the root inode index) aren't in critical need of being -sanitized. - -The sanitization of agblklog is basically checking that no obvious -overflows happen on agblklog, and then ensuring agblocks is contained in -the range (2^(sb_agblklog-1), 2^sb_agblklog]. - -This is part of XSA-443 / CVE-2023-34325 - -Reported-by: Ferdinand Nölscher -Signed-off-by: Alejandro Vallejo -Reviewed-by: Jan Beulich ---- - tools/libfsimage/xfs/fsys_xfs.c | 48 ++++++++++++++++++++++++++------- - tools/libfsimage/xfs/xfs.h | 12 +++++++++ - 2 files changed, 50 insertions(+), 10 deletions(-) - -diff --git a/tools/libfsimage/xfs/fsys_xfs.c b/tools/libfsimage/xfs/fsys_xfs.c -index 4720bb4505c8..e4eb7e1ee26f 100644 ---- a/tools/libfsimage/xfs/fsys_xfs.c -+++ b/tools/libfsimage/xfs/fsys_xfs.c -@@ -17,6 +17,7 @@ - * along with this program; If not, see . - */ - -+#include - #include - #include "xfs.h" - -@@ -433,29 +434,56 @@ first_dentry (fsi_file_t *ffi, xfs_ino_t *ino) - return next_dentry (ffi, ino); - } - -+static bool -+xfs_sb_is_invalid (const xfs_sb_t *super) -+{ -+ return (le32(super->sb_magicnum) != XFS_SB_MAGIC) -+ || ((le16(super->sb_versionnum) & XFS_SB_VERSION_NUMBITS) != -+ XFS_SB_VERSION_4) -+ || (super->sb_inodelog < XFS_SB_INODELOG_MIN) -+ || (super->sb_inodelog > XFS_SB_INODELOG_MAX) -+ || (super->sb_blocklog < XFS_SB_BLOCKLOG_MIN) -+ || (super->sb_blocklog > XFS_SB_BLOCKLOG_MAX) -+ || (super->sb_blocklog < super->sb_inodelog) -+ || (super->sb_agblklog > XFS_SB_AGBLKLOG_MAX) -+ || ((1ull << super->sb_agblklog) < le32(super->sb_agblocks)) -+ || (((1ull << super->sb_agblklog) >> 1) >= -+ le32(super->sb_agblocks)) -+ || ((super->sb_blocklog + super->sb_dirblklog) >= -+ XFS_SB_DIRBLK_NUMBITS); -+} -+ - static int - xfs_mount (fsi_file_t *ffi, const char *options) - { - xfs_sb_t super; - - if (!devread (ffi, 0, 0, sizeof(super), (char *)&super) -- || (le32(super.sb_magicnum) != XFS_SB_MAGIC) -- || ((le16(super.sb_versionnum) -- & XFS_SB_VERSION_NUMBITS) != XFS_SB_VERSION_4) ) { -+ || xfs_sb_is_invalid(&super)) { - return 0; - } - -- xfs.bsize = le32 (super.sb_blocksize); -- xfs.blklog = super.sb_blocklog; -- xfs.bdlog = xfs.blklog - SECTOR_BITS; -+ /* -+ * Not sanitized. It's exclusively used to generate disk addresses, -+ * so it's not important from a security standpoint. -+ */ - xfs.rootino = le64 (super.sb_rootino); -- xfs.isize = le16 (super.sb_inodesize); -- xfs.agblocks = le32 (super.sb_agblocks); -- xfs.dirbsize = xfs.bsize << super.sb_dirblklog; - -- xfs.inopblog = super.sb_inopblog; -+ /* -+ * Sanitized to be consistent with each other, only used to -+ * generate disk addresses, so it's safe -+ */ -+ xfs.agblocks = le32 (super.sb_agblocks); - xfs.agblklog = super.sb_agblklog; - -+ /* Derived from sanitized parameters */ -+ xfs.bsize = 1 << super.sb_blocklog; -+ xfs.blklog = super.sb_blocklog; -+ xfs.bdlog = super.sb_blocklog - SECTOR_BITS; -+ xfs.isize = 1 << super.sb_inodelog; -+ xfs.dirbsize = 1 << (super.sb_blocklog + super.sb_dirblklog); -+ xfs.inopblog = super.sb_blocklog - super.sb_inodelog; -+ - xfs.btnode_ptr0_off = - ((xfs.bsize - sizeof(xfs_btree_block_t)) / - (sizeof (xfs_bmbt_key_t) + sizeof (xfs_bmbt_ptr_t))) -diff --git a/tools/libfsimage/xfs/xfs.h b/tools/libfsimage/xfs/xfs.h -index 40699281e44d..b87e37d3d7e9 100644 ---- a/tools/libfsimage/xfs/xfs.h -+++ b/tools/libfsimage/xfs/xfs.h -@@ -134,6 +134,18 @@ typedef struct xfs_sb - xfs_uint8_t sb_dummy[7]; /* padding */ - } xfs_sb_t; - -+/* Bound taken from xfs.c in GRUB2. It doesn't exist in the spec */ -+#define XFS_SB_DIRBLK_NUMBITS 27 -+/* Implied by the XFS specification. The minimum block size is 512 octets */ -+#define XFS_SB_BLOCKLOG_MIN 9 -+/* Implied by the XFS specification. The maximum block size is 65536 octets */ -+#define XFS_SB_BLOCKLOG_MAX 16 -+/* Implied by the XFS specification. The minimum inode size is 256 octets */ -+#define XFS_SB_INODELOG_MIN 8 -+/* Implied by the XFS specification. The maximum inode size is 2048 octets */ -+#define XFS_SB_INODELOG_MAX 11 -+/* High bound for sb_agblklog */ -+#define XFS_SB_AGBLKLOG_MAX 32 - - /* those are from xfs_btree.h */ - --- -2.42.0 - diff --git a/xsa443-4.17-04.patch b/xsa443-4.17-04.patch deleted file mode 100644 index dde095e..0000000 --- a/xsa443-4.17-04.patch +++ /dev/null @@ -1,61 +0,0 @@ -From c66fd01277939634c624c8340838682d9d4fd839 Mon Sep 17 00:00:00 2001 -From: Alejandro Vallejo -Date: Thu, 14 Sep 2023 13:22:53 +0100 -Subject: [PATCH 04/11] libfsimage/xfs: Add compile-time check to libfsimage - -Adds the common tools include folder to the -I compile flags -of libfsimage. This allows us to use: - xen-tools/common-macros.h:BUILD_BUG_ON() - -With it, statically assert a sanitized "blocklog - SECTOR_BITS" cannot -underflow. - -This is part of XSA-443 / CVE-2023-34325 - -Signed-off-by: Alejandro Vallejo -Reviewed-by: Jan Beulich ---- - tools/libfsimage/common.mk | 2 +- - tools/libfsimage/xfs/fsys_xfs.c | 4 +++- - 2 files changed, 4 insertions(+), 2 deletions(-) - -diff --git a/tools/libfsimage/common.mk b/tools/libfsimage/common.mk -index 4fc8c6679599..e4336837d045 100644 ---- a/tools/libfsimage/common.mk -+++ b/tools/libfsimage/common.mk -@@ -1,7 +1,7 @@ - include $(XEN_ROOT)/tools/Rules.mk - - FSDIR := $(libdir)/xenfsimage --CFLAGS += -Wno-unknown-pragmas -I$(XEN_ROOT)/tools/libfsimage/common/ -DFSIMAGE_FSDIR=\"$(FSDIR)\" -+CFLAGS += -Wno-unknown-pragmas -I$(XEN_ROOT)/tools/libfsimage/common/ $(CFLAGS_xeninclude) -DFSIMAGE_FSDIR=\"$(FSDIR)\" - CFLAGS += -D_GNU_SOURCE - LDFLAGS += -L../common/ - -diff --git a/tools/libfsimage/xfs/fsys_xfs.c b/tools/libfsimage/xfs/fsys_xfs.c -index e4eb7e1ee26f..4a8dd6f2397b 100644 ---- a/tools/libfsimage/xfs/fsys_xfs.c -+++ b/tools/libfsimage/xfs/fsys_xfs.c -@@ -19,6 +19,7 @@ - - #include - #include -+#include - #include "xfs.h" - - #define MAX_LINK_COUNT 8 -@@ -477,9 +478,10 @@ xfs_mount (fsi_file_t *ffi, const char *options) - xfs.agblklog = super.sb_agblklog; - - /* Derived from sanitized parameters */ -+ BUILD_BUG_ON(XFS_SB_BLOCKLOG_MIN < SECTOR_BITS); -+ xfs.bdlog = super.sb_blocklog - SECTOR_BITS; - xfs.bsize = 1 << super.sb_blocklog; - xfs.blklog = super.sb_blocklog; -- xfs.bdlog = super.sb_blocklog - SECTOR_BITS; - xfs.isize = 1 << super.sb_inodelog; - xfs.dirbsize = 1 << (super.sb_blocklog + super.sb_dirblklog); - xfs.inopblog = super.sb_blocklog - super.sb_inodelog; --- -2.42.0 - diff --git a/xsa443-4.17-05.patch b/xsa443-4.17-05.patch deleted file mode 100644 index b2f5daa..0000000 --- a/xsa443-4.17-05.patch +++ /dev/null @@ -1,59 +0,0 @@ -From ad5d0db5e68e5d4e79255fa85d9cb0069bb1c5d5 Mon Sep 17 00:00:00 2001 -From: Alejandro Vallejo -Date: Mon, 25 Sep 2023 18:32:21 +0100 -Subject: [PATCH 05/11] tools/pygrub: Remove unnecessary hypercall - -There's a hypercall being issued in order to determine whether PV64 is -supported, but since Xen 4.3 that's strictly true so it's not required. - -Plus, this way we can avoid mapping the privcmd interface altogether in the -depriv pygrub. - -This is part of XSA-443 / CVE-2023-34325 - -Signed-off-by: Alejandro Vallejo -Reviewed-by: Andrew Cooper ---- - tools/pygrub/src/pygrub | 12 +----------- - 1 file changed, 1 insertion(+), 11 deletions(-) - -diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub -index ce7ab0eb8cf3..ce4e07d3e823 100755 ---- a/tools/pygrub/src/pygrub -+++ b/tools/pygrub/src/pygrub -@@ -18,7 +18,6 @@ import os, sys, string, struct, tempfile, re, traceback, stat, errno - import copy - import logging - import platform --import xen.lowlevel.xc - - import curses, _curses, curses.textpad, curses.ascii - import getopt -@@ -668,14 +667,6 @@ def run_grub(file, entry, fs, cfg_args): - - return grubcfg - --def supports64bitPVguest(): -- xc = xen.lowlevel.xc.xc() -- caps = xc.xeninfo()['xen_caps'].split(" ") -- for cap in caps: -- if cap == "xen-3.0-x86_64": -- return True -- return False -- - # If nothing has been specified, look for a Solaris domU. If found, perform the - # necessary tweaks. - def sniff_solaris(fs, cfg): -@@ -684,8 +675,7 @@ def sniff_solaris(fs, cfg): - return cfg - - if not cfg["kernel"]: -- if supports64bitPVguest() and \ -- fs.file_exists("/platform/i86xpv/kernel/amd64/unix"): -+ if fs.file_exists("/platform/i86xpv/kernel/amd64/unix"): - cfg["kernel"] = "/platform/i86xpv/kernel/amd64/unix" - cfg["ramdisk"] = "/platform/i86pc/amd64/boot_archive" - elif fs.file_exists("/platform/i86xpv/kernel/unix"): --- -2.42.0 - diff --git a/xsa443-4.17-06.patch b/xsa443-4.17-06.patch deleted file mode 100644 index 22af109..0000000 --- a/xsa443-4.17-06.patch +++ /dev/null @@ -1,65 +0,0 @@ -From d3ceb0b314005a656dd2ca4b2821575a36f8426d Mon Sep 17 00:00:00 2001 -From: Alejandro Vallejo -Date: Mon, 25 Sep 2023 18:32:22 +0100 -Subject: [PATCH 06/11] tools/pygrub: Small refactors - -Small tidy up to ensure output_directory always has a trailing '/' to ease -concatenating paths and that `output` can only be a filename or None. - -This is part of XSA-443 / CVE-2023-34325 - -Signed-off-by: Alejandro Vallejo -Acked-by: Andrew Cooper ---- - tools/pygrub/src/pygrub | 10 +++++----- - 1 file changed, 5 insertions(+), 5 deletions(-) - -diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub -index ce4e07d3e823..1042c05b8676 100755 ---- a/tools/pygrub/src/pygrub -+++ b/tools/pygrub/src/pygrub -@@ -793,7 +793,7 @@ if __name__ == "__main__": - debug = False - not_really = False - output_format = "sxp" -- output_directory = "/var/run/xen/pygrub" -+ output_directory = "/var/run/xen/pygrub/" - - # what was passed in - incfg = { "kernel": None, "ramdisk": None, "args": "" } -@@ -815,7 +815,8 @@ if __name__ == "__main__": - usage() - sys.exit() - elif o in ("--output",): -- output = a -+ if a != "-": -+ output = a - elif o in ("--kernel",): - incfg["kernel"] = a - elif o in ("--ramdisk",): -@@ -847,12 +848,11 @@ if __name__ == "__main__": - if not os.path.isdir(a): - print("%s is not an existing directory" % a) - sys.exit(1) -- output_directory = a -+ output_directory = a + '/' - - if debug: - logging.basicConfig(level=logging.DEBUG) - -- - try: - os.makedirs(output_directory, 0o700) - except OSError as e: -@@ -861,7 +861,7 @@ if __name__ == "__main__": - else: - raise - -- if output is None or output == "-": -+ if output is None: - fd = sys.stdout.fileno() - else: - fd = os.open(output, os.O_WRONLY) --- -2.42.0 - diff --git a/xsa443-4.17-07.patch b/xsa443-4.17-07.patch deleted file mode 100644 index 94da883..0000000 --- a/xsa443-4.17-07.patch +++ /dev/null @@ -1,105 +0,0 @@ -From 9e80cfecde338cea0db136c2fb5ed78d6081e05f Mon Sep 17 00:00:00 2001 -From: Alejandro Vallejo -Date: Mon, 25 Sep 2023 18:32:23 +0100 -Subject: [PATCH 07/11] tools/pygrub: Open the output files earlier - -This patch allows pygrub to get ahold of every RW file descriptor it needs -early on. A later patch will clamp the filesystem it can access so it can't -obtain any others. - -This is part of XSA-443 / CVE-2023-34325 - -Signed-off-by: Alejandro Vallejo -Acked-by: Andrew Cooper ---- - tools/pygrub/src/pygrub | 37 ++++++++++++++++++++++--------------- - 1 file changed, 22 insertions(+), 15 deletions(-) - -diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub -index 1042c05b8676..91e2ec2ab105 100755 ---- a/tools/pygrub/src/pygrub -+++ b/tools/pygrub/src/pygrub -@@ -738,8 +738,7 @@ if __name__ == "__main__": - def usage(): - print("Usage: %s [-q|--quiet] [-i|--interactive] [-l|--list-entries] [-n|--not-really] [--output=] [--kernel=] [--ramdisk=] [--args=] [--entry=] [--output-directory=] [--output-format=sxp|simple|simple0] [--offset=] " %(sys.argv[0],), file=sys.stderr) - -- def copy_from_image(fs, file_to_read, file_type, output_directory, -- not_really): -+ def copy_from_image(fs, file_to_read, file_type, fd_dst, path_dst, not_really): - if not_really: - if fs.file_exists(file_to_read): - return "<%s:%s>" % (file_type, file_to_read) -@@ -750,21 +749,18 @@ if __name__ == "__main__": - except Exception as e: - print(e, file=sys.stderr) - sys.exit("Error opening %s in guest" % file_to_read) -- (tfd, ret) = tempfile.mkstemp(prefix="boot_"+file_type+".", -- dir=output_directory) - dataoff = 0 - while True: - data = datafile.read(FS_READ_MAX, dataoff) - if len(data) == 0: -- os.close(tfd) -+ os.close(fd_dst) - del datafile -- return ret -+ return - try: -- os.write(tfd, data) -+ os.write(fd_dst, data) - except Exception as e: - print(e, file=sys.stderr) -- os.close(tfd) -- os.unlink(ret) -+ os.unlink(path_dst) - del datafile - sys.exit("Error writing temporary copy of "+file_type) - dataoff += len(data) -@@ -861,6 +857,14 @@ if __name__ == "__main__": - else: - raise - -+ if not_really: -+ fd_kernel = path_kernel = fd_ramdisk = path_ramdisk = None -+ else: -+ (fd_kernel, path_kernel) = tempfile.mkstemp(prefix="boot_kernel.", -+ dir=output_directory) -+ (fd_ramdisk, path_ramdisk) = tempfile.mkstemp(prefix="boot_ramdisk.", -+ dir=output_directory) -+ - if output is None: - fd = sys.stdout.fileno() - else: -@@ -920,20 +924,23 @@ if __name__ == "__main__": - if fs is None: - raise RuntimeError("Unable to find partition containing kernel") - -- bootcfg["kernel"] = copy_from_image(fs, chosencfg["kernel"], "kernel", -- output_directory, not_really) -+ copy_from_image(fs, chosencfg["kernel"], "kernel", -+ fd_kernel, path_kernel, not_really) -+ bootcfg["kernel"] = path_kernel - - if chosencfg["ramdisk"]: - try: -- bootcfg["ramdisk"] = copy_from_image(fs, chosencfg["ramdisk"], -- "ramdisk", output_directory, -- not_really) -+ copy_from_image(fs, chosencfg["ramdisk"], "ramdisk", -+ fd_ramdisk, path_ramdisk, not_really) - except: - if not not_really: -- os.unlink(bootcfg["kernel"]) -+ os.unlink(path_kernel) - raise -+ bootcfg["ramdisk"] = path_ramdisk - else: - initrd = None -+ if not not_really: -+ os.unlink(path_ramdisk) - - args = None - if chosencfg["args"]: --- -2.42.0 - diff --git a/xsa443-4.17-08.patch b/xsa443-4.17-08.patch deleted file mode 100644 index bd7de1d..0000000 --- a/xsa443-4.17-08.patch +++ /dev/null @@ -1,126 +0,0 @@ -From 2fb4cdcedd8720f78c4bd44739a5d30dd1a7d9a5 Mon Sep 17 00:00:00 2001 -From: Alejandro Vallejo -Date: Mon, 25 Sep 2023 18:32:24 +0100 -Subject: [PATCH 08/11] tools/libfsimage: Export a new function to preload all - plugins - -This is work required in order to let pygrub operate in highly deprivileged -chroot mode. This patch adds a function that preloads every plugin, hence -ensuring that a on function exit, every shared library is loaded in memory. - -The new "init" function is supposed to be used before depriv, but that's -fine because it's not acting on untrusted data. - -This is part of XSA-443 / CVE-2023-34325 - -Signed-off-by: Alejandro Vallejo -Acked-by: Andrew Cooper ---- - tools/libfsimage/common/fsimage_plugin.c | 4 ++-- - tools/libfsimage/common/mapfile-GNU | 1 + - tools/libfsimage/common/mapfile-SunOS | 1 + - tools/libfsimage/common/xenfsimage.h | 8 ++++++++ - tools/pygrub/src/fsimage/fsimage.c | 15 +++++++++++++++ - 5 files changed, 27 insertions(+), 2 deletions(-) - -diff --git a/tools/libfsimage/common/fsimage_plugin.c b/tools/libfsimage/common/fsimage_plugin.c -index de1412b4233a..d0cb9e96a654 100644 ---- a/tools/libfsimage/common/fsimage_plugin.c -+++ b/tools/libfsimage/common/fsimage_plugin.c -@@ -119,7 +119,7 @@ fail: - return (-1); - } - --static int load_plugins(void) -+int fsi_init(void) - { - const char *fsdir = getenv("XEN_FSIMAGE_FSDIR"); - struct dirent *dp = NULL; -@@ -180,7 +180,7 @@ int find_plugin(fsi_t *fsi, const char *path, const char *options) - fsi_plugin_t *fp; - int ret = 0; - -- if (plugins == NULL && (ret = load_plugins()) != 0) -+ if (plugins == NULL && (ret = fsi_init()) != 0) - goto out; - - for (fp = plugins; fp != NULL; fp = fp->fp_next) { -diff --git a/tools/libfsimage/common/mapfile-GNU b/tools/libfsimage/common/mapfile-GNU -index 26d4d7a69ec7..2d54d527d7f5 100644 ---- a/tools/libfsimage/common/mapfile-GNU -+++ b/tools/libfsimage/common/mapfile-GNU -@@ -1,6 +1,7 @@ - VERSION { - libfsimage.so.1.0 { - global: -+ fsi_init; - fsi_open_fsimage; - fsi_close_fsimage; - fsi_file_exists; -diff --git a/tools/libfsimage/common/mapfile-SunOS b/tools/libfsimage/common/mapfile-SunOS -index e99b90b65077..48deedb4252f 100644 ---- a/tools/libfsimage/common/mapfile-SunOS -+++ b/tools/libfsimage/common/mapfile-SunOS -@@ -1,5 +1,6 @@ - libfsimage.so.1.0 { - global: -+ fsi_init; - fsi_open_fsimage; - fsi_close_fsimage; - fsi_file_exists; -diff --git a/tools/libfsimage/common/xenfsimage.h b/tools/libfsimage/common/xenfsimage.h -index 201abd54f23a..341883b2d71a 100644 ---- a/tools/libfsimage/common/xenfsimage.h -+++ b/tools/libfsimage/common/xenfsimage.h -@@ -35,6 +35,14 @@ extern C { - typedef struct fsi fsi_t; - typedef struct fsi_file fsi_file_t; - -+/* -+ * Optional initialization function. If invoked it loads the associated -+ * dynamic libraries for the backends ahead of time. This is required if -+ * the library is to run as part of a highly deprivileged executable, as -+ * the libraries may not be reachable after depriv. -+ */ -+int fsi_init(void); -+ - fsi_t *fsi_open_fsimage(const char *, uint64_t, const char *); - void fsi_close_fsimage(fsi_t *); - -diff --git a/tools/pygrub/src/fsimage/fsimage.c b/tools/pygrub/src/fsimage/fsimage.c -index 2ebbbe35df92..92fbf2851f01 100644 ---- a/tools/pygrub/src/fsimage/fsimage.c -+++ b/tools/pygrub/src/fsimage/fsimage.c -@@ -286,6 +286,15 @@ fsimage_getbootstring(PyObject *o, PyObject *args) - return Py_BuildValue("s", bootstring); - } - -+static PyObject * -+fsimage_init(PyObject *o, PyObject *args) -+{ -+ if (!PyArg_ParseTuple(args, "")) -+ return (NULL); -+ -+ return Py_BuildValue("i", fsi_init()); -+} -+ - PyDoc_STRVAR(fsimage_open__doc__, - "open(name, [offset=off]) - Open the given file as a filesystem image.\n" - "\n" -@@ -297,7 +306,13 @@ PyDoc_STRVAR(fsimage_getbootstring__doc__, - "getbootstring(fs) - Return the boot string needed for this file system " - "or NULL if none is needed.\n"); - -+PyDoc_STRVAR(fsimage_init__doc__, -+ "init() - Loads every dynamic library contained in xenfsimage " -+ "into memory so that it can be used in chrooted environments.\n"); -+ - static struct PyMethodDef fsimage_module_methods[] = { -+ { "init", (PyCFunction)fsimage_init, -+ METH_VARARGS, fsimage_init__doc__ }, - { "open", (PyCFunction)fsimage_open, - METH_VARARGS|METH_KEYWORDS, fsimage_open__doc__ }, - { "getbootstring", (PyCFunction)fsimage_getbootstring, --- -2.42.0 - diff --git a/xsa443-4.17-09.patch b/xsa443-4.17-09.patch deleted file mode 100644 index 2e3ebd8..0000000 --- a/xsa443-4.17-09.patch +++ /dev/null @@ -1,307 +0,0 @@ -From 150771ce86a07e469e34941a63c56e2cf242223b Mon Sep 17 00:00:00 2001 -From: Alejandro Vallejo -Date: Mon, 25 Sep 2023 18:32:25 +0100 -Subject: [PATCH 09/11] tools/pygrub: Deprivilege pygrub - -Introduce a --runas= flag to deprivilege pygrub on Linux and *BSDs. It -also implicitly creates a chroot env where it drops a deprivileged forked -process. The chroot itself is cleaned up at the end. - -If the --runas arg is present, then pygrub forks, leaving the child to -deprivilege itself, and waiting for it to complete. When the child exists, -the parent performs cleanup and exits with the same error code. - -This is roughly what the child does: - 1. Initialize libfsimage (this loads every .so in memory so the chroot - can avoid bind-mounting /{,usr}/lib* - 2. Create a temporary empty chroot directory - 3. Mount tmpfs in it - 4. Bind mount the disk inside, because libfsimage expects a path, not a - file descriptor. - 5. Remount the root tmpfs to be stricter (ro,nosuid,nodev) - 6. Set RLIMIT_FSIZE to a sensibly high amount (128 MiB) - 7. Depriv gid, groups and uid - -With this scheme in place, the "output" files are writable (up to -RLIMIT_FSIZE octets) and the exposed filesystem is immutable and contains -the single only file we can't easily get rid of (the disk). - -If running on Linux, the child process also unshares mount, IPC, and -network namespaces before dropping its privileges. - -This is part of XSA-443 / CVE-2023-34325 - -Signed-off-by: Alejandro Vallejo -Acked-by: Andrew Cooper ---- - tools/pygrub/setup.py | 2 +- - tools/pygrub/src/pygrub | 162 +++++++++++++++++++++++++++++++++++++--- - 2 files changed, 154 insertions(+), 10 deletions(-) - -diff --git a/tools/pygrub/setup.py b/tools/pygrub/setup.py -index 0e4e3d02d372..06b96733d020 100644 ---- a/tools/pygrub/setup.py -+++ b/tools/pygrub/setup.py -@@ -17,7 +17,7 @@ xenfsimage = Extension("xenfsimage", - pkgs = [ 'grub' ] - - setup(name='pygrub', -- version='0.6', -+ version='0.7', - description='Boot loader that looks a lot like grub for Xen', - author='Jeremy Katz', - author_email='katzj@redhat.com', -diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub -index 91e2ec2ab105..7cea496ade08 100755 ---- a/tools/pygrub/src/pygrub -+++ b/tools/pygrub/src/pygrub -@@ -16,8 +16,11 @@ from __future__ import print_function - - import os, sys, string, struct, tempfile, re, traceback, stat, errno - import copy -+import ctypes, ctypes.util - import logging - import platform -+import resource -+import subprocess - - import curses, _curses, curses.textpad, curses.ascii - import getopt -@@ -27,10 +30,135 @@ import grub.GrubConf - import grub.LiloConf - import grub.ExtLinuxConf - --PYGRUB_VER = 0.6 -+PYGRUB_VER = 0.7 - FS_READ_MAX = 1024 * 1024 - SECTOR_SIZE = 512 - -+# Unless provided through the env variable PYGRUB_MAX_FILE_SIZE_MB, then -+# this is the maximum filesize allowed for files written by the depriv -+# pygrub -+LIMIT_FSIZE = 128 << 20 -+ -+CLONE_NEWNS = 0x00020000 # mount namespace -+CLONE_NEWNET = 0x40000000 # network namespace -+CLONE_NEWIPC = 0x08000000 # IPC namespace -+ -+def unshare(flags): -+ if not sys.platform.startswith("linux"): -+ print("skip_unshare reason=not_linux platform=%s", sys.platform, file=sys.stderr) -+ return -+ -+ libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True) -+ unshare_prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, use_errno=True) -+ unshare = unshare_prototype(('unshare', libc)) -+ -+ if unshare(flags) < 0: -+ raise OSError(ctypes.get_errno(), os.strerror(ctypes.get_errno())) -+ -+def bind_mount(src, dst, options): -+ open(dst, "a").close() # touch -+ -+ rc = subprocess.call(["mount", "--bind", "-o", options, src, dst]) -+ if rc != 0: -+ raise RuntimeError("bad_mount: src=%s dst=%s opts=%s" % -+ (src, dst, options)) -+ -+def downgrade_rlimits(): -+ # Wipe the authority to use unrequired resources -+ resource.setrlimit(resource.RLIMIT_NPROC, (0, 0)) -+ resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) -+ resource.setrlimit(resource.RLIMIT_MEMLOCK, (0, 0)) -+ -+ # py2's resource module doesn't know about resource.RLIMIT_MSGQUEUE -+ # -+ # TODO: Use resource.RLIMIT_MSGQUEUE after python2 is deprecated -+ if sys.platform.startswith('linux'): -+ RLIMIT_MSGQUEUE = 12 -+ resource.setrlimit(RLIMIT_MSGQUEUE, (0, 0)) -+ -+ # The final look of the filesystem for this process is fully RO, but -+ # note we have some file descriptor already open (notably, kernel and -+ # ramdisk). In order to avoid a compromised pygrub from filling up the -+ # filesystem we set RLIMIT_FSIZE to a high bound, so that the file -+ # write permissions are bound. -+ fsize = LIMIT_FSIZE -+ if "PYGRUB_MAX_FILE_SIZE_MB" in os.environ.keys(): -+ fsize = os.environ["PYGRUB_MAX_FILE_SIZE_MB"] << 20 -+ -+ resource.setrlimit(resource.RLIMIT_FSIZE, (fsize, fsize)) -+ -+def depriv(output_directory, output, device, uid, path_kernel, path_ramdisk): -+ # The only point of this call is to force the loading of libfsimage. -+ # That way, we don't need to bind-mount it into the chroot -+ rc = xenfsimage.init() -+ if rc != 0: -+ os.unlink(path_ramdisk) -+ os.unlink(path_kernel) -+ raise RuntimeError("bad_xenfsimage: rc=%d" % rc) -+ -+ # Create a temporary directory for the chroot -+ chroot = tempfile.mkdtemp(prefix=str(uid)+'-', dir=output_directory) + '/' -+ device_path = '/device' -+ -+ pid = os.fork() -+ if pid: -+ # parent -+ _, rc = os.waitpid(pid, 0) -+ -+ for path in [path_kernel, path_ramdisk]: -+ # If the child didn't write anything, just get rid of it, -+ # otherwise we end up consuming a 0-size file when parsing -+ # systems without a ramdisk that the ultimate caller of pygrub -+ # may just be unaware of -+ if rc != 0 or os.path.getsize(path) == 0: -+ os.unlink(path) -+ -+ # Normally, unshare(CLONE_NEWNS) will ensure this is not required. -+ # However, this syscall doesn't exist in *BSD systems and doesn't -+ # auto-unmount everything on older Linux kernels (At least as of -+ # Linux 4.19, but it seems fixed in 5.15). Either way, -+ # recursively unmount everything if needed. Quietly. -+ with open('/dev/null', 'w') as devnull: -+ subprocess.call(["umount", "-f", chroot + device_path], -+ stdout=devnull, stderr=devnull) -+ subprocess.call(["umount", "-f", chroot], -+ stdout=devnull, stderr=devnull) -+ os.rmdir(chroot) -+ -+ sys.exit(rc) -+ -+ # By unsharing the namespace we're making sure it's all bulk-released -+ # at the end, when the namespaces disappear. This means the kernel does -+ # (almost) all the cleanup for us and the parent just has to remove the -+ # temporary directory. -+ unshare(CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWNET) -+ -+ # Set sensible limits using the setrlimit interface -+ downgrade_rlimits() -+ -+ # We'll mount tmpfs on the chroot to ensure the deprivileged child -+ # cannot affect the persistent state. It's RW now in order to -+ # bind-mount the device, but note it's remounted RO after that. -+ rc = subprocess.call(["mount", "-t", "tmpfs", "none", chroot]) -+ if rc != 0: -+ raise RuntimeError("mount_tmpfs rc=%d dst=\"%s\"" % (rc, chroot)) -+ -+ # Bind the untrusted device RO -+ bind_mount(device, chroot + device_path, "ro,nosuid,noexec") -+ -+ rc = subprocess.call(["mount", "-t", "tmpfs", "-o", "remount,ro,nosuid,noexec,nodev", "none", chroot]) -+ if rc != 0: -+ raise RuntimeError("remount_tmpfs rc=%d dst=\"%s\"" % (rc, chroot)) -+ -+ # Drop superpowers! -+ os.chroot(chroot) -+ os.chdir('/') -+ os.setgid(uid) -+ os.setgroups([uid]) -+ os.setuid(uid) -+ -+ return device_path -+ - def read_size_roundup(fd, size): - if platform.system() != 'FreeBSD': - return size -@@ -736,7 +864,7 @@ if __name__ == "__main__": - sel = None - - def usage(): -- print("Usage: %s [-q|--quiet] [-i|--interactive] [-l|--list-entries] [-n|--not-really] [--output=] [--kernel=] [--ramdisk=] [--args=] [--entry=] [--output-directory=] [--output-format=sxp|simple|simple0] [--offset=] " %(sys.argv[0],), file=sys.stderr) -+ print("Usage: %s [-q|--quiet] [-i|--interactive] [-l|--list-entries] [-n|--not-really] [--output=] [--kernel=] [--ramdisk=] [--args=] [--entry=] [--output-directory=] [--output-format=sxp|simple|simple0] [--runas=] [--offset=] " %(sys.argv[0],), file=sys.stderr) - - def copy_from_image(fs, file_to_read, file_type, fd_dst, path_dst, not_really): - if not_really: -@@ -760,7 +888,8 @@ if __name__ == "__main__": - os.write(fd_dst, data) - except Exception as e: - print(e, file=sys.stderr) -- os.unlink(path_dst) -+ if path_dst: -+ os.unlink(path_dst) - del datafile - sys.exit("Error writing temporary copy of "+file_type) - dataoff += len(data) -@@ -769,7 +898,7 @@ if __name__ == "__main__": - opts, args = getopt.gnu_getopt(sys.argv[1:], 'qilnh::', - ["quiet", "interactive", "list-entries", "not-really", "help", - "output=", "output-format=", "output-directory=", "offset=", -- "entry=", "kernel=", -+ "runas=", "entry=", "kernel=", - "ramdisk=", "args=", "isconfig", "debug"]) - except getopt.GetoptError: - usage() -@@ -790,6 +919,7 @@ if __name__ == "__main__": - not_really = False - output_format = "sxp" - output_directory = "/var/run/xen/pygrub/" -+ uid = None - - # what was passed in - incfg = { "kernel": None, "ramdisk": None, "args": "" } -@@ -813,6 +943,13 @@ if __name__ == "__main__": - elif o in ("--output",): - if a != "-": - output = a -+ elif o in ("--runas",): -+ try: -+ uid = int(a) -+ except ValueError: -+ print("runas value must be an integer user id") -+ usage() -+ sys.exit(1) - elif o in ("--kernel",): - incfg["kernel"] = a - elif o in ("--ramdisk",): -@@ -849,6 +986,10 @@ if __name__ == "__main__": - if debug: - logging.basicConfig(level=logging.DEBUG) - -+ if interactive and uid: -+ print("In order to use --runas, you must also set --entry or -q", file=sys.stderr) -+ sys.exit(1) -+ - try: - os.makedirs(output_directory, 0o700) - except OSError as e: -@@ -870,6 +1011,9 @@ if __name__ == "__main__": - else: - fd = os.open(output, os.O_WRONLY) - -+ if uid: -+ file = depriv(output_directory, output, file, uid, path_kernel, path_ramdisk) -+ - # debug - if isconfig: - chosencfg = run_grub(file, entry, fs, incfg["args"]) -@@ -925,21 +1069,21 @@ if __name__ == "__main__": - raise RuntimeError("Unable to find partition containing kernel") - - copy_from_image(fs, chosencfg["kernel"], "kernel", -- fd_kernel, path_kernel, not_really) -+ fd_kernel, None if uid else path_kernel, not_really) - bootcfg["kernel"] = path_kernel - - if chosencfg["ramdisk"]: - try: - copy_from_image(fs, chosencfg["ramdisk"], "ramdisk", -- fd_ramdisk, path_ramdisk, not_really) -+ fd_ramdisk, None if uid else path_ramdisk, not_really) - except: -- if not not_really: -- os.unlink(path_kernel) -+ if not uid and not not_really: -+ os.unlink(path_kernel) - raise - bootcfg["ramdisk"] = path_ramdisk - else: - initrd = None -- if not not_really: -+ if not uid and not not_really: - os.unlink(path_ramdisk) - - args = None --- -2.42.0 - diff --git a/xsa443-4.17-10.patch b/xsa443-4.17-10.patch deleted file mode 100644 index 7c91f32..0000000 --- a/xsa443-4.17-10.patch +++ /dev/null @@ -1,250 +0,0 @@ -From 698b451473a6d868ca0f60a124fc4f31d81cd7b1 Mon Sep 17 00:00:00 2001 -From: Roger Pau Monne -Date: Mon, 25 Sep 2023 14:30:20 +0200 -Subject: [PATCH 10/11] libxl: add support for running bootloader in restricted - mode -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Much like the device model depriv mode, add the same kind of support for the -bootloader. Such feature allows passing a UID as a parameter for the -bootloader to run as, together with the bootloader itself taking the necessary -actions to isolate. - -Note that the user to run the bootloader as must have the right permissions to -access the guest disk image (in read mode only), and that the bootloader will -be run in non-interactive mode when restricted. - -If enabled bootloader restrict mode will attempt to re-use the user(s) from the -QEMU depriv implementation if no user is provided on the configuration file or -the environment. See docs/features/qemu-deprivilege.pandoc for more -information about how to setup those users. - -Bootloader restrict mode is not enabled by default as it requires certain -setup to be done first (setup of the user(s) to use in restrict mode). - -This is part of XSA-443 / CVE-2023-34325 - -Signed-off-by: Roger Pau Monné -Reviewed-by: Anthony PERARD ---- - docs/man/xl.1.pod.in | 33 +++++++++++ - tools/libs/light/libxl_bootloader.c | 89 ++++++++++++++++++++++++++++- - tools/libs/light/libxl_dm.c | 8 +-- - tools/libs/light/libxl_internal.h | 8 +++ - 4 files changed, 131 insertions(+), 7 deletions(-) - -diff --git a/docs/man/xl.1.pod.in b/docs/man/xl.1.pod.in -index 101e14241d1c..4831e122427d 100644 ---- a/docs/man/xl.1.pod.in -+++ b/docs/man/xl.1.pod.in -@@ -1957,6 +1957,39 @@ ignored: - - =back - -+=head1 ENVIRONMENT VARIABLES -+ -+The following environment variables shall affect the execution of xl: -+ -+=over 4 -+ -+=item LIBXL_BOOTLOADER_RESTRICT -+ -+Attempt to restrict the bootloader after startup, to limit the -+consequences of security vulnerabilities due to parsing guest -+owned image files. -+ -+See docs/features/qemu-deprivilege.pandoc for more information -+on how to setup the unprivileged users. -+ -+Note that running the bootloader in restricted mode also implies using -+non-interactive mode, and the disk image must be readable by the -+restricted user. -+ -+Having this variable set is equivalent to enabling the option, even if the -+value is 0. -+ -+=item LIBXL_BOOTLOADER_USER -+ -+When using bootloader_restrict, run the bootloader as this user. If -+not set the default QEMU restrict users will be used. -+ -+NOTE: Each domain MUST have a SEPARATE username. -+ -+See docs/features/qemu-deprivilege.pandoc for more information. -+ -+=back -+ - =head1 SEE ALSO - - The following man pages: -diff --git a/tools/libs/light/libxl_bootloader.c b/tools/libs/light/libxl_bootloader.c -index 108329b4a5bb..23c0ef3e8935 100644 ---- a/tools/libs/light/libxl_bootloader.c -+++ b/tools/libs/light/libxl_bootloader.c -@@ -14,6 +14,7 @@ - - #include "libxl_osdeps.h" /* must come before any other headers */ - -+#include - #include - #ifdef HAVE_UTMP_H - #include -@@ -42,8 +43,71 @@ static void bootloader_arg(libxl__bootloader_state *bl, const char *arg) - bl->args[bl->nargs++] = arg; - } - --static void make_bootloader_args(libxl__gc *gc, libxl__bootloader_state *bl, -- const char *bootloader_path) -+static int bootloader_uid(libxl__gc *gc, domid_t guest_domid, -+ const char *user, uid_t *intended_uid) -+{ -+ struct passwd *user_base, user_pwbuf; -+ int rc; -+ -+ if (user) { -+ rc = userlookup_helper_getpwnam(gc, user, &user_pwbuf, &user_base); -+ if (rc) return rc; -+ -+ if (!user_base) { -+ LOGD(ERROR, guest_domid, "Couldn't find user %s", user); -+ return ERROR_INVAL; -+ } -+ -+ *intended_uid = user_base->pw_uid; -+ return 0; -+ } -+ -+ /* Re-use QEMU user range for the bootloader. */ -+ rc = userlookup_helper_getpwnam(gc, LIBXL_QEMU_USER_RANGE_BASE, -+ &user_pwbuf, &user_base); -+ if (rc) return rc; -+ -+ if (user_base) { -+ struct passwd *user_clash, user_clash_pwbuf; -+ uid_t temp_uid = user_base->pw_uid + guest_domid; -+ -+ rc = userlookup_helper_getpwuid(gc, temp_uid, &user_clash_pwbuf, -+ &user_clash); -+ if (rc) return rc; -+ -+ if (user_clash) { -+ LOGD(ERROR, guest_domid, -+ "wanted to use uid %ld (%s + %d) but that is user %s !", -+ (long)temp_uid, LIBXL_QEMU_USER_RANGE_BASE, -+ guest_domid, user_clash->pw_name); -+ return ERROR_INVAL; -+ } -+ -+ *intended_uid = temp_uid; -+ return 0; -+ } -+ -+ rc = userlookup_helper_getpwnam(gc, LIBXL_QEMU_USER_SHARED, &user_pwbuf, -+ &user_base); -+ if (rc) return rc; -+ -+ if (user_base) { -+ LOGD(WARN, guest_domid, "Could not find user %s, falling back to %s", -+ LIBXL_QEMU_USER_RANGE_BASE, LIBXL_QEMU_USER_SHARED); -+ *intended_uid = user_base->pw_uid; -+ -+ return 0; -+ } -+ -+ LOGD(ERROR, guest_domid, -+ "Could not find user %s or range base pseudo-user %s, cannot restrict", -+ LIBXL_QEMU_USER_SHARED, LIBXL_QEMU_USER_RANGE_BASE); -+ -+ return ERROR_INVAL; -+} -+ -+static int make_bootloader_args(libxl__gc *gc, libxl__bootloader_state *bl, -+ const char *bootloader_path) - { - const libxl_domain_build_info *info = bl->info; - -@@ -61,6 +125,23 @@ static void make_bootloader_args(libxl__gc *gc, libxl__bootloader_state *bl, - ARG(GCSPRINTF("--ramdisk=%s", info->ramdisk)); - if (info->cmdline && *info->cmdline != '\0') - ARG(GCSPRINTF("--args=%s", info->cmdline)); -+ if (getenv("LIBXL_BOOTLOADER_RESTRICT") || -+ getenv("LIBXL_BOOTLOADER_USER")) { -+ uid_t uid = -1; -+ int rc = bootloader_uid(gc, bl->domid, getenv("LIBXL_BOOTLOADER_USER"), -+ &uid); -+ -+ if (rc) return rc; -+ -+ assert(uid != -1); -+ if (!uid) { -+ LOGD(ERROR, bl->domid, "bootloader restrict UID is 0 (root)!"); -+ return ERROR_INVAL; -+ } -+ LOGD(DEBUG, bl->domid, "using uid %ld", (long)uid); -+ ARG(GCSPRINTF("--runas=%ld", (long)uid)); -+ ARG("--quiet"); -+ } - - ARG(GCSPRINTF("--output=%s", bl->outputpath)); - ARG("--output-format=simple0"); -@@ -79,6 +160,7 @@ static void make_bootloader_args(libxl__gc *gc, libxl__bootloader_state *bl, - /* Sentinel for execv */ - ARG(NULL); - -+ return 0; - #undef ARG - } - -@@ -443,7 +525,8 @@ static void bootloader_disk_attached_cb(libxl__egc *egc, - bootloader = bltmp; - } - -- make_bootloader_args(gc, bl, bootloader); -+ rc = make_bootloader_args(gc, bl, bootloader); -+ if (rc) goto out; - - bl->openpty.ao = ao; - bl->openpty.callback = bootloader_gotptys; -diff --git a/tools/libs/light/libxl_dm.c b/tools/libs/light/libxl_dm.c -index fc264a3a13a6..14b593110f7c 100644 ---- a/tools/libs/light/libxl_dm.c -+++ b/tools/libs/light/libxl_dm.c -@@ -80,10 +80,10 @@ static int libxl__create_qemu_logfile(libxl__gc *gc, char *name) - * On error, return a libxl-style error code. - */ - #define DEFINE_USERLOOKUP_HELPER(NAME,SPEC_TYPE,STRUCTNAME,SYSCONF) \ -- static int userlookup_helper_##NAME(libxl__gc *gc, \ -- SPEC_TYPE spec, \ -- struct STRUCTNAME *resultbuf, \ -- struct STRUCTNAME **out) \ -+ int userlookup_helper_##NAME(libxl__gc *gc, \ -+ SPEC_TYPE spec, \ -+ struct STRUCTNAME *resultbuf, \ -+ struct STRUCTNAME **out) \ - { \ - struct STRUCTNAME *resultp = NULL; \ - char *buf = NULL; \ -diff --git a/tools/libs/light/libxl_internal.h b/tools/libs/light/libxl_internal.h -index 7ad38de30e0b..f1e3a9a15b13 100644 ---- a/tools/libs/light/libxl_internal.h -+++ b/tools/libs/light/libxl_internal.h -@@ -4873,6 +4873,14 @@ struct libxl__cpu_policy { - struct xc_msr *msr; - }; - -+struct passwd; -+_hidden int userlookup_helper_getpwnam(libxl__gc*, const char *user, -+ struct passwd *res, -+ struct passwd **out); -+_hidden int userlookup_helper_getpwuid(libxl__gc*, uid_t uid, -+ struct passwd *res, -+ struct passwd **out); -+ - #endif - - /* --- -2.42.0 - diff --git a/xsa443-4.17-11.patch b/xsa443-4.17-11.patch deleted file mode 100644 index 27e6f78..0000000 --- a/xsa443-4.17-11.patch +++ /dev/null @@ -1,157 +0,0 @@ -From 9d480426bfa2c68843ac8395b512e06fbdbcf53e Mon Sep 17 00:00:00 2001 -From: Roger Pau Monne -Date: Thu, 28 Sep 2023 12:22:35 +0200 -Subject: [PATCH 11/11] libxl: limit bootloader execution in restricted mode -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Introduce a timeout for bootloader execution when running in restricted mode. - -Allow overwriting the default time out with an environment provided value. - -This is part of XSA-443 / CVE-2023-34325 - -Signed-off-by: Roger Pau Monné -Reviewed-by: Anthony PERARD ---- - docs/man/xl.1.pod.in | 8 ++++++ - tools/libs/light/libxl_bootloader.c | 40 +++++++++++++++++++++++++++++ - tools/libs/light/libxl_internal.h | 2 ++ - 3 files changed, 50 insertions(+) - -diff --git a/docs/man/xl.1.pod.in b/docs/man/xl.1.pod.in -index 4831e122427d..c3eb6570ab8b 100644 ---- a/docs/man/xl.1.pod.in -+++ b/docs/man/xl.1.pod.in -@@ -1988,6 +1988,14 @@ NOTE: Each domain MUST have a SEPARATE username. - - See docs/features/qemu-deprivilege.pandoc for more information. - -+=item LIBXL_BOOTLOADER_TIMEOUT -+ -+Timeout in seconds for bootloader execution when running in restricted mode. -+Otherwise the build time default in LIBXL_BOOTLOADER_TIMEOUT will be used. -+ -+If defined the value must be an unsigned integer between 0 and INT_MAX, -+otherwise behavior is undefined. Setting to 0 disables the timeout. -+ - =back - - =head1 SEE ALSO -diff --git a/tools/libs/light/libxl_bootloader.c b/tools/libs/light/libxl_bootloader.c -index 23c0ef3e8935..ee26d08f3765 100644 ---- a/tools/libs/light/libxl_bootloader.c -+++ b/tools/libs/light/libxl_bootloader.c -@@ -30,6 +30,8 @@ static void bootloader_keystrokes_copyfail(libxl__egc *egc, - libxl__datacopier_state *dc, int rc, int onwrite, int errnoval); - static void bootloader_display_copyfail(libxl__egc *egc, - libxl__datacopier_state *dc, int rc, int onwrite, int errnoval); -+static void bootloader_timeout(libxl__egc *egc, libxl__ev_time *ev, -+ const struct timeval *requested_abs, int rc); - static void bootloader_domaindeath(libxl__egc*, libxl__domaindeathcheck *dc, - int rc); - static void bootloader_finished(libxl__egc *egc, libxl__ev_child *child, -@@ -297,6 +299,7 @@ void libxl__bootloader_init(libxl__bootloader_state *bl) - bl->ptys[0].master = bl->ptys[0].slave = 0; - bl->ptys[1].master = bl->ptys[1].slave = 0; - libxl__ev_child_init(&bl->child); -+ libxl__ev_time_init(&bl->time); - libxl__domaindeathcheck_init(&bl->deathcheck); - bl->keystrokes.ao = bl->ao; libxl__datacopier_init(&bl->keystrokes); - bl->display.ao = bl->ao; libxl__datacopier_init(&bl->display); -@@ -314,6 +317,7 @@ static void bootloader_cleanup(libxl__egc *egc, libxl__bootloader_state *bl) - libxl__domaindeathcheck_stop(gc,&bl->deathcheck); - libxl__datacopier_kill(&bl->keystrokes); - libxl__datacopier_kill(&bl->display); -+ libxl__ev_time_deregister(gc, &bl->time); - for (i=0; i<2; i++) { - libxl__carefd_close(bl->ptys[i].master); - libxl__carefd_close(bl->ptys[i].slave); -@@ -375,6 +379,7 @@ static void bootloader_stop(libxl__egc *egc, - - libxl__datacopier_kill(&bl->keystrokes); - libxl__datacopier_kill(&bl->display); -+ libxl__ev_time_deregister(gc, &bl->time); - if (libxl__ev_child_inuse(&bl->child)) { - r = kill(bl->child.pid, SIGTERM); - if (r) LOGED(WARN, bl->domid, "%sfailed to kill bootloader [%lu]", -@@ -637,6 +642,25 @@ static void bootloader_gotptys(libxl__egc *egc, libxl__openpty_state *op) - - struct termios termattr; - -+ if (getenv("LIBXL_BOOTLOADER_RESTRICT") || -+ getenv("LIBXL_BOOTLOADER_USER")) { -+ const char *timeout_env = getenv("LIBXL_BOOTLOADER_TIMEOUT"); -+ int timeout = timeout_env ? atoi(timeout_env) -+ : LIBXL_BOOTLOADER_TIMEOUT; -+ -+ if (timeout) { -+ /* Set execution timeout */ -+ rc = libxl__ev_time_register_rel(ao, &bl->time, -+ bootloader_timeout, -+ timeout * 1000); -+ if (rc) { -+ LOGED(ERROR, bl->domid, -+ "unable to register timeout for bootloader execution"); -+ goto out; -+ } -+ } -+ } -+ - pid_t pid = libxl__ev_child_fork(gc, &bl->child, bootloader_finished); - if (pid == -1) { - rc = ERROR_FAIL; -@@ -702,6 +726,21 @@ static void bootloader_display_copyfail(libxl__egc *egc, - libxl__bootloader_state *bl = CONTAINER_OF(dc, *bl, display); - bootloader_copyfail(egc, "bootloader output", bl, 1, rc,onwrite,errnoval); - } -+static void bootloader_timeout(libxl__egc *egc, libxl__ev_time *ev, -+ const struct timeval *requested_abs, int rc) -+{ -+ libxl__bootloader_state *bl = CONTAINER_OF(ev, *bl, time); -+ STATE_AO_GC(bl->ao); -+ -+ libxl__ev_time_deregister(gc, &bl->time); -+ -+ assert(libxl__ev_child_inuse(&bl->child)); -+ LOGD(ERROR, bl->domid, "killing bootloader because of timeout"); -+ -+ libxl__ev_child_kill_deregister(ao, &bl->child, SIGKILL); -+ -+ bootloader_callback(egc, bl, rc); -+} - - static void bootloader_domaindeath(libxl__egc *egc, - libxl__domaindeathcheck *dc, -@@ -718,6 +757,7 @@ static void bootloader_finished(libxl__egc *egc, libxl__ev_child *child, - STATE_AO_GC(bl->ao); - int rc; - -+ libxl__ev_time_deregister(gc, &bl->time); - libxl__datacopier_kill(&bl->keystrokes); - libxl__datacopier_kill(&bl->display); - -diff --git a/tools/libs/light/libxl_internal.h b/tools/libs/light/libxl_internal.h -index f1e3a9a15b13..d05783617ff5 100644 ---- a/tools/libs/light/libxl_internal.h -+++ b/tools/libs/light/libxl_internal.h -@@ -102,6 +102,7 @@ - #define LIBXL_QMP_CMD_TIMEOUT 10 - #define LIBXL_STUBDOM_START_TIMEOUT 30 - #define LIBXL_QEMU_BODGE_TIMEOUT 2 -+#define LIBXL_BOOTLOADER_TIMEOUT 120 - #define LIBXL_XENCONSOLE_LIMIT 1048576 - #define LIBXL_XENCONSOLE_PROTOCOL "vt100" - #define LIBXL_MAXMEM_CONSTANT 1024 -@@ -3744,6 +3745,7 @@ struct libxl__bootloader_state { - libxl__openpty_state openpty; - libxl__openpty_result ptys[2]; /* [0] is for bootloader */ - libxl__ev_child child; -+ libxl__ev_time time; - libxl__domaindeathcheck deathcheck; - int nargs, argsspace; - const char **args; --- -2.42.0 - diff --git a/xsa444-4.17-1.patch b/xsa444-4.17-1.patch deleted file mode 100644 index 5a4b2e5..0000000 --- a/xsa444-4.17-1.patch +++ /dev/null @@ -1,93 +0,0 @@ -From: Andrew Cooper -Subject: x86/svm: Fix asymmetry with AMD DR MASK context switching - -The handling of MSR_DR{0..3}_MASK is asymmetric between PV and HVM guests. - -HVM guests context switch in based on the guest view of DBEXT, whereas PV -guest switch in base on the host capability. Both guest types leave the -context dirty for the next vCPU. - -This leads to the following issue: - - * PV or HVM guest has debugging active (%dr7 + mask) - * Switch-out deactivates %dr7 but leaves other state stale in hardware - * Another HVM guest with masks unavailable has debugging active - * Switch in loads %dr7 but leaves the mask MSRs alone - -Now, the second guest's vCPU is operating in the context of the prior vCPU's -mask MSR, while the environment the vCPU can see says there are no mask MSRs. - -As a stopgap, adjust the HVM path to switch in the masks based on host -capabilities rather than guest visibility (i.e. like the PV path). Adjustment -of the intercepts still needs to be dependent on the guest visibility of -DBEXT. - -This is part of XSA-444 / CVE-2023-34327 - -Fixes: c097f54912d3 ("x86/SVM: support data breakpoint extension registers") -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c -index e8f50e7c5ec7..fd32600ae364 100644 ---- a/xen/arch/x86/hvm/svm/svm.c -+++ b/xen/arch/x86/hvm/svm/svm.c -@@ -339,6 +339,10 @@ static void svm_save_dr(struct vcpu *v) - v->arch.hvm.flag_dr_dirty = 0; - vmcb_set_dr_intercepts(vmcb, ~0u); - -+ /* -+ * The guest can only have changed the mask MSRs if we previous dropped -+ * intercepts. Re-read them from hardware. -+ */ - if ( v->domain->arch.cpuid->extd.dbext ) - { - svm_intercept_msr(v, MSR_AMD64_DR0_ADDRESS_MASK, MSR_INTERCEPT_RW); -@@ -370,17 +374,25 @@ static void __restore_debug_registers(struct vmcb_struct *vmcb, struct vcpu *v) - - ASSERT(v == current); - -- if ( v->domain->arch.cpuid->extd.dbext ) -+ /* -+ * Both the PV and HVM paths leave stale DR_MASK values in hardware on -+ * context-switch-out. If we're activating %dr7 for the guest, we must -+ * sync the DR_MASKs too, whether or not the guest can see them. -+ */ -+ if ( boot_cpu_has(X86_FEATURE_DBEXT) ) - { -- svm_intercept_msr(v, MSR_AMD64_DR0_ADDRESS_MASK, MSR_INTERCEPT_NONE); -- svm_intercept_msr(v, MSR_AMD64_DR1_ADDRESS_MASK, MSR_INTERCEPT_NONE); -- svm_intercept_msr(v, MSR_AMD64_DR2_ADDRESS_MASK, MSR_INTERCEPT_NONE); -- svm_intercept_msr(v, MSR_AMD64_DR3_ADDRESS_MASK, MSR_INTERCEPT_NONE); -- - wrmsrl(MSR_AMD64_DR0_ADDRESS_MASK, v->arch.msrs->dr_mask[0]); - wrmsrl(MSR_AMD64_DR1_ADDRESS_MASK, v->arch.msrs->dr_mask[1]); - wrmsrl(MSR_AMD64_DR2_ADDRESS_MASK, v->arch.msrs->dr_mask[2]); - wrmsrl(MSR_AMD64_DR3_ADDRESS_MASK, v->arch.msrs->dr_mask[3]); -+ -+ if ( v->domain->arch.cpuid->extd.dbext ) -+ { -+ svm_intercept_msr(v, MSR_AMD64_DR0_ADDRESS_MASK, MSR_INTERCEPT_NONE); -+ svm_intercept_msr(v, MSR_AMD64_DR1_ADDRESS_MASK, MSR_INTERCEPT_NONE); -+ svm_intercept_msr(v, MSR_AMD64_DR2_ADDRESS_MASK, MSR_INTERCEPT_NONE); -+ svm_intercept_msr(v, MSR_AMD64_DR3_ADDRESS_MASK, MSR_INTERCEPT_NONE); -+ } - } - - write_debugreg(0, v->arch.dr[0]); -diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c -index e65cc6004148..06c4f3868b7a 100644 ---- a/xen/arch/x86/traps.c -+++ b/xen/arch/x86/traps.c -@@ -2281,6 +2281,11 @@ void activate_debugregs(const struct vcpu *curr) - if ( curr->arch.dr7 & DR7_ACTIVE_MASK ) - write_debugreg(7, curr->arch.dr7); - -+ /* -+ * Both the PV and HVM paths leave stale DR_MASK values in hardware on -+ * context-switch-out. If we're activating %dr7 for the guest, we must -+ * sync the DR_MASKs too, whether or not the guest can see them. -+ */ - if ( boot_cpu_has(X86_FEATURE_DBEXT) ) - { - wrmsrl(MSR_AMD64_DR0_ADDRESS_MASK, curr->arch.msrs->dr_mask[0]); diff --git a/xsa444-4.17-2.patch b/xsa444-4.17-2.patch deleted file mode 100644 index 2687bd1..0000000 --- a/xsa444-4.17-2.patch +++ /dev/null @@ -1,72 +0,0 @@ -From: Andrew Cooper -Subject: x86/pv: Correct the auditing of guest breakpoint addresses - -The use of access_ok() is buggy, because it permits access to the compat -translation area. 64bit PV guests don't use the XLAT area, but on AMD -hardware, the DBEXT feature allows a breakpoint to match up to a 4G aligned -region, allowing the breakpoint to reach outside of the XLAT area. - -Prior to c/s cda16c1bb223 ("x86: mirror compat argument translation area for -32-bit PV"), the live GDT was within 4G of the XLAT area. - -All together, this allowed a malicious 64bit PV guest on AMD hardware to place -a breakpoint over the live GDT, and trigger a #DB livelock (CVE-2015-8104). - -Introduce breakpoint_addr_ok() and explain why __addr_ok() happens to be an -appropriate check in this case. - -For Xen 4.14 and later, this is a latent bug because the XLAT area has moved -to be on its own with nothing interesting adjacent. For Xen 4.13 and older on -AMD hardware, this fixes a PV-trigger-able DoS. - -This is part of XSA-444 / CVE-2023-34328. - -Fixes: 65e355490817 ("x86/PV: support data breakpoint extension registers") -Signed-off-by: Andrew Cooper -Reviewed-by: Roger Pau Monné -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/include/asm/debugreg.h b/xen/arch/x86/include/asm/debugreg.h -index c57914efc6e8..cc298265244b 100644 ---- a/xen/arch/x86/include/asm/debugreg.h -+++ b/xen/arch/x86/include/asm/debugreg.h -@@ -77,6 +77,26 @@ - asm volatile ( "mov %%db" #reg ",%0" : "=r" (__val) ); \ - __val; \ - }) -+ -+/* -+ * Architecturally, %dr{0..3} can have any arbitrary value. However, Xen -+ * can't allow the guest to breakpoint the Xen address range, so we limit the -+ * guest to the lower canonical half, or above the Xen range in the higher -+ * canonical half. -+ * -+ * Breakpoint lengths are specified to mask the low order address bits, -+ * meaning all breakpoints are naturally aligned. With %dr7, the widest -+ * breakpoint is 8 bytes. With DBEXT, the widest breakpoint is 4G. Both of -+ * the Xen boundaries have >4G alignment. -+ * -+ * In principle we should account for HYPERVISOR_COMPAT_VIRT_START(d), but -+ * 64bit Xen has never enforced this for compat guests, and there's no problem -+ * (to Xen) if the guest breakpoints it's alias of the M2P. Skipping this -+ * aspect simplifies the logic, and causes us not to reject a migrating guest -+ * which operated fine on prior versions of Xen. -+ */ -+#define breakpoint_addr_ok(a) __addr_ok(a) -+ - long set_debugreg(struct vcpu *, unsigned int reg, unsigned long value); - void activate_debugregs(const struct vcpu *); - -diff --git a/xen/arch/x86/pv/misc-hypercalls.c b/xen/arch/x86/pv/misc-hypercalls.c -index aaaf70eb6330..f8636de907ae 100644 ---- a/xen/arch/x86/pv/misc-hypercalls.c -+++ b/xen/arch/x86/pv/misc-hypercalls.c -@@ -72,7 +72,7 @@ long set_debugreg(struct vcpu *v, unsigned int reg, unsigned long value) - switch ( reg ) - { - case 0 ... 3: -- if ( !access_ok(value, sizeof(long)) ) -+ if ( !breakpoint_addr_ok(value) ) - return -EPERM; - - v->arch.dr[reg] = value; diff --git a/xsa445-4.17.patch b/xsa445-4.17.patch deleted file mode 100644 index db66d7c..0000000 --- a/xsa445-4.17.patch +++ /dev/null @@ -1,63 +0,0 @@ -From a43127d4f1f9a364334fe16b6239c211b35fd238 Mon Sep 17 00:00:00 2001 -From: Roger Pau Monne -Date: Wed, 11 Oct 2023 13:14:21 +0200 -Subject: [PATCH] iommu/amd-vi: use correct level for quarantine domain page - tables -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The current setup of the quarantine page tables assumes that the quarantine -domain (dom_io) has been initialized with an address width of -DEFAULT_DOMAIN_ADDRESS_WIDTH (48). - -However dom_io being a PV domain gets the AMD-Vi IOMMU page tables levels based -on the maximum (hot pluggable) RAM address, and hence on systems with no RAM -above the 512GB mark only 3 page-table levels are configured in the IOMMU. - -On systems without RAM above the 512GB boundary amd_iommu_quarantine_init() -will setup page tables for the scratch page with 4 levels, while the IOMMU will -be configured to use 3 levels only. The page destined to be used as level 1, -and to contain a directory of PTEs ends up being the address in a PTE itself, -and thus level 1 page becomes the leaf page. Without the level mismatch it's -level 0 page that should be the leaf page instead. - -The level 1 page won't be used as such, and hence it's not possible to use it -to gain access to other memory on the system. However that page is not cleared -in amd_iommu_quarantine_init() as part of re-initialization of the device -quarantine page tables, and hence data on the level 1 page can be leaked -between device usages. - -Fix this by making sure the paging levels setup by amd_iommu_quarantine_init() -match the number configured on the IOMMUs. - -Note that IVMD regions are not affected by this issue, as those areas are -mapped taking the configured paging levels into account. - -This is XSA-445 / CVE-2023-46835 - -Fixes: ea38867831da ('x86 / iommu: set up a scratch page in the quarantine domain') -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich ---- - xen/drivers/passthrough/amd/iommu_map.c | 4 +--- - 1 file changed, 1 insertion(+), 3 deletions(-) - -diff --git a/xen/drivers/passthrough/amd/iommu_map.c b/xen/drivers/passthrough/amd/iommu_map.c -index 993bac6f8878..e0f4fe736a8d 100644 ---- a/xen/drivers/passthrough/amd/iommu_map.c -+++ b/xen/drivers/passthrough/amd/iommu_map.c -@@ -837,9 +837,7 @@ static int fill_qpt(union amd_iommu_pte *this, unsigned int level, - int cf_check amd_iommu_quarantine_init(struct pci_dev *pdev, bool scratch_page) - { - struct domain_iommu *hd = dom_iommu(dom_io); -- unsigned long end_gfn = -- 1ul << (DEFAULT_DOMAIN_ADDRESS_WIDTH - PAGE_SHIFT); -- unsigned int level = amd_iommu_get_paging_mode(end_gfn); -+ unsigned int level = hd->arch.amd.paging_mode; - unsigned int req_id = get_dma_requestor_id(pdev->seg, pdev->sbdf.bdf); - const struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(pdev->seg); - int rc; --- -2.42.0 - diff --git a/xsa446.patch b/xsa446.patch deleted file mode 100644 index acf1d0f..0000000 --- a/xsa446.patch +++ /dev/null @@ -1,115 +0,0 @@ -From 80d5aada598c3a800a350003d5d582931545e13c Mon Sep 17 00:00:00 2001 -From: Andrew Cooper -Date: Thu, 26 Oct 2023 14:37:38 +0100 -Subject: [PATCH] x86/spec-ctrl: Remove conditional IRQs-on-ness for INT - $0x80/0x82 paths -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Before speculation defences, some paths in Xen could genuinely get away with -being IRQs-on at entry. But XPTI invalidated this property on most paths, and -attempting to maintain it on the remaining paths was a mistake. - -Fast forward, and DO_SPEC_CTRL_COND_IBPB (protection for AMD BTC/SRSO) is not -IRQ-safe, running with IRQs enabled in some cases. The other actions taken on -these paths happen to be IRQ-safe. - -Make entry_int82() and int80_direct_trap() unconditionally Interrupt Gates -rather than Trap Gates. Remove the conditional re-adjustment of -int80_direct_trap() in smp_prepare_cpus(), and have entry_int82() explicitly -enable interrupts when safe to do so. - -In smp_prepare_cpus(), with the conditional re-adjustment removed, the -clearing of pv_cr3 is the only remaining action gated on XPTI, and it is out -of place anyway, repeating work already done by smp_prepare_boot_cpu(). Drop -the entire if() condition to avoid leaving an incorrect vestigial remnant. - -Also drop comments which make incorrect statements about when its safe to -enable interrupts. - -This is XSA-446 / CVE-2023-46836 - -Signed-off-by: Andrew Cooper -Reviewed-by: Roger Pau Monné ---- - xen/arch/x86/pv/traps.c | 4 ++-- - xen/arch/x86/smpboot.c | 14 -------------- - xen/arch/x86/x86_64/compat/entry.S | 2 ++ - xen/arch/x86/x86_64/entry.S | 1 - - 4 files changed, 4 insertions(+), 17 deletions(-) - -diff --git a/xen/arch/x86/pv/traps.c b/xen/arch/x86/pv/traps.c -index 74f333da7e1c..240d1a2db7a3 100644 ---- a/xen/arch/x86/pv/traps.c -+++ b/xen/arch/x86/pv/traps.c -@@ -139,11 +139,11 @@ void __init pv_trap_init(void) - #ifdef CONFIG_PV32 - /* The 32-on-64 hypercall vector is only accessible from ring 1. */ - _set_gate(idt_table + HYPERCALL_VECTOR, -- SYS_DESC_trap_gate, 1, entry_int82); -+ SYS_DESC_irq_gate, 1, entry_int82); - #endif - - /* Fast trap for int80 (faster than taking the #GP-fixup path). */ -- _set_gate(idt_table + LEGACY_SYSCALL_VECTOR, SYS_DESC_trap_gate, 3, -+ _set_gate(idt_table + LEGACY_SYSCALL_VECTOR, SYS_DESC_irq_gate, 3, - &int80_direct_trap); - - open_softirq(NMI_SOFTIRQ, nmi_softirq); -diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c -index 3a1a659082c6..4c54ecbc91d7 100644 ---- a/xen/arch/x86/smpboot.c -+++ b/xen/arch/x86/smpboot.c -@@ -1158,20 +1158,6 @@ void __init smp_prepare_cpus(void) - - stack_base[0] = (void *)((unsigned long)stack_start & ~(STACK_SIZE - 1)); - -- if ( opt_xpti_hwdom || opt_xpti_domu ) -- { -- get_cpu_info()->pv_cr3 = 0; -- --#ifdef CONFIG_PV -- /* -- * All entry points which may need to switch page tables have to start -- * with interrupts off. Re-write what pv_trap_init() has put there. -- */ -- _set_gate(idt_table + LEGACY_SYSCALL_VECTOR, SYS_DESC_irq_gate, 3, -- &int80_direct_trap); --#endif -- } -- - set_nr_sockets(); - - socket_cpumask = xzalloc_array(cpumask_t *, nr_sockets); -diff --git a/xen/arch/x86/x86_64/compat/entry.S b/xen/arch/x86/x86_64/compat/entry.S -index bd5abd8040bd..fcc3a721f147 100644 ---- a/xen/arch/x86/x86_64/compat/entry.S -+++ b/xen/arch/x86/x86_64/compat/entry.S -@@ -21,6 +21,8 @@ ENTRY(entry_int82) - SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, %rdx=0, Clob: acd */ - /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ - -+ sti -+ - CR4_PV32_RESTORE - - GET_CURRENT(bx) -diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S -index 5ca74f5f62b2..9a7b129aa7e4 100644 ---- a/xen/arch/x86/x86_64/entry.S -+++ b/xen/arch/x86/x86_64/entry.S -@@ -327,7 +327,6 @@ ENTRY(sysenter_entry) - #ifdef CONFIG_XEN_SHSTK - ALTERNATIVE "", "setssbsy", X86_FEATURE_XEN_SHSTK - #endif -- /* sti could live here when we don't switch page tables below. */ - pushq $FLAT_USER_SS - pushq $0 - pushfq - -base-commit: 7befef87cc9b1bb8ca15d866ce1ecd9165ccb58c -prerequisite-patch-id: 142a87c707411d49e136c3fb76f1b14963ec6dc8 --- -2.30.2 - From 343a901c216405ca8ebe8c7b3ce21eafb7b5c495 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 13 Dec 2023 17:39:02 +0000 Subject: [PATCH 080/137] arm32: The cache may not be properly cleaned/invalidated (take two) [XSA-447, CVE-2023-46837] rebuild for OCaml-5.1.1 --- xen.spec | 9 +++- xsa447.patch | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 xsa447.patch diff --git a/xen.spec b/xen.spec index 07bb3d2..c0bd413 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.18.0 -Release: 1%{?dist} +Release: 2%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -112,6 +112,7 @@ Patch46: xen.efi.build.patch Patch47: xen.gcc13.fixes.patch Patch49: xen.python3.12.patch Patch50: xen.ocaml5.fixes.patch +Patch51: xsa447.patch %if %build_qemutrad @@ -328,6 +329,7 @@ manage Xen virtual machines. %if "%dist" != ".fc38" %patch 50 -p1 %endif +%patch 51 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -934,6 +936,11 @@ fi %endif %changelog +* Wed Dec 13 2023 Michael Young - 4.18.0-2 +- arm32: The cache may not be properly cleaned/invalidated (take two) + [XSA-447, CVE-2023-46837] +- rebuild for OCaml-5.1.1 + * Wed Nov 29 2023 Michael Young - 4.18.0-1 - update to xen-4.18.0 rebase xen.canonicalize.patch and xen.ocaml5.fixes.patch diff --git a/xsa447.patch b/xsa447.patch new file mode 100644 index 0000000..2e26396 --- /dev/null +++ b/xsa447.patch @@ -0,0 +1,117 @@ +From 084c7312fa6c1d4a7fa343efa1d7d73693dafff4 Mon Sep 17 00:00:00 2001 +From: Michal Orzel +Date: Thu, 23 Nov 2023 15:53:02 +0100 +Subject: [PATCH] xen/arm: page: Avoid pointer overflow on cache clean & + invalidate + +On Arm32, after cleaning and invalidating the last dcache line of the top +domheap page i.e. VA = 0xfffff000 (as a result of flushing the page to +RAM), we end up adding the value of a dcache line size to the pointer +once again, which results in a pointer arithmetic overflow (with 64B line +size, operation 0xffffffc0 + 0x40 overflows to 0x0). Such behavior is +undefined and given the wide range of compiler versions we support, it is +difficult to determine what could happen in such scenario. + +Modify clean_and_invalidate_dcache_va_range() as well as +clean_dcache_va_range() and invalidate_dcache_va_range() due to similarity +of handling to prevent pointer arithmetic overflow. Modify the loops to +use an additional variable to store the index of the next cacheline. +Add an assert to prevent passing a region that wraps around which is +illegal and would end up in a page fault anyway (region 0-2MB is +unmapped). Lastly, return early if size passed is 0. + +Note that on Arm64, we don't have this problem given that the max VA +space we support is 48-bits. + +This is XSA-447 / CVE-2023-46837. + +Signed-off-by: Michal Orzel +Reviewed-by: Julien Grall +--- + xen/arch/arm/include/asm/page.h | 35 ++++++++++++++++++++++++++------- + 1 file changed, 28 insertions(+), 7 deletions(-) + +diff --git a/xen/arch/arm/include/asm/page.h b/xen/arch/arm/include/asm/page.h +index ebaf5964f114..69f817d1e68a 100644 +--- a/xen/arch/arm/include/asm/page.h ++++ b/xen/arch/arm/include/asm/page.h +@@ -162,6 +162,13 @@ static inline size_t read_dcache_line_bytes(void) + static inline int invalidate_dcache_va_range(const void *p, unsigned long size) + { + size_t cacheline_mask = dcache_line_bytes - 1; ++ unsigned long idx = 0; ++ ++ if ( !size ) ++ return 0; ++ ++ /* Passing a region that wraps around is illegal */ ++ ASSERT(((uintptr_t)p + size - 1) >= (uintptr_t)p); + + dsb(sy); /* So the CPU issues all writes to the range */ + +@@ -174,11 +181,11 @@ static inline int invalidate_dcache_va_range(const void *p, unsigned long size) + } + + for ( ; size >= dcache_line_bytes; +- p += dcache_line_bytes, size -= dcache_line_bytes ) +- asm volatile (__invalidate_dcache_one(0) : : "r" (p)); ++ idx += dcache_line_bytes, size -= dcache_line_bytes ) ++ asm volatile (__invalidate_dcache_one(0) : : "r" (p + idx)); + + if ( size > 0 ) +- asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (p)); ++ asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (p + idx)); + + dsb(sy); /* So we know the flushes happen before continuing */ + +@@ -188,14 +195,21 @@ static inline int invalidate_dcache_va_range(const void *p, unsigned long size) + static inline int clean_dcache_va_range(const void *p, unsigned long size) + { + size_t cacheline_mask = dcache_line_bytes - 1; ++ unsigned long idx = 0; ++ ++ if ( !size ) ++ return 0; ++ ++ /* Passing a region that wraps around is illegal */ ++ ASSERT(((uintptr_t)p + size - 1) >= (uintptr_t)p); + + dsb(sy); /* So the CPU issues all writes to the range */ + size += (uintptr_t)p & cacheline_mask; + size = (size + cacheline_mask) & ~cacheline_mask; + p = (void *)((uintptr_t)p & ~cacheline_mask); + for ( ; size >= dcache_line_bytes; +- p += dcache_line_bytes, size -= dcache_line_bytes ) +- asm volatile (__clean_dcache_one(0) : : "r" (p)); ++ idx += dcache_line_bytes, size -= dcache_line_bytes ) ++ asm volatile (__clean_dcache_one(0) : : "r" (p + idx)); + dsb(sy); /* So we know the flushes happen before continuing */ + /* ARM callers assume that dcache_* functions cannot fail. */ + return 0; +@@ -205,14 +219,21 @@ static inline int clean_and_invalidate_dcache_va_range + (const void *p, unsigned long size) + { + size_t cacheline_mask = dcache_line_bytes - 1; ++ unsigned long idx = 0; ++ ++ if ( !size ) ++ return 0; ++ ++ /* Passing a region that wraps around is illegal */ ++ ASSERT(((uintptr_t)p + size - 1) >= (uintptr_t)p); + + dsb(sy); /* So the CPU issues all writes to the range */ + size += (uintptr_t)p & cacheline_mask; + size = (size + cacheline_mask) & ~cacheline_mask; + p = (void *)((uintptr_t)p & ~cacheline_mask); + for ( ; size >= dcache_line_bytes; +- p += dcache_line_bytes, size -= dcache_line_bytes ) +- asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (p)); ++ idx += dcache_line_bytes, size -= dcache_line_bytes ) ++ asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (p + idx)); + dsb(sy); /* So we know the flushes happen before continuing */ + /* ARM callers assume that dcache_* functions cannot fail. */ + return 0; +-- +2.40.1 + From 869574bf2f57093775e262e13333ea9396f76b85 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Sat, 27 Jan 2024 09:30:44 +0000 Subject: [PATCH 081/137] Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index c0bd413..380064a 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.18.0 -Release: 2%{?dist} +Release: 3%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -936,6 +936,9 @@ fi %endif %changelog +* Sat Jan 27 2024 Fedora Release Engineering - 4.18.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + * Wed Dec 13 2023 Michael Young - 4.18.0-2 - arm32: The cache may not be properly cleaned/invalidated (take two) [XSA-447, CVE-2023-46837] From b644c52cfa0d914d33b9369462074e60faa60320 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Sat, 3 Feb 2024 09:47:57 +0000 Subject: [PATCH 082/137] build fixes for gcc14, replace stubs-32.h requirement with glibc32 --- newlib.gcc14.fixes.patch | 388 +++++++++++++++++++++++++++++++++++++++ xen.gcc14.fixes.patch | 24 +++ xen.spec | 13 +- 3 files changed, 422 insertions(+), 3 deletions(-) create mode 100644 newlib.gcc14.fixes.patch create mode 100644 xen.gcc14.fixes.patch diff --git a/newlib.gcc14.fixes.patch b/newlib.gcc14.fixes.patch new file mode 100644 index 0000000..bdb3594 --- /dev/null +++ b/newlib.gcc14.fixes.patch @@ -0,0 +1,388 @@ +--- xen-4.18.0/stubdom/newlib-makedoc.patch.orig 2023-11-16 21:44:21.000000000 +0000 ++++ xen-4.18.0/stubdom/newlib-makedoc.patch 2024-02-01 20:32:40.565785056 +0000 +@@ -33,3 +33,385 @@ + #include + + #define DEF_SIZE 5000 ++--- ++ ++patch makedoc.c for gcc14 build ++ ++--- newlib-1.16.0/newlib/doc/makedoc.c.orig 2007-01-02 16:59:59.000000000 +0000 +++++ newlib-1.16.0/newlib/doc/makedoc.c 2024-02-01 20:26:32.415396758 +0000 ++@@ -63,25 +63,20 @@ ++ ++ ++ ++-static void DEFUN(init_string_with_size,(buffer, size), ++- string_type *buffer AND ++- unsigned int size ) +++static void init_string_with_size (string_type *buffer, unsigned int size) ++ { ++ buffer->write_idx = 0; ++ buffer->size = size; ++ buffer->ptr = malloc(size); ++ } ++ ++-static void DEFUN(init_string,(buffer), ++- string_type *buffer) +++static void init_string (string_type *buffer) ++ { ++ init_string_with_size(buffer, DEF_SIZE); ++ ++ } ++ ++-static int DEFUN(find, (str, what), ++- string_type *str AND ++- char *what) +++static int find (string_type *str, char *what) ++ { ++ unsigned int i; ++ char *p; ++@@ -97,30 +92,24 @@ ++ ++ } ++ ++-static void DEFUN(write_buffer,(buffer), ++- string_type *buffer) +++static void write_buffer (string_type *buffer) ++ { ++ fwrite(buffer->ptr, buffer->write_idx, 1, stdout); ++ } ++ ++ ++-static void DEFUN(delete_string,(buffer), ++- string_type *buffer) +++static void delete_string (string_type *buffer) ++ { ++ free(buffer->ptr); ++ } ++ ++ ++-static char *DEFUN(addr, (buffer, idx), ++- string_type *buffer AND ++- unsigned int idx) +++static char * addr (string_type *buffer, unsigned int idx) ++ { ++ return buffer->ptr + idx; ++ } ++ ++-static char DEFUN(at,(buffer, pos), ++- string_type *buffer AND ++- unsigned int pos) +++static char at (string_type *buffer, unsigned int pos) ++ { ++ if ( pos >= buffer->write_idx) ++ { ++@@ -129,9 +118,7 @@ ++ return buffer->ptr[pos]; ++ } ++ ++-static void DEFUN(catchar,(buffer, ch), ++- string_type *buffer AND ++- char ch) +++static void catchar (string_type *buffer, char ch) ++ { ++ if (buffer->write_idx == buffer->size) ++ { ++@@ -143,9 +130,7 @@ ++ } ++ ++ ++-static void DEFUN(overwrite_string,(dst, src), ++- string_type *dst AND ++- string_type *src) +++static void overwrite_string (string_type *dst, string_type *src) ++ { ++ free(dst->ptr); ++ dst->size = src->size; ++@@ -153,9 +138,7 @@ ++ dst->ptr = src->ptr; ++ } ++ ++-static void DEFUN(catstr,(dst, src), ++- string_type *dst AND ++- string_type *src) +++static void catstr ( string_type *dst, string_type *src) ++ { ++ unsigned int i; ++ for (i = 0; i < src->write_idx; i++) ++@@ -165,9 +148,7 @@ ++ } ++ ++ ++-static void DEFUN(cattext,(buffer, string), ++- string_type *buffer AND ++- char *string) +++static void cattext (string_type *buffer, char *string) ++ { ++ ++ while (*string) ++@@ -177,10 +158,7 @@ ++ } ++ } ++ ++-static void DEFUN(catbuf,(buffer, buf, len), ++- string_type *buffer AND ++- char *buf AND ++- unsigned int len) +++static void catbuf ( string_type *buffer, char *buf, unsigned int len) ++ { ++ ++ while (len--) ++@@ -192,10 +170,7 @@ ++ ++ ++ ++-static unsigned int ++-DEFUN(skip_white_and_stars,(src, idx), ++- string_type *src AND ++- unsigned int idx) +++static unsigned int skip_white_and_stars (string_type *src, unsigned int idx) ++ { ++ while (isspace(at(src,idx)) ++ || (at(src,idx) == '*' && at(src,idx +1) !='/')) ++@@ -216,10 +191,23 @@ ++ stinst_type *pc; ++ stinst_type sstack[STACK]; ++ stinst_type *ssp = &sstack[0]; ++-int istack[STACK]; ++-int *isp = &istack[0]; ++ ++-typedef int *word_type; +++#if defined(__PTRDIFF_TYPE__) +++typedef signed __PTRDIFF_TYPE__ intptr_t; +++typedef unsigned __PTRDIFF_TYPE__ uintptr_t; +++#else +++/* +++ * Fallback to hardcoded values, +++ * should be valid on cpu's with 32bit int/32bit void* +++ */ +++typedef signed long intptr_t; +++typedef unsigned long uintptr_t; +++#endif +++ +++uintptr_t istack[STACK]; +++uintptr_t *isp = &istack[0]; +++ +++typedef uintptr_t *word_type; ++ ++ ++ ++@@ -236,8 +224,7 @@ ++ typedef struct dict_struct dict_type; ++ #define WORD(x) static void x() ++ ++-static void DEFUN(exec,(word), ++- dict_type *word) +++static void exec (dict_type *word) ++ { ++ pc = word->code; ++ while (*pc) ++@@ -267,7 +254,7 @@ ++ { ++ isp++; ++ pc++; ++- *isp = (int)(*pc); +++ *isp = (uintptr_t)(*pc); ++ pc++; ++ ++ } ++@@ -294,10 +281,7 @@ ++ Blank lines are turned into one blank line ++ */ ++ ++-static void ++-DEFUN(remove_noncomments,(src,dst), ++- string_type *src AND ++- string_type *dst) +++static void remove_noncomments (string_type *src, string_type *dst) ++ { ++ unsigned int idx = 0; ++ ++@@ -349,8 +333,7 @@ ++ ++ */ ++ ++-static void ++-DEFUN_VOID(exfunstuff) +++static void exfunstuff (void) ++ { ++ unsigned int openp; ++ unsigned int fname; ++@@ -522,8 +505,7 @@ ++ } ++ ++ /* Mod tos so that only lines with leading dots remain */ ++-static void ++-DEFUN_VOID(outputdots) +++static void (outputdots (void)) ++ { ++ unsigned int idx = 0; ++ string_type out; ++@@ -772,10 +754,7 @@ ++ ++ } ++ /* A command is all upper case,and alone on a line */ ++-static int ++-DEFUN( iscommand,(ptr, idx), ++- string_type *ptr AND ++- unsigned int idx) +++static int iscommand (string_type *ptr, unsigned int idx) ++ { ++ unsigned int len = 0; ++ while (at(ptr,idx)) { ++@@ -797,10 +776,7 @@ ++ } ++ ++ ++-DEFUN(copy_past_newline,(ptr, idx, dst), ++- string_type *ptr AND ++- unsigned int idx AND ++- string_type *dst) +++unsigned int copy_past_newline (string_type *ptr, unsigned int idx, string_type *dst) ++ { ++ while (at(ptr, idx) && at(ptr, idx) != '\n') ++ { ++@@ -1060,10 +1036,7 @@ ++ ++ } ++ ++-char * ++-DEFUN(nextword,(string, word), ++- char *string AND ++- char **word) +++char * nextword (char *string, char **word) ++ { ++ char *word_start; ++ int idx; ++@@ -1138,9 +1111,7 @@ ++ ++ } ++ dict_type *root; ++-dict_type * ++-DEFUN(lookup_word,(word), ++- char *word) +++dict_type * lookup_word (char *word) ++ { ++ dict_type *ptr = root; ++ while (ptr) { ++@@ -1154,7 +1125,7 @@ ++ ++ } ++ ++-static void DEFUN_VOID(perform) +++static int perform (void) ++ { ++ tos = stack; ++ ++@@ -1192,9 +1163,7 @@ ++ } ++ } ++ ++-dict_type * ++-DEFUN(newentry,(word), ++- char *word) +++dict_type * newentry (char *word) ++ { ++ dict_type *new = (dict_type *)malloc(sizeof(dict_type)); ++ new->word = word; ++@@ -1208,10 +1177,7 @@ ++ } ++ ++ ++-unsigned int ++-DEFUN(add_to_definition,(entry, word), ++- dict_type *entry AND ++- stinst_type word) +++unsigned int add_to_definition (dict_type *entry, stinst_type word) ++ { ++ if (entry->code_end == entry->code_length) ++ { ++@@ -1232,9 +1198,7 @@ ++ ++ ++ void ++-DEFUN(add_intrinsic,(name, func), ++- char *name AND ++- void (*func)()) +++add_intrinsic (char *name, void (*func)(void)) ++ { ++ dict_type *new = newentry(name); ++ add_to_definition(new, func); ++@@ -1247,9 +1211,7 @@ ++ ++ } ++ ++-void ++-DEFUN(add_var,(name), ++- char *name) +++void add_var (char *name) ++ { ++ dict_type *new = newentry(name); ++ add_to_definition(new, push_number); ++@@ -1262,8 +1224,7 @@ ++ ++ ++ void ++-DEFUN(compile, (string), ++- char *string) +++compile (char *string) ++ ++ { ++ int jstack[STACK]; ++@@ -1315,11 +1276,11 @@ ++ /* Got a number, embedd the magic push number ++ function */ ++ add_to_definition(ptr, push_number); ++- add_to_definition(ptr, atol(word)); +++ add_to_definition(ptr, (stinst_type)atol(word)); ++ break; ++ default: ++ add_to_definition(ptr, call); ++- add_to_definition(ptr, lookup_word(word)); +++ add_to_definition(ptr, (stinst_type)lookup_word(word)); ++ } ++ ++ string = nextword(string, &word); ++@@ -1336,9 +1297,9 @@ ++ } ++ ++ ++-static void DEFUN_VOID(bang) +++static void bang (void) ++ { ++-*(int *)((isp[0])) = isp[-1]; +++*(uintptr_t *)((isp[0])) = isp[-1]; ++ isp-=2; ++ pc++; ++ ++@@ -1359,9 +1320,7 @@ ++ ++ ++ ++-static void DEFUN(read_in, (str, file), ++- string_type *str AND ++- FILE *file) +++static void read_in (string_type *str, FILE *file) ++ { ++ char buff[10000]; ++ unsigned int r; ++@@ -1378,15 +1337,13 @@ ++ } ++ ++ ++-static void DEFUN_VOID(usage) +++static void usage (void) ++ { ++ fprintf(stderr,"usage: -[d|i|g] file\n"); ++ exit(33); ++ } ++ ++-int DEFUN(main,(ac,av), ++-int ac AND ++-char *av[]) +++int main (int ac, char *av[]) ++ { ++ unsigned int i; ++ diff --git a/xen.gcc14.fixes.patch b/xen.gcc14.fixes.patch new file mode 100644 index 0000000..cc27089 --- /dev/null +++ b/xen.gcc14.fixes.patch @@ -0,0 +1,24 @@ +Work around newlib build problems with gcc14 + +--- xen-4.18.0/stubdom/Makefile.orig 2024-01-15 20:35:36.941740548 +0000 ++++ xen-4.18.0/stubdom/Makefile 2024-01-29 22:29:29.067367523 +0000 +@@ -45,6 +45,7 @@ + NEWLIB_CFLAGS+=-D_I386MACH_ALLOW_HW_INTERRUPTS + STUBDOM_SUPPORTED=1 + endif ++NEWLIB_CFLAGS+=-Wno-error=implicit-function-declaration + + CROSS_ROOT=cross-root-$(GNU_TARGET_ARCH) + CROSS_PREFIX=$(CURDIR)/$(CROSS_ROOT) +--- xen-4.18.0/extras/mini-os/include/posix/sys/mman.h.orig 2022-07-04 09:52:38.000000000 +0100 ++++ xen-4.18.0/extras/mini-os/include/posix/sys/mman.h 2024-02-01 23:00:19.923263409 +0000 +@@ -16,7 +16,7 @@ + + void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset) asm("mmap64"); + int munmap(void *start, size_t length); +-static inline mlock(const void *addr, size_t len) { return 0; } +-static inline munlock(const void *addr, size_t len) { return 0; } ++static inline int mlock(const void *addr, size_t len) { return 0; } ++static inline int munlock(const void *addr, size_t len) { return 0; } + + #endif /* _POSIX_SYS_MMAN_H */ diff --git a/xen.spec b/xen.spec index 380064a..350fd29 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.18.0 -Release: 3%{?dist} +Release: 4%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -113,6 +113,8 @@ Patch47: xen.gcc13.fixes.patch Patch49: xen.python3.12.patch Patch50: xen.ocaml5.fixes.patch Patch51: xsa447.patch +Patch52: xen.gcc14.fixes.patch +Patch53: newlib.gcc14.fixes.patch %if %build_qemutrad @@ -127,9 +129,9 @@ BuildRequires: dev86 %endif BuildRequires: python3-devel ncurses-devel python3-setuptools BuildRequires: perl-interpreter perl-generators -%ifarch %{ix86} x86_64 +%ifarch x86_64 # so that x86_64 builds pick up glibc32 correctly -BuildRequires: /usr/include/gnu/stubs-32.h +BuildRequires: glibc32 %endif BuildRequires: gettext BuildRequires: gnutls-devel @@ -330,6 +332,8 @@ manage Xen virtual machines. %patch 50 -p1 %endif %patch 51 -p1 +%patch 52 -p1 +%patch 53 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -936,6 +940,9 @@ fi %endif %changelog +* Sat Feb 03 2024 Michael Young - 4.18.0-4 +- build fixes for gcc14, replace stubs-32.h requirement with glibc32 + * Sat Jan 27 2024 Fedora Release Engineering - 4.18.0-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild From 950832bad0fbf2411446da7e84264114fb5b1ef0 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Sun, 4 Feb 2024 19:57:54 +0000 Subject: [PATCH 083/137] 2 security updates + drop glibc32 requirement pci: phantom functions assigned to incorrect contexts [XSA-449, CVE-2023-46839] VT-d: Failure to quarantine devices in !HVM build [XSA-450, CVE-2023-46840] --- xen.spec | 17 +++++++--- xsa449.patch | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++ xsa450.patch | 59 ++++++++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+), 5 deletions(-) create mode 100644 xsa449.patch create mode 100644 xsa450.patch diff --git a/xen.spec b/xen.spec index 350fd29..7c58b1a 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.18.0 -Release: 4%{?dist} +Release: 5%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -115,6 +115,8 @@ Patch50: xen.ocaml5.fixes.patch Patch51: xsa447.patch Patch52: xen.gcc14.fixes.patch Patch53: newlib.gcc14.fixes.patch +Patch54: xsa449.patch +Patch55: xsa450.patch %if %build_qemutrad @@ -129,10 +131,6 @@ BuildRequires: dev86 %endif BuildRequires: python3-devel ncurses-devel python3-setuptools BuildRequires: perl-interpreter perl-generators -%ifarch x86_64 -# so that x86_64 builds pick up glibc32 correctly -BuildRequires: glibc32 -%endif BuildRequires: gettext BuildRequires: gnutls-devel BuildRequires: openssl-devel @@ -334,6 +332,8 @@ manage Xen virtual machines. %patch 51 -p1 %patch 52 -p1 %patch 53 -p1 +%patch 54 -p1 +%patch 55 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -940,6 +940,13 @@ fi %endif %changelog +* Sun Feb 04 2024 Michael Young - 4.18.0-5 +- pci: phantom functions assigned to incorrect contexts [XSA-449, + CVE-2023-46839] +- VT-d: Failure to quarantine devices in !HVM build [XSA-450, + CVE-2023-46840] +- the glibc32 doesn't seem to add anything to the build so drop it + * Sat Feb 03 2024 Michael Young - 4.18.0-4 - build fixes for gcc14, replace stubs-32.h requirement with glibc32 diff --git a/xsa449.patch b/xsa449.patch new file mode 100644 index 0000000..80aeac2 --- /dev/null +++ b/xsa449.patch @@ -0,0 +1,89 @@ +From d8b92b21b224126860978e4c604302f3c1e3bf75 Mon Sep 17 00:00:00 2001 +From: Roger Pau Monne +Date: Wed, 13 Dec 2023 15:51:59 +0100 +Subject: [PATCH] pci: fail device assignment if phantom functions cannot be + assigned +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The current behavior is that no error is reported if (some) phantom functions +fail to be assigned during device add or assignment, so the operation succeeds +even if some phantom functions are not correctly setup. + +This can lead to devices possibly being successfully assigned to a domU while +some of the device phantom functions are still assigned to dom0. Even when the +device is assigned domIO before being assigned to a domU phantom functions +might fail to be assigned to domIO, and also fail to be assigned to the domU, +leaving them assigned to dom0. + +Since the device can generate requests using the IDs of those phantom +functions, given the scenario above a device in such state would be in control +of a domU, but still capable of generating transactions that use a context ID +targeting dom0 owned memory. + +Modify device assign in order to attempt to deassign the device if phantom +functions failed to be assigned. + +Note that device addition is not modified in the same way, as in that case the +device is assigned to a trusted domain, and hence partial assign can lead to +device malfunction but not a security issue. + +This is XSA-449 / CVE-2023-46839 + +Fixes: 4e9950dc1bd2 ('IOMMU: add phantom function support') +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +--- + xen/drivers/passthrough/pci.c | 27 +++++++++++++++++++++------ + 1 file changed, 21 insertions(+), 6 deletions(-) + +diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c +index 1439d1ef2b26..47c0eee7bdcc 100644 +--- a/xen/drivers/passthrough/pci.c ++++ b/xen/drivers/passthrough/pci.c +@@ -1488,11 +1488,10 @@ static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) + + pdev->fault.count = 0; + +- if ( (rc = iommu_call(hd->platform_ops, assign_device, d, devfn, +- pci_to_dev(pdev), flag)) ) +- goto done; ++ rc = iommu_call(hd->platform_ops, assign_device, d, devfn, pci_to_dev(pdev), ++ flag); + +- for ( ; pdev->phantom_stride; rc = 0 ) ++ while ( pdev->phantom_stride && !rc ) + { + devfn += pdev->phantom_stride; + if ( PCI_SLOT(devfn) != PCI_SLOT(pdev->devfn) ) +@@ -1503,8 +1502,24 @@ static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) + + done: + if ( rc ) +- printk(XENLOG_G_WARNING "%pd: assign (%pp) failed (%d)\n", +- d, &PCI_SBDF(seg, bus, devfn), rc); ++ { ++ printk(XENLOG_G_WARNING "%pd: assign %s(%pp) failed (%d)\n", ++ d, devfn != pdev->devfn ? "phantom function " : "", ++ &PCI_SBDF(seg, bus, devfn), rc); ++ ++ if ( devfn != pdev->devfn && deassign_device(d, seg, bus, pdev->devfn) ) ++ { ++ /* ++ * Device with phantom functions that failed to both assign and ++ * rollback. Mark the device as broken and crash the target domain, ++ * as the state of the functions at this point is unknown and Xen ++ * has no way to assert consistent context assignment among them. ++ */ ++ pdev->broken = true; ++ if ( !is_hardware_domain(d) && d != dom_io ) ++ domain_crash(d); ++ } ++ } + /* The device is assigned to dom_io so mark it as quarantined */ + else if ( d == dom_io ) + pdev->quarantine = true; +-- +2.43.0 + diff --git a/xsa450.patch b/xsa450.patch new file mode 100644 index 0000000..e94933b --- /dev/null +++ b/xsa450.patch @@ -0,0 +1,59 @@ +From: Andrew Cooper +Subject: VT-d: Fix "else" vs "#endif" misplacement + +In domain_pgd_maddr() the "#endif" is misplaced with respect to "else". This +generates incorrect logic when CONFIG_HVM is compiled out, as the "else" body +is executed unconditionally. + +Rework the logic to use IS_ENABLED() instead of explicit #ifdef-ary, as it's +clearer to follow. This in turn involves adjusting p2m_get_pagetable() to +compile when CONFIG_HVM is disabled. + +This is XSA-450 / CVE-2023-46840. + +Reported-by: Reported-by: Teddy Astie +Fixes: 033ff90aa9c1 ("x86/P2M: p2m_{alloc,free}_ptp() and p2m_alloc_table() are HVM-only") +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/include/asm/p2m.h b/xen/arch/x86/include/asm/p2m.h +index 32f3f394b05a..6ada585eaac2 100644 +--- a/xen/arch/x86/include/asm/p2m.h ++++ b/xen/arch/x86/include/asm/p2m.h +@@ -435,7 +435,14 @@ static inline bool p2m_is_altp2m(const struct p2m_domain *p2m) + return p2m->p2m_class == p2m_alternate; + } + +-#define p2m_get_pagetable(p2m) ((p2m)->phys_table) ++#ifdef CONFIG_HVM ++static inline pagetable_t p2m_get_pagetable(const struct p2m_domain *p2m) ++{ ++ return p2m->phys_table; ++} ++#else ++pagetable_t p2m_get_pagetable(const struct p2m_domain *p2m); ++#endif + + /* + * Ensure any deferred p2m TLB flush has been completed on all VCPUs. +diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c +index 99b642f12ef9..4244855032ee 100644 +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -438,15 +438,13 @@ static paddr_t domain_pgd_maddr(struct domain *d, paddr_t pgd_maddr, + + if ( pgd_maddr ) + /* nothing */; +-#ifdef CONFIG_HVM +- else if ( iommu_use_hap_pt(d) ) ++ else if ( IS_ENABLED(CONFIG_HVM) && iommu_use_hap_pt(d) ) + { + pagetable_t pgt = p2m_get_pagetable(p2m_get_hostp2m(d)); + + pgd_maddr = pagetable_get_paddr(pgt); + } + else +-#endif + { + if ( !hd->arch.vtd.pgd_maddr ) + { From ef85dabcd352e1c093d9eba10b7e3e01c6059ea3 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 27 Feb 2024 20:02:19 +0000 Subject: [PATCH 084/137] x86: shadow stack vs exceptions from emulation stubs [XSA-451, CVE-2023-46841] (#2266326) --- xen.spec | 8 +- xsa451-4.18.patch | 188 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 xsa451-4.18.patch diff --git a/xen.spec b/xen.spec index 7c58b1a..39fd394 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.18.0 -Release: 5%{?dist} +Release: 6%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -117,6 +117,7 @@ Patch52: xen.gcc14.fixes.patch Patch53: newlib.gcc14.fixes.patch Patch54: xsa449.patch Patch55: xsa450.patch +Patch56: xsa451-4.18.patch %if %build_qemutrad @@ -334,6 +335,7 @@ manage Xen virtual machines. %patch 53 -p1 %patch 54 -p1 %patch 55 -p1 +%patch 56 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -940,6 +942,10 @@ fi %endif %changelog +* Tue Feb 27 2024 Michael Young - 4.18.0-6 +- x86: shadow stack vs exceptions from emulation stubs - [XSA-451, + CVE-2023-46841] (#2266326) + * Sun Feb 04 2024 Michael Young - 4.18.0-5 - pci: phantom functions assigned to incorrect contexts [XSA-449, CVE-2023-46839] diff --git a/xsa451-4.18.patch b/xsa451-4.18.patch new file mode 100644 index 0000000..721f3f3 --- /dev/null +++ b/xsa451-4.18.patch @@ -0,0 +1,188 @@ +From: Jan Beulich +Subject: x86: account for shadow stack in exception-from-stub recovery + +Dealing with exceptions raised from within emulation stubs involves +discarding return address (replaced by exception related information). +Such discarding of course also requires removing the corresponding entry +from the shadow stack. + +Also amend the comment in fixup_exception_return(), to further clarify +why use of ptr[1] can't be an out-of-bounds access. + +While touching do_invalid_op() also add a missing fall-through +annotation. + +This is CVE-2023-46841 / XSA-451. + +Fixes: 209fb9919b50 ("x86/extable: Adjust extable handling to be shadow stack compatible") +Signed-off-by: Jan Beulich +Reviewed-by: Andrew Cooper + +--- a/xen/arch/x86/extable.c ++++ b/xen/arch/x86/extable.c +@@ -86,26 +86,29 @@ search_one_extable(const struct exceptio + } + + unsigned long +-search_exception_table(const struct cpu_user_regs *regs) ++search_exception_table(const struct cpu_user_regs *regs, unsigned long *stub_ra) + { + const struct virtual_region *region = find_text_region(regs->rip); + unsigned long stub = this_cpu(stubs.addr); + + if ( region && region->ex ) ++ { ++ *stub_ra = 0; + return search_one_extable(region->ex, region->ex_end, regs->rip); ++ } + + if ( regs->rip >= stub + STUB_BUF_SIZE / 2 && + regs->rip < stub + STUB_BUF_SIZE && + regs->rsp > (unsigned long)regs && + regs->rsp < (unsigned long)get_cpu_info() ) + { +- unsigned long retptr = *(unsigned long *)regs->rsp; ++ unsigned long retaddr = *(unsigned long *)regs->rsp, fixup; + +- region = find_text_region(retptr); +- retptr = region && region->ex +- ? search_one_extable(region->ex, region->ex_end, retptr) +- : 0; +- if ( retptr ) ++ region = find_text_region(retaddr); ++ fixup = region && region->ex ++ ? search_one_extable(region->ex, region->ex_end, retaddr) ++ : 0; ++ if ( fixup ) + { + /* + * Put trap number and error code on the stack (in place of the +@@ -117,7 +120,8 @@ search_exception_table(const struct cpu_ + }; + + *(unsigned long *)regs->rsp = token.raw; +- return retptr; ++ *stub_ra = retaddr; ++ return fixup; + } + } + +--- a/xen/arch/x86/include/asm/uaccess.h ++++ b/xen/arch/x86/include/asm/uaccess.h +@@ -421,7 +421,8 @@ union stub_exception_token { + unsigned long raw; + }; + +-extern unsigned long search_exception_table(const struct cpu_user_regs *regs); ++extern unsigned long search_exception_table(const struct cpu_user_regs *regs, ++ unsigned long *stub_ra); + extern void sort_exception_tables(void); + extern void sort_exception_table(struct exception_table_entry *start, + const struct exception_table_entry *stop); +--- a/xen/arch/x86/traps.c ++++ b/xen/arch/x86/traps.c +@@ -845,7 +845,7 @@ void do_unhandled_trap(struct cpu_user_r + } + + static void fixup_exception_return(struct cpu_user_regs *regs, +- unsigned long fixup) ++ unsigned long fixup, unsigned long stub_ra) + { + if ( IS_ENABLED(CONFIG_XEN_SHSTK) ) + { +@@ -862,7 +862,8 @@ static void fixup_exception_return(struc + /* + * Search for %rip. The shstk currently looks like this: + * +- * ... [Likely pointed to by SSP] ++ * tok [Supervisor token, == &tok | BUSY, only with FRED inactive] ++ * ... [Pointed to by SSP for most exceptions, empty in IST cases] + * %cs [== regs->cs] + * %rip [== regs->rip] + * SSP [Likely points to 3 slots higher, above %cs] +@@ -880,7 +881,56 @@ static void fixup_exception_return(struc + */ + if ( ptr[0] == regs->rip && ptr[1] == regs->cs ) + { ++ unsigned long primary_shstk = ++ (ssp & ~(STACK_SIZE - 1)) + ++ (PRIMARY_SHSTK_SLOT + 1) * PAGE_SIZE - 8; ++ + wrss(fixup, ptr); ++ ++ if ( !stub_ra ) ++ goto shstk_done; ++ ++ /* ++ * Stub recovery ought to happen only when the outer context ++ * was on the main shadow stack. We need to also "pop" the ++ * stub's return address from the interrupted context's shadow ++ * stack. That is, ++ * - if we're still on the main stack, we need to move the ++ * entire stack (up to and including the exception frame) ++ * up by one slot, incrementing the original SSP in the ++ * exception frame, ++ * - if we're on an IST stack, we need to increment the ++ * original SSP. ++ */ ++ BUG_ON((ptr[-1] ^ primary_shstk) >> PAGE_SHIFT); ++ ++ if ( (ssp ^ primary_shstk) >> PAGE_SHIFT ) ++ { ++ /* ++ * We're on an IST stack. First make sure the two return ++ * addresses actually match. Then increment the interrupted ++ * context's SSP. ++ */ ++ BUG_ON(stub_ra != *(unsigned long*)ptr[-1]); ++ wrss(ptr[-1] + 8, &ptr[-1]); ++ goto shstk_done; ++ } ++ ++ /* Make sure the two return addresses actually match. */ ++ BUG_ON(stub_ra != ptr[2]); ++ ++ /* Move exception frame, updating SSP there. */ ++ wrss(ptr[1], &ptr[2]); /* %cs */ ++ wrss(ptr[0], &ptr[1]); /* %rip */ ++ wrss(ptr[-1] + 8, &ptr[0]); /* SSP */ ++ ++ /* Move all newer entries. */ ++ while ( --ptr != _p(ssp) ) ++ wrss(ptr[-1], &ptr[0]); ++ ++ /* Finally account for our own stack having shifted up. */ ++ asm volatile ( "incsspd %0" :: "r" (2) ); ++ + goto shstk_done; + } + } +@@ -901,7 +951,8 @@ static void fixup_exception_return(struc + + static bool extable_fixup(struct cpu_user_regs *regs, bool print) + { +- unsigned long fixup = search_exception_table(regs); ++ unsigned long stub_ra = 0; ++ unsigned long fixup = search_exception_table(regs, &stub_ra); + + if ( unlikely(fixup == 0) ) + return false; +@@ -915,7 +966,7 @@ static bool extable_fixup(struct cpu_use + vector_name(regs->entry_vector), regs->error_code, + _p(regs->rip), _p(regs->rip), _p(fixup)); + +- fixup_exception_return(regs, fixup); ++ fixup_exception_return(regs, fixup, stub_ra); + this_cpu(last_extable_addr) = regs->rip; + + return true; +@@ -1183,7 +1234,8 @@ void do_invalid_op(struct cpu_user_regs + { + case BUGFRAME_run_fn: + case BUGFRAME_warn: +- fixup_exception_return(regs, (unsigned long)eip); ++ fixup_exception_return(regs, (unsigned long)eip, 0); ++ fallthrough; + case BUGFRAME_bug: + case BUGFRAME_assert: + return; From 4d6e2c00074f47eab6cf7adaa8d5bbe0b71e6cb9 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 13 Mar 2024 20:26:24 +0000 Subject: [PATCH 085/137] 2 security updates x86: Register File Data Sampling [XSA-452, CVE-2023-28746] GhostRace: Speculative Race Conditions [XSA-453, CVE-2024-2193] --- ...28a2a742069af203e90c613c5c93e23c9755.patch | 47 +++ ...0f77a5cc671d30c5ef3318f5b8e9b74e4aa3.patch | 55 +++ ...07f7d51fe49df8bc70f83e49ce06c92e4e54.patch | 51 +++ ...162cafd27f2865a3102a2ec0f386a02ed03d.patch | 99 +++++ xen.spec | 43 +- xsa452-4.18-1.patch | 304 ++++++++++++++ xsa452-4.18-2.patch | 90 +++++ xsa452-4.18-3.patch | 135 +++++++ xsa452-4.18-4.patch | 197 +++++++++ xsa452-4.18-5.patch | 239 +++++++++++ xsa452-4.18-6.patch | 163 ++++++++ xsa452-4.18-7.patch | 307 ++++++++++++++ xsa453-4.18-1.patch | 50 +++ xsa453-4.18-2.patch | 314 ++++++++++++++ xsa453-4.18-3.patch | 113 ++++++ xsa453-4.18-4.patch | 75 ++++ xsa453-4.18-5.patch | 382 ++++++++++++++++++ xsa453-4.18-6.patch | 61 +++ xsa453-4.18-7.patch | 201 +++++++++ 19 files changed, 2925 insertions(+), 1 deletion(-) create mode 100644 xen.git-576528a2a742069af203e90c613c5c93e23c9755.patch create mode 100644 xen.git-60e00f77a5cc671d30c5ef3318f5b8e9b74e4aa3.patch create mode 100644 xen.git-6e9507f7d51fe49df8bc70f83e49ce06c92e4e54.patch create mode 100644 xen.git-de17162cafd27f2865a3102a2ec0f386a02ed03d.patch create mode 100644 xsa452-4.18-1.patch create mode 100644 xsa452-4.18-2.patch create mode 100644 xsa452-4.18-3.patch create mode 100644 xsa452-4.18-4.patch create mode 100644 xsa452-4.18-5.patch create mode 100644 xsa452-4.18-6.patch create mode 100644 xsa452-4.18-7.patch create mode 100644 xsa453-4.18-1.patch create mode 100644 xsa453-4.18-2.patch create mode 100644 xsa453-4.18-3.patch create mode 100644 xsa453-4.18-4.patch create mode 100644 xsa453-4.18-5.patch create mode 100644 xsa453-4.18-6.patch create mode 100644 xsa453-4.18-7.patch diff --git a/xen.git-576528a2a742069af203e90c613c5c93e23c9755.patch b/xen.git-576528a2a742069af203e90c613c5c93e23c9755.patch new file mode 100644 index 0000000..20acd78 --- /dev/null +++ b/xen.git-576528a2a742069af203e90c613c5c93e23c9755.patch @@ -0,0 +1,47 @@ +From 576528a2a742069af203e90c613c5c93e23c9755 Mon Sep 17 00:00:00 2001 +From: =?utf8?q?Roger=20Pau=20Monn=C3=A9?= +Date: Tue, 27 Feb 2024 14:58:40 +0100 +Subject: [PATCH] x86/spec: do not print thunk option selection if not built-in +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf8 +Content-Transfer-Encoding: 8bit + +Since the thunk built-in enable is printed as part of the "Compiled-in +support:" line, avoid printing anything in "Xen settings:" if the thunk is +disabled at build time. + +Note the BTI-Thunk option printing is also adjusted to print a colon in the +same way the other options on the line do. + +Requested-by: Jan Beulich +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +--- + xen/arch/x86/spec_ctrl.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 74d2136d42..752225faa6 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -504,11 +504,12 @@ static void __init print_details(enum ind_thunk thunk) + "\n"); + + /* Settings for Xen's protection, irrespective of guests. */ +- printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s%s%s, Other:%s%s%s%s%s%s\n", +- thunk == THUNK_NONE ? "N/A" : +- thunk == THUNK_RETPOLINE ? "RETPOLINE" : +- thunk == THUNK_LFENCE ? "LFENCE" : +- thunk == THUNK_JMP ? "JMP" : "?", ++ printk(" Xen settings: %s%sSPEC_CTRL: %s%s%s%s%s, Other:%s%s%s%s%s%s\n", ++ thunk != THUNK_NONE ? "BTI-Thunk: " : "", ++ thunk == THUNK_NONE ? "" : ++ thunk == THUNK_RETPOLINE ? "RETPOLINE, " : ++ thunk == THUNK_LFENCE ? "LFENCE, " : ++ thunk == THUNK_JMP ? "JMP, " : "?, ", + (!boot_cpu_has(X86_FEATURE_IBRSB) && + !boot_cpu_has(X86_FEATURE_IBRS)) ? "No" : + (default_xen_spec_ctrl & SPEC_CTRL_IBRS) ? "IBRS+" : "IBRS-", +-- +2.30.2 + diff --git a/xen.git-60e00f77a5cc671d30c5ef3318f5b8e9b74e4aa3.patch b/xen.git-60e00f77a5cc671d30c5ef3318f5b8e9b74e4aa3.patch new file mode 100644 index 0000000..a1b9f49 --- /dev/null +++ b/xen.git-60e00f77a5cc671d30c5ef3318f5b8e9b74e4aa3.patch @@ -0,0 +1,55 @@ +From 60e00f77a5cc671d30c5ef3318f5b8e9b74e4aa3 Mon Sep 17 00:00:00 2001 +From: =?utf8?q?Roger=20Pau=20Monn=C3=A9?= +Date: Mon, 26 Feb 2024 16:06:42 +0100 +Subject: [PATCH] x86/spec: fix BRANCH_HARDEN option to only be set when + build-enabled +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf8 +Content-Transfer-Encoding: 8bit + +The current logic to handle the BRANCH_HARDEN option will report it as enabled +even when build-time disabled. Fix this by only allowing the option to be set +when support for it is built into Xen. + +Fixes: 2d6f36daa086 ('x86/nospec: Introduce CONFIG_SPECULATIVE_HARDEN_BRANCH') +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +--- + xen/arch/x86/spec_ctrl.c | 14 ++++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) + +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 421fe3f640..503f1c7a37 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -50,7 +50,8 @@ static int8_t __initdata opt_psfd = -1; + int8_t __ro_after_init opt_ibpb_ctxt_switch = -1; + int8_t __read_mostly opt_eager_fpu = -1; + int8_t __read_mostly opt_l1d_flush = -1; +-static bool __initdata opt_branch_harden = true; ++static bool __initdata opt_branch_harden = ++ IS_ENABLED(CONFIG_SPECULATIVE_HARDEN_BRANCH); + + bool __initdata bsp_delay_spec_ctrl; + uint8_t __read_mostly default_xen_spec_ctrl; +@@ -268,7 +269,16 @@ static int __init cf_check parse_spec_ctrl(const char *s) + else if ( (val = parse_boolean("l1d-flush", s, ss)) >= 0 ) + opt_l1d_flush = val; + else if ( (val = parse_boolean("branch-harden", s, ss)) >= 0 ) +- opt_branch_harden = val; ++ { ++ if ( IS_ENABLED(CONFIG_SPECULATIVE_HARDEN_BRANCH) ) ++ opt_branch_harden = val; ++ else ++ { ++ no_config_param("SPECULATIVE_HARDEN_BRANCH", "spec-ctrl", s, ++ ss); ++ rc = -EINVAL; ++ } ++ } + else if ( (val = parse_boolean("srb-lock", s, ss)) >= 0 ) + opt_srb_lock = val; + else if ( (val = parse_boolean("unpriv-mmio", s, ss)) >= 0 ) +-- +2.30.2 + diff --git a/xen.git-6e9507f7d51fe49df8bc70f83e49ce06c92e4e54.patch b/xen.git-6e9507f7d51fe49df8bc70f83e49ce06c92e4e54.patch new file mode 100644 index 0000000..a78c792 --- /dev/null +++ b/xen.git-6e9507f7d51fe49df8bc70f83e49ce06c92e4e54.patch @@ -0,0 +1,51 @@ +From 6e9507f7d51fe49df8bc70f83e49ce06c92e4e54 Mon Sep 17 00:00:00 2001 +From: =?utf8?q?Roger=20Pau=20Monn=C3=A9?= +Date: Tue, 27 Feb 2024 14:57:52 +0100 +Subject: [PATCH] x86/spec: print the built-in SPECULATIVE_HARDEN_* options +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf8 +Content-Transfer-Encoding: 8bit + +Just like it's done for INDIRECT_THUNK and SHADOW_PAGING. + +Reported-by: Jan Beulich +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +--- + xen/arch/x86/spec_ctrl.c | 14 +++++++++++++- + 1 file changed, 13 insertions(+), 1 deletion(-) + +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 503f1c7a37..2d17cbe25f 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -476,13 +476,25 @@ static void __init print_details(enum ind_thunk thunk) + (e21a & cpufeat_mask(X86_FEATURE_SBPB)) ? " SBPB" : ""); + + /* Compiled-in support which pertains to mitigations. */ +- if ( IS_ENABLED(CONFIG_INDIRECT_THUNK) || IS_ENABLED(CONFIG_SHADOW_PAGING) ) ++ if ( IS_ENABLED(CONFIG_INDIRECT_THUNK) || IS_ENABLED(CONFIG_SHADOW_PAGING) || ++ IS_ENABLED(CONFIG_SPECULATIVE_HARDEN_ARRAY) || ++ IS_ENABLED(CONFIG_SPECULATIVE_HARDEN_BRANCH) || ++ IS_ENABLED(CONFIG_SPECULATIVE_HARDEN_GUEST_ACCESS) ) + printk(" Compiled-in support:" + #ifdef CONFIG_INDIRECT_THUNK + " INDIRECT_THUNK" + #endif + #ifdef CONFIG_SHADOW_PAGING + " SHADOW_PAGING" ++#endif ++#ifdef CONFIG_SPECULATIVE_HARDEN_ARRAY ++ " HARDEN_ARRAY" ++#endif ++#ifdef CONFIG_SPECULATIVE_HARDEN_BRANCH ++ " HARDEN_BRANCH" ++#endif ++#ifdef CONFIG_SPECULATIVE_HARDEN_GUEST_ACCESS ++ " HARDEN_GUEST_ACCESS" + #endif + "\n"); + +-- +2.30.2 + diff --git a/xen.git-de17162cafd27f2865a3102a2ec0f386a02ed03d.patch b/xen.git-de17162cafd27f2865a3102a2ec0f386a02ed03d.patch new file mode 100644 index 0000000..6043d48 --- /dev/null +++ b/xen.git-de17162cafd27f2865a3102a2ec0f386a02ed03d.patch @@ -0,0 +1,99 @@ +From de17162cafd27f2865a3102a2ec0f386a02ed03d Mon Sep 17 00:00:00 2001 +From: Andrew Cooper +Date: Thu, 29 Feb 2024 11:26:40 +0000 +Subject: [PATCH] x86/cpu-policy: Allow for levelling of VERW side effects +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf8 +Content-Transfer-Encoding: 8bit + +MD_CLEAR and FB_CLEAR need OR-ing across a migrate pool. Allow this, by +having them unconditinally set in max, with the host values reflected in +default. Annotate the bits as having special properies. + +Signed-off-by: Andrew Cooper +Reviewed-by: Roger Pau Monné +--- + xen/arch/x86/cpu-policy.c | 24 +++++++++++++++++++++ + xen/arch/x86/include/asm/cpufeature.h | 1 + + xen/include/public/arch-x86/cpufeatureset.h | 4 ++-- + 3 files changed, 27 insertions(+), 2 deletions(-) + +diff --git a/xen/arch/x86/cpu-policy.c b/xen/arch/x86/cpu-policy.c +index 609db6946f..2c6f03057b 100644 +--- a/xen/arch/x86/cpu-policy.c ++++ b/xen/arch/x86/cpu-policy.c +@@ -442,6 +442,16 @@ static void __init guest_common_max_feature_adjustments(uint32_t *fs) + __set_bit(X86_FEATURE_RSBA, fs); + __set_bit(X86_FEATURE_RRSBA, fs); + ++ /* ++ * These bits indicate that the VERW instruction may have gained ++ * scrubbing side effects. With pooling, they mean "you might migrate ++ * somewhere where scrubbing is necessary", and may need exposing on ++ * unaffected hardware. This is fine, because the VERW instruction ++ * has been around since the 286. ++ */ ++ __set_bit(X86_FEATURE_MD_CLEAR, fs); ++ __set_bit(X86_FEATURE_FB_CLEAR, fs); ++ + /* + * The Gather Data Sampling microcode mitigation (August 2023) has an + * adverse performance impact on the CLWB instruction on SKX/CLX/CPX. +@@ -486,6 +496,20 @@ static void __init guest_common_default_feature_adjustments(uint32_t *fs) + cpu_has_rdrand && !is_forced_cpu_cap(X86_FEATURE_RDRAND) ) + __clear_bit(X86_FEATURE_RDRAND, fs); + ++ /* ++ * These bits indicate that the VERW instruction may have gained ++ * scrubbing side effects. The max policy has them set for migration ++ * reasons, so reset the default policy back to the host values in ++ * case we're unaffected. ++ */ ++ __clear_bit(X86_FEATURE_MD_CLEAR, fs); ++ if ( cpu_has_md_clear ) ++ __set_bit(X86_FEATURE_MD_CLEAR, fs); ++ ++ __clear_bit(X86_FEATURE_FB_CLEAR, fs); ++ if ( cpu_has_fb_clear ) ++ __set_bit(X86_FEATURE_FB_CLEAR, fs); ++ + /* + * The Gather Data Sampling microcode mitigation (August 2023) has an + * adverse performance impact on the CLWB instruction on SKX/CLX/CPX. +diff --git a/xen/arch/x86/include/asm/cpufeature.h b/xen/arch/x86/include/asm/cpufeature.h +index be16492c68..ad24d0fa88 100644 +--- a/xen/arch/x86/include/asm/cpufeature.h ++++ b/xen/arch/x86/include/asm/cpufeature.h +@@ -178,6 +178,7 @@ static inline bool boot_cpu_has(unsigned int feat) + #define cpu_has_avx512_4fmaps boot_cpu_has(X86_FEATURE_AVX512_4FMAPS) + #define cpu_has_avx512_vp2intersect boot_cpu_has(X86_FEATURE_AVX512_VP2INTERSECT) + #define cpu_has_srbds_ctrl boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ++#define cpu_has_md_clear boot_cpu_has(X86_FEATURE_MD_CLEAR) + #define cpu_has_rtm_always_abort boot_cpu_has(X86_FEATURE_RTM_ALWAYS_ABORT) + #define cpu_has_tsx_force_abort boot_cpu_has(X86_FEATURE_TSX_FORCE_ABORT) + #define cpu_has_serialize boot_cpu_has(X86_FEATURE_SERIALIZE) +diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h +index b230d3a690..0374cec3a2 100644 +--- a/xen/include/public/arch-x86/cpufeatureset.h ++++ b/xen/include/public/arch-x86/cpufeatureset.h +@@ -262,7 +262,7 @@ XEN_CPUFEATURE(AVX512_4FMAPS, 9*32+ 3) /*A AVX512 Multiply Accumulation Single + XEN_CPUFEATURE(FSRM, 9*32+ 4) /*A Fast Short REP MOVS */ + XEN_CPUFEATURE(AVX512_VP2INTERSECT, 9*32+8) /*a VP2INTERSECT{D,Q} insns */ + XEN_CPUFEATURE(SRBDS_CTRL, 9*32+ 9) /* MSR_MCU_OPT_CTRL and RNGDS_MITG_DIS. */ +-XEN_CPUFEATURE(MD_CLEAR, 9*32+10) /*A VERW clears microarchitectural buffers */ ++XEN_CPUFEATURE(MD_CLEAR, 9*32+10) /*!A VERW clears microarchitectural buffers */ + XEN_CPUFEATURE(RTM_ALWAYS_ABORT, 9*32+11) /*! June 2021 TSX defeaturing in microcode. */ + XEN_CPUFEATURE(TSX_FORCE_ABORT, 9*32+13) /* MSR_TSX_FORCE_ABORT.RTM_ABORT */ + XEN_CPUFEATURE(SERIALIZE, 9*32+14) /*A SERIALIZE insn */ +@@ -334,7 +334,7 @@ XEN_CPUFEATURE(DOITM, 16*32+12) /* Data Operand Invariant Timing + XEN_CPUFEATURE(SBDR_SSDP_NO, 16*32+13) /*A No Shared Buffer Data Read or Sideband Stale Data Propagation */ + XEN_CPUFEATURE(FBSDP_NO, 16*32+14) /*A No Fill Buffer Stale Data Propagation */ + XEN_CPUFEATURE(PSDP_NO, 16*32+15) /*A No Primary Stale Data Propagation */ +-XEN_CPUFEATURE(FB_CLEAR, 16*32+17) /*A Fill Buffers cleared by VERW */ ++XEN_CPUFEATURE(FB_CLEAR, 16*32+17) /*!A Fill Buffers cleared by VERW */ + XEN_CPUFEATURE(FB_CLEAR_CTRL, 16*32+18) /* MSR_OPT_CPU_CTRL.FB_CLEAR_DIS */ + XEN_CPUFEATURE(RRSBA, 16*32+19) /*! Restricted RSB Alternative */ + XEN_CPUFEATURE(BHI_NO, 16*32+20) /*A No Branch History Injection */ +-- +2.30.2 + diff --git a/xen.spec b/xen.spec index 39fd394..1236568 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.18.0 -Release: 6%{?dist} +Release: 7%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -118,6 +118,24 @@ Patch53: newlib.gcc14.fixes.patch Patch54: xsa449.patch Patch55: xsa450.patch Patch56: xsa451-4.18.patch +Patch57: xen.git-de17162cafd27f2865a3102a2ec0f386a02ed03d.patch +Patch58: xsa452-4.18-1.patch +Patch59: xsa452-4.18-2.patch +Patch60: xsa452-4.18-3.patch +Patch61: xsa452-4.18-4.patch +Patch62: xsa452-4.18-5.patch +Patch63: xsa452-4.18-6.patch +Patch64: xsa452-4.18-7.patch +Patch65: xen.git-60e00f77a5cc671d30c5ef3318f5b8e9b74e4aa3.patch +Patch66: xen.git-6e9507f7d51fe49df8bc70f83e49ce06c92e4e54.patch +Patch67: xen.git-576528a2a742069af203e90c613c5c93e23c9755.patch +Patch68: xsa453-4.18-1.patch +Patch69: xsa453-4.18-2.patch +Patch70: xsa453-4.18-3.patch +Patch71: xsa453-4.18-4.patch +Patch72: xsa453-4.18-5.patch +Patch73: xsa453-4.18-6.patch +Patch74: xsa453-4.18-7.patch %if %build_qemutrad @@ -336,6 +354,24 @@ manage Xen virtual machines. %patch 54 -p1 %patch 55 -p1 %patch 56 -p1 +%patch 57 -p1 +%patch 58 -p1 +%patch 59 -p1 +%patch 60 -p1 +%patch 61 -p1 +%patch 62 -p1 +%patch 63 -p1 +%patch 64 -p1 +%patch 65 -p1 +%patch 66 -p1 +%patch 67 -p1 +%patch 68 -p1 +%patch 69 -p1 +%patch 70 -p1 +%patch 71 -p1 +%patch 72 -p1 +%patch 73 -p1 +%patch 74 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -942,6 +978,11 @@ fi %endif %changelog +* Wed Mar 13 2024 Michael Young - 4.18.0-7 +- x86: Register File Data Sampling [XSA-452, CVE-2023-28746] +- GhostRace: Speculative Race Conditions [XSA-453, CVE-2024-2193] +- additional patches so above applies cleanly + * Tue Feb 27 2024 Michael Young - 4.18.0-6 - x86: shadow stack vs exceptions from emulation stubs - [XSA-451, CVE-2023-46841] (#2266326) diff --git a/xsa452-4.18-1.patch b/xsa452-4.18-1.patch new file mode 100644 index 0000000..cae848f --- /dev/null +++ b/xsa452-4.18-1.patch @@ -0,0 +1,304 @@ +From: Andrew Cooper +Subject: x86/entry: Introduce EFRAME_* constants + +restore_all_guest() does a lot of manipulation of the stack after popping the +GPRs, and uses raw %rsp displacements to do so. Also, almost all entrypaths +use raw %rsp displacements prior to pushing GPRs. + +Provide better mnemonics, to aid readability and reduce the chance of errors +when editing. + +No functional change. The resulting binary is identical. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 37541208f119a9c552c6c6c3246ea61be0d44035) + +diff --git a/xen/arch/x86/x86_64/asm-offsets.c b/xen/arch/x86/x86_64/asm-offsets.c +index 57b73a4e6214..2fc4d9130a4d 100644 +--- a/xen/arch/x86/x86_64/asm-offsets.c ++++ b/xen/arch/x86/x86_64/asm-offsets.c +@@ -51,6 +51,23 @@ void __dummy__(void) + OFFSET(UREGS_kernel_sizeof, struct cpu_user_regs, es); + BLANK(); + ++ /* ++ * EFRAME_* is for the entry/exit logic where %rsp is pointing at ++ * UREGS_error_code and GPRs are still/already guest values. ++ */ ++#define OFFSET_EF(sym, mem) \ ++ DEFINE(sym, offsetof(struct cpu_user_regs, mem) - \ ++ offsetof(struct cpu_user_regs, error_code)) ++ ++ OFFSET_EF(EFRAME_entry_vector, entry_vector); ++ OFFSET_EF(EFRAME_rip, rip); ++ OFFSET_EF(EFRAME_cs, cs); ++ OFFSET_EF(EFRAME_eflags, eflags); ++ OFFSET_EF(EFRAME_rsp, rsp); ++ BLANK(); ++ ++#undef OFFSET_EF ++ + OFFSET(VCPU_processor, struct vcpu, processor); + OFFSET(VCPU_domain, struct vcpu, domain); + OFFSET(VCPU_vcpu_info, struct vcpu, vcpu_info_area.map); +diff --git a/xen/arch/x86/x86_64/compat/entry.S b/xen/arch/x86/x86_64/compat/entry.S +index fcc3a721f147..cb473f08eebd 100644 +--- a/xen/arch/x86/x86_64/compat/entry.S ++++ b/xen/arch/x86/x86_64/compat/entry.S +@@ -15,7 +15,7 @@ ENTRY(entry_int82) + ENDBR64 + ALTERNATIVE "", clac, X86_FEATURE_XEN_SMAP + pushq $0 +- movl $HYPERCALL_VECTOR, 4(%rsp) ++ movl $HYPERCALL_VECTOR, EFRAME_entry_vector(%rsp) + SAVE_ALL compat=1 /* DPL1 gate, restricted to 32bit PV guests only. */ + + SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, %rdx=0, Clob: acd */ +diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S +index 9a7b129aa7e4..968da9d727b1 100644 +--- a/xen/arch/x86/x86_64/entry.S ++++ b/xen/arch/x86/x86_64/entry.S +@@ -190,15 +190,15 @@ restore_all_guest: + SPEC_CTRL_EXIT_TO_PV /* Req: a=spec_ctrl %rsp=regs/cpuinfo, Clob: cd */ + + RESTORE_ALL +- testw $TRAP_syscall,4(%rsp) ++ testw $TRAP_syscall, EFRAME_entry_vector(%rsp) + jz iret_exit_to_guest + +- movq 24(%rsp),%r11 # RFLAGS ++ mov EFRAME_eflags(%rsp), %r11 + andq $~(X86_EFLAGS_IOPL | X86_EFLAGS_VM), %r11 + orq $X86_EFLAGS_IF,%r11 + + /* Don't use SYSRET path if the return address is not canonical. */ +- movq 8(%rsp),%rcx ++ mov EFRAME_rip(%rsp), %rcx + sarq $47,%rcx + incl %ecx + cmpl $1,%ecx +@@ -213,20 +213,20 @@ restore_all_guest: + ALTERNATIVE "", rag_clrssbsy, X86_FEATURE_XEN_SHSTK + #endif + +- movq 8(%rsp), %rcx # RIP +- cmpw $FLAT_USER_CS32,16(%rsp)# CS +- movq 32(%rsp),%rsp # RSP ++ mov EFRAME_rip(%rsp), %rcx ++ cmpw $FLAT_USER_CS32, EFRAME_cs(%rsp) ++ mov EFRAME_rsp(%rsp), %rsp + je 1f + sysretq + 1: sysretl + + ALIGN + .Lrestore_rcx_iret_exit_to_guest: +- movq 8(%rsp), %rcx # RIP ++ mov EFRAME_rip(%rsp), %rcx + /* No special register assumptions. */ + iret_exit_to_guest: +- andl $~(X86_EFLAGS_IOPL | X86_EFLAGS_VM), 24(%rsp) +- orl $X86_EFLAGS_IF,24(%rsp) ++ andl $~(X86_EFLAGS_IOPL | X86_EFLAGS_VM), EFRAME_eflags(%rsp) ++ orl $X86_EFLAGS_IF, EFRAME_eflags(%rsp) + addq $8,%rsp + .Lft0: iretq + _ASM_PRE_EXTABLE(.Lft0, handle_exception) +@@ -257,7 +257,7 @@ ENTRY(lstar_enter) + pushq $FLAT_KERNEL_CS64 + pushq %rcx + pushq $0 +- movl $TRAP_syscall, 4(%rsp) ++ movl $TRAP_syscall, EFRAME_entry_vector(%rsp) + SAVE_ALL + + SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, %rdx=0, Clob: acd */ +@@ -294,7 +294,7 @@ ENTRY(cstar_enter) + pushq $FLAT_USER_CS32 + pushq %rcx + pushq $0 +- movl $TRAP_syscall, 4(%rsp) ++ movl $TRAP_syscall, EFRAME_entry_vector(%rsp) + SAVE_ALL + + SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, %rdx=0, Clob: acd */ +@@ -335,7 +335,7 @@ GLOBAL(sysenter_eflags_saved) + pushq $3 /* ring 3 null cs */ + pushq $0 /* null rip */ + pushq $0 +- movl $TRAP_syscall, 4(%rsp) ++ movl $TRAP_syscall, EFRAME_entry_vector(%rsp) + SAVE_ALL + + SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, %rdx=0, Clob: acd */ +@@ -389,7 +389,7 @@ ENTRY(int80_direct_trap) + ENDBR64 + ALTERNATIVE "", clac, X86_FEATURE_XEN_SMAP + pushq $0 +- movl $0x80, 4(%rsp) ++ movl $0x80, EFRAME_entry_vector(%rsp) + SAVE_ALL + + SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, %rdx=0, Clob: acd */ +@@ -649,7 +649,7 @@ ret_from_intr: + .section .init.text, "ax", @progbits + ENTRY(early_page_fault) + ENDBR64 +- movl $X86_EXC_PF, 4(%rsp) ++ movl $X86_EXC_PF, EFRAME_entry_vector(%rsp) + SAVE_ALL + movq %rsp, %rdi + call do_early_page_fault +@@ -716,7 +716,7 @@ ENTRY(common_interrupt) + + ENTRY(entry_PF) + ENDBR64 +- movl $X86_EXC_PF, 4(%rsp) ++ movl $X86_EXC_PF, EFRAME_entry_vector(%rsp) + /* No special register assumptions. */ + GLOBAL(handle_exception) + ALTERNATIVE "", clac, X86_FEATURE_XEN_SMAP +@@ -890,90 +890,90 @@ FATAL_exception_with_ints_disabled: + ENTRY(entry_DE) + ENDBR64 + pushq $0 +- movl $X86_EXC_DE, 4(%rsp) ++ movl $X86_EXC_DE, EFRAME_entry_vector(%rsp) + jmp handle_exception + + ENTRY(entry_MF) + ENDBR64 + pushq $0 +- movl $X86_EXC_MF, 4(%rsp) ++ movl $X86_EXC_MF, EFRAME_entry_vector(%rsp) + jmp handle_exception + + ENTRY(entry_XM) + ENDBR64 + pushq $0 +- movl $X86_EXC_XM, 4(%rsp) ++ movl $X86_EXC_XM, EFRAME_entry_vector(%rsp) + jmp handle_exception + + ENTRY(entry_NM) + ENDBR64 + pushq $0 +- movl $X86_EXC_NM, 4(%rsp) ++ movl $X86_EXC_NM, EFRAME_entry_vector(%rsp) + jmp handle_exception + + ENTRY(entry_DB) + ENDBR64 + pushq $0 +- movl $X86_EXC_DB, 4(%rsp) ++ movl $X86_EXC_DB, EFRAME_entry_vector(%rsp) + jmp handle_ist_exception + + ENTRY(entry_BP) + ENDBR64 + pushq $0 +- movl $X86_EXC_BP, 4(%rsp) ++ movl $X86_EXC_BP, EFRAME_entry_vector(%rsp) + jmp handle_exception + + ENTRY(entry_OF) + ENDBR64 + pushq $0 +- movl $X86_EXC_OF, 4(%rsp) ++ movl $X86_EXC_OF, EFRAME_entry_vector(%rsp) + jmp handle_exception + + ENTRY(entry_BR) + ENDBR64 + pushq $0 +- movl $X86_EXC_BR, 4(%rsp) ++ movl $X86_EXC_BR, EFRAME_entry_vector(%rsp) + jmp handle_exception + + ENTRY(entry_UD) + ENDBR64 + pushq $0 +- movl $X86_EXC_UD, 4(%rsp) ++ movl $X86_EXC_UD, EFRAME_entry_vector(%rsp) + jmp handle_exception + + ENTRY(entry_TS) + ENDBR64 +- movl $X86_EXC_TS, 4(%rsp) ++ movl $X86_EXC_TS, EFRAME_entry_vector(%rsp) + jmp handle_exception + + ENTRY(entry_NP) + ENDBR64 +- movl $X86_EXC_NP, 4(%rsp) ++ movl $X86_EXC_NP, EFRAME_entry_vector(%rsp) + jmp handle_exception + + ENTRY(entry_SS) + ENDBR64 +- movl $X86_EXC_SS, 4(%rsp) ++ movl $X86_EXC_SS, EFRAME_entry_vector(%rsp) + jmp handle_exception + + ENTRY(entry_GP) + ENDBR64 +- movl $X86_EXC_GP, 4(%rsp) ++ movl $X86_EXC_GP, EFRAME_entry_vector(%rsp) + jmp handle_exception + + ENTRY(entry_AC) + ENDBR64 +- movl $X86_EXC_AC, 4(%rsp) ++ movl $X86_EXC_AC, EFRAME_entry_vector(%rsp) + jmp handle_exception + + ENTRY(entry_CP) + ENDBR64 +- movl $X86_EXC_CP, 4(%rsp) ++ movl $X86_EXC_CP, EFRAME_entry_vector(%rsp) + jmp handle_exception + + ENTRY(entry_DF) + ENDBR64 +- movl $X86_EXC_DF, 4(%rsp) ++ movl $X86_EXC_DF, EFRAME_entry_vector(%rsp) + /* Set AC to reduce chance of further SMAP faults */ + ALTERNATIVE "", stac, X86_FEATURE_XEN_SMAP + SAVE_ALL +@@ -998,7 +998,7 @@ ENTRY(entry_DF) + ENTRY(entry_NMI) + ENDBR64 + pushq $0 +- movl $X86_EXC_NMI, 4(%rsp) ++ movl $X86_EXC_NMI, EFRAME_entry_vector(%rsp) + handle_ist_exception: + ALTERNATIVE "", clac, X86_FEATURE_XEN_SMAP + SAVE_ALL +@@ -1130,7 +1130,7 @@ handle_ist_exception: + ENTRY(entry_MC) + ENDBR64 + pushq $0 +- movl $X86_EXC_MC, 4(%rsp) ++ movl $X86_EXC_MC, EFRAME_entry_vector(%rsp) + jmp handle_ist_exception + + /* No op trap handler. Required for kexec crash path. */ +@@ -1167,7 +1167,7 @@ autogen_stubs: /* Automatically generated stubs. */ + 1: + ENDBR64 + pushq $0 +- movb $vec,4(%rsp) ++ movb $vec, EFRAME_entry_vector(%rsp) + jmp common_interrupt + + entrypoint 1b +@@ -1181,7 +1181,7 @@ autogen_stubs: /* Automatically generated stubs. */ + test $8,%spl /* 64bit exception frames are 16 byte aligned, but the word */ + jz 2f /* size is 8 bytes. Check whether the processor gave us an */ + pushq $0 /* error code, and insert an empty one if not. */ +-2: movb $vec,4(%rsp) ++2: movb $vec, EFRAME_entry_vector(%rsp) + jmp handle_exception + + entrypoint 1b diff --git a/xsa452-4.18-2.patch b/xsa452-4.18-2.patch new file mode 100644 index 0000000..4535397 --- /dev/null +++ b/xsa452-4.18-2.patch @@ -0,0 +1,90 @@ +From: Andrew Cooper +Subject: x86: Resync intel-family.h from Linux + +From v6.8-rc6 + +Signed-off-by: Andrew Cooper +Acked-by: Jan Beulich +(cherry picked from commit 195e75371b13c4f7ecdf7b5c50aed0d02f2d7ce8) + +diff --git a/xen/arch/x86/include/asm/intel-family.h b/xen/arch/x86/include/asm/intel-family.h +index ffc49151befe..b65e9c46b922 100644 +--- a/xen/arch/x86/include/asm/intel-family.h ++++ b/xen/arch/x86/include/asm/intel-family.h +@@ -26,6 +26,9 @@ + * _G - parts with extra graphics on + * _X - regular server parts + * _D - micro server parts ++ * _N,_P - other mobile parts ++ * _H - premium mobile parts ++ * _S - other client parts + * + * Historical OPTDIFFs: + * +@@ -37,6 +40,9 @@ + * their own names :-( + */ + ++/* Wildcard match for FAM6 so X86_MATCH_INTEL_FAM6_MODEL(ANY) works */ ++#define INTEL_FAM6_ANY X86_MODEL_ANY ++ + #define INTEL_FAM6_CORE_YONAH 0x0E + + #define INTEL_FAM6_CORE2_MEROM 0x0F +@@ -93,8 +99,6 @@ + #define INTEL_FAM6_ICELAKE_L 0x7E /* Sunny Cove */ + #define INTEL_FAM6_ICELAKE_NNPI 0x9D /* Sunny Cove */ + +-#define INTEL_FAM6_LAKEFIELD 0x8A /* Sunny Cove / Tremont */ +- + #define INTEL_FAM6_ROCKETLAKE 0xA7 /* Cypress Cove */ + + #define INTEL_FAM6_TIGERLAKE_L 0x8C /* Willow Cove */ +@@ -102,12 +106,31 @@ + + #define INTEL_FAM6_SAPPHIRERAPIDS_X 0x8F /* Golden Cove */ + ++#define INTEL_FAM6_EMERALDRAPIDS_X 0xCF ++ ++#define INTEL_FAM6_GRANITERAPIDS_X 0xAD ++#define INTEL_FAM6_GRANITERAPIDS_D 0xAE ++ ++/* "Hybrid" Processors (P-Core/E-Core) */ ++ ++#define INTEL_FAM6_LAKEFIELD 0x8A /* Sunny Cove / Tremont */ ++ + #define INTEL_FAM6_ALDERLAKE 0x97 /* Golden Cove / Gracemont */ + #define INTEL_FAM6_ALDERLAKE_L 0x9A /* Golden Cove / Gracemont */ + +-#define INTEL_FAM6_RAPTORLAKE 0xB7 ++#define INTEL_FAM6_RAPTORLAKE 0xB7 /* Raptor Cove / Enhanced Gracemont */ ++#define INTEL_FAM6_RAPTORLAKE_P 0xBA ++#define INTEL_FAM6_RAPTORLAKE_S 0xBF ++ ++#define INTEL_FAM6_METEORLAKE 0xAC ++#define INTEL_FAM6_METEORLAKE_L 0xAA ++ ++#define INTEL_FAM6_ARROWLAKE_H 0xC5 ++#define INTEL_FAM6_ARROWLAKE 0xC6 ++ ++#define INTEL_FAM6_LUNARLAKE_M 0xBD + +-/* "Small Core" Processors (Atom) */ ++/* "Small Core" Processors (Atom/E-Core) */ + + #define INTEL_FAM6_ATOM_BONNELL 0x1C /* Diamondville, Pineview */ + #define INTEL_FAM6_ATOM_BONNELL_MID 0x26 /* Silverthorne, Lincroft */ +@@ -134,6 +157,13 @@ + #define INTEL_FAM6_ATOM_TREMONT 0x96 /* Elkhart Lake */ + #define INTEL_FAM6_ATOM_TREMONT_L 0x9C /* Jasper Lake */ + ++#define INTEL_FAM6_ATOM_GRACEMONT 0xBE /* Alderlake N */ ++ ++#define INTEL_FAM6_ATOM_CRESTMONT_X 0xAF /* Sierra Forest */ ++#define INTEL_FAM6_ATOM_CRESTMONT 0xB6 /* Grand Ridge */ ++ ++#define INTEL_FAM6_ATOM_DARKMONT_X 0xDD /* Clearwater Forest */ ++ + /* Xeon Phi */ + + #define INTEL_FAM6_XEON_PHI_KNL 0x57 /* Knights Landing */ diff --git a/xsa452-4.18-3.patch b/xsa452-4.18-3.patch new file mode 100644 index 0000000..bc9059c --- /dev/null +++ b/xsa452-4.18-3.patch @@ -0,0 +1,135 @@ +From: Andrew Cooper +Subject: x86/vmx: Perform VERW flushing later in the VMExit path + +Broken out of the following patch because this change is subtle enough on its +own. See it for the rational of why we're moving VERW. + +As for how, extend the trick already used to hold one condition in +flags (RESUME vs LAUNCH) through the POPing of GPRs. + +Move the MOV CR earlier. Intel specify flags to be undefined across it. + +Encode the two conditions we want using SF and PF. See the code comment for +exactly how. + +Leave a comment to explain the lack of any content around +SPEC_CTRL_EXIT_TO_VMX, but leave the block in place. Sods law says if we +delete it, we'll need to reintroduce it. + +This is part of XSA-452 / CVE-2023-28746. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 475fa20b7384464210f42bad7195f87bd6f1c63f) + +diff --git a/xen/arch/x86/hvm/vmx/entry.S b/xen/arch/x86/hvm/vmx/entry.S +index e3f60d5a82f7..1bead826caa3 100644 +--- a/xen/arch/x86/hvm/vmx/entry.S ++++ b/xen/arch/x86/hvm/vmx/entry.S +@@ -87,17 +87,39 @@ UNLIKELY_END(realmode) + + /* WARNING! `ret`, `call *`, `jmp *` not safe beyond this point. */ + /* SPEC_CTRL_EXIT_TO_VMX Req: %rsp=regs/cpuinfo Clob: */ +- DO_SPEC_CTRL_COND_VERW ++ /* ++ * All speculation safety work happens to be elsewhere. VERW is after ++ * popping the GPRs, while restoring the guest MSR_SPEC_CTRL is left ++ * to the MSR load list. ++ */ + + mov VCPU_hvm_guest_cr2(%rbx),%rax ++ mov %rax, %cr2 ++ ++ /* ++ * We need to perform two conditional actions (VERW, and Resume vs ++ * Launch) after popping GPRs. With some cunning, we can encode both ++ * of these in eflags together. ++ * ++ * Parity is only calculated over the bottom byte of the answer, while ++ * Sign is simply the top bit. ++ * ++ * Therefore, the final OR instruction ends up producing: ++ * SF = VCPU_vmx_launched ++ * PF = !SCF_verw ++ */ ++ BUILD_BUG_ON(SCF_verw & ~0xff) ++ movzbl VCPU_vmx_launched(%rbx), %ecx ++ shl $31, %ecx ++ movzbl CPUINFO_spec_ctrl_flags(%rsp), %eax ++ and $SCF_verw, %eax ++ or %eax, %ecx + + pop %r15 + pop %r14 + pop %r13 + pop %r12 + pop %rbp +- mov %rax,%cr2 +- cmpb $0,VCPU_vmx_launched(%rbx) + pop %rbx + pop %r11 + pop %r10 +@@ -108,7 +130,13 @@ UNLIKELY_END(realmode) + pop %rdx + pop %rsi + pop %rdi +- je .Lvmx_launch ++ ++ jpe .L_skip_verw ++ /* VERW clobbers ZF, but preserves all others, including SF. */ ++ verw STK_REL(CPUINFO_verw_sel, CPUINFO_error_code)(%rsp) ++.L_skip_verw: ++ ++ jns .Lvmx_launch + + /*.Lvmx_resume:*/ + VMRESUME +diff --git a/xen/arch/x86/include/asm/asm_defns.h b/xen/arch/x86/include/asm/asm_defns.h +index baaaccb26e17..56ae26e54265 100644 +--- a/xen/arch/x86/include/asm/asm_defns.h ++++ b/xen/arch/x86/include/asm/asm_defns.h +@@ -81,6 +81,14 @@ register unsigned long current_stack_pointer asm("rsp"); + + #ifdef __ASSEMBLY__ + ++.macro BUILD_BUG_ON condstr, cond:vararg ++ .if \cond ++ .error "Condition \"\condstr\" not satisfied" ++ .endif ++.endm ++/* preprocessor macro to make error message more user friendly */ ++#define BUILD_BUG_ON(cond) BUILD_BUG_ON #cond, cond ++ + #ifdef HAVE_AS_QUOTED_SYM + #define SUBSECTION_LBL(tag) \ + .ifndef .L.tag; \ +diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h +index 6cb7c1b9491e..525745a06608 100644 +--- a/xen/arch/x86/include/asm/spec_ctrl_asm.h ++++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h +@@ -152,6 +152,13 @@ + #endif + .endm + ++/* ++ * Helper to improve the readibility of stack dispacements with %rsp in ++ * unusual positions. Both @field and @top_of_stack should be constants from ++ * the same object. @top_of_stack should be where %rsp is currently pointing. ++ */ ++#define STK_REL(field, top_of_stk) ((field) - (top_of_stk)) ++ + .macro DO_SPEC_CTRL_COND_VERW + /* + * Requires %rsp=cpuinfo +diff --git a/xen/arch/x86/x86_64/asm-offsets.c b/xen/arch/x86/x86_64/asm-offsets.c +index 2fc4d9130a4d..0d336788989f 100644 +--- a/xen/arch/x86/x86_64/asm-offsets.c ++++ b/xen/arch/x86/x86_64/asm-offsets.c +@@ -135,6 +135,7 @@ void __dummy__(void) + #endif + + OFFSET(CPUINFO_guest_cpu_user_regs, struct cpu_info, guest_cpu_user_regs); ++ OFFSET(CPUINFO_error_code, struct cpu_info, guest_cpu_user_regs.error_code); + OFFSET(CPUINFO_verw_sel, struct cpu_info, verw_sel); + OFFSET(CPUINFO_current_vcpu, struct cpu_info, current_vcpu); + OFFSET(CPUINFO_per_cpu_offset, struct cpu_info, per_cpu_offset); diff --git a/xsa452-4.18-4.patch b/xsa452-4.18-4.patch new file mode 100644 index 0000000..0ccff77 --- /dev/null +++ b/xsa452-4.18-4.patch @@ -0,0 +1,197 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Perform VERW flushing later in exit paths + +On parts vulnerable to RFDS, VERW's side effects are extended to scrub all +non-architectural entries in various Physical Register Files. To remove all +of Xen's values, the VERW must be after popping the GPRs. + +Rework SPEC_CTRL_COND_VERW to default to an CPUINFO_error_code %rsp position, +but with overrides for other contexts. Identify that it clobbers eflags; this +is particularly relevant for the SYSRET path. + +For the IST exit return to Xen, have the main SPEC_CTRL_EXIT_TO_XEN put a +shadow copy of spec_ctrl_flags, as GPRs can't be used at the point we want to +issue the VERW. + +This is part of XSA-452 / CVE-2023-28746. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 0a666cf2cd99df6faf3eebc81a1fc286e4eca4c7) + +diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h +index 525745a06608..13acebc75dff 100644 +--- a/xen/arch/x86/include/asm/spec_ctrl_asm.h ++++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h +@@ -159,16 +159,23 @@ + */ + #define STK_REL(field, top_of_stk) ((field) - (top_of_stk)) + +-.macro DO_SPEC_CTRL_COND_VERW ++.macro SPEC_CTRL_COND_VERW \ ++ scf=STK_REL(CPUINFO_spec_ctrl_flags, CPUINFO_error_code), \ ++ sel=STK_REL(CPUINFO_verw_sel, CPUINFO_error_code) + /* +- * Requires %rsp=cpuinfo ++ * Requires \scf and \sel as %rsp-relative expressions ++ * Clobbers eflags ++ * ++ * VERW needs to run after guest GPRs have been restored, where only %rsp is ++ * good to use. Default to expecting %rsp pointing at CPUINFO_error_code. ++ * Contexts where this is not true must provide an alternative \scf and \sel. + * + * Issue a VERW for its flushing side effect, if indicated. This is a Spectre + * v1 gadget, but the IRET/VMEntry is serialising. + */ +- testb $SCF_verw, CPUINFO_spec_ctrl_flags(%rsp) ++ testb $SCF_verw, \scf(%rsp) + jz .L\@_verw_skip +- verw CPUINFO_verw_sel(%rsp) ++ verw \sel(%rsp) + .L\@_verw_skip: + .endm + +@@ -286,8 +293,6 @@ + */ + ALTERNATIVE "", DO_SPEC_CTRL_EXIT_TO_GUEST, X86_FEATURE_SC_MSR_PV + +- DO_SPEC_CTRL_COND_VERW +- + ALTERNATIVE "", DO_SPEC_CTRL_DIV, X86_FEATURE_SC_DIV + .endm + +@@ -367,7 +372,7 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): + */ + .macro SPEC_CTRL_EXIT_TO_XEN + /* +- * Requires %r12=ist_exit, %r14=stack_end ++ * Requires %r12=ist_exit, %r14=stack_end, %rsp=regs + * Clobbers %rax, %rbx, %rcx, %rdx + */ + movzbl STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14), %ebx +@@ -395,11 +400,18 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): + test %r12, %r12 + jz .L\@_skip_ist_exit + +- /* Logically DO_SPEC_CTRL_COND_VERW but without the %rsp=cpuinfo dependency */ +- testb $SCF_verw, %bl +- jz .L\@_skip_verw +- verw STACK_CPUINFO_FIELD(verw_sel)(%r14) +-.L\@_skip_verw: ++ /* ++ * Stash SCF and verw_sel above eflags in the case of an IST_exit. The ++ * VERW logic needs to run after guest GPRs have been restored; i.e. where ++ * we cannot use %r12 or %r14 for the purposes they have here. ++ * ++ * When the CPU pushed this exception frame, it zero-extended eflags. ++ * Therefore it is safe for the VERW logic to look at the stashed SCF ++ * outside of the ist_exit condition. Also, this stashing won't influence ++ * any other restore_all_guest() paths. ++ */ ++ or $(__HYPERVISOR_DS32 << 16), %ebx ++ mov %ebx, UREGS_eflags + 4(%rsp) /* EFRAME_shadow_scf/sel */ + + ALTERNATIVE "", DO_SPEC_CTRL_DIV, X86_FEATURE_SC_DIV + +diff --git a/xen/arch/x86/x86_64/asm-offsets.c b/xen/arch/x86/x86_64/asm-offsets.c +index 0d336788989f..85c7d0c98967 100644 +--- a/xen/arch/x86/x86_64/asm-offsets.c ++++ b/xen/arch/x86/x86_64/asm-offsets.c +@@ -55,14 +55,22 @@ void __dummy__(void) + * EFRAME_* is for the entry/exit logic where %rsp is pointing at + * UREGS_error_code and GPRs are still/already guest values. + */ +-#define OFFSET_EF(sym, mem) \ ++#define OFFSET_EF(sym, mem, ...) \ + DEFINE(sym, offsetof(struct cpu_user_regs, mem) - \ +- offsetof(struct cpu_user_regs, error_code)) ++ offsetof(struct cpu_user_regs, error_code) __VA_ARGS__) + + OFFSET_EF(EFRAME_entry_vector, entry_vector); + OFFSET_EF(EFRAME_rip, rip); + OFFSET_EF(EFRAME_cs, cs); + OFFSET_EF(EFRAME_eflags, eflags); ++ ++ /* ++ * These aren't real fields. They're spare space, used by the IST ++ * exit-to-xen path. ++ */ ++ OFFSET_EF(EFRAME_shadow_scf, eflags, +4); ++ OFFSET_EF(EFRAME_shadow_sel, eflags, +6); ++ + OFFSET_EF(EFRAME_rsp, rsp); + BLANK(); + +@@ -136,6 +144,7 @@ void __dummy__(void) + + OFFSET(CPUINFO_guest_cpu_user_regs, struct cpu_info, guest_cpu_user_regs); + OFFSET(CPUINFO_error_code, struct cpu_info, guest_cpu_user_regs.error_code); ++ OFFSET(CPUINFO_rip, struct cpu_info, guest_cpu_user_regs.rip); + OFFSET(CPUINFO_verw_sel, struct cpu_info, verw_sel); + OFFSET(CPUINFO_current_vcpu, struct cpu_info, current_vcpu); + OFFSET(CPUINFO_per_cpu_offset, struct cpu_info, per_cpu_offset); +diff --git a/xen/arch/x86/x86_64/compat/entry.S b/xen/arch/x86/x86_64/compat/entry.S +index cb473f08eebd..3bbe3a79a5b7 100644 +--- a/xen/arch/x86/x86_64/compat/entry.S ++++ b/xen/arch/x86/x86_64/compat/entry.S +@@ -161,6 +161,12 @@ ENTRY(compat_restore_all_guest) + SPEC_CTRL_EXIT_TO_PV /* Req: a=spec_ctrl %rsp=regs/cpuinfo, Clob: cd */ + + RESTORE_ALL adj=8 compat=1 ++ ++ /* Account for ev/ec having already been popped off the stack. */ ++ SPEC_CTRL_COND_VERW \ ++ scf=STK_REL(CPUINFO_spec_ctrl_flags, CPUINFO_rip), \ ++ sel=STK_REL(CPUINFO_verw_sel, CPUINFO_rip) ++ + .Lft0: iretq + _ASM_PRE_EXTABLE(.Lft0, handle_exception) + +diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S +index 968da9d727b1..2c7512130f49 100644 +--- a/xen/arch/x86/x86_64/entry.S ++++ b/xen/arch/x86/x86_64/entry.S +@@ -214,6 +214,9 @@ restore_all_guest: + #endif + + mov EFRAME_rip(%rsp), %rcx ++ ++ SPEC_CTRL_COND_VERW /* Req: %rsp=eframe Clob: efl */ ++ + cmpw $FLAT_USER_CS32, EFRAME_cs(%rsp) + mov EFRAME_rsp(%rsp), %rsp + je 1f +@@ -227,6 +230,9 @@ restore_all_guest: + iret_exit_to_guest: + andl $~(X86_EFLAGS_IOPL | X86_EFLAGS_VM), EFRAME_eflags(%rsp) + orl $X86_EFLAGS_IF, EFRAME_eflags(%rsp) ++ ++ SPEC_CTRL_COND_VERW /* Req: %rsp=eframe Clob: efl */ ++ + addq $8,%rsp + .Lft0: iretq + _ASM_PRE_EXTABLE(.Lft0, handle_exception) +@@ -679,9 +685,22 @@ UNLIKELY_START(ne, exit_cr3) + UNLIKELY_END(exit_cr3) + + /* WARNING! `ret`, `call *`, `jmp *` not safe beyond this point. */ +- SPEC_CTRL_EXIT_TO_XEN /* Req: %r12=ist_exit %r14=end, Clob: abcd */ ++ SPEC_CTRL_EXIT_TO_XEN /* Req: %r12=ist_exit %r14=end %rsp=regs, Clob: abcd */ + + RESTORE_ALL adj=8 ++ ++ /* ++ * When the CPU pushed this exception frame, it zero-extended eflags. ++ * For an IST exit, SPEC_CTRL_EXIT_TO_XEN stashed shadow copies of ++ * spec_ctrl_flags and ver_sel above eflags, as we can't use any GPRs, ++ * and we're at a random place on the stack, not in a CPUFINFO block. ++ * ++ * Account for ev/ec having already been popped off the stack. ++ */ ++ SPEC_CTRL_COND_VERW \ ++ scf=STK_REL(EFRAME_shadow_scf, EFRAME_rip), \ ++ sel=STK_REL(EFRAME_shadow_sel, EFRAME_rip) ++ + iretq + + ENTRY(common_interrupt) diff --git a/xsa452-4.18-5.patch b/xsa452-4.18-5.patch new file mode 100644 index 0000000..d55e454 --- /dev/null +++ b/xsa452-4.18-5.patch @@ -0,0 +1,239 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Rename VERW related options + +VERW is going to be used for a 3rd purpose, and the existing nomenclature +didn't survive the Stale MMIO issues terribly well. + +Rename the command line option from `md-clear=` to `verw=`. This is more +consistent with other options which tend to be named based on what they're +doing, not which feature enumeration they use behind the scenes. Retain +`md-clear=` as a deprecated alias. + +Rename opt_md_clear_{pv,hvm} and opt_fb_clear_mmio to opt_verw_{pv,hvm,mmio}, +which has a side effect of making spec_ctrl_init_domain() rather clearer to +follow. + +No functional change. + +This is part of XSA-452 / CVE-2023-28746. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit f7603ca252e4226739eb3129a5290ee3da3f8ea4) + +diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc +index 582d6741d182..fbf16839249a 100644 +--- a/docs/misc/xen-command-line.pandoc ++++ b/docs/misc/xen-command-line.pandoc +@@ -2370,7 +2370,7 @@ By default SSBD will be mitigated at runtime (i.e `ssbd=runtime`). + + ### spec-ctrl (x86) + > `= List of [ , xen=, {pv,hvm}=, +-> {msr-sc,rsb,md-clear,ibpb-entry}=|{pv,hvm}=, ++> {msr-sc,rsb,verw,ibpb-entry}=|{pv,hvm}=, + > bti-thunk=retpoline|lfence|jmp, {ibrs,ibpb,ssbd,psfd, + > eager-fpu,l1d-flush,branch-harden,srb-lock, + > unpriv-mmio,gds-mit,div-scrub}= ]` +@@ -2395,7 +2395,7 @@ in place for guests to use. + + Use of a positive boolean value for either of these options is invalid. + +-The `pv=`, `hvm=`, `msr-sc=`, `rsb=`, `md-clear=` and `ibpb-entry=` options ++The `pv=`, `hvm=`, `msr-sc=`, `rsb=`, `verw=` and `ibpb-entry=` options + offer fine grained control over the primitives by Xen. These impact Xen's + ability to protect itself, and/or Xen's ability to virtualise support for + guests to use. +@@ -2412,11 +2412,12 @@ guests to use. + guests and if disabled, guests will be unable to use IBRS/STIBP/SSBD/etc. + * `rsb=` offers control over whether to overwrite the Return Stack Buffer / + Return Address Stack on entry to Xen and on idle. +-* `md-clear=` offers control over whether to use VERW to flush +- microarchitectural buffers on idle and exit from Xen. *Note: For +- compatibility with development versions of this fix, `mds=` is also accepted +- on Xen 4.12 and earlier as an alias. Consult vendor documentation in +- preference to here.* ++* `verw=` offers control over whether to use VERW for its scrubbing side ++ effects at appropriate privilege transitions. The exact side effects are ++ microarchitecture and microcode specific. *Note: `md-clear=` is accepted as ++ a deprecated alias. For compatibility with development versions of XSA-297, ++ `mds=` is also accepted on Xen 4.12 and earlier as an alias. Consult vendor ++ documentation in preference to here.* + * `ibpb-entry=` offers control over whether IBPB (Indirect Branch Prediction + Barrier) is used on entry to Xen. This is used by default on hardware + vulnerable to Branch Type Confusion, and hardware vulnerable to Speculative +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index a965b6db28ba..c42d8cdc22d6 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -25,8 +25,8 @@ static bool __initdata opt_msr_sc_pv = true; + static bool __initdata opt_msr_sc_hvm = true; + static int8_t __initdata opt_rsb_pv = -1; + static bool __initdata opt_rsb_hvm = true; +-static int8_t __ro_after_init opt_md_clear_pv = -1; +-static int8_t __ro_after_init opt_md_clear_hvm = -1; ++static int8_t __ro_after_init opt_verw_pv = -1; ++static int8_t __ro_after_init opt_verw_hvm = -1; + + static int8_t __ro_after_init opt_ibpb_entry_pv = -1; + static int8_t __ro_after_init opt_ibpb_entry_hvm = -1; +@@ -66,7 +66,7 @@ static bool __initdata cpu_has_bug_mds; /* Any other M{LP,SB,FB}DS combination. + + static int8_t __initdata opt_srb_lock = -1; + static bool __initdata opt_unpriv_mmio; +-static bool __ro_after_init opt_fb_clear_mmio; ++static bool __ro_after_init opt_verw_mmio; + static int8_t __initdata opt_gds_mit = -1; + static int8_t __initdata opt_div_scrub = -1; + +@@ -108,8 +108,8 @@ static int __init cf_check parse_spec_ctrl(const char *s) + disable_common: + opt_rsb_pv = false; + opt_rsb_hvm = false; +- opt_md_clear_pv = 0; +- opt_md_clear_hvm = 0; ++ opt_verw_pv = 0; ++ opt_verw_hvm = 0; + opt_ibpb_entry_pv = 0; + opt_ibpb_entry_hvm = 0; + opt_ibpb_entry_dom0 = false; +@@ -140,14 +140,14 @@ static int __init cf_check parse_spec_ctrl(const char *s) + { + opt_msr_sc_pv = val; + opt_rsb_pv = val; +- opt_md_clear_pv = val; ++ opt_verw_pv = val; + opt_ibpb_entry_pv = val; + } + else if ( (val = parse_boolean("hvm", s, ss)) >= 0 ) + { + opt_msr_sc_hvm = val; + opt_rsb_hvm = val; +- opt_md_clear_hvm = val; ++ opt_verw_hvm = val; + opt_ibpb_entry_hvm = val; + } + else if ( (val = parse_boolean("msr-sc", s, ss)) != -1 ) +@@ -192,21 +192,22 @@ static int __init cf_check parse_spec_ctrl(const char *s) + break; + } + } +- else if ( (val = parse_boolean("md-clear", s, ss)) != -1 ) ++ else if ( (val = parse_boolean("verw", s, ss)) != -1 || ++ (val = parse_boolean("md-clear", s, ss)) != -1 ) + { + switch ( val ) + { + case 0: + case 1: +- opt_md_clear_pv = opt_md_clear_hvm = val; ++ opt_verw_pv = opt_verw_hvm = val; + break; + + case -2: +- s += strlen("md-clear="); ++ s += (*s == 'v') ? strlen("verw=") : strlen("md-clear="); + if ( (val = parse_boolean("pv", s, ss)) >= 0 ) +- opt_md_clear_pv = val; ++ opt_verw_pv = val; + else if ( (val = parse_boolean("hvm", s, ss)) >= 0 ) +- opt_md_clear_hvm = val; ++ opt_verw_hvm = val; + else + default: + rc = -EINVAL; +@@ -528,8 +529,8 @@ static void __init print_details(enum ind_thunk thunk) + opt_srb_lock ? " SRB_LOCK+" : " SRB_LOCK-", + opt_ibpb_ctxt_switch ? " IBPB-ctxt" : "", + opt_l1d_flush ? " L1D_FLUSH" : "", +- opt_md_clear_pv || opt_md_clear_hvm || +- opt_fb_clear_mmio ? " VERW" : "", ++ opt_verw_pv || opt_verw_hvm || ++ opt_verw_mmio ? " VERW" : "", + opt_div_scrub ? " DIV" : "", + opt_branch_harden ? " BRANCH_HARDEN" : ""); + +@@ -550,13 +551,13 @@ static void __init print_details(enum ind_thunk thunk) + boot_cpu_has(X86_FEATURE_SC_RSB_HVM) || + boot_cpu_has(X86_FEATURE_IBPB_ENTRY_HVM) || + amd_virt_spec_ctrl || +- opt_eager_fpu || opt_md_clear_hvm) ? "" : " None", ++ opt_eager_fpu || opt_verw_hvm) ? "" : " None", + boot_cpu_has(X86_FEATURE_SC_MSR_HVM) ? " MSR_SPEC_CTRL" : "", + (boot_cpu_has(X86_FEATURE_SC_MSR_HVM) || + amd_virt_spec_ctrl) ? " MSR_VIRT_SPEC_CTRL" : "", + boot_cpu_has(X86_FEATURE_SC_RSB_HVM) ? " RSB" : "", + opt_eager_fpu ? " EAGER_FPU" : "", +- opt_md_clear_hvm ? " MD_CLEAR" : "", ++ opt_verw_hvm ? " VERW" : "", + boot_cpu_has(X86_FEATURE_IBPB_ENTRY_HVM) ? " IBPB-entry" : ""); + + #endif +@@ -565,11 +566,11 @@ static void __init print_details(enum ind_thunk thunk) + (boot_cpu_has(X86_FEATURE_SC_MSR_PV) || + boot_cpu_has(X86_FEATURE_SC_RSB_PV) || + boot_cpu_has(X86_FEATURE_IBPB_ENTRY_PV) || +- opt_eager_fpu || opt_md_clear_pv) ? "" : " None", ++ opt_eager_fpu || opt_verw_pv) ? "" : " None", + boot_cpu_has(X86_FEATURE_SC_MSR_PV) ? " MSR_SPEC_CTRL" : "", + boot_cpu_has(X86_FEATURE_SC_RSB_PV) ? " RSB" : "", + opt_eager_fpu ? " EAGER_FPU" : "", +- opt_md_clear_pv ? " MD_CLEAR" : "", ++ opt_verw_pv ? " VERW" : "", + boot_cpu_has(X86_FEATURE_IBPB_ENTRY_PV) ? " IBPB-entry" : ""); + + printk(" XPTI (64-bit PV only): Dom0 %s, DomU %s (with%s PCID)\n", +@@ -1502,8 +1503,8 @@ void spec_ctrl_init_domain(struct domain *d) + { + bool pv = is_pv_domain(d); + +- bool verw = ((pv ? opt_md_clear_pv : opt_md_clear_hvm) || +- (opt_fb_clear_mmio && is_iommu_enabled(d))); ++ bool verw = ((pv ? opt_verw_pv : opt_verw_hvm) || ++ (opt_verw_mmio && is_iommu_enabled(d))); + + bool ibpb = ((pv ? opt_ibpb_entry_pv : opt_ibpb_entry_hvm) && + (d->domain_id != 0 || opt_ibpb_entry_dom0)); +@@ -1866,19 +1867,20 @@ void __init init_speculation_mitigations(void) + * the return-to-guest path. + */ + if ( opt_unpriv_mmio ) +- opt_fb_clear_mmio = cpu_has_fb_clear; ++ opt_verw_mmio = cpu_has_fb_clear; + + /* + * By default, enable PV and HVM mitigations on MDS-vulnerable hardware. + * This will only be a token effort for MLPDS/MFBDS when HT is enabled, + * but it is somewhat better than nothing. + */ +- if ( opt_md_clear_pv == -1 ) +- opt_md_clear_pv = ((cpu_has_bug_mds || cpu_has_bug_msbds_only) && +- boot_cpu_has(X86_FEATURE_MD_CLEAR)); +- if ( opt_md_clear_hvm == -1 ) +- opt_md_clear_hvm = ((cpu_has_bug_mds || cpu_has_bug_msbds_only) && +- boot_cpu_has(X86_FEATURE_MD_CLEAR)); ++ if ( opt_verw_pv == -1 ) ++ opt_verw_pv = ((cpu_has_bug_mds || cpu_has_bug_msbds_only) && ++ cpu_has_md_clear); ++ ++ if ( opt_verw_hvm == -1 ) ++ opt_verw_hvm = ((cpu_has_bug_mds || cpu_has_bug_msbds_only) && ++ cpu_has_md_clear); + + /* + * Enable MDS/MMIO defences as applicable. The Idle blocks need using if +@@ -1891,12 +1893,12 @@ void __init init_speculation_mitigations(void) + * MDS mitigations. L1D_FLUSH is not safe for MMIO mitigations.) + * + * After calculating the appropriate idle setting, simplify +- * opt_md_clear_hvm to mean just "should we VERW on the way into HVM ++ * opt_verw_hvm to mean just "should we VERW on the way into HVM + * guests", so spec_ctrl_init_domain() can calculate suitable settings. + */ +- if ( opt_md_clear_pv || opt_md_clear_hvm || opt_fb_clear_mmio ) ++ if ( opt_verw_pv || opt_verw_hvm || opt_verw_mmio ) + setup_force_cpu_cap(X86_FEATURE_SC_VERW_IDLE); +- opt_md_clear_hvm &= !cpu_has_skip_l1dfl && !opt_l1d_flush; ++ opt_verw_hvm &= !cpu_has_skip_l1dfl && !opt_l1d_flush; + + /* + * Warn the user if they are on MLPDS/MFBDS-vulnerable hardware with HT diff --git a/xsa452-4.18-6.patch b/xsa452-4.18-6.patch new file mode 100644 index 0000000..422dc62 --- /dev/null +++ b/xsa452-4.18-6.patch @@ -0,0 +1,163 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: VERW-handling adjustments + +... before we add yet more complexity to this logic. Mostly expanded +comments, but with three minor changes. + +1) Introduce cpu_has_useful_md_clear to simplify later logic in this patch and + future ones. + +2) We only ever need SC_VERW_IDLE when SMT is active. If SMT isn't active, + then there's no re-partition of pipeline resources based on thread-idleness + to worry about. + +3) The logic to adjust HVM VERW based on L1D_FLUSH is unmaintainable and, as + it turns out, wrong. SKIP_L1DFL is just a hint bit, whereas opt_l1d_flush + is the relevant decision of whether to use L1D_FLUSH based on + susceptibility and user preference. + + Rewrite the logic so it can be followed, and incorporate the fact that when + FB_CLEAR is visible, L1D_FLUSH isn't a safe substitution. + +This is part of XSA-452 / CVE-2023-28746. + +Signed-off-by: Andrew Cooper +Acked-by: Jan Beulich +(cherry picked from commit 1eb91a8a06230b4b64228c9a380194f8cfe6c5e2) + +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index c42d8cdc22d6..a4afcd8570e2 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -1519,7 +1519,7 @@ void __init init_speculation_mitigations(void) + { + enum ind_thunk thunk = THUNK_DEFAULT; + bool has_spec_ctrl, ibrs = false, hw_smt_enabled; +- bool cpu_has_bug_taa, retpoline_safe; ++ bool cpu_has_bug_taa, cpu_has_useful_md_clear, retpoline_safe; + + hw_smt_enabled = check_smt_enabled(); + +@@ -1855,50 +1855,97 @@ void __init init_speculation_mitigations(void) + "enabled. Please assess your configuration and choose an\n" + "explicit 'smt=' setting. See XSA-273.\n"); + ++ /* ++ * A brief summary of VERW-related changes. ++ * ++ * https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/technical-documentation/intel-analysis-microarchitectural-data-sampling.html ++ * https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/technical-documentation/processor-mmio-stale-data-vulnerabilities.html ++ * ++ * Relevant ucodes: ++ * ++ * - May 2019, for MDS. Introduces the MD_CLEAR CPUID bit and VERW side ++ * effects to scrub Store/Load/Fill buffers as applicable. MD_CLEAR ++ * exists architecturally, even when the side effects have been removed. ++ * ++ * Use VERW to scrub on return-to-guest. Parts with L1D_FLUSH to ++ * mitigate L1TF have the same side effect, so no need to do both. ++ * ++ * Various Atoms suffer from Store-buffer sampling only. Store buffers ++ * are statically partitioned between non-idle threads, so scrubbing is ++ * wanted when going idle too. ++ * ++ * Load ports and Fill buffers are competitively shared between threads. ++ * SMT must be disabled for VERW scrubbing to be fully effective. ++ * ++ * - November 2019, for TAA. Extended VERW side effects to TSX-enabled ++ * MDS_NO parts. ++ * ++ * - February 2022, for Client TSX de-feature. Removed VERW side effects ++ * from Client CPUs only. ++ * ++ * - May 2022, for MMIO Stale Data. (Re)introduced Fill Buffer scrubbing ++ * on all MMIO-affected parts which didn't already have it for MDS ++ * reasons, enumerating FB_CLEAR on those parts only. ++ * ++ * If FB_CLEAR is enumerated, L1D_FLUSH does not have the same scrubbing ++ * side effects as VERW and cannot be used in its place. ++ */ + mds_calculations(); + + /* +- * Parts which enumerate FB_CLEAR are those which are post-MDS_NO and have +- * reintroduced the VERW fill buffer flushing side effect because of a +- * susceptibility to FBSDP. ++ * Parts which enumerate FB_CLEAR are those with now-updated microcode ++ * which weren't susceptible to the original MFBDS (and therefore didn't ++ * have Fill Buffer scrubbing side effects to begin with, or were Client ++ * MDS_NO non-TAA_NO parts where the scrubbing was removed), but have had ++ * the scrubbing reintroduced because of a susceptibility to FBSDP. + * + * If unprivileged guests have (or will have) MMIO mappings, we can + * mitigate cross-domain leakage of fill buffer data by issuing VERW on +- * the return-to-guest path. ++ * the return-to-guest path. This is only a token effort if SMT is ++ * active. + */ + if ( opt_unpriv_mmio ) + opt_verw_mmio = cpu_has_fb_clear; + + /* +- * By default, enable PV and HVM mitigations on MDS-vulnerable hardware. +- * This will only be a token effort for MLPDS/MFBDS when HT is enabled, +- * but it is somewhat better than nothing. ++ * MD_CLEAR is enumerated architecturally forevermore, even after the ++ * scrubbing side effects have been removed. Create ourselves an version ++ * which expressed whether we think MD_CLEAR is having any useful side ++ * effect. ++ */ ++ cpu_has_useful_md_clear = (cpu_has_md_clear && ++ (cpu_has_bug_mds || cpu_has_bug_msbds_only)); ++ ++ /* ++ * By default, use VERW scrubbing on applicable hardware, if we think it's ++ * going to have an effect. This will only be a token effort for ++ * MLPDS/MFBDS when SMT is enabled. + */ + if ( opt_verw_pv == -1 ) +- opt_verw_pv = ((cpu_has_bug_mds || cpu_has_bug_msbds_only) && +- cpu_has_md_clear); ++ opt_verw_pv = cpu_has_useful_md_clear; + + if ( opt_verw_hvm == -1 ) +- opt_verw_hvm = ((cpu_has_bug_mds || cpu_has_bug_msbds_only) && +- cpu_has_md_clear); ++ opt_verw_hvm = cpu_has_useful_md_clear; + + /* +- * Enable MDS/MMIO defences as applicable. The Idle blocks need using if +- * either the PV or HVM MDS defences are used, or if we may give MMIO +- * access to untrusted guests. +- * +- * HVM is more complicated. The MD_CLEAR microcode extends L1D_FLUSH with +- * equivalent semantics to avoid needing to perform both flushes on the +- * HVM path. Therefore, we don't need VERW in addition to L1D_FLUSH (for +- * MDS mitigations. L1D_FLUSH is not safe for MMIO mitigations.) +- * +- * After calculating the appropriate idle setting, simplify +- * opt_verw_hvm to mean just "should we VERW on the way into HVM +- * guests", so spec_ctrl_init_domain() can calculate suitable settings. ++ * If SMT is active, and we're protecting against MDS or MMIO stale data, ++ * we need to scrub before going idle as well as on return to guest. ++ * Various pipeline resources are repartitioned amongst non-idle threads. + */ +- if ( opt_verw_pv || opt_verw_hvm || opt_verw_mmio ) ++ if ( ((cpu_has_useful_md_clear && (opt_verw_pv || opt_verw_hvm)) || ++ opt_verw_mmio) && hw_smt_enabled ) + setup_force_cpu_cap(X86_FEATURE_SC_VERW_IDLE); +- opt_verw_hvm &= !cpu_has_skip_l1dfl && !opt_l1d_flush; ++ ++ /* ++ * After calculating the appropriate idle setting, simplify opt_verw_hvm ++ * to mean just "should we VERW on the way into HVM guests", so ++ * spec_ctrl_init_domain() can calculate suitable settings. ++ * ++ * It is only safe to use L1D_FLUSH in place of VERW when MD_CLEAR is the ++ * only *_CLEAR we can see. ++ */ ++ if ( opt_l1d_flush && cpu_has_md_clear && !cpu_has_fb_clear ) ++ opt_verw_hvm = false; + + /* + * Warn the user if they are on MLPDS/MFBDS-vulnerable hardware with HT diff --git a/xsa452-4.18-7.patch b/xsa452-4.18-7.patch new file mode 100644 index 0000000..a252db0 --- /dev/null +++ b/xsa452-4.18-7.patch @@ -0,0 +1,307 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Mitigation Register File Data Sampling + +RFDS affects Atom cores, also branded E-cores, between the Goldmont and +Gracemont microarchitectures. This includes Alder Lake and Raptor Lake hybrid +clien systems which have a mix of Gracemont and other types of cores. + +Two new bits have been defined; RFDS_CLEAR to indicate VERW has more side +effets, and RFDS_NO to incidate that the system is unaffected. Plenty of +unaffected CPUs won't be getting RFDS_NO retrofitted in microcode, so we +synthesise it. Alder Lake and Raptor Lake Xeon-E's are unaffected due to +their platform configuration, and we must use the Hybrid CPUID bit to +distinguish them from their non-Xeon counterparts. + +Like MD_CLEAR and FB_CLEAR, RFDS_CLEAR needs OR-ing across a resource pool, so +set it in the max policies and reflect the host setting in default. + +This is part of XSA-452 / CVE-2023-28746. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit fb5b6f6744713410c74cfc12b7176c108e3c9a31) + +diff --git a/tools/misc/xen-cpuid.c b/tools/misc/xen-cpuid.c +index 7370f1b56ef9..52e451a806c1 100644 +--- a/tools/misc/xen-cpuid.c ++++ b/tools/misc/xen-cpuid.c +@@ -172,7 +172,7 @@ static const char *const str_7d0[32] = + [ 8] = "avx512-vp2intersect", [ 9] = "srbds-ctrl", + [10] = "md-clear", [11] = "rtm-always-abort", + /* 12 */ [13] = "tsx-force-abort", +- [14] = "serialize", ++ [14] = "serialize", [15] = "hybrid", + [16] = "tsxldtrk", + [18] = "pconfig", + [20] = "cet-ibt", +@@ -245,7 +245,8 @@ static const char *const str_m10Al[32] = + [20] = "bhi-no", [21] = "xapic-status", + /* 22 */ [23] = "ovrclk-status", + [24] = "pbrsb-no", [25] = "gds-ctrl", +- [26] = "gds-no", ++ [26] = "gds-no", [27] = "rfds-no", ++ [28] = "rfds-clear", + }; + + static const char *const str_m10Ah[32] = +diff --git a/xen/arch/x86/cpu-policy.c b/xen/arch/x86/cpu-policy.c +index c7c5e99b7b4c..12e621b97de6 100644 +--- a/xen/arch/x86/cpu-policy.c ++++ b/xen/arch/x86/cpu-policy.c +@@ -451,6 +451,7 @@ static void __init guest_common_max_feature_adjustments(uint32_t *fs) + */ + __set_bit(X86_FEATURE_MD_CLEAR, fs); + __set_bit(X86_FEATURE_FB_CLEAR, fs); ++ __set_bit(X86_FEATURE_RFDS_CLEAR, fs); + + /* + * The Gather Data Sampling microcode mitigation (August 2023) has an +@@ -500,6 +501,10 @@ static void __init guest_common_default_feature_adjustments(uint32_t *fs) + if ( cpu_has_fb_clear ) + __set_bit(X86_FEATURE_FB_CLEAR, fs); + ++ __clear_bit(X86_FEATURE_RFDS_CLEAR, fs); ++ if ( cpu_has_rfds_clear ) ++ __set_bit(X86_FEATURE_RFDS_CLEAR, fs); ++ + /* + * The Gather Data Sampling microcode mitigation (August 2023) has an + * adverse performance impact on the CLWB instruction on SKX/CLX/CPX. +diff --git a/xen/arch/x86/include/asm/cpufeature.h b/xen/arch/x86/include/asm/cpufeature.h +index 76ef2aeb1de6..3c57f55de075 100644 +--- a/xen/arch/x86/include/asm/cpufeature.h ++++ b/xen/arch/x86/include/asm/cpufeature.h +@@ -181,6 +181,7 @@ static inline bool boot_cpu_has(unsigned int feat) + #define cpu_has_rtm_always_abort boot_cpu_has(X86_FEATURE_RTM_ALWAYS_ABORT) + #define cpu_has_tsx_force_abort boot_cpu_has(X86_FEATURE_TSX_FORCE_ABORT) + #define cpu_has_serialize boot_cpu_has(X86_FEATURE_SERIALIZE) ++#define cpu_has_hybrid boot_cpu_has(X86_FEATURE_HYBRID) + #define cpu_has_avx512_fp16 boot_cpu_has(X86_FEATURE_AVX512_FP16) + #define cpu_has_arch_caps boot_cpu_has(X86_FEATURE_ARCH_CAPS) + +@@ -208,6 +209,8 @@ static inline bool boot_cpu_has(unsigned int feat) + #define cpu_has_rrsba boot_cpu_has(X86_FEATURE_RRSBA) + #define cpu_has_gds_ctrl boot_cpu_has(X86_FEATURE_GDS_CTRL) + #define cpu_has_gds_no boot_cpu_has(X86_FEATURE_GDS_NO) ++#define cpu_has_rfds_no boot_cpu_has(X86_FEATURE_RFDS_NO) ++#define cpu_has_rfds_clear boot_cpu_has(X86_FEATURE_RFDS_CLEAR) + + /* Synthesized. */ + #define cpu_has_arch_perfmon boot_cpu_has(X86_FEATURE_ARCH_PERFMON) +diff --git a/xen/arch/x86/include/asm/msr-index.h b/xen/arch/x86/include/asm/msr-index.h +index 82a81bd0a232..85ef28a612e0 100644 +--- a/xen/arch/x86/include/asm/msr-index.h ++++ b/xen/arch/x86/include/asm/msr-index.h +@@ -89,6 +89,8 @@ + #define ARCH_CAPS_PBRSB_NO (_AC(1, ULL) << 24) + #define ARCH_CAPS_GDS_CTRL (_AC(1, ULL) << 25) + #define ARCH_CAPS_GDS_NO (_AC(1, ULL) << 26) ++#define ARCH_CAPS_RFDS_NO (_AC(1, ULL) << 27) ++#define ARCH_CAPS_RFDS_CLEAR (_AC(1, ULL) << 28) + + #define MSR_FLUSH_CMD 0x0000010b + #define FLUSH_CMD_L1D (_AC(1, ULL) << 0) +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index a4afcd8570e2..8165379fed94 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -12,6 +12,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -435,7 +436,7 @@ static void __init print_details(enum ind_thunk thunk) + * Hardware read-only information, stating immunity to certain issues, or + * suggestions of which mitigation to use. + */ +- printk(" Hardware hints:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", ++ printk(" Hardware hints:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", + (caps & ARCH_CAPS_RDCL_NO) ? " RDCL_NO" : "", + (caps & ARCH_CAPS_EIBRS) ? " EIBRS" : "", + (caps & ARCH_CAPS_RSBA) ? " RSBA" : "", +@@ -451,6 +452,7 @@ static void __init print_details(enum ind_thunk thunk) + (caps & ARCH_CAPS_FB_CLEAR) ? " FB_CLEAR" : "", + (caps & ARCH_CAPS_PBRSB_NO) ? " PBRSB_NO" : "", + (caps & ARCH_CAPS_GDS_NO) ? " GDS_NO" : "", ++ (caps & ARCH_CAPS_RFDS_NO) ? " RFDS_NO" : "", + (e8b & cpufeat_mask(X86_FEATURE_IBRS_ALWAYS)) ? " IBRS_ALWAYS" : "", + (e8b & cpufeat_mask(X86_FEATURE_STIBP_ALWAYS)) ? " STIBP_ALWAYS" : "", + (e8b & cpufeat_mask(X86_FEATURE_IBRS_FAST)) ? " IBRS_FAST" : "", +@@ -461,7 +463,7 @@ static void __init print_details(enum ind_thunk thunk) + (e21a & cpufeat_mask(X86_FEATURE_SRSO_NO)) ? " SRSO_NO" : ""); + + /* Hardware features which need driving to mitigate issues. */ +- printk(" Hardware features:%s%s%s%s%s%s%s%s%s%s%s%s%s\n", ++ printk(" Hardware features:%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", + (e8b & cpufeat_mask(X86_FEATURE_IBPB)) || + (_7d0 & cpufeat_mask(X86_FEATURE_IBRSB)) ? " IBPB" : "", + (e8b & cpufeat_mask(X86_FEATURE_IBRS)) || +@@ -479,6 +481,7 @@ static void __init print_details(enum ind_thunk thunk) + (caps & ARCH_CAPS_TSX_CTRL) ? " TSX_CTRL" : "", + (caps & ARCH_CAPS_FB_CLEAR_CTRL) ? " FB_CLEAR_CTRL" : "", + (caps & ARCH_CAPS_GDS_CTRL) ? " GDS_CTRL" : "", ++ (caps & ARCH_CAPS_RFDS_CLEAR) ? " RFDS_CLEAR" : "", + (e21a & cpufeat_mask(X86_FEATURE_SBPB)) ? " SBPB" : ""); + + /* Compiled-in support which pertains to mitigations. */ +@@ -1347,6 +1350,83 @@ static __init void mds_calculations(void) + } + } + ++/* ++ * Register File Data Sampling affects Atom cores from the Goldmont to ++ * Gracemont microarchitectures. The March 2024 microcode adds RFDS_NO to ++ * some but not all unaffected parts, and RFDS_CLEAR to affected parts still ++ * in support. ++ * ++ * Alder Lake and Raptor Lake client CPUs have a mix of P cores ++ * (Golden/Raptor Cove, not vulnerable) and E cores (Gracemont, ++ * vulnerable), and both enumerate RFDS_CLEAR. ++ * ++ * Both exist in a Xeon SKU, which has the E cores (Gracemont) disabled by ++ * platform configuration, and enumerate RFDS_NO. ++ * ++ * With older parts, or with out-of-date microcode, synthesise RFDS_NO when ++ * safe to do so. ++ * ++ * https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/advisory-guidance/register-file-data-sampling.html ++ */ ++static void __init rfds_calculations(void) ++{ ++ /* RFDS is only known to affect Intel Family 6 processors at this time. */ ++ if ( boot_cpu_data.x86_vendor != X86_VENDOR_INTEL || ++ boot_cpu_data.x86 != 6 ) ++ return; ++ ++ /* ++ * If RFDS_NO or RFDS_CLEAR are visible, we've either got suitable ++ * microcode, or an RFDS-aware hypervisor is levelling us in a pool. ++ */ ++ if ( cpu_has_rfds_no || cpu_has_rfds_clear ) ++ return; ++ ++ /* If we're virtualised, don't attempt to synthesise RFDS_NO. */ ++ if ( cpu_has_hypervisor ) ++ return; ++ ++ /* ++ * Not all CPUs are expected to get a microcode update enumerating one of ++ * RFDS_{NO,CLEAR}, or we might have out-of-date microcode. ++ */ ++ switch ( boot_cpu_data.x86_model ) ++ { ++ case INTEL_FAM6_ALDERLAKE: ++ case INTEL_FAM6_RAPTORLAKE: ++ /* ++ * Alder Lake and Raptor Lake might be a client SKU (with the ++ * Gracemont cores active, and therefore vulnerable) or might be a ++ * server SKU (with the Gracemont cores disabled, and therefore not ++ * vulnerable). ++ * ++ * See if the CPU identifies as hybrid to distinguish the two cases. ++ */ ++ if ( !cpu_has_hybrid ) ++ break; ++ fallthrough; ++ case INTEL_FAM6_ALDERLAKE_L: ++ case INTEL_FAM6_RAPTORLAKE_P: ++ case INTEL_FAM6_RAPTORLAKE_S: ++ ++ case INTEL_FAM6_ATOM_GOLDMONT: /* Apollo Lake */ ++ case INTEL_FAM6_ATOM_GOLDMONT_D: /* Denverton */ ++ case INTEL_FAM6_ATOM_GOLDMONT_PLUS: /* Gemini Lake */ ++ case INTEL_FAM6_ATOM_TREMONT_D: /* Snow Ridge / Parker Ridge */ ++ case INTEL_FAM6_ATOM_TREMONT: /* Elkhart Lake */ ++ case INTEL_FAM6_ATOM_TREMONT_L: /* Jasper Lake */ ++ case INTEL_FAM6_ATOM_GRACEMONT: /* Alder Lake N */ ++ return; ++ } ++ ++ /* ++ * We appear to be on an unaffected CPU which didn't enumerate RFDS_NO, ++ * perhaps because of it's age or because of out-of-date microcode. ++ * Synthesise it. ++ */ ++ setup_force_cpu_cap(X86_FEATURE_RFDS_NO); ++} ++ + static bool __init cpu_has_gds(void) + { + /* +@@ -1860,6 +1940,7 @@ void __init init_speculation_mitigations(void) + * + * https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/technical-documentation/intel-analysis-microarchitectural-data-sampling.html + * https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/technical-documentation/processor-mmio-stale-data-vulnerabilities.html ++ * https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/advisory-guidance/register-file-data-sampling.html + * + * Relevant ucodes: + * +@@ -1889,8 +1970,12 @@ void __init init_speculation_mitigations(void) + * + * If FB_CLEAR is enumerated, L1D_FLUSH does not have the same scrubbing + * side effects as VERW and cannot be used in its place. ++ * ++ * - March 2023, for RFDS. Enumerate RFDS_CLEAR to mean that VERW now ++ * scrubs non-architectural entries from certain register files. + */ + mds_calculations(); ++ rfds_calculations(); + + /* + * Parts which enumerate FB_CLEAR are those with now-updated microcode +@@ -1922,15 +2007,19 @@ void __init init_speculation_mitigations(void) + * MLPDS/MFBDS when SMT is enabled. + */ + if ( opt_verw_pv == -1 ) +- opt_verw_pv = cpu_has_useful_md_clear; ++ opt_verw_pv = cpu_has_useful_md_clear || cpu_has_rfds_clear; + + if ( opt_verw_hvm == -1 ) +- opt_verw_hvm = cpu_has_useful_md_clear; ++ opt_verw_hvm = cpu_has_useful_md_clear || cpu_has_rfds_clear; + + /* + * If SMT is active, and we're protecting against MDS or MMIO stale data, + * we need to scrub before going idle as well as on return to guest. + * Various pipeline resources are repartitioned amongst non-idle threads. ++ * ++ * We don't need to scrub on idle for RFDS. There are no affected cores ++ * which support SMT, despite there being affected cores in hybrid systems ++ * which have SMT elsewhere in the platform. + */ + if ( ((cpu_has_useful_md_clear && (opt_verw_pv || opt_verw_hvm)) || + opt_verw_mmio) && hw_smt_enabled ) +@@ -1944,7 +2033,8 @@ void __init init_speculation_mitigations(void) + * It is only safe to use L1D_FLUSH in place of VERW when MD_CLEAR is the + * only *_CLEAR we can see. + */ +- if ( opt_l1d_flush && cpu_has_md_clear && !cpu_has_fb_clear ) ++ if ( opt_l1d_flush && cpu_has_md_clear && !cpu_has_fb_clear && ++ !cpu_has_rfds_clear ) + opt_verw_hvm = false; + + /* +diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h +index 337aaa9c770b..8e17ef670fff 100644 +--- a/xen/include/public/arch-x86/cpufeatureset.h ++++ b/xen/include/public/arch-x86/cpufeatureset.h +@@ -266,6 +266,7 @@ XEN_CPUFEATURE(MD_CLEAR, 9*32+10) /*!A VERW clears microarchitectural buffe + XEN_CPUFEATURE(RTM_ALWAYS_ABORT, 9*32+11) /*! June 2021 TSX defeaturing in microcode. */ + XEN_CPUFEATURE(TSX_FORCE_ABORT, 9*32+13) /* MSR_TSX_FORCE_ABORT.RTM_ABORT */ + XEN_CPUFEATURE(SERIALIZE, 9*32+14) /*A SERIALIZE insn */ ++XEN_CPUFEATURE(HYBRID, 9*32+15) /* Heterogeneous platform */ + XEN_CPUFEATURE(TSXLDTRK, 9*32+16) /*a TSX load tracking suspend/resume insns */ + XEN_CPUFEATURE(CET_IBT, 9*32+20) /* CET - Indirect Branch Tracking */ + XEN_CPUFEATURE(AVX512_FP16, 9*32+23) /*A AVX512 FP16 instructions */ +@@ -338,6 +339,8 @@ XEN_CPUFEATURE(OVRCLK_STATUS, 16*32+23) /* MSR_OVERCLOCKING_STATUS */ + XEN_CPUFEATURE(PBRSB_NO, 16*32+24) /*A No Post-Barrier RSB predictions */ + XEN_CPUFEATURE(GDS_CTRL, 16*32+25) /* MCU_OPT_CTRL.GDS_MIT_{DIS,LOCK} */ + XEN_CPUFEATURE(GDS_NO, 16*32+26) /*A No Gather Data Sampling */ ++XEN_CPUFEATURE(RFDS_NO, 16*32+27) /*A No Register File Data Sampling */ ++XEN_CPUFEATURE(RFDS_CLEAR, 16*32+28) /*!A Register File(s) cleared by VERW */ + + /* Intel-defined CPU features, MSR_ARCH_CAPS 0x10a.edx, word 17 */ + diff --git a/xsa453-4.18-1.patch b/xsa453-4.18-1.patch new file mode 100644 index 0000000..a3c0f05 --- /dev/null +++ b/xsa453-4.18-1.patch @@ -0,0 +1,50 @@ +From: Andrew Cooper +Subject: xen: Swap order of actions in the FREE*() macros + +Wherever possible, it is a good idea to NULL out the visible reference to an +object prior to freeing it. The FREE*() macros already collect together both +parts, making it easy to adjust. + +This has a marginal code generation improvement, as some of the calls to the +free() function can be tailcall optimised. + +No functional change. + +Signed-off-by: Andrew Cooper +Acked-by: Jan Beulich +(cherry picked from commit c4f427ec879e7c0df6d44d02561e8bee838a293e) + +diff --git a/xen/include/xen/mm.h b/xen/include/xen/mm.h +index 8b9618609f77..8bc5f4249d1b 100644 +--- a/xen/include/xen/mm.h ++++ b/xen/include/xen/mm.h +@@ -91,8 +91,9 @@ bool scrub_free_pages(void); + + /* Free an allocation, and zero the pointer to it. */ + #define FREE_XENHEAP_PAGES(p, o) do { \ +- free_xenheap_pages(p, o); \ ++ void *_ptr_ = (p); \ + (p) = NULL; \ ++ free_xenheap_pages(_ptr_, o); \ + } while ( false ) + #define FREE_XENHEAP_PAGE(p) FREE_XENHEAP_PAGES(p, 0) + +diff --git a/xen/include/xen/xmalloc.h b/xen/include/xen/xmalloc.h +index 16979a117c6a..d857298011c1 100644 +--- a/xen/include/xen/xmalloc.h ++++ b/xen/include/xen/xmalloc.h +@@ -66,9 +66,10 @@ + extern void xfree(void *); + + /* Free an allocation, and zero the pointer to it. */ +-#define XFREE(p) do { \ +- xfree(p); \ +- (p) = NULL; \ ++#define XFREE(p) do { \ ++ void *_ptr_ = (p); \ ++ (p) = NULL; \ ++ xfree(_ptr_); \ + } while ( false ) + + /* Underlying functions */ + diff --git a/xsa453-4.18-2.patch b/xsa453-4.18-2.patch new file mode 100644 index 0000000..01a7e05 --- /dev/null +++ b/xsa453-4.18-2.patch @@ -0,0 +1,314 @@ +From: =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= +Subject: x86/spinlock: introduce support for blocking speculation into + critical regions + +Introduce a new Kconfig option to block speculation into lock protected +critical regions. The Kconfig option is enabled by default, but the mitigation +won't be engaged unless it's explicitly enabled in the command line using +`spec-ctrl=lock-harden`. + +Convert the spinlock acquire macros into always-inline functions, and introduce +a speculation barrier after the lock has been taken. Note the speculation +barrier is not placed inside the implementation of the spin lock functions, as +to prevent speculation from falling through the call to the lock functions +resulting in the barrier also being skipped. + +trylock variants are protected using a construct akin to the existing +evaluate_nospec(). + +This patch only implements the speculation barrier for x86. + +Note spin locks are the only locking primitive taken care in this change, +further locking primitives will be adjusted by separate changes. + +This is part of XSA-453 / CVE-2024-2193 + +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +(cherry picked from commit 7ef0084418e188d05f338c3e028fbbe8b6924afa) + +diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc +index fbf16839249a..3f9f9167182f 100644 +--- a/docs/misc/xen-command-line.pandoc ++++ b/docs/misc/xen-command-line.pandoc +@@ -2373,7 +2373,7 @@ By default SSBD will be mitigated at runtime (i.e `ssbd=runtime`). + > {msr-sc,rsb,verw,ibpb-entry}=|{pv,hvm}=, + > bti-thunk=retpoline|lfence|jmp, {ibrs,ibpb,ssbd,psfd, + > eager-fpu,l1d-flush,branch-harden,srb-lock, +-> unpriv-mmio,gds-mit,div-scrub}= ]` ++> unpriv-mmio,gds-mit,div-scrub,lock-harden}= ]` + + Controls for speculative execution sidechannel mitigations. By default, Xen + will pick the most appropriate mitigations based on compiled in support, +@@ -2500,6 +2500,11 @@ On all hardware, the `div-scrub=` option can be used to force or prevent Xen + from mitigating the DIV-leakage vulnerability. By default, Xen will mitigate + DIV-leakage on hardware believed to be vulnerable. + ++If Xen is compiled with `CONFIG_SPECULATIVE_HARDEN_LOCK`, the `lock-harden=` ++boolean can be used to force or prevent Xen from using speculation barriers to ++protect lock critical regions. This mitigation won't be engaged by default, ++and needs to be explicitly enabled on the command line. ++ + ### sync_console + > `= ` + +diff --git a/xen/arch/x86/include/asm/cpufeatures.h b/xen/arch/x86/include/asm/cpufeatures.h +index c3aad21c3b43..7e8221fd85dd 100644 +--- a/xen/arch/x86/include/asm/cpufeatures.h ++++ b/xen/arch/x86/include/asm/cpufeatures.h +@@ -24,7 +24,7 @@ XEN_CPUFEATURE(APERFMPERF, X86_SYNTH( 8)) /* APERFMPERF */ + XEN_CPUFEATURE(MFENCE_RDTSC, X86_SYNTH( 9)) /* MFENCE synchronizes RDTSC */ + XEN_CPUFEATURE(XEN_SMEP, X86_SYNTH(10)) /* SMEP gets used by Xen itself */ + XEN_CPUFEATURE(XEN_SMAP, X86_SYNTH(11)) /* SMAP gets used by Xen itself */ +-/* Bit 12 unused. */ ++XEN_CPUFEATURE(SC_NO_LOCK_HARDEN, X86_SYNTH(12)) /* (Disable) Lock critical region hardening */ + XEN_CPUFEATURE(IND_THUNK_LFENCE, X86_SYNTH(13)) /* Use IND_THUNK_LFENCE */ + XEN_CPUFEATURE(IND_THUNK_JMP, X86_SYNTH(14)) /* Use IND_THUNK_JMP */ + XEN_CPUFEATURE(SC_NO_BRANCH_HARDEN, X86_SYNTH(15)) /* (Disable) Conditional branch hardening */ +diff --git a/xen/arch/x86/include/asm/nospec.h b/xen/arch/x86/include/asm/nospec.h +index 7150e76b87fb..0725839e1982 100644 +--- a/xen/arch/x86/include/asm/nospec.h ++++ b/xen/arch/x86/include/asm/nospec.h +@@ -38,6 +38,32 @@ static always_inline void block_speculation(void) + barrier_nospec_true(); + } + ++static always_inline void arch_block_lock_speculation(void) ++{ ++ alternative("lfence", "", X86_FEATURE_SC_NO_LOCK_HARDEN); ++} ++ ++/* Allow to insert a read memory barrier into conditionals */ ++static always_inline bool barrier_lock_true(void) ++{ ++ alternative("lfence #nospec-true", "", X86_FEATURE_SC_NO_LOCK_HARDEN); ++ return true; ++} ++ ++static always_inline bool barrier_lock_false(void) ++{ ++ alternative("lfence #nospec-false", "", X86_FEATURE_SC_NO_LOCK_HARDEN); ++ return false; ++} ++ ++static always_inline bool arch_lock_evaluate_nospec(bool condition) ++{ ++ if ( condition ) ++ return barrier_lock_true(); ++ else ++ return barrier_lock_false(); ++} ++ + #endif /* _ASM_X86_NOSPEC_H */ + + /* +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 8165379fed94..5dfc4ed69ec5 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -53,6 +53,7 @@ int8_t __read_mostly opt_eager_fpu = -1; + int8_t __read_mostly opt_l1d_flush = -1; + static bool __initdata opt_branch_harden = + IS_ENABLED(CONFIG_SPECULATIVE_HARDEN_BRANCH); ++static bool __initdata opt_lock_harden; + + bool __initdata bsp_delay_spec_ctrl; + uint8_t __read_mostly default_xen_spec_ctrl; +@@ -121,6 +122,7 @@ static int __init cf_check parse_spec_ctrl(const char *s) + opt_ssbd = false; + opt_l1d_flush = 0; + opt_branch_harden = false; ++ opt_lock_harden = false; + opt_srb_lock = 0; + opt_unpriv_mmio = false; + opt_gds_mit = 0; +@@ -286,6 +288,16 @@ static int __init cf_check parse_spec_ctrl(const char *s) + rc = -EINVAL; + } + } ++ else if ( (val = parse_boolean("lock-harden", s, ss)) >= 0 ) ++ { ++ if ( IS_ENABLED(CONFIG_SPECULATIVE_HARDEN_LOCK) ) ++ opt_lock_harden = val; ++ else ++ { ++ no_config_param("SPECULATIVE_HARDEN_LOCK", "spec-ctrl", s, ss); ++ rc = -EINVAL; ++ } ++ } + else if ( (val = parse_boolean("srb-lock", s, ss)) >= 0 ) + opt_srb_lock = val; + else if ( (val = parse_boolean("unpriv-mmio", s, ss)) >= 0 ) +@@ -488,7 +500,8 @@ static void __init print_details(enum ind_thunk thunk) + if ( IS_ENABLED(CONFIG_INDIRECT_THUNK) || IS_ENABLED(CONFIG_SHADOW_PAGING) || + IS_ENABLED(CONFIG_SPECULATIVE_HARDEN_ARRAY) || + IS_ENABLED(CONFIG_SPECULATIVE_HARDEN_BRANCH) || +- IS_ENABLED(CONFIG_SPECULATIVE_HARDEN_GUEST_ACCESS) ) ++ IS_ENABLED(CONFIG_SPECULATIVE_HARDEN_GUEST_ACCESS) || ++ IS_ENABLED(CONFIG_SPECULATIVE_HARDEN_LOCK) ) + printk(" Compiled-in support:" + #ifdef CONFIG_INDIRECT_THUNK + " INDIRECT_THUNK" +@@ -504,11 +517,14 @@ static void __init print_details(enum ind_thunk thunk) + #endif + #ifdef CONFIG_SPECULATIVE_HARDEN_GUEST_ACCESS + " HARDEN_GUEST_ACCESS" ++#endif ++#ifdef CONFIG_SPECULATIVE_HARDEN_LOCK ++ " HARDEN_LOCK" + #endif + "\n"); + + /* Settings for Xen's protection, irrespective of guests. */ +- printk(" Xen settings: %s%sSPEC_CTRL: %s%s%s%s%s, Other:%s%s%s%s%s%s\n", ++ printk(" Xen settings: %s%sSPEC_CTRL: %s%s%s%s%s, Other:%s%s%s%s%s%s%s\n", + thunk != THUNK_NONE ? "BTI-Thunk: " : "", + thunk == THUNK_NONE ? "" : + thunk == THUNK_RETPOLINE ? "RETPOLINE, " : +@@ -535,7 +551,8 @@ static void __init print_details(enum ind_thunk thunk) + opt_verw_pv || opt_verw_hvm || + opt_verw_mmio ? " VERW" : "", + opt_div_scrub ? " DIV" : "", +- opt_branch_harden ? " BRANCH_HARDEN" : ""); ++ opt_branch_harden ? " BRANCH_HARDEN" : "", ++ opt_lock_harden ? " LOCK_HARDEN" : ""); + + /* L1TF diagnostics, printed if vulnerable or PV shadowing is in use. */ + if ( cpu_has_bug_l1tf || opt_pv_l1tf_hwdom || opt_pv_l1tf_domu ) +@@ -1918,6 +1935,9 @@ void __init init_speculation_mitigations(void) + if ( !opt_branch_harden ) + setup_force_cpu_cap(X86_FEATURE_SC_NO_BRANCH_HARDEN); + ++ if ( !opt_lock_harden ) ++ setup_force_cpu_cap(X86_FEATURE_SC_NO_LOCK_HARDEN); ++ + /* + * We do not disable HT by default on affected hardware. + * +diff --git a/xen/common/Kconfig b/xen/common/Kconfig +index 4d6fe051641d..3361a6d89257 100644 +--- a/xen/common/Kconfig ++++ b/xen/common/Kconfig +@@ -188,6 +188,23 @@ config SPECULATIVE_HARDEN_GUEST_ACCESS + + If unsure, say Y. + ++config SPECULATIVE_HARDEN_LOCK ++ bool "Speculative lock context hardening" ++ default y ++ depends on X86 ++ help ++ Contemporary processors may use speculative execution as a ++ performance optimisation, but this can potentially be abused by an ++ attacker to leak data via speculative sidechannels. ++ ++ One source of data leakage is via speculative accesses to lock ++ critical regions. ++ ++ This option is disabled by default at run time, and needs to be ++ enabled on the command line. ++ ++ If unsure, say Y. ++ + endmenu + + config DIT_DEFAULT +diff --git a/xen/include/xen/nospec.h b/xen/include/xen/nospec.h +index 76255bc46efe..455284640396 100644 +--- a/xen/include/xen/nospec.h ++++ b/xen/include/xen/nospec.h +@@ -70,6 +70,21 @@ static inline unsigned long array_index_mask_nospec(unsigned long index, + #define array_access_nospec(array, index) \ + (array)[array_index_nospec(index, ARRAY_SIZE(array))] + ++static always_inline void block_lock_speculation(void) ++{ ++#ifdef CONFIG_SPECULATIVE_HARDEN_LOCK ++ arch_block_lock_speculation(); ++#endif ++} ++ ++static always_inline bool lock_evaluate_nospec(bool condition) ++{ ++#ifdef CONFIG_SPECULATIVE_HARDEN_LOCK ++ return arch_lock_evaluate_nospec(condition); ++#endif ++ return condition; ++} ++ + #endif /* XEN_NOSPEC_H */ + + /* +diff --git a/xen/include/xen/spinlock.h b/xen/include/xen/spinlock.h +index e7a1c1aa8988..28fce5615e5c 100644 +--- a/xen/include/xen/spinlock.h ++++ b/xen/include/xen/spinlock.h +@@ -1,6 +1,7 @@ + #ifndef __SPINLOCK_H__ + #define __SPINLOCK_H__ + ++#include + #include + #include + +@@ -195,13 +196,30 @@ int _spin_trylock_recursive(spinlock_t *lock); + void _spin_lock_recursive(spinlock_t *lock); + void _spin_unlock_recursive(spinlock_t *lock); + +-#define spin_lock(l) _spin_lock(l) +-#define spin_lock_cb(l, c, d) _spin_lock_cb(l, c, d) +-#define spin_lock_irq(l) _spin_lock_irq(l) ++static always_inline void spin_lock(spinlock_t *l) ++{ ++ _spin_lock(l); ++ block_lock_speculation(); ++} ++ ++static always_inline void spin_lock_cb(spinlock_t *l, void (*c)(void *data), ++ void *d) ++{ ++ _spin_lock_cb(l, c, d); ++ block_lock_speculation(); ++} ++ ++static always_inline void spin_lock_irq(spinlock_t *l) ++{ ++ _spin_lock_irq(l); ++ block_lock_speculation(); ++} ++ + #define spin_lock_irqsave(l, f) \ + ({ \ + BUILD_BUG_ON(sizeof(f) != sizeof(unsigned long)); \ + ((f) = _spin_lock_irqsave(l)); \ ++ block_lock_speculation(); \ + }) + + #define spin_unlock(l) _spin_unlock(l) +@@ -209,7 +227,7 @@ void _spin_unlock_recursive(spinlock_t *lock); + #define spin_unlock_irqrestore(l, f) _spin_unlock_irqrestore(l, f) + + #define spin_is_locked(l) _spin_is_locked(l) +-#define spin_trylock(l) _spin_trylock(l) ++#define spin_trylock(l) lock_evaluate_nospec(_spin_trylock(l)) + + #define spin_trylock_irqsave(lock, flags) \ + ({ \ +@@ -230,8 +248,15 @@ void _spin_unlock_recursive(spinlock_t *lock); + * are any critical regions that cannot form part of such a set, they can use + * standard spin_[un]lock(). + */ +-#define spin_trylock_recursive(l) _spin_trylock_recursive(l) +-#define spin_lock_recursive(l) _spin_lock_recursive(l) ++#define spin_trylock_recursive(l) \ ++ lock_evaluate_nospec(_spin_trylock_recursive(l)) ++ ++static always_inline void spin_lock_recursive(spinlock_t *l) ++{ ++ _spin_lock_recursive(l); ++ block_lock_speculation(); ++} ++ + #define spin_unlock_recursive(l) _spin_unlock_recursive(l) + + #endif /* __SPINLOCK_H__ */ diff --git a/xsa453-4.18-3.patch b/xsa453-4.18-3.patch new file mode 100644 index 0000000..ecaff41 --- /dev/null +++ b/xsa453-4.18-3.patch @@ -0,0 +1,113 @@ +From: =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= +Subject: rwlock: introduce support for blocking speculation into critical + regions + +Introduce inline wrappers as required and add direct calls to +block_lock_speculation() in order to prevent speculation into the rwlock +protected critical regions. + +Note the rwlock primitives are adjusted to use the non speculation safe variants +of the spinlock handlers, as a speculation barrier is added in the rwlock +calling wrappers. + +trylock variants are protected by using lock_evaluate_nospec(). + +This is part of XSA-453 / CVE-2024-2193 + +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +(cherry picked from commit a1fb15f61692b1fa9945fc51f55471ace49cdd59) + +diff --git a/xen/common/rwlock.c b/xen/common/rwlock.c +index 18224a4bb5d6..290602936df6 100644 +--- a/xen/common/rwlock.c ++++ b/xen/common/rwlock.c +@@ -34,8 +34,11 @@ void queue_read_lock_slowpath(rwlock_t *lock) + + /* + * Put the reader into the wait queue. ++ * ++ * Use the speculation unsafe helper, as it's the caller responsibility to ++ * issue a speculation barrier if required. + */ +- spin_lock(&lock->lock); ++ _spin_lock(&lock->lock); + + /* + * At the head of the wait queue now, wait until the writer state +@@ -66,8 +69,13 @@ void queue_write_lock_slowpath(rwlock_t *lock) + { + u32 cnts; + +- /* Put the writer into the wait queue. */ +- spin_lock(&lock->lock); ++ /* ++ * Put the writer into the wait queue. ++ * ++ * Use the speculation unsafe helper, as it's the caller responsibility to ++ * issue a speculation barrier if required. ++ */ ++ _spin_lock(&lock->lock); + + /* Try to acquire the lock directly if no reader is present. */ + if ( !atomic_read(&lock->cnts) && +diff --git a/xen/include/xen/rwlock.h b/xen/include/xen/rwlock.h +index e0d2b41c5c7e..9a0d3ec23847 100644 +--- a/xen/include/xen/rwlock.h ++++ b/xen/include/xen/rwlock.h +@@ -259,27 +259,49 @@ static inline int _rw_is_write_locked(const rwlock_t *lock) + return (atomic_read(&lock->cnts) & _QW_WMASK) == _QW_LOCKED; + } + +-#define read_lock(l) _read_lock(l) +-#define read_lock_irq(l) _read_lock_irq(l) ++static always_inline void read_lock(rwlock_t *l) ++{ ++ _read_lock(l); ++ block_lock_speculation(); ++} ++ ++static always_inline void read_lock_irq(rwlock_t *l) ++{ ++ _read_lock_irq(l); ++ block_lock_speculation(); ++} ++ + #define read_lock_irqsave(l, f) \ + ({ \ + BUILD_BUG_ON(sizeof(f) != sizeof(unsigned long)); \ + ((f) = _read_lock_irqsave(l)); \ ++ block_lock_speculation(); \ + }) + + #define read_unlock(l) _read_unlock(l) + #define read_unlock_irq(l) _read_unlock_irq(l) + #define read_unlock_irqrestore(l, f) _read_unlock_irqrestore(l, f) +-#define read_trylock(l) _read_trylock(l) ++#define read_trylock(l) lock_evaluate_nospec(_read_trylock(l)) ++ ++static always_inline void write_lock(rwlock_t *l) ++{ ++ _write_lock(l); ++ block_lock_speculation(); ++} ++ ++static always_inline void write_lock_irq(rwlock_t *l) ++{ ++ _write_lock_irq(l); ++ block_lock_speculation(); ++} + +-#define write_lock(l) _write_lock(l) +-#define write_lock_irq(l) _write_lock_irq(l) + #define write_lock_irqsave(l, f) \ + ({ \ + BUILD_BUG_ON(sizeof(f) != sizeof(unsigned long)); \ + ((f) = _write_lock_irqsave(l)); \ ++ block_lock_speculation(); \ + }) +-#define write_trylock(l) _write_trylock(l) ++#define write_trylock(l) lock_evaluate_nospec(_write_trylock(l)) + + #define write_unlock(l) _write_unlock(l) + #define write_unlock_irq(l) _write_unlock_irq(l) diff --git a/xsa453-4.18-4.patch b/xsa453-4.18-4.patch new file mode 100644 index 0000000..a3c6b3c --- /dev/null +++ b/xsa453-4.18-4.patch @@ -0,0 +1,75 @@ +From: =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= +Subject: percpu-rwlock: introduce support for blocking speculation into + critical regions + +Add direct calls to block_lock_speculation() where required in order to prevent +speculation into the lock protected critical regions. Also convert +_percpu_read_lock() from inline to always_inline. + +Note that _percpu_write_lock() has been modified the use the non speculation +safe of the locking primites, as a speculation is added unconditionally by the +calling wrapper. + +This is part of XSA-453 / CVE-2024-2193 + +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +(cherry picked from commit f218daf6d3a3b847736d37c6a6b76031a0d08441) + +diff --git a/xen/common/rwlock.c b/xen/common/rwlock.c +index 290602936df6..f5a249bcc240 100644 +--- a/xen/common/rwlock.c ++++ b/xen/common/rwlock.c +@@ -129,8 +129,12 @@ void _percpu_write_lock(percpu_rwlock_t **per_cpudata, + /* + * First take the write lock to protect against other writers or slow + * path readers. ++ * ++ * Note we use the speculation unsafe variant of write_lock(), as the ++ * calling wrapper already adds a speculation barrier after the lock has ++ * been taken. + */ +- write_lock(&percpu_rwlock->rwlock); ++ _write_lock(&percpu_rwlock->rwlock); + + /* Now set the global variable so that readers start using read_lock. */ + percpu_rwlock->writer_activating = 1; +diff --git a/xen/include/xen/rwlock.h b/xen/include/xen/rwlock.h +index 9a0d3ec23847..9e35ee2edf8f 100644 +--- a/xen/include/xen/rwlock.h ++++ b/xen/include/xen/rwlock.h +@@ -338,8 +338,8 @@ static inline void _percpu_rwlock_owner_check(percpu_rwlock_t **per_cpudata, + #define percpu_rwlock_resource_init(l, owner) \ + (*(l) = (percpu_rwlock_t)PERCPU_RW_LOCK_UNLOCKED(&get_per_cpu_var(owner))) + +-static inline void _percpu_read_lock(percpu_rwlock_t **per_cpudata, +- percpu_rwlock_t *percpu_rwlock) ++static always_inline void _percpu_read_lock(percpu_rwlock_t **per_cpudata, ++ percpu_rwlock_t *percpu_rwlock) + { + /* Validate the correct per_cpudata variable has been provided. */ + _percpu_rwlock_owner_check(per_cpudata, percpu_rwlock); +@@ -374,6 +374,8 @@ static inline void _percpu_read_lock(percpu_rwlock_t **per_cpudata, + } + else + { ++ /* Other branch already has a speculation barrier in read_lock(). */ ++ block_lock_speculation(); + /* All other paths have implicit check_lock() calls via read_lock(). */ + check_lock(&percpu_rwlock->rwlock.lock.debug, false); + } +@@ -430,8 +432,12 @@ static inline void _percpu_write_unlock(percpu_rwlock_t **per_cpudata, + _percpu_read_lock(&get_per_cpu_var(percpu), lock) + #define percpu_read_unlock(percpu, lock) \ + _percpu_read_unlock(&get_per_cpu_var(percpu), lock) +-#define percpu_write_lock(percpu, lock) \ +- _percpu_write_lock(&get_per_cpu_var(percpu), lock) ++ ++#define percpu_write_lock(percpu, lock) \ ++({ \ ++ _percpu_write_lock(&get_per_cpu_var(percpu), lock); \ ++ block_lock_speculation(); \ ++}) + #define percpu_write_unlock(percpu, lock) \ + _percpu_write_unlock(&get_per_cpu_var(percpu), lock) + diff --git a/xsa453-4.18-5.patch b/xsa453-4.18-5.patch new file mode 100644 index 0000000..aab46e3 --- /dev/null +++ b/xsa453-4.18-5.patch @@ -0,0 +1,382 @@ +From: =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= +Subject: locking: attempt to ensure lock wrappers are always inline + +In order to prevent the locking speculation barriers from being inside of +`call`ed functions that could be speculatively bypassed. + +While there also add an extra locking barrier to _mm_write_lock() in the branch +taken when the lock is already held. + +Note some functions are switched to use the unsafe variants (without speculation +barrier) of the locking primitives, but a speculation barrier is always added +to the exposed public lock wrapping helper. That's the case with +sched_spin_lock_double() or pcidevs_lock() for example. + +This is part of XSA-453 / CVE-2024-2193 + +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +(cherry picked from commit 197ecd838a2aaf959a469df3696d4559c4f8b762) + +diff --git a/xen/arch/x86/hvm/vpt.c b/xen/arch/x86/hvm/vpt.c +index 8f53e88d6706..e1d6845a2844 100644 +--- a/xen/arch/x86/hvm/vpt.c ++++ b/xen/arch/x86/hvm/vpt.c +@@ -150,7 +150,7 @@ static int pt_irq_masked(struct periodic_time *pt) + * pt->vcpu field, because another thread holding the pt_migrate lock + * may already be spinning waiting for your vcpu lock. + */ +-static void pt_vcpu_lock(struct vcpu *v) ++static always_inline void pt_vcpu_lock(struct vcpu *v) + { + spin_lock(&v->arch.hvm.tm_lock); + } +@@ -169,9 +169,13 @@ static void pt_vcpu_unlock(struct vcpu *v) + * need to take an additional lock that protects against pt->vcpu + * changing. + */ +-static void pt_lock(struct periodic_time *pt) ++static always_inline void pt_lock(struct periodic_time *pt) + { +- read_lock(&pt->vcpu->domain->arch.hvm.pl_time->pt_migrate); ++ /* ++ * Use the speculation unsafe variant for the first lock, as the following ++ * lock taking helper already includes a speculation barrier. ++ */ ++ _read_lock(&pt->vcpu->domain->arch.hvm.pl_time->pt_migrate); + spin_lock(&pt->vcpu->arch.hvm.tm_lock); + } + +diff --git a/xen/arch/x86/include/asm/irq.h b/xen/arch/x86/include/asm/irq.h +index a87af47ece22..465ab39bb041 100644 +--- a/xen/arch/x86/include/asm/irq.h ++++ b/xen/arch/x86/include/asm/irq.h +@@ -174,6 +174,7 @@ void cf_check irq_complete_move(struct irq_desc *desc); + + extern struct irq_desc *irq_desc; + ++/* Not speculation safe, only used for AP bringup. */ + void lock_vector_lock(void); + void unlock_vector_lock(void); + +diff --git a/xen/arch/x86/mm/mm-locks.h b/xen/arch/x86/mm/mm-locks.h +index 5a3f96fbaadd..5ec080c02fd8 100644 +--- a/xen/arch/x86/mm/mm-locks.h ++++ b/xen/arch/x86/mm/mm-locks.h +@@ -74,8 +74,8 @@ static inline void _set_lock_level(int l) + this_cpu(mm_lock_level) = l; + } + +-static inline void _mm_lock(const struct domain *d, mm_lock_t *l, +- const char *func, int level, int rec) ++static always_inline void _mm_lock(const struct domain *d, mm_lock_t *l, ++ const char *func, int level, int rec) + { + if ( !((mm_locked_by_me(l)) && rec) ) + _check_lock_level(d, level); +@@ -125,8 +125,8 @@ static inline int mm_write_locked_by_me(mm_rwlock_t *l) + return (l->locker == get_processor_id()); + } + +-static inline void _mm_write_lock(const struct domain *d, mm_rwlock_t *l, +- const char *func, int level) ++static always_inline void _mm_write_lock(const struct domain *d, mm_rwlock_t *l, ++ const char *func, int level) + { + if ( !mm_write_locked_by_me(l) ) + { +@@ -137,6 +137,8 @@ static inline void _mm_write_lock(const struct domain *d, mm_rwlock_t *l, + l->unlock_level = _get_lock_level(); + _set_lock_level(_lock_level(d, level)); + } ++ else ++ block_speculation(); + l->recurse_count++; + } + +@@ -150,8 +152,8 @@ static inline void mm_write_unlock(mm_rwlock_t *l) + percpu_write_unlock(p2m_percpu_rwlock, &l->lock); + } + +-static inline void _mm_read_lock(const struct domain *d, mm_rwlock_t *l, +- int level) ++static always_inline void _mm_read_lock(const struct domain *d, mm_rwlock_t *l, ++ int level) + { + _check_lock_level(d, level); + percpu_read_lock(p2m_percpu_rwlock, &l->lock); +@@ -166,15 +168,15 @@ static inline void mm_read_unlock(mm_rwlock_t *l) + + /* This wrapper uses the line number to express the locking order below */ + #define declare_mm_lock(name) \ +- static inline void mm_lock_##name(const struct domain *d, mm_lock_t *l, \ +- const char *func, int rec) \ ++ static always_inline void mm_lock_##name( \ ++ const struct domain *d, mm_lock_t *l, const char *func, int rec) \ + { _mm_lock(d, l, func, MM_LOCK_ORDER_##name, rec); } + #define declare_mm_rwlock(name) \ +- static inline void mm_write_lock_##name(const struct domain *d, \ +- mm_rwlock_t *l, const char *func) \ ++ static always_inline void mm_write_lock_##name( \ ++ const struct domain *d, mm_rwlock_t *l, const char *func) \ + { _mm_write_lock(d, l, func, MM_LOCK_ORDER_##name); } \ +- static inline void mm_read_lock_##name(const struct domain *d, \ +- mm_rwlock_t *l) \ ++ static always_inline void mm_read_lock_##name(const struct domain *d, \ ++ mm_rwlock_t *l) \ + { _mm_read_lock(d, l, MM_LOCK_ORDER_##name); } + /* These capture the name of the calling function */ + #define mm_lock(name, d, l) mm_lock_##name(d, l, __func__, 0) +@@ -309,7 +311,7 @@ declare_mm_lock(altp2mlist) + #define MM_LOCK_ORDER_altp2m 40 + declare_mm_rwlock(altp2m); + +-static inline void p2m_lock(struct p2m_domain *p) ++static always_inline void p2m_lock(struct p2m_domain *p) + { + if ( p2m_is_altp2m(p) ) + mm_write_lock(altp2m, p->domain, &p->lock); +diff --git a/xen/arch/x86/mm/p2m-pod.c b/xen/arch/x86/mm/p2m-pod.c +index 9969eb45fa8c..9be67b63ce3e 100644 +--- a/xen/arch/x86/mm/p2m-pod.c ++++ b/xen/arch/x86/mm/p2m-pod.c +@@ -24,7 +24,7 @@ + #define superpage_aligned(_x) (((_x)&(SUPERPAGE_PAGES-1))==0) + + /* Enforce lock ordering when grabbing the "external" page_alloc lock */ +-static inline void lock_page_alloc(struct p2m_domain *p2m) ++static always_inline void lock_page_alloc(struct p2m_domain *p2m) + { + page_alloc_mm_pre_lock(p2m->domain); + spin_lock(&(p2m->domain->page_alloc_lock)); +diff --git a/xen/common/event_channel.c b/xen/common/event_channel.c +index a7a004a08429..66f924a7b091 100644 +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -45,7 +45,7 @@ + * just assume the event channel is free or unbound at the moment when the + * evtchn_read_trylock() returns false. + */ +-static inline void evtchn_write_lock(struct evtchn *evtchn) ++static always_inline void evtchn_write_lock(struct evtchn *evtchn) + { + write_lock(&evtchn->lock); + +@@ -351,7 +351,8 @@ int evtchn_alloc_unbound(evtchn_alloc_unbound_t *alloc, evtchn_port_t port) + return rc; + } + +-static void double_evtchn_lock(struct evtchn *lchn, struct evtchn *rchn) ++static always_inline void double_evtchn_lock(struct evtchn *lchn, ++ struct evtchn *rchn) + { + ASSERT(lchn != rchn); + +diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c +index 89b7811c51c3..934924cbda66 100644 +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -403,7 +403,7 @@ static inline void act_set_gfn(struct active_grant_entry *act, gfn_t gfn) + + static DEFINE_PERCPU_RWLOCK_GLOBAL(grant_rwlock); + +-static inline void grant_read_lock(struct grant_table *gt) ++static always_inline void grant_read_lock(struct grant_table *gt) + { + percpu_read_lock(grant_rwlock, >->lock); + } +@@ -413,7 +413,7 @@ static inline void grant_read_unlock(struct grant_table *gt) + percpu_read_unlock(grant_rwlock, >->lock); + } + +-static inline void grant_write_lock(struct grant_table *gt) ++static always_inline void grant_write_lock(struct grant_table *gt) + { + percpu_write_lock(grant_rwlock, >->lock); + } +@@ -450,7 +450,7 @@ nr_active_grant_frames(struct grant_table *gt) + return num_act_frames_from_sha_frames(nr_grant_frames(gt)); + } + +-static inline struct active_grant_entry * ++static always_inline struct active_grant_entry * + active_entry_acquire(struct grant_table *t, grant_ref_t e) + { + struct active_grant_entry *act; +diff --git a/xen/common/sched/core.c b/xen/common/sched/core.c +index 901782bbb416..34ad39b9ad0b 100644 +--- a/xen/common/sched/core.c ++++ b/xen/common/sched/core.c +@@ -348,23 +348,28 @@ uint64_t get_cpu_idle_time(unsigned int cpu) + * This avoids dead- or live-locks when this code is running on both + * cpus at the same time. + */ +-static void sched_spin_lock_double(spinlock_t *lock1, spinlock_t *lock2, +- unsigned long *flags) ++static always_inline void sched_spin_lock_double( ++ spinlock_t *lock1, spinlock_t *lock2, unsigned long *flags) + { ++ /* ++ * In order to avoid extra overhead, use the locking primitives without the ++ * speculation barrier, and introduce a single barrier here. ++ */ + if ( lock1 == lock2 ) + { +- spin_lock_irqsave(lock1, *flags); ++ *flags = _spin_lock_irqsave(lock1); + } + else if ( lock1 < lock2 ) + { +- spin_lock_irqsave(lock1, *flags); +- spin_lock(lock2); ++ *flags = _spin_lock_irqsave(lock1); ++ _spin_lock(lock2); + } + else + { +- spin_lock_irqsave(lock2, *flags); +- spin_lock(lock1); ++ *flags = _spin_lock_irqsave(lock2); ++ _spin_lock(lock1); + } ++ block_lock_speculation(); + } + + static void sched_spin_unlock_double(spinlock_t *lock1, spinlock_t *lock2, +diff --git a/xen/common/sched/private.h b/xen/common/sched/private.h +index c516976c3740..3b97f1576782 100644 +--- a/xen/common/sched/private.h ++++ b/xen/common/sched/private.h +@@ -207,8 +207,24 @@ DECLARE_PER_CPU(cpumask_t, cpumask_scratch); + #define cpumask_scratch (&this_cpu(cpumask_scratch)) + #define cpumask_scratch_cpu(c) (&per_cpu(cpumask_scratch, c)) + ++/* ++ * Deal with _spin_lock_irqsave() returning the flags value instead of storing ++ * it in a passed parameter. ++ */ ++#define _sched_spinlock0(lock, irq) _spin_lock##irq(lock) ++#define _sched_spinlock1(lock, irq, arg) ({ \ ++ BUILD_BUG_ON(sizeof(arg) != sizeof(unsigned long)); \ ++ (arg) = _spin_lock##irq(lock); \ ++}) ++ ++#define _sched_spinlock__(nr) _sched_spinlock ## nr ++#define _sched_spinlock_(nr) _sched_spinlock__(nr) ++#define _sched_spinlock(lock, irq, args...) \ ++ _sched_spinlock_(count_args(args))(lock, irq, ## args) ++ + #define sched_lock(kind, param, cpu, irq, arg...) \ +-static inline spinlock_t *kind##_schedule_lock##irq(param EXTRA_TYPE(arg)) \ ++static always_inline spinlock_t \ ++*kind##_schedule_lock##irq(param EXTRA_TYPE(arg)) \ + { \ + for ( ; ; ) \ + { \ +@@ -220,10 +236,16 @@ static inline spinlock_t *kind##_schedule_lock##irq(param EXTRA_TYPE(arg)) \ + * \ + * It may also be the case that v->processor may change but the \ + * lock may be the same; this will succeed in that case. \ ++ * \ ++ * Use the speculation unsafe locking helper, there's a speculation \ ++ * barrier before returning to the caller. \ + */ \ +- spin_lock##irq(lock, ## arg); \ ++ _sched_spinlock(lock, irq, ## arg); \ + if ( likely(lock == get_sched_res(cpu)->schedule_lock) ) \ ++ { \ ++ block_lock_speculation(); \ + return lock; \ ++ } \ + spin_unlock##irq(lock, ## arg); \ + } \ + } +diff --git a/xen/common/timer.c b/xen/common/timer.c +index 0fddfa74879e..38eb5fd20d36 100644 +--- a/xen/common/timer.c ++++ b/xen/common/timer.c +@@ -239,7 +239,7 @@ static inline void deactivate_timer(struct timer *timer) + list_add(&timer->inactive, &per_cpu(timers, timer->cpu).inactive); + } + +-static inline bool_t timer_lock(struct timer *timer) ++static inline bool_t timer_lock_unsafe(struct timer *timer) + { + unsigned int cpu; + +@@ -253,7 +253,8 @@ static inline bool_t timer_lock(struct timer *timer) + rcu_read_unlock(&timer_cpu_read_lock); + return 0; + } +- spin_lock(&per_cpu(timers, cpu).lock); ++ /* Use the speculation unsafe variant, the wrapper has the barrier. */ ++ _spin_lock(&per_cpu(timers, cpu).lock); + if ( likely(timer->cpu == cpu) ) + break; + spin_unlock(&per_cpu(timers, cpu).lock); +@@ -266,8 +267,9 @@ static inline bool_t timer_lock(struct timer *timer) + #define timer_lock_irqsave(t, flags) ({ \ + bool_t __x; \ + local_irq_save(flags); \ +- if ( !(__x = timer_lock(t)) ) \ ++ if ( !(__x = timer_lock_unsafe(t)) ) \ + local_irq_restore(flags); \ ++ block_lock_speculation(); \ + __x; \ + }) + +diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c +index e99837b6e141..2a1e7ee89a5d 100644 +--- a/xen/drivers/passthrough/pci.c ++++ b/xen/drivers/passthrough/pci.c +@@ -52,9 +52,10 @@ struct pci_seg { + + static spinlock_t _pcidevs_lock = SPIN_LOCK_UNLOCKED; + +-void pcidevs_lock(void) ++/* Do not use, as it has no speculation barrier, use pcidevs_lock() instead. */ ++void pcidevs_lock_unsafe(void) + { +- spin_lock_recursive(&_pcidevs_lock); ++ _spin_lock_recursive(&_pcidevs_lock); + } + + void pcidevs_unlock(void) +diff --git a/xen/include/xen/event.h b/xen/include/xen/event.h +index 8e509e078475..f1472ea1ebe5 100644 +--- a/xen/include/xen/event.h ++++ b/xen/include/xen/event.h +@@ -114,12 +114,12 @@ void notify_via_xen_event_channel(struct domain *ld, int lport); + #define bucket_from_port(d, p) \ + ((group_from_port(d, p))[((p) % EVTCHNS_PER_GROUP) / EVTCHNS_PER_BUCKET]) + +-static inline void evtchn_read_lock(struct evtchn *evtchn) ++static always_inline void evtchn_read_lock(struct evtchn *evtchn) + { + read_lock(&evtchn->lock); + } + +-static inline bool evtchn_read_trylock(struct evtchn *evtchn) ++static always_inline bool evtchn_read_trylock(struct evtchn *evtchn) + { + return read_trylock(&evtchn->lock); + } +diff --git a/xen/include/xen/pci.h b/xen/include/xen/pci.h +index 251b8761a8e9..a71bed36be29 100644 +--- a/xen/include/xen/pci.h ++++ b/xen/include/xen/pci.h +@@ -155,8 +155,12 @@ struct pci_dev { + * devices, it also sync the access to the msi capability that is not + * interrupt handling related (the mask bit register). + */ +- +-void pcidevs_lock(void); ++void pcidevs_lock_unsafe(void); ++static always_inline void pcidevs_lock(void) ++{ ++ pcidevs_lock_unsafe(); ++ block_lock_speculation(); ++} + void pcidevs_unlock(void); + bool __must_check pcidevs_locked(void); + diff --git a/xsa453-4.18-6.patch b/xsa453-4.18-6.patch new file mode 100644 index 0000000..e8104c5 --- /dev/null +++ b/xsa453-4.18-6.patch @@ -0,0 +1,61 @@ +From: =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= +Subject: x86/mm: add speculation barriers to open coded locks + +Add a speculation barrier to the clearly identified open-coded lock taking +functions. + +Note that the memory sharing page_lock() replacement (_page_lock()) is left +as-is, as the code is experimental and not security supported. + +This is part of XSA-453 / CVE-2024-2193 + +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +(cherry picked from commit 42a572a38e22a97d86a4b648a22597628d5b42e4) + +diff --git a/xen/arch/x86/include/asm/mm.h b/xen/arch/x86/include/asm/mm.h +index 05dfe35502c8..d1b1fee99b7d 100644 +--- a/xen/arch/x86/include/asm/mm.h ++++ b/xen/arch/x86/include/asm/mm.h +@@ -399,7 +399,9 @@ const struct platform_bad_page *get_platform_badpages(unsigned int *array_size); + * The use of PGT_locked in mem_sharing does not collide, since mem_sharing is + * only supported for hvm guests, which do not have PV PTEs updated. + */ +-int page_lock(struct page_info *page); ++int page_lock_unsafe(struct page_info *page); ++#define page_lock(pg) lock_evaluate_nospec(page_lock_unsafe(pg)) ++ + void page_unlock(struct page_info *page); + + void put_page_type(struct page_info *page); +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index ab0acbfea6e5..000fd0fb558b 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -2017,7 +2017,7 @@ static inline bool current_locked_page_ne_check(struct page_info *page) { + #define current_locked_page_ne_check(x) true + #endif + +-int page_lock(struct page_info *page) ++int page_lock_unsafe(struct page_info *page) + { + unsigned long x, nx; + +@@ -2078,7 +2078,7 @@ void page_unlock(struct page_info *page) + * l3t_lock(), so to avoid deadlock we must avoid grabbing them in + * reverse order. + */ +-static void l3t_lock(struct page_info *page) ++static always_inline void l3t_lock(struct page_info *page) + { + unsigned long x, nx; + +@@ -2087,6 +2087,8 @@ static void l3t_lock(struct page_info *page) + cpu_relax(); + nx = x | PGT_locked; + } while ( cmpxchg(&page->u.inuse.type_info, x, nx) != x ); ++ ++ block_lock_speculation(); + } + + static void l3t_unlock(struct page_info *page) diff --git a/xsa453-4.18-7.patch b/xsa453-4.18-7.patch new file mode 100644 index 0000000..48f0bf9 --- /dev/null +++ b/xsa453-4.18-7.patch @@ -0,0 +1,201 @@ +From: =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= +Subject: x86: protect conditional lock taking from speculative execution + +Conditionally taken locks that use the pattern: + +if ( lock ) + spin_lock(...); + +Need an else branch in order to issue an speculation barrier in the else case, +just like it's done in case the lock needs to be acquired. + +eval_nospec() could be used on the condition itself, but that would result in a +double barrier on the branch where the lock is taken. + +Introduce a new pair of helpers, {gfn,spin}_lock_if() that can be used to +conditionally take a lock in a speculation safe way. + +This is part of XSA-453 / CVE-2024-2193 + +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +(cherry picked from commit 03cf7ca23e0e876075954c558485b267b7d02406) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 000fd0fb558b..45bfbc2522f7 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -5007,8 +5007,7 @@ static l3_pgentry_t *virt_to_xen_l3e(unsigned long v) + if ( !l3t ) + return NULL; + UNMAP_DOMAIN_PAGE(l3t); +- if ( locking ) +- spin_lock(&map_pgdir_lock); ++ spin_lock_if(locking, &map_pgdir_lock); + if ( !(l4e_get_flags(*pl4e) & _PAGE_PRESENT) ) + { + l4_pgentry_t l4e = l4e_from_mfn(l3mfn, __PAGE_HYPERVISOR); +@@ -5045,8 +5044,7 @@ static l2_pgentry_t *virt_to_xen_l2e(unsigned long v) + return NULL; + } + UNMAP_DOMAIN_PAGE(l2t); +- if ( locking ) +- spin_lock(&map_pgdir_lock); ++ spin_lock_if(locking, &map_pgdir_lock); + if ( !(l3e_get_flags(*pl3e) & _PAGE_PRESENT) ) + { + l3e_write(pl3e, l3e_from_mfn(l2mfn, __PAGE_HYPERVISOR)); +@@ -5084,8 +5082,7 @@ l1_pgentry_t *virt_to_xen_l1e(unsigned long v) + return NULL; + } + UNMAP_DOMAIN_PAGE(l1t); +- if ( locking ) +- spin_lock(&map_pgdir_lock); ++ spin_lock_if(locking, &map_pgdir_lock); + if ( !(l2e_get_flags(*pl2e) & _PAGE_PRESENT) ) + { + l2e_write(pl2e, l2e_from_mfn(l1mfn, __PAGE_HYPERVISOR)); +@@ -5116,6 +5113,8 @@ l1_pgentry_t *virt_to_xen_l1e(unsigned long v) + do { \ + if ( locking ) \ + l3t_lock(page); \ ++ else \ ++ block_lock_speculation(); \ + } while ( false ) + + #define L3T_UNLOCK(page) \ +@@ -5331,8 +5330,7 @@ int map_pages_to_xen( + if ( l3e_get_flags(ol3e) & _PAGE_GLOBAL ) + flush_flags |= FLUSH_TLB_GLOBAL; + +- if ( locking ) +- spin_lock(&map_pgdir_lock); ++ spin_lock_if(locking, &map_pgdir_lock); + if ( (l3e_get_flags(*pl3e) & _PAGE_PRESENT) && + (l3e_get_flags(*pl3e) & _PAGE_PSE) ) + { +@@ -5436,8 +5434,7 @@ int map_pages_to_xen( + if ( l2e_get_flags(*pl2e) & _PAGE_GLOBAL ) + flush_flags |= FLUSH_TLB_GLOBAL; + +- if ( locking ) +- spin_lock(&map_pgdir_lock); ++ spin_lock_if(locking, &map_pgdir_lock); + if ( (l2e_get_flags(*pl2e) & _PAGE_PRESENT) && + (l2e_get_flags(*pl2e) & _PAGE_PSE) ) + { +@@ -5478,8 +5475,7 @@ int map_pages_to_xen( + unsigned long base_mfn; + const l1_pgentry_t *l1t; + +- if ( locking ) +- spin_lock(&map_pgdir_lock); ++ spin_lock_if(locking, &map_pgdir_lock); + + ol2e = *pl2e; + /* +@@ -5533,8 +5529,7 @@ int map_pages_to_xen( + unsigned long base_mfn; + const l2_pgentry_t *l2t; + +- if ( locking ) +- spin_lock(&map_pgdir_lock); ++ spin_lock_if(locking, &map_pgdir_lock); + + ol3e = *pl3e; + /* +@@ -5678,8 +5673,7 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + l3e_get_flags(*pl3e))); + UNMAP_DOMAIN_PAGE(l2t); + +- if ( locking ) +- spin_lock(&map_pgdir_lock); ++ spin_lock_if(locking, &map_pgdir_lock); + if ( (l3e_get_flags(*pl3e) & _PAGE_PRESENT) && + (l3e_get_flags(*pl3e) & _PAGE_PSE) ) + { +@@ -5738,8 +5732,7 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + l2e_get_flags(*pl2e) & ~_PAGE_PSE)); + UNMAP_DOMAIN_PAGE(l1t); + +- if ( locking ) +- spin_lock(&map_pgdir_lock); ++ spin_lock_if(locking, &map_pgdir_lock); + if ( (l2e_get_flags(*pl2e) & _PAGE_PRESENT) && + (l2e_get_flags(*pl2e) & _PAGE_PSE) ) + { +@@ -5783,8 +5776,7 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + */ + if ( (nf & _PAGE_PRESENT) || ((v != e) && (l1_table_offset(v) != 0)) ) + continue; +- if ( locking ) +- spin_lock(&map_pgdir_lock); ++ spin_lock_if(locking, &map_pgdir_lock); + + /* + * L2E may be already cleared, or set to a superpage, by +@@ -5831,8 +5823,7 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + if ( (nf & _PAGE_PRESENT) || + ((v != e) && (l2_table_offset(v) + l1_table_offset(v) != 0)) ) + continue; +- if ( locking ) +- spin_lock(&map_pgdir_lock); ++ spin_lock_if(locking, &map_pgdir_lock); + + /* + * L3E may be already cleared, or set to a superpage, by +diff --git a/xen/arch/x86/mm/mm-locks.h b/xen/arch/x86/mm/mm-locks.h +index 5ec080c02fd8..b4960fb90eff 100644 +--- a/xen/arch/x86/mm/mm-locks.h ++++ b/xen/arch/x86/mm/mm-locks.h +@@ -335,6 +335,15 @@ static inline void p2m_unlock(struct p2m_domain *p) + #define p2m_locked_by_me(p) mm_write_locked_by_me(&(p)->lock) + #define gfn_locked_by_me(p,g) p2m_locked_by_me(p) + ++static always_inline void gfn_lock_if(bool condition, struct p2m_domain *p2m, ++ gfn_t gfn, unsigned int order) ++{ ++ if ( condition ) ++ gfn_lock(p2m, gfn, order); ++ else ++ block_lock_speculation(); ++} ++ + /* PoD lock (per-p2m-table) + * + * Protects private PoD data structs: entry and cache +diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c +index 0983bd71d9a9..22ab1d606e8a 100644 +--- a/xen/arch/x86/mm/p2m.c ++++ b/xen/arch/x86/mm/p2m.c +@@ -280,9 +280,8 @@ mfn_t p2m_get_gfn_type_access(struct p2m_domain *p2m, gfn_t gfn, + if ( q & P2M_UNSHARE ) + q |= P2M_ALLOC; + +- if ( locked ) +- /* Grab the lock here, don't release until put_gfn */ +- gfn_lock(p2m, gfn, 0); ++ /* Grab the lock here, don't release until put_gfn */ ++ gfn_lock_if(locked, p2m, gfn, 0); + + mfn = p2m->get_entry(p2m, gfn, t, a, q, page_order, NULL); + +diff --git a/xen/include/xen/spinlock.h b/xen/include/xen/spinlock.h +index 28fce5615e5c..c830df3430a3 100644 +--- a/xen/include/xen/spinlock.h ++++ b/xen/include/xen/spinlock.h +@@ -222,6 +222,14 @@ static always_inline void spin_lock_irq(spinlock_t *l) + block_lock_speculation(); \ + }) + ++/* Conditionally take a spinlock in a speculation safe way. */ ++static always_inline void spin_lock_if(bool condition, spinlock_t *l) ++{ ++ if ( condition ) ++ _spin_lock(l); ++ block_lock_speculation(); ++} ++ + #define spin_unlock(l) _spin_unlock(l) + #define spin_unlock_irq(l) _spin_unlock_irq(l) + #define spin_unlock_irqrestore(l, f) _spin_unlock_irqrestore(l, f) From 81b1db8227e9e4377fab84deb03ed078c2b7f943 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 20 Mar 2024 19:12:09 +0000 Subject: [PATCH 086/137] update to xen-4.18.1 --- .gitignore | 2 +- sources | 2 +- xen.gcc12.fixes.patch | 2 +- ...28a2a742069af203e90c613c5c93e23c9755.patch | 47 --- ...0f77a5cc671d30c5ef3318f5b8e9b74e4aa3.patch | 55 --- ...07f7d51fe49df8bc70f83e49ce06c92e4e54.patch | 51 --- ...162cafd27f2865a3102a2ec0f386a02ed03d.patch | 99 ----- xen.spec | 53 +-- xsa447.patch | 117 ------ xsa449.patch | 89 ---- xsa450.patch | 59 --- xsa451-4.18.patch | 188 --------- xsa452-4.18-1.patch | 304 -------------- xsa452-4.18-2.patch | 90 ----- xsa452-4.18-3.patch | 135 ------- xsa452-4.18-4.patch | 197 --------- xsa452-4.18-5.patch | 239 ----------- xsa452-4.18-6.patch | 163 -------- xsa452-4.18-7.patch | 307 -------------- xsa453-4.18-1.patch | 50 --- xsa453-4.18-2.patch | 314 -------------- xsa453-4.18-3.patch | 113 ------ xsa453-4.18-4.patch | 75 ---- xsa453-4.18-5.patch | 382 ------------------ xsa453-4.18-6.patch | 61 --- xsa453-4.18-7.patch | 201 --------- 26 files changed, 10 insertions(+), 3385 deletions(-) delete mode 100644 xen.git-576528a2a742069af203e90c613c5c93e23c9755.patch delete mode 100644 xen.git-60e00f77a5cc671d30c5ef3318f5b8e9b74e4aa3.patch delete mode 100644 xen.git-6e9507f7d51fe49df8bc70f83e49ce06c92e4e54.patch delete mode 100644 xen.git-de17162cafd27f2865a3102a2ec0f386a02ed03d.patch delete mode 100644 xsa447.patch delete mode 100644 xsa449.patch delete mode 100644 xsa450.patch delete mode 100644 xsa451-4.18.patch delete mode 100644 xsa452-4.18-1.patch delete mode 100644 xsa452-4.18-2.patch delete mode 100644 xsa452-4.18-3.patch delete mode 100644 xsa452-4.18-4.patch delete mode 100644 xsa452-4.18-5.patch delete mode 100644 xsa452-4.18-6.patch delete mode 100644 xsa452-4.18-7.patch delete mode 100644 xsa453-4.18-1.patch delete mode 100644 xsa453-4.18-2.patch delete mode 100644 xsa453-4.18-3.patch delete mode 100644 xsa453-4.18-4.patch delete mode 100644 xsa453-4.18-5.patch delete mode 100644 xsa453-4.18-6.patch delete mode 100644 xsa453-4.18-7.patch diff --git a/.gitignore b/.gitignore index 7c63a9b..97a51c2 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.18.0.tar.gz +/xen-4.18.1.tar.gz diff --git a/sources b/sources index 1aaa8a6..8d4dabc 100644 --- a/sources +++ b/sources @@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.18.0.tar.gz) = 4cc9fd155144045a173c5f8ecc45f149817f1034eec618cb6f8b0494ef2fb5b95c4c60cf0bf4bec4bef8a622c35b6a3cb7dedc38e6d95e726f1611c73ddb3273 +SHA512 (xen-4.18.1.tar.gz) = 6d67c177a31dae6979c111498f65cff2a7a792299cc72e658ae9f926468c14092807de94b6e269849fafdb6ed5a9d076400ecde0c4fec3b2b4bdb5f5ef39fcbb diff --git a/xen.gcc12.fixes.patch b/xen.gcc12.fixes.patch index 66d13b1..b35440f 100644 --- a/xen.gcc12.fixes.patch +++ b/xen.gcc12.fixes.patch @@ -1,7 +1,7 @@ --- xen-4.16.0/Config.mk.orig 2021-11-30 11:42:42.000000000 +0000 +++ xen-4.16.0/Config.mk 2022-01-24 20:25:16.687125822 +0000 @@ -186,6 +186,7 @@ - $(call cc-option-add,CFLAGS,CC,-Wdeclaration-after-statement) + $(call cc-option-add,CFLAGS,CC,-Wno-unused-but-set-variable) $(call cc-option-add,CFLAGS,CC,-Wno-unused-local-typedefs) +$(call cc-option-add,CFLAGS,CC,-Wno-error=array-bounds) diff --git a/xen.git-576528a2a742069af203e90c613c5c93e23c9755.patch b/xen.git-576528a2a742069af203e90c613c5c93e23c9755.patch deleted file mode 100644 index 20acd78..0000000 --- a/xen.git-576528a2a742069af203e90c613c5c93e23c9755.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 576528a2a742069af203e90c613c5c93e23c9755 Mon Sep 17 00:00:00 2001 -From: =?utf8?q?Roger=20Pau=20Monn=C3=A9?= -Date: Tue, 27 Feb 2024 14:58:40 +0100 -Subject: [PATCH] x86/spec: do not print thunk option selection if not built-in -MIME-Version: 1.0 -Content-Type: text/plain; charset=utf8 -Content-Transfer-Encoding: 8bit - -Since the thunk built-in enable is printed as part of the "Compiled-in -support:" line, avoid printing anything in "Xen settings:" if the thunk is -disabled at build time. - -Note the BTI-Thunk option printing is also adjusted to print a colon in the -same way the other options on the line do. - -Requested-by: Jan Beulich -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich ---- - xen/arch/x86/spec_ctrl.c | 11 ++++++----- - 1 file changed, 6 insertions(+), 5 deletions(-) - -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 74d2136d42..752225faa6 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -504,11 +504,12 @@ static void __init print_details(enum ind_thunk thunk) - "\n"); - - /* Settings for Xen's protection, irrespective of guests. */ -- printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s%s%s, Other:%s%s%s%s%s%s\n", -- thunk == THUNK_NONE ? "N/A" : -- thunk == THUNK_RETPOLINE ? "RETPOLINE" : -- thunk == THUNK_LFENCE ? "LFENCE" : -- thunk == THUNK_JMP ? "JMP" : "?", -+ printk(" Xen settings: %s%sSPEC_CTRL: %s%s%s%s%s, Other:%s%s%s%s%s%s\n", -+ thunk != THUNK_NONE ? "BTI-Thunk: " : "", -+ thunk == THUNK_NONE ? "" : -+ thunk == THUNK_RETPOLINE ? "RETPOLINE, " : -+ thunk == THUNK_LFENCE ? "LFENCE, " : -+ thunk == THUNK_JMP ? "JMP, " : "?, ", - (!boot_cpu_has(X86_FEATURE_IBRSB) && - !boot_cpu_has(X86_FEATURE_IBRS)) ? "No" : - (default_xen_spec_ctrl & SPEC_CTRL_IBRS) ? "IBRS+" : "IBRS-", --- -2.30.2 - diff --git a/xen.git-60e00f77a5cc671d30c5ef3318f5b8e9b74e4aa3.patch b/xen.git-60e00f77a5cc671d30c5ef3318f5b8e9b74e4aa3.patch deleted file mode 100644 index a1b9f49..0000000 --- a/xen.git-60e00f77a5cc671d30c5ef3318f5b8e9b74e4aa3.patch +++ /dev/null @@ -1,55 +0,0 @@ -From 60e00f77a5cc671d30c5ef3318f5b8e9b74e4aa3 Mon Sep 17 00:00:00 2001 -From: =?utf8?q?Roger=20Pau=20Monn=C3=A9?= -Date: Mon, 26 Feb 2024 16:06:42 +0100 -Subject: [PATCH] x86/spec: fix BRANCH_HARDEN option to only be set when - build-enabled -MIME-Version: 1.0 -Content-Type: text/plain; charset=utf8 -Content-Transfer-Encoding: 8bit - -The current logic to handle the BRANCH_HARDEN option will report it as enabled -even when build-time disabled. Fix this by only allowing the option to be set -when support for it is built into Xen. - -Fixes: 2d6f36daa086 ('x86/nospec: Introduce CONFIG_SPECULATIVE_HARDEN_BRANCH') -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich ---- - xen/arch/x86/spec_ctrl.c | 14 ++++++++++++-- - 1 file changed, 12 insertions(+), 2 deletions(-) - -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 421fe3f640..503f1c7a37 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -50,7 +50,8 @@ static int8_t __initdata opt_psfd = -1; - int8_t __ro_after_init opt_ibpb_ctxt_switch = -1; - int8_t __read_mostly opt_eager_fpu = -1; - int8_t __read_mostly opt_l1d_flush = -1; --static bool __initdata opt_branch_harden = true; -+static bool __initdata opt_branch_harden = -+ IS_ENABLED(CONFIG_SPECULATIVE_HARDEN_BRANCH); - - bool __initdata bsp_delay_spec_ctrl; - uint8_t __read_mostly default_xen_spec_ctrl; -@@ -268,7 +269,16 @@ static int __init cf_check parse_spec_ctrl(const char *s) - else if ( (val = parse_boolean("l1d-flush", s, ss)) >= 0 ) - opt_l1d_flush = val; - else if ( (val = parse_boolean("branch-harden", s, ss)) >= 0 ) -- opt_branch_harden = val; -+ { -+ if ( IS_ENABLED(CONFIG_SPECULATIVE_HARDEN_BRANCH) ) -+ opt_branch_harden = val; -+ else -+ { -+ no_config_param("SPECULATIVE_HARDEN_BRANCH", "spec-ctrl", s, -+ ss); -+ rc = -EINVAL; -+ } -+ } - else if ( (val = parse_boolean("srb-lock", s, ss)) >= 0 ) - opt_srb_lock = val; - else if ( (val = parse_boolean("unpriv-mmio", s, ss)) >= 0 ) --- -2.30.2 - diff --git a/xen.git-6e9507f7d51fe49df8bc70f83e49ce06c92e4e54.patch b/xen.git-6e9507f7d51fe49df8bc70f83e49ce06c92e4e54.patch deleted file mode 100644 index a78c792..0000000 --- a/xen.git-6e9507f7d51fe49df8bc70f83e49ce06c92e4e54.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 6e9507f7d51fe49df8bc70f83e49ce06c92e4e54 Mon Sep 17 00:00:00 2001 -From: =?utf8?q?Roger=20Pau=20Monn=C3=A9?= -Date: Tue, 27 Feb 2024 14:57:52 +0100 -Subject: [PATCH] x86/spec: print the built-in SPECULATIVE_HARDEN_* options -MIME-Version: 1.0 -Content-Type: text/plain; charset=utf8 -Content-Transfer-Encoding: 8bit - -Just like it's done for INDIRECT_THUNK and SHADOW_PAGING. - -Reported-by: Jan Beulich -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich ---- - xen/arch/x86/spec_ctrl.c | 14 +++++++++++++- - 1 file changed, 13 insertions(+), 1 deletion(-) - -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 503f1c7a37..2d17cbe25f 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -476,13 +476,25 @@ static void __init print_details(enum ind_thunk thunk) - (e21a & cpufeat_mask(X86_FEATURE_SBPB)) ? " SBPB" : ""); - - /* Compiled-in support which pertains to mitigations. */ -- if ( IS_ENABLED(CONFIG_INDIRECT_THUNK) || IS_ENABLED(CONFIG_SHADOW_PAGING) ) -+ if ( IS_ENABLED(CONFIG_INDIRECT_THUNK) || IS_ENABLED(CONFIG_SHADOW_PAGING) || -+ IS_ENABLED(CONFIG_SPECULATIVE_HARDEN_ARRAY) || -+ IS_ENABLED(CONFIG_SPECULATIVE_HARDEN_BRANCH) || -+ IS_ENABLED(CONFIG_SPECULATIVE_HARDEN_GUEST_ACCESS) ) - printk(" Compiled-in support:" - #ifdef CONFIG_INDIRECT_THUNK - " INDIRECT_THUNK" - #endif - #ifdef CONFIG_SHADOW_PAGING - " SHADOW_PAGING" -+#endif -+#ifdef CONFIG_SPECULATIVE_HARDEN_ARRAY -+ " HARDEN_ARRAY" -+#endif -+#ifdef CONFIG_SPECULATIVE_HARDEN_BRANCH -+ " HARDEN_BRANCH" -+#endif -+#ifdef CONFIG_SPECULATIVE_HARDEN_GUEST_ACCESS -+ " HARDEN_GUEST_ACCESS" - #endif - "\n"); - --- -2.30.2 - diff --git a/xen.git-de17162cafd27f2865a3102a2ec0f386a02ed03d.patch b/xen.git-de17162cafd27f2865a3102a2ec0f386a02ed03d.patch deleted file mode 100644 index 6043d48..0000000 --- a/xen.git-de17162cafd27f2865a3102a2ec0f386a02ed03d.patch +++ /dev/null @@ -1,99 +0,0 @@ -From de17162cafd27f2865a3102a2ec0f386a02ed03d Mon Sep 17 00:00:00 2001 -From: Andrew Cooper -Date: Thu, 29 Feb 2024 11:26:40 +0000 -Subject: [PATCH] x86/cpu-policy: Allow for levelling of VERW side effects -MIME-Version: 1.0 -Content-Type: text/plain; charset=utf8 -Content-Transfer-Encoding: 8bit - -MD_CLEAR and FB_CLEAR need OR-ing across a migrate pool. Allow this, by -having them unconditinally set in max, with the host values reflected in -default. Annotate the bits as having special properies. - -Signed-off-by: Andrew Cooper -Reviewed-by: Roger Pau Monné ---- - xen/arch/x86/cpu-policy.c | 24 +++++++++++++++++++++ - xen/arch/x86/include/asm/cpufeature.h | 1 + - xen/include/public/arch-x86/cpufeatureset.h | 4 ++-- - 3 files changed, 27 insertions(+), 2 deletions(-) - -diff --git a/xen/arch/x86/cpu-policy.c b/xen/arch/x86/cpu-policy.c -index 609db6946f..2c6f03057b 100644 ---- a/xen/arch/x86/cpu-policy.c -+++ b/xen/arch/x86/cpu-policy.c -@@ -442,6 +442,16 @@ static void __init guest_common_max_feature_adjustments(uint32_t *fs) - __set_bit(X86_FEATURE_RSBA, fs); - __set_bit(X86_FEATURE_RRSBA, fs); - -+ /* -+ * These bits indicate that the VERW instruction may have gained -+ * scrubbing side effects. With pooling, they mean "you might migrate -+ * somewhere where scrubbing is necessary", and may need exposing on -+ * unaffected hardware. This is fine, because the VERW instruction -+ * has been around since the 286. -+ */ -+ __set_bit(X86_FEATURE_MD_CLEAR, fs); -+ __set_bit(X86_FEATURE_FB_CLEAR, fs); -+ - /* - * The Gather Data Sampling microcode mitigation (August 2023) has an - * adverse performance impact on the CLWB instruction on SKX/CLX/CPX. -@@ -486,6 +496,20 @@ static void __init guest_common_default_feature_adjustments(uint32_t *fs) - cpu_has_rdrand && !is_forced_cpu_cap(X86_FEATURE_RDRAND) ) - __clear_bit(X86_FEATURE_RDRAND, fs); - -+ /* -+ * These bits indicate that the VERW instruction may have gained -+ * scrubbing side effects. The max policy has them set for migration -+ * reasons, so reset the default policy back to the host values in -+ * case we're unaffected. -+ */ -+ __clear_bit(X86_FEATURE_MD_CLEAR, fs); -+ if ( cpu_has_md_clear ) -+ __set_bit(X86_FEATURE_MD_CLEAR, fs); -+ -+ __clear_bit(X86_FEATURE_FB_CLEAR, fs); -+ if ( cpu_has_fb_clear ) -+ __set_bit(X86_FEATURE_FB_CLEAR, fs); -+ - /* - * The Gather Data Sampling microcode mitigation (August 2023) has an - * adverse performance impact on the CLWB instruction on SKX/CLX/CPX. -diff --git a/xen/arch/x86/include/asm/cpufeature.h b/xen/arch/x86/include/asm/cpufeature.h -index be16492c68..ad24d0fa88 100644 ---- a/xen/arch/x86/include/asm/cpufeature.h -+++ b/xen/arch/x86/include/asm/cpufeature.h -@@ -178,6 +178,7 @@ static inline bool boot_cpu_has(unsigned int feat) - #define cpu_has_avx512_4fmaps boot_cpu_has(X86_FEATURE_AVX512_4FMAPS) - #define cpu_has_avx512_vp2intersect boot_cpu_has(X86_FEATURE_AVX512_VP2INTERSECT) - #define cpu_has_srbds_ctrl boot_cpu_has(X86_FEATURE_SRBDS_CTRL) -+#define cpu_has_md_clear boot_cpu_has(X86_FEATURE_MD_CLEAR) - #define cpu_has_rtm_always_abort boot_cpu_has(X86_FEATURE_RTM_ALWAYS_ABORT) - #define cpu_has_tsx_force_abort boot_cpu_has(X86_FEATURE_TSX_FORCE_ABORT) - #define cpu_has_serialize boot_cpu_has(X86_FEATURE_SERIALIZE) -diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h -index b230d3a690..0374cec3a2 100644 ---- a/xen/include/public/arch-x86/cpufeatureset.h -+++ b/xen/include/public/arch-x86/cpufeatureset.h -@@ -262,7 +262,7 @@ XEN_CPUFEATURE(AVX512_4FMAPS, 9*32+ 3) /*A AVX512 Multiply Accumulation Single - XEN_CPUFEATURE(FSRM, 9*32+ 4) /*A Fast Short REP MOVS */ - XEN_CPUFEATURE(AVX512_VP2INTERSECT, 9*32+8) /*a VP2INTERSECT{D,Q} insns */ - XEN_CPUFEATURE(SRBDS_CTRL, 9*32+ 9) /* MSR_MCU_OPT_CTRL and RNGDS_MITG_DIS. */ --XEN_CPUFEATURE(MD_CLEAR, 9*32+10) /*A VERW clears microarchitectural buffers */ -+XEN_CPUFEATURE(MD_CLEAR, 9*32+10) /*!A VERW clears microarchitectural buffers */ - XEN_CPUFEATURE(RTM_ALWAYS_ABORT, 9*32+11) /*! June 2021 TSX defeaturing in microcode. */ - XEN_CPUFEATURE(TSX_FORCE_ABORT, 9*32+13) /* MSR_TSX_FORCE_ABORT.RTM_ABORT */ - XEN_CPUFEATURE(SERIALIZE, 9*32+14) /*A SERIALIZE insn */ -@@ -334,7 +334,7 @@ XEN_CPUFEATURE(DOITM, 16*32+12) /* Data Operand Invariant Timing - XEN_CPUFEATURE(SBDR_SSDP_NO, 16*32+13) /*A No Shared Buffer Data Read or Sideband Stale Data Propagation */ - XEN_CPUFEATURE(FBSDP_NO, 16*32+14) /*A No Fill Buffer Stale Data Propagation */ - XEN_CPUFEATURE(PSDP_NO, 16*32+15) /*A No Primary Stale Data Propagation */ --XEN_CPUFEATURE(FB_CLEAR, 16*32+17) /*A Fill Buffers cleared by VERW */ -+XEN_CPUFEATURE(FB_CLEAR, 16*32+17) /*!A Fill Buffers cleared by VERW */ - XEN_CPUFEATURE(FB_CLEAR_CTRL, 16*32+18) /* MSR_OPT_CPU_CTRL.FB_CLEAR_DIS */ - XEN_CPUFEATURE(RRSBA, 16*32+19) /*! Restricted RSB Alternative */ - XEN_CPUFEATURE(BHI_NO, 16*32+20) /*A No Branch History Injection */ --- -2.30.2 - diff --git a/xen.spec b/xen.spec index 1236568..06a5573 100644 --- a/xen.spec +++ b/xen.spec @@ -54,8 +54,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.18.0 -Release: 7%{?dist} +Version: 4.18.1 +Release: 1%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -112,30 +112,8 @@ Patch46: xen.efi.build.patch Patch47: xen.gcc13.fixes.patch Patch49: xen.python3.12.patch Patch50: xen.ocaml5.fixes.patch -Patch51: xsa447.patch Patch52: xen.gcc14.fixes.patch Patch53: newlib.gcc14.fixes.patch -Patch54: xsa449.patch -Patch55: xsa450.patch -Patch56: xsa451-4.18.patch -Patch57: xen.git-de17162cafd27f2865a3102a2ec0f386a02ed03d.patch -Patch58: xsa452-4.18-1.patch -Patch59: xsa452-4.18-2.patch -Patch60: xsa452-4.18-3.patch -Patch61: xsa452-4.18-4.patch -Patch62: xsa452-4.18-5.patch -Patch63: xsa452-4.18-6.patch -Patch64: xsa452-4.18-7.patch -Patch65: xen.git-60e00f77a5cc671d30c5ef3318f5b8e9b74e4aa3.patch -Patch66: xen.git-6e9507f7d51fe49df8bc70f83e49ce06c92e4e54.patch -Patch67: xen.git-576528a2a742069af203e90c613c5c93e23c9755.patch -Patch68: xsa453-4.18-1.patch -Patch69: xsa453-4.18-2.patch -Patch70: xsa453-4.18-3.patch -Patch71: xsa453-4.18-4.patch -Patch72: xsa453-4.18-5.patch -Patch73: xsa453-4.18-6.patch -Patch74: xsa453-4.18-7.patch %if %build_qemutrad @@ -348,30 +326,8 @@ manage Xen virtual machines. %if "%dist" != ".fc38" %patch 50 -p1 %endif -%patch 51 -p1 %patch 52 -p1 %patch 53 -p1 -%patch 54 -p1 -%patch 55 -p1 -%patch 56 -p1 -%patch 57 -p1 -%patch 58 -p1 -%patch 59 -p1 -%patch 60 -p1 -%patch 61 -p1 -%patch 62 -p1 -%patch 63 -p1 -%patch 64 -p1 -%patch 65 -p1 -%patch 66 -p1 -%patch 67 -p1 -%patch 68 -p1 -%patch 69 -p1 -%patch 70 -p1 -%patch 71 -p1 -%patch 72 -p1 -%patch 73 -p1 -%patch 74 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -978,6 +934,11 @@ fi %endif %changelog +* Wed Mar 20 2024 Michael Young - 4.18.1-1 +- update to xen-4.18.1 + rebase xen.gcc12.fixes.patch + remove patches now included or superceded upstream + * Wed Mar 13 2024 Michael Young - 4.18.0-7 - x86: Register File Data Sampling [XSA-452, CVE-2023-28746] - GhostRace: Speculative Race Conditions [XSA-453, CVE-2024-2193] diff --git a/xsa447.patch b/xsa447.patch deleted file mode 100644 index 2e26396..0000000 --- a/xsa447.patch +++ /dev/null @@ -1,117 +0,0 @@ -From 084c7312fa6c1d4a7fa343efa1d7d73693dafff4 Mon Sep 17 00:00:00 2001 -From: Michal Orzel -Date: Thu, 23 Nov 2023 15:53:02 +0100 -Subject: [PATCH] xen/arm: page: Avoid pointer overflow on cache clean & - invalidate - -On Arm32, after cleaning and invalidating the last dcache line of the top -domheap page i.e. VA = 0xfffff000 (as a result of flushing the page to -RAM), we end up adding the value of a dcache line size to the pointer -once again, which results in a pointer arithmetic overflow (with 64B line -size, operation 0xffffffc0 + 0x40 overflows to 0x0). Such behavior is -undefined and given the wide range of compiler versions we support, it is -difficult to determine what could happen in such scenario. - -Modify clean_and_invalidate_dcache_va_range() as well as -clean_dcache_va_range() and invalidate_dcache_va_range() due to similarity -of handling to prevent pointer arithmetic overflow. Modify the loops to -use an additional variable to store the index of the next cacheline. -Add an assert to prevent passing a region that wraps around which is -illegal and would end up in a page fault anyway (region 0-2MB is -unmapped). Lastly, return early if size passed is 0. - -Note that on Arm64, we don't have this problem given that the max VA -space we support is 48-bits. - -This is XSA-447 / CVE-2023-46837. - -Signed-off-by: Michal Orzel -Reviewed-by: Julien Grall ---- - xen/arch/arm/include/asm/page.h | 35 ++++++++++++++++++++++++++------- - 1 file changed, 28 insertions(+), 7 deletions(-) - -diff --git a/xen/arch/arm/include/asm/page.h b/xen/arch/arm/include/asm/page.h -index ebaf5964f114..69f817d1e68a 100644 ---- a/xen/arch/arm/include/asm/page.h -+++ b/xen/arch/arm/include/asm/page.h -@@ -162,6 +162,13 @@ static inline size_t read_dcache_line_bytes(void) - static inline int invalidate_dcache_va_range(const void *p, unsigned long size) - { - size_t cacheline_mask = dcache_line_bytes - 1; -+ unsigned long idx = 0; -+ -+ if ( !size ) -+ return 0; -+ -+ /* Passing a region that wraps around is illegal */ -+ ASSERT(((uintptr_t)p + size - 1) >= (uintptr_t)p); - - dsb(sy); /* So the CPU issues all writes to the range */ - -@@ -174,11 +181,11 @@ static inline int invalidate_dcache_va_range(const void *p, unsigned long size) - } - - for ( ; size >= dcache_line_bytes; -- p += dcache_line_bytes, size -= dcache_line_bytes ) -- asm volatile (__invalidate_dcache_one(0) : : "r" (p)); -+ idx += dcache_line_bytes, size -= dcache_line_bytes ) -+ asm volatile (__invalidate_dcache_one(0) : : "r" (p + idx)); - - if ( size > 0 ) -- asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (p)); -+ asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (p + idx)); - - dsb(sy); /* So we know the flushes happen before continuing */ - -@@ -188,14 +195,21 @@ static inline int invalidate_dcache_va_range(const void *p, unsigned long size) - static inline int clean_dcache_va_range(const void *p, unsigned long size) - { - size_t cacheline_mask = dcache_line_bytes - 1; -+ unsigned long idx = 0; -+ -+ if ( !size ) -+ return 0; -+ -+ /* Passing a region that wraps around is illegal */ -+ ASSERT(((uintptr_t)p + size - 1) >= (uintptr_t)p); - - dsb(sy); /* So the CPU issues all writes to the range */ - size += (uintptr_t)p & cacheline_mask; - size = (size + cacheline_mask) & ~cacheline_mask; - p = (void *)((uintptr_t)p & ~cacheline_mask); - for ( ; size >= dcache_line_bytes; -- p += dcache_line_bytes, size -= dcache_line_bytes ) -- asm volatile (__clean_dcache_one(0) : : "r" (p)); -+ idx += dcache_line_bytes, size -= dcache_line_bytes ) -+ asm volatile (__clean_dcache_one(0) : : "r" (p + idx)); - dsb(sy); /* So we know the flushes happen before continuing */ - /* ARM callers assume that dcache_* functions cannot fail. */ - return 0; -@@ -205,14 +219,21 @@ static inline int clean_and_invalidate_dcache_va_range - (const void *p, unsigned long size) - { - size_t cacheline_mask = dcache_line_bytes - 1; -+ unsigned long idx = 0; -+ -+ if ( !size ) -+ return 0; -+ -+ /* Passing a region that wraps around is illegal */ -+ ASSERT(((uintptr_t)p + size - 1) >= (uintptr_t)p); - - dsb(sy); /* So the CPU issues all writes to the range */ - size += (uintptr_t)p & cacheline_mask; - size = (size + cacheline_mask) & ~cacheline_mask; - p = (void *)((uintptr_t)p & ~cacheline_mask); - for ( ; size >= dcache_line_bytes; -- p += dcache_line_bytes, size -= dcache_line_bytes ) -- asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (p)); -+ idx += dcache_line_bytes, size -= dcache_line_bytes ) -+ asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (p + idx)); - dsb(sy); /* So we know the flushes happen before continuing */ - /* ARM callers assume that dcache_* functions cannot fail. */ - return 0; --- -2.40.1 - diff --git a/xsa449.patch b/xsa449.patch deleted file mode 100644 index 80aeac2..0000000 --- a/xsa449.patch +++ /dev/null @@ -1,89 +0,0 @@ -From d8b92b21b224126860978e4c604302f3c1e3bf75 Mon Sep 17 00:00:00 2001 -From: Roger Pau Monne -Date: Wed, 13 Dec 2023 15:51:59 +0100 -Subject: [PATCH] pci: fail device assignment if phantom functions cannot be - assigned -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The current behavior is that no error is reported if (some) phantom functions -fail to be assigned during device add or assignment, so the operation succeeds -even if some phantom functions are not correctly setup. - -This can lead to devices possibly being successfully assigned to a domU while -some of the device phantom functions are still assigned to dom0. Even when the -device is assigned domIO before being assigned to a domU phantom functions -might fail to be assigned to domIO, and also fail to be assigned to the domU, -leaving them assigned to dom0. - -Since the device can generate requests using the IDs of those phantom -functions, given the scenario above a device in such state would be in control -of a domU, but still capable of generating transactions that use a context ID -targeting dom0 owned memory. - -Modify device assign in order to attempt to deassign the device if phantom -functions failed to be assigned. - -Note that device addition is not modified in the same way, as in that case the -device is assigned to a trusted domain, and hence partial assign can lead to -device malfunction but not a security issue. - -This is XSA-449 / CVE-2023-46839 - -Fixes: 4e9950dc1bd2 ('IOMMU: add phantom function support') -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich ---- - xen/drivers/passthrough/pci.c | 27 +++++++++++++++++++++------ - 1 file changed, 21 insertions(+), 6 deletions(-) - -diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c -index 1439d1ef2b26..47c0eee7bdcc 100644 ---- a/xen/drivers/passthrough/pci.c -+++ b/xen/drivers/passthrough/pci.c -@@ -1488,11 +1488,10 @@ static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) - - pdev->fault.count = 0; - -- if ( (rc = iommu_call(hd->platform_ops, assign_device, d, devfn, -- pci_to_dev(pdev), flag)) ) -- goto done; -+ rc = iommu_call(hd->platform_ops, assign_device, d, devfn, pci_to_dev(pdev), -+ flag); - -- for ( ; pdev->phantom_stride; rc = 0 ) -+ while ( pdev->phantom_stride && !rc ) - { - devfn += pdev->phantom_stride; - if ( PCI_SLOT(devfn) != PCI_SLOT(pdev->devfn) ) -@@ -1503,8 +1502,24 @@ static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) - - done: - if ( rc ) -- printk(XENLOG_G_WARNING "%pd: assign (%pp) failed (%d)\n", -- d, &PCI_SBDF(seg, bus, devfn), rc); -+ { -+ printk(XENLOG_G_WARNING "%pd: assign %s(%pp) failed (%d)\n", -+ d, devfn != pdev->devfn ? "phantom function " : "", -+ &PCI_SBDF(seg, bus, devfn), rc); -+ -+ if ( devfn != pdev->devfn && deassign_device(d, seg, bus, pdev->devfn) ) -+ { -+ /* -+ * Device with phantom functions that failed to both assign and -+ * rollback. Mark the device as broken and crash the target domain, -+ * as the state of the functions at this point is unknown and Xen -+ * has no way to assert consistent context assignment among them. -+ */ -+ pdev->broken = true; -+ if ( !is_hardware_domain(d) && d != dom_io ) -+ domain_crash(d); -+ } -+ } - /* The device is assigned to dom_io so mark it as quarantined */ - else if ( d == dom_io ) - pdev->quarantine = true; --- -2.43.0 - diff --git a/xsa450.patch b/xsa450.patch deleted file mode 100644 index e94933b..0000000 --- a/xsa450.patch +++ /dev/null @@ -1,59 +0,0 @@ -From: Andrew Cooper -Subject: VT-d: Fix "else" vs "#endif" misplacement - -In domain_pgd_maddr() the "#endif" is misplaced with respect to "else". This -generates incorrect logic when CONFIG_HVM is compiled out, as the "else" body -is executed unconditionally. - -Rework the logic to use IS_ENABLED() instead of explicit #ifdef-ary, as it's -clearer to follow. This in turn involves adjusting p2m_get_pagetable() to -compile when CONFIG_HVM is disabled. - -This is XSA-450 / CVE-2023-46840. - -Reported-by: Reported-by: Teddy Astie -Fixes: 033ff90aa9c1 ("x86/P2M: p2m_{alloc,free}_ptp() and p2m_alloc_table() are HVM-only") -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/include/asm/p2m.h b/xen/arch/x86/include/asm/p2m.h -index 32f3f394b05a..6ada585eaac2 100644 ---- a/xen/arch/x86/include/asm/p2m.h -+++ b/xen/arch/x86/include/asm/p2m.h -@@ -435,7 +435,14 @@ static inline bool p2m_is_altp2m(const struct p2m_domain *p2m) - return p2m->p2m_class == p2m_alternate; - } - --#define p2m_get_pagetable(p2m) ((p2m)->phys_table) -+#ifdef CONFIG_HVM -+static inline pagetable_t p2m_get_pagetable(const struct p2m_domain *p2m) -+{ -+ return p2m->phys_table; -+} -+#else -+pagetable_t p2m_get_pagetable(const struct p2m_domain *p2m); -+#endif - - /* - * Ensure any deferred p2m TLB flush has been completed on all VCPUs. -diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c -index 99b642f12ef9..4244855032ee 100644 ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -438,15 +438,13 @@ static paddr_t domain_pgd_maddr(struct domain *d, paddr_t pgd_maddr, - - if ( pgd_maddr ) - /* nothing */; --#ifdef CONFIG_HVM -- else if ( iommu_use_hap_pt(d) ) -+ else if ( IS_ENABLED(CONFIG_HVM) && iommu_use_hap_pt(d) ) - { - pagetable_t pgt = p2m_get_pagetable(p2m_get_hostp2m(d)); - - pgd_maddr = pagetable_get_paddr(pgt); - } - else --#endif - { - if ( !hd->arch.vtd.pgd_maddr ) - { diff --git a/xsa451-4.18.patch b/xsa451-4.18.patch deleted file mode 100644 index 721f3f3..0000000 --- a/xsa451-4.18.patch +++ /dev/null @@ -1,188 +0,0 @@ -From: Jan Beulich -Subject: x86: account for shadow stack in exception-from-stub recovery - -Dealing with exceptions raised from within emulation stubs involves -discarding return address (replaced by exception related information). -Such discarding of course also requires removing the corresponding entry -from the shadow stack. - -Also amend the comment in fixup_exception_return(), to further clarify -why use of ptr[1] can't be an out-of-bounds access. - -While touching do_invalid_op() also add a missing fall-through -annotation. - -This is CVE-2023-46841 / XSA-451. - -Fixes: 209fb9919b50 ("x86/extable: Adjust extable handling to be shadow stack compatible") -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper - ---- a/xen/arch/x86/extable.c -+++ b/xen/arch/x86/extable.c -@@ -86,26 +86,29 @@ search_one_extable(const struct exceptio - } - - unsigned long --search_exception_table(const struct cpu_user_regs *regs) -+search_exception_table(const struct cpu_user_regs *regs, unsigned long *stub_ra) - { - const struct virtual_region *region = find_text_region(regs->rip); - unsigned long stub = this_cpu(stubs.addr); - - if ( region && region->ex ) -+ { -+ *stub_ra = 0; - return search_one_extable(region->ex, region->ex_end, regs->rip); -+ } - - if ( regs->rip >= stub + STUB_BUF_SIZE / 2 && - regs->rip < stub + STUB_BUF_SIZE && - regs->rsp > (unsigned long)regs && - regs->rsp < (unsigned long)get_cpu_info() ) - { -- unsigned long retptr = *(unsigned long *)regs->rsp; -+ unsigned long retaddr = *(unsigned long *)regs->rsp, fixup; - -- region = find_text_region(retptr); -- retptr = region && region->ex -- ? search_one_extable(region->ex, region->ex_end, retptr) -- : 0; -- if ( retptr ) -+ region = find_text_region(retaddr); -+ fixup = region && region->ex -+ ? search_one_extable(region->ex, region->ex_end, retaddr) -+ : 0; -+ if ( fixup ) - { - /* - * Put trap number and error code on the stack (in place of the -@@ -117,7 +120,8 @@ search_exception_table(const struct cpu_ - }; - - *(unsigned long *)regs->rsp = token.raw; -- return retptr; -+ *stub_ra = retaddr; -+ return fixup; - } - } - ---- a/xen/arch/x86/include/asm/uaccess.h -+++ b/xen/arch/x86/include/asm/uaccess.h -@@ -421,7 +421,8 @@ union stub_exception_token { - unsigned long raw; - }; - --extern unsigned long search_exception_table(const struct cpu_user_regs *regs); -+extern unsigned long search_exception_table(const struct cpu_user_regs *regs, -+ unsigned long *stub_ra); - extern void sort_exception_tables(void); - extern void sort_exception_table(struct exception_table_entry *start, - const struct exception_table_entry *stop); ---- a/xen/arch/x86/traps.c -+++ b/xen/arch/x86/traps.c -@@ -845,7 +845,7 @@ void do_unhandled_trap(struct cpu_user_r - } - - static void fixup_exception_return(struct cpu_user_regs *regs, -- unsigned long fixup) -+ unsigned long fixup, unsigned long stub_ra) - { - if ( IS_ENABLED(CONFIG_XEN_SHSTK) ) - { -@@ -862,7 +862,8 @@ static void fixup_exception_return(struc - /* - * Search for %rip. The shstk currently looks like this: - * -- * ... [Likely pointed to by SSP] -+ * tok [Supervisor token, == &tok | BUSY, only with FRED inactive] -+ * ... [Pointed to by SSP for most exceptions, empty in IST cases] - * %cs [== regs->cs] - * %rip [== regs->rip] - * SSP [Likely points to 3 slots higher, above %cs] -@@ -880,7 +881,56 @@ static void fixup_exception_return(struc - */ - if ( ptr[0] == regs->rip && ptr[1] == regs->cs ) - { -+ unsigned long primary_shstk = -+ (ssp & ~(STACK_SIZE - 1)) + -+ (PRIMARY_SHSTK_SLOT + 1) * PAGE_SIZE - 8; -+ - wrss(fixup, ptr); -+ -+ if ( !stub_ra ) -+ goto shstk_done; -+ -+ /* -+ * Stub recovery ought to happen only when the outer context -+ * was on the main shadow stack. We need to also "pop" the -+ * stub's return address from the interrupted context's shadow -+ * stack. That is, -+ * - if we're still on the main stack, we need to move the -+ * entire stack (up to and including the exception frame) -+ * up by one slot, incrementing the original SSP in the -+ * exception frame, -+ * - if we're on an IST stack, we need to increment the -+ * original SSP. -+ */ -+ BUG_ON((ptr[-1] ^ primary_shstk) >> PAGE_SHIFT); -+ -+ if ( (ssp ^ primary_shstk) >> PAGE_SHIFT ) -+ { -+ /* -+ * We're on an IST stack. First make sure the two return -+ * addresses actually match. Then increment the interrupted -+ * context's SSP. -+ */ -+ BUG_ON(stub_ra != *(unsigned long*)ptr[-1]); -+ wrss(ptr[-1] + 8, &ptr[-1]); -+ goto shstk_done; -+ } -+ -+ /* Make sure the two return addresses actually match. */ -+ BUG_ON(stub_ra != ptr[2]); -+ -+ /* Move exception frame, updating SSP there. */ -+ wrss(ptr[1], &ptr[2]); /* %cs */ -+ wrss(ptr[0], &ptr[1]); /* %rip */ -+ wrss(ptr[-1] + 8, &ptr[0]); /* SSP */ -+ -+ /* Move all newer entries. */ -+ while ( --ptr != _p(ssp) ) -+ wrss(ptr[-1], &ptr[0]); -+ -+ /* Finally account for our own stack having shifted up. */ -+ asm volatile ( "incsspd %0" :: "r" (2) ); -+ - goto shstk_done; - } - } -@@ -901,7 +951,8 @@ static void fixup_exception_return(struc - - static bool extable_fixup(struct cpu_user_regs *regs, bool print) - { -- unsigned long fixup = search_exception_table(regs); -+ unsigned long stub_ra = 0; -+ unsigned long fixup = search_exception_table(regs, &stub_ra); - - if ( unlikely(fixup == 0) ) - return false; -@@ -915,7 +966,7 @@ static bool extable_fixup(struct cpu_use - vector_name(regs->entry_vector), regs->error_code, - _p(regs->rip), _p(regs->rip), _p(fixup)); - -- fixup_exception_return(regs, fixup); -+ fixup_exception_return(regs, fixup, stub_ra); - this_cpu(last_extable_addr) = regs->rip; - - return true; -@@ -1183,7 +1234,8 @@ void do_invalid_op(struct cpu_user_regs - { - case BUGFRAME_run_fn: - case BUGFRAME_warn: -- fixup_exception_return(regs, (unsigned long)eip); -+ fixup_exception_return(regs, (unsigned long)eip, 0); -+ fallthrough; - case BUGFRAME_bug: - case BUGFRAME_assert: - return; diff --git a/xsa452-4.18-1.patch b/xsa452-4.18-1.patch deleted file mode 100644 index cae848f..0000000 --- a/xsa452-4.18-1.patch +++ /dev/null @@ -1,304 +0,0 @@ -From: Andrew Cooper -Subject: x86/entry: Introduce EFRAME_* constants - -restore_all_guest() does a lot of manipulation of the stack after popping the -GPRs, and uses raw %rsp displacements to do so. Also, almost all entrypaths -use raw %rsp displacements prior to pushing GPRs. - -Provide better mnemonics, to aid readability and reduce the chance of errors -when editing. - -No functional change. The resulting binary is identical. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -(cherry picked from commit 37541208f119a9c552c6c6c3246ea61be0d44035) - -diff --git a/xen/arch/x86/x86_64/asm-offsets.c b/xen/arch/x86/x86_64/asm-offsets.c -index 57b73a4e6214..2fc4d9130a4d 100644 ---- a/xen/arch/x86/x86_64/asm-offsets.c -+++ b/xen/arch/x86/x86_64/asm-offsets.c -@@ -51,6 +51,23 @@ void __dummy__(void) - OFFSET(UREGS_kernel_sizeof, struct cpu_user_regs, es); - BLANK(); - -+ /* -+ * EFRAME_* is for the entry/exit logic where %rsp is pointing at -+ * UREGS_error_code and GPRs are still/already guest values. -+ */ -+#define OFFSET_EF(sym, mem) \ -+ DEFINE(sym, offsetof(struct cpu_user_regs, mem) - \ -+ offsetof(struct cpu_user_regs, error_code)) -+ -+ OFFSET_EF(EFRAME_entry_vector, entry_vector); -+ OFFSET_EF(EFRAME_rip, rip); -+ OFFSET_EF(EFRAME_cs, cs); -+ OFFSET_EF(EFRAME_eflags, eflags); -+ OFFSET_EF(EFRAME_rsp, rsp); -+ BLANK(); -+ -+#undef OFFSET_EF -+ - OFFSET(VCPU_processor, struct vcpu, processor); - OFFSET(VCPU_domain, struct vcpu, domain); - OFFSET(VCPU_vcpu_info, struct vcpu, vcpu_info_area.map); -diff --git a/xen/arch/x86/x86_64/compat/entry.S b/xen/arch/x86/x86_64/compat/entry.S -index fcc3a721f147..cb473f08eebd 100644 ---- a/xen/arch/x86/x86_64/compat/entry.S -+++ b/xen/arch/x86/x86_64/compat/entry.S -@@ -15,7 +15,7 @@ ENTRY(entry_int82) - ENDBR64 - ALTERNATIVE "", clac, X86_FEATURE_XEN_SMAP - pushq $0 -- movl $HYPERCALL_VECTOR, 4(%rsp) -+ movl $HYPERCALL_VECTOR, EFRAME_entry_vector(%rsp) - SAVE_ALL compat=1 /* DPL1 gate, restricted to 32bit PV guests only. */ - - SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, %rdx=0, Clob: acd */ -diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S -index 9a7b129aa7e4..968da9d727b1 100644 ---- a/xen/arch/x86/x86_64/entry.S -+++ b/xen/arch/x86/x86_64/entry.S -@@ -190,15 +190,15 @@ restore_all_guest: - SPEC_CTRL_EXIT_TO_PV /* Req: a=spec_ctrl %rsp=regs/cpuinfo, Clob: cd */ - - RESTORE_ALL -- testw $TRAP_syscall,4(%rsp) -+ testw $TRAP_syscall, EFRAME_entry_vector(%rsp) - jz iret_exit_to_guest - -- movq 24(%rsp),%r11 # RFLAGS -+ mov EFRAME_eflags(%rsp), %r11 - andq $~(X86_EFLAGS_IOPL | X86_EFLAGS_VM), %r11 - orq $X86_EFLAGS_IF,%r11 - - /* Don't use SYSRET path if the return address is not canonical. */ -- movq 8(%rsp),%rcx -+ mov EFRAME_rip(%rsp), %rcx - sarq $47,%rcx - incl %ecx - cmpl $1,%ecx -@@ -213,20 +213,20 @@ restore_all_guest: - ALTERNATIVE "", rag_clrssbsy, X86_FEATURE_XEN_SHSTK - #endif - -- movq 8(%rsp), %rcx # RIP -- cmpw $FLAT_USER_CS32,16(%rsp)# CS -- movq 32(%rsp),%rsp # RSP -+ mov EFRAME_rip(%rsp), %rcx -+ cmpw $FLAT_USER_CS32, EFRAME_cs(%rsp) -+ mov EFRAME_rsp(%rsp), %rsp - je 1f - sysretq - 1: sysretl - - ALIGN - .Lrestore_rcx_iret_exit_to_guest: -- movq 8(%rsp), %rcx # RIP -+ mov EFRAME_rip(%rsp), %rcx - /* No special register assumptions. */ - iret_exit_to_guest: -- andl $~(X86_EFLAGS_IOPL | X86_EFLAGS_VM), 24(%rsp) -- orl $X86_EFLAGS_IF,24(%rsp) -+ andl $~(X86_EFLAGS_IOPL | X86_EFLAGS_VM), EFRAME_eflags(%rsp) -+ orl $X86_EFLAGS_IF, EFRAME_eflags(%rsp) - addq $8,%rsp - .Lft0: iretq - _ASM_PRE_EXTABLE(.Lft0, handle_exception) -@@ -257,7 +257,7 @@ ENTRY(lstar_enter) - pushq $FLAT_KERNEL_CS64 - pushq %rcx - pushq $0 -- movl $TRAP_syscall, 4(%rsp) -+ movl $TRAP_syscall, EFRAME_entry_vector(%rsp) - SAVE_ALL - - SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, %rdx=0, Clob: acd */ -@@ -294,7 +294,7 @@ ENTRY(cstar_enter) - pushq $FLAT_USER_CS32 - pushq %rcx - pushq $0 -- movl $TRAP_syscall, 4(%rsp) -+ movl $TRAP_syscall, EFRAME_entry_vector(%rsp) - SAVE_ALL - - SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, %rdx=0, Clob: acd */ -@@ -335,7 +335,7 @@ GLOBAL(sysenter_eflags_saved) - pushq $3 /* ring 3 null cs */ - pushq $0 /* null rip */ - pushq $0 -- movl $TRAP_syscall, 4(%rsp) -+ movl $TRAP_syscall, EFRAME_entry_vector(%rsp) - SAVE_ALL - - SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, %rdx=0, Clob: acd */ -@@ -389,7 +389,7 @@ ENTRY(int80_direct_trap) - ENDBR64 - ALTERNATIVE "", clac, X86_FEATURE_XEN_SMAP - pushq $0 -- movl $0x80, 4(%rsp) -+ movl $0x80, EFRAME_entry_vector(%rsp) - SAVE_ALL - - SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, %rdx=0, Clob: acd */ -@@ -649,7 +649,7 @@ ret_from_intr: - .section .init.text, "ax", @progbits - ENTRY(early_page_fault) - ENDBR64 -- movl $X86_EXC_PF, 4(%rsp) -+ movl $X86_EXC_PF, EFRAME_entry_vector(%rsp) - SAVE_ALL - movq %rsp, %rdi - call do_early_page_fault -@@ -716,7 +716,7 @@ ENTRY(common_interrupt) - - ENTRY(entry_PF) - ENDBR64 -- movl $X86_EXC_PF, 4(%rsp) -+ movl $X86_EXC_PF, EFRAME_entry_vector(%rsp) - /* No special register assumptions. */ - GLOBAL(handle_exception) - ALTERNATIVE "", clac, X86_FEATURE_XEN_SMAP -@@ -890,90 +890,90 @@ FATAL_exception_with_ints_disabled: - ENTRY(entry_DE) - ENDBR64 - pushq $0 -- movl $X86_EXC_DE, 4(%rsp) -+ movl $X86_EXC_DE, EFRAME_entry_vector(%rsp) - jmp handle_exception - - ENTRY(entry_MF) - ENDBR64 - pushq $0 -- movl $X86_EXC_MF, 4(%rsp) -+ movl $X86_EXC_MF, EFRAME_entry_vector(%rsp) - jmp handle_exception - - ENTRY(entry_XM) - ENDBR64 - pushq $0 -- movl $X86_EXC_XM, 4(%rsp) -+ movl $X86_EXC_XM, EFRAME_entry_vector(%rsp) - jmp handle_exception - - ENTRY(entry_NM) - ENDBR64 - pushq $0 -- movl $X86_EXC_NM, 4(%rsp) -+ movl $X86_EXC_NM, EFRAME_entry_vector(%rsp) - jmp handle_exception - - ENTRY(entry_DB) - ENDBR64 - pushq $0 -- movl $X86_EXC_DB, 4(%rsp) -+ movl $X86_EXC_DB, EFRAME_entry_vector(%rsp) - jmp handle_ist_exception - - ENTRY(entry_BP) - ENDBR64 - pushq $0 -- movl $X86_EXC_BP, 4(%rsp) -+ movl $X86_EXC_BP, EFRAME_entry_vector(%rsp) - jmp handle_exception - - ENTRY(entry_OF) - ENDBR64 - pushq $0 -- movl $X86_EXC_OF, 4(%rsp) -+ movl $X86_EXC_OF, EFRAME_entry_vector(%rsp) - jmp handle_exception - - ENTRY(entry_BR) - ENDBR64 - pushq $0 -- movl $X86_EXC_BR, 4(%rsp) -+ movl $X86_EXC_BR, EFRAME_entry_vector(%rsp) - jmp handle_exception - - ENTRY(entry_UD) - ENDBR64 - pushq $0 -- movl $X86_EXC_UD, 4(%rsp) -+ movl $X86_EXC_UD, EFRAME_entry_vector(%rsp) - jmp handle_exception - - ENTRY(entry_TS) - ENDBR64 -- movl $X86_EXC_TS, 4(%rsp) -+ movl $X86_EXC_TS, EFRAME_entry_vector(%rsp) - jmp handle_exception - - ENTRY(entry_NP) - ENDBR64 -- movl $X86_EXC_NP, 4(%rsp) -+ movl $X86_EXC_NP, EFRAME_entry_vector(%rsp) - jmp handle_exception - - ENTRY(entry_SS) - ENDBR64 -- movl $X86_EXC_SS, 4(%rsp) -+ movl $X86_EXC_SS, EFRAME_entry_vector(%rsp) - jmp handle_exception - - ENTRY(entry_GP) - ENDBR64 -- movl $X86_EXC_GP, 4(%rsp) -+ movl $X86_EXC_GP, EFRAME_entry_vector(%rsp) - jmp handle_exception - - ENTRY(entry_AC) - ENDBR64 -- movl $X86_EXC_AC, 4(%rsp) -+ movl $X86_EXC_AC, EFRAME_entry_vector(%rsp) - jmp handle_exception - - ENTRY(entry_CP) - ENDBR64 -- movl $X86_EXC_CP, 4(%rsp) -+ movl $X86_EXC_CP, EFRAME_entry_vector(%rsp) - jmp handle_exception - - ENTRY(entry_DF) - ENDBR64 -- movl $X86_EXC_DF, 4(%rsp) -+ movl $X86_EXC_DF, EFRAME_entry_vector(%rsp) - /* Set AC to reduce chance of further SMAP faults */ - ALTERNATIVE "", stac, X86_FEATURE_XEN_SMAP - SAVE_ALL -@@ -998,7 +998,7 @@ ENTRY(entry_DF) - ENTRY(entry_NMI) - ENDBR64 - pushq $0 -- movl $X86_EXC_NMI, 4(%rsp) -+ movl $X86_EXC_NMI, EFRAME_entry_vector(%rsp) - handle_ist_exception: - ALTERNATIVE "", clac, X86_FEATURE_XEN_SMAP - SAVE_ALL -@@ -1130,7 +1130,7 @@ handle_ist_exception: - ENTRY(entry_MC) - ENDBR64 - pushq $0 -- movl $X86_EXC_MC, 4(%rsp) -+ movl $X86_EXC_MC, EFRAME_entry_vector(%rsp) - jmp handle_ist_exception - - /* No op trap handler. Required for kexec crash path. */ -@@ -1167,7 +1167,7 @@ autogen_stubs: /* Automatically generated stubs. */ - 1: - ENDBR64 - pushq $0 -- movb $vec,4(%rsp) -+ movb $vec, EFRAME_entry_vector(%rsp) - jmp common_interrupt - - entrypoint 1b -@@ -1181,7 +1181,7 @@ autogen_stubs: /* Automatically generated stubs. */ - test $8,%spl /* 64bit exception frames are 16 byte aligned, but the word */ - jz 2f /* size is 8 bytes. Check whether the processor gave us an */ - pushq $0 /* error code, and insert an empty one if not. */ --2: movb $vec,4(%rsp) -+2: movb $vec, EFRAME_entry_vector(%rsp) - jmp handle_exception - - entrypoint 1b diff --git a/xsa452-4.18-2.patch b/xsa452-4.18-2.patch deleted file mode 100644 index 4535397..0000000 --- a/xsa452-4.18-2.patch +++ /dev/null @@ -1,90 +0,0 @@ -From: Andrew Cooper -Subject: x86: Resync intel-family.h from Linux - -From v6.8-rc6 - -Signed-off-by: Andrew Cooper -Acked-by: Jan Beulich -(cherry picked from commit 195e75371b13c4f7ecdf7b5c50aed0d02f2d7ce8) - -diff --git a/xen/arch/x86/include/asm/intel-family.h b/xen/arch/x86/include/asm/intel-family.h -index ffc49151befe..b65e9c46b922 100644 ---- a/xen/arch/x86/include/asm/intel-family.h -+++ b/xen/arch/x86/include/asm/intel-family.h -@@ -26,6 +26,9 @@ - * _G - parts with extra graphics on - * _X - regular server parts - * _D - micro server parts -+ * _N,_P - other mobile parts -+ * _H - premium mobile parts -+ * _S - other client parts - * - * Historical OPTDIFFs: - * -@@ -37,6 +40,9 @@ - * their own names :-( - */ - -+/* Wildcard match for FAM6 so X86_MATCH_INTEL_FAM6_MODEL(ANY) works */ -+#define INTEL_FAM6_ANY X86_MODEL_ANY -+ - #define INTEL_FAM6_CORE_YONAH 0x0E - - #define INTEL_FAM6_CORE2_MEROM 0x0F -@@ -93,8 +99,6 @@ - #define INTEL_FAM6_ICELAKE_L 0x7E /* Sunny Cove */ - #define INTEL_FAM6_ICELAKE_NNPI 0x9D /* Sunny Cove */ - --#define INTEL_FAM6_LAKEFIELD 0x8A /* Sunny Cove / Tremont */ -- - #define INTEL_FAM6_ROCKETLAKE 0xA7 /* Cypress Cove */ - - #define INTEL_FAM6_TIGERLAKE_L 0x8C /* Willow Cove */ -@@ -102,12 +106,31 @@ - - #define INTEL_FAM6_SAPPHIRERAPIDS_X 0x8F /* Golden Cove */ - -+#define INTEL_FAM6_EMERALDRAPIDS_X 0xCF -+ -+#define INTEL_FAM6_GRANITERAPIDS_X 0xAD -+#define INTEL_FAM6_GRANITERAPIDS_D 0xAE -+ -+/* "Hybrid" Processors (P-Core/E-Core) */ -+ -+#define INTEL_FAM6_LAKEFIELD 0x8A /* Sunny Cove / Tremont */ -+ - #define INTEL_FAM6_ALDERLAKE 0x97 /* Golden Cove / Gracemont */ - #define INTEL_FAM6_ALDERLAKE_L 0x9A /* Golden Cove / Gracemont */ - --#define INTEL_FAM6_RAPTORLAKE 0xB7 -+#define INTEL_FAM6_RAPTORLAKE 0xB7 /* Raptor Cove / Enhanced Gracemont */ -+#define INTEL_FAM6_RAPTORLAKE_P 0xBA -+#define INTEL_FAM6_RAPTORLAKE_S 0xBF -+ -+#define INTEL_FAM6_METEORLAKE 0xAC -+#define INTEL_FAM6_METEORLAKE_L 0xAA -+ -+#define INTEL_FAM6_ARROWLAKE_H 0xC5 -+#define INTEL_FAM6_ARROWLAKE 0xC6 -+ -+#define INTEL_FAM6_LUNARLAKE_M 0xBD - --/* "Small Core" Processors (Atom) */ -+/* "Small Core" Processors (Atom/E-Core) */ - - #define INTEL_FAM6_ATOM_BONNELL 0x1C /* Diamondville, Pineview */ - #define INTEL_FAM6_ATOM_BONNELL_MID 0x26 /* Silverthorne, Lincroft */ -@@ -134,6 +157,13 @@ - #define INTEL_FAM6_ATOM_TREMONT 0x96 /* Elkhart Lake */ - #define INTEL_FAM6_ATOM_TREMONT_L 0x9C /* Jasper Lake */ - -+#define INTEL_FAM6_ATOM_GRACEMONT 0xBE /* Alderlake N */ -+ -+#define INTEL_FAM6_ATOM_CRESTMONT_X 0xAF /* Sierra Forest */ -+#define INTEL_FAM6_ATOM_CRESTMONT 0xB6 /* Grand Ridge */ -+ -+#define INTEL_FAM6_ATOM_DARKMONT_X 0xDD /* Clearwater Forest */ -+ - /* Xeon Phi */ - - #define INTEL_FAM6_XEON_PHI_KNL 0x57 /* Knights Landing */ diff --git a/xsa452-4.18-3.patch b/xsa452-4.18-3.patch deleted file mode 100644 index bc9059c..0000000 --- a/xsa452-4.18-3.patch +++ /dev/null @@ -1,135 +0,0 @@ -From: Andrew Cooper -Subject: x86/vmx: Perform VERW flushing later in the VMExit path - -Broken out of the following patch because this change is subtle enough on its -own. See it for the rational of why we're moving VERW. - -As for how, extend the trick already used to hold one condition in -flags (RESUME vs LAUNCH) through the POPing of GPRs. - -Move the MOV CR earlier. Intel specify flags to be undefined across it. - -Encode the two conditions we want using SF and PF. See the code comment for -exactly how. - -Leave a comment to explain the lack of any content around -SPEC_CTRL_EXIT_TO_VMX, but leave the block in place. Sods law says if we -delete it, we'll need to reintroduce it. - -This is part of XSA-452 / CVE-2023-28746. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -(cherry picked from commit 475fa20b7384464210f42bad7195f87bd6f1c63f) - -diff --git a/xen/arch/x86/hvm/vmx/entry.S b/xen/arch/x86/hvm/vmx/entry.S -index e3f60d5a82f7..1bead826caa3 100644 ---- a/xen/arch/x86/hvm/vmx/entry.S -+++ b/xen/arch/x86/hvm/vmx/entry.S -@@ -87,17 +87,39 @@ UNLIKELY_END(realmode) - - /* WARNING! `ret`, `call *`, `jmp *` not safe beyond this point. */ - /* SPEC_CTRL_EXIT_TO_VMX Req: %rsp=regs/cpuinfo Clob: */ -- DO_SPEC_CTRL_COND_VERW -+ /* -+ * All speculation safety work happens to be elsewhere. VERW is after -+ * popping the GPRs, while restoring the guest MSR_SPEC_CTRL is left -+ * to the MSR load list. -+ */ - - mov VCPU_hvm_guest_cr2(%rbx),%rax -+ mov %rax, %cr2 -+ -+ /* -+ * We need to perform two conditional actions (VERW, and Resume vs -+ * Launch) after popping GPRs. With some cunning, we can encode both -+ * of these in eflags together. -+ * -+ * Parity is only calculated over the bottom byte of the answer, while -+ * Sign is simply the top bit. -+ * -+ * Therefore, the final OR instruction ends up producing: -+ * SF = VCPU_vmx_launched -+ * PF = !SCF_verw -+ */ -+ BUILD_BUG_ON(SCF_verw & ~0xff) -+ movzbl VCPU_vmx_launched(%rbx), %ecx -+ shl $31, %ecx -+ movzbl CPUINFO_spec_ctrl_flags(%rsp), %eax -+ and $SCF_verw, %eax -+ or %eax, %ecx - - pop %r15 - pop %r14 - pop %r13 - pop %r12 - pop %rbp -- mov %rax,%cr2 -- cmpb $0,VCPU_vmx_launched(%rbx) - pop %rbx - pop %r11 - pop %r10 -@@ -108,7 +130,13 @@ UNLIKELY_END(realmode) - pop %rdx - pop %rsi - pop %rdi -- je .Lvmx_launch -+ -+ jpe .L_skip_verw -+ /* VERW clobbers ZF, but preserves all others, including SF. */ -+ verw STK_REL(CPUINFO_verw_sel, CPUINFO_error_code)(%rsp) -+.L_skip_verw: -+ -+ jns .Lvmx_launch - - /*.Lvmx_resume:*/ - VMRESUME -diff --git a/xen/arch/x86/include/asm/asm_defns.h b/xen/arch/x86/include/asm/asm_defns.h -index baaaccb26e17..56ae26e54265 100644 ---- a/xen/arch/x86/include/asm/asm_defns.h -+++ b/xen/arch/x86/include/asm/asm_defns.h -@@ -81,6 +81,14 @@ register unsigned long current_stack_pointer asm("rsp"); - - #ifdef __ASSEMBLY__ - -+.macro BUILD_BUG_ON condstr, cond:vararg -+ .if \cond -+ .error "Condition \"\condstr\" not satisfied" -+ .endif -+.endm -+/* preprocessor macro to make error message more user friendly */ -+#define BUILD_BUG_ON(cond) BUILD_BUG_ON #cond, cond -+ - #ifdef HAVE_AS_QUOTED_SYM - #define SUBSECTION_LBL(tag) \ - .ifndef .L.tag; \ -diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h -index 6cb7c1b9491e..525745a06608 100644 ---- a/xen/arch/x86/include/asm/spec_ctrl_asm.h -+++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h -@@ -152,6 +152,13 @@ - #endif - .endm - -+/* -+ * Helper to improve the readibility of stack dispacements with %rsp in -+ * unusual positions. Both @field and @top_of_stack should be constants from -+ * the same object. @top_of_stack should be where %rsp is currently pointing. -+ */ -+#define STK_REL(field, top_of_stk) ((field) - (top_of_stk)) -+ - .macro DO_SPEC_CTRL_COND_VERW - /* - * Requires %rsp=cpuinfo -diff --git a/xen/arch/x86/x86_64/asm-offsets.c b/xen/arch/x86/x86_64/asm-offsets.c -index 2fc4d9130a4d..0d336788989f 100644 ---- a/xen/arch/x86/x86_64/asm-offsets.c -+++ b/xen/arch/x86/x86_64/asm-offsets.c -@@ -135,6 +135,7 @@ void __dummy__(void) - #endif - - OFFSET(CPUINFO_guest_cpu_user_regs, struct cpu_info, guest_cpu_user_regs); -+ OFFSET(CPUINFO_error_code, struct cpu_info, guest_cpu_user_regs.error_code); - OFFSET(CPUINFO_verw_sel, struct cpu_info, verw_sel); - OFFSET(CPUINFO_current_vcpu, struct cpu_info, current_vcpu); - OFFSET(CPUINFO_per_cpu_offset, struct cpu_info, per_cpu_offset); diff --git a/xsa452-4.18-4.patch b/xsa452-4.18-4.patch deleted file mode 100644 index 0ccff77..0000000 --- a/xsa452-4.18-4.patch +++ /dev/null @@ -1,197 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Perform VERW flushing later in exit paths - -On parts vulnerable to RFDS, VERW's side effects are extended to scrub all -non-architectural entries in various Physical Register Files. To remove all -of Xen's values, the VERW must be after popping the GPRs. - -Rework SPEC_CTRL_COND_VERW to default to an CPUINFO_error_code %rsp position, -but with overrides for other contexts. Identify that it clobbers eflags; this -is particularly relevant for the SYSRET path. - -For the IST exit return to Xen, have the main SPEC_CTRL_EXIT_TO_XEN put a -shadow copy of spec_ctrl_flags, as GPRs can't be used at the point we want to -issue the VERW. - -This is part of XSA-452 / CVE-2023-28746. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -(cherry picked from commit 0a666cf2cd99df6faf3eebc81a1fc286e4eca4c7) - -diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h -index 525745a06608..13acebc75dff 100644 ---- a/xen/arch/x86/include/asm/spec_ctrl_asm.h -+++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h -@@ -159,16 +159,23 @@ - */ - #define STK_REL(field, top_of_stk) ((field) - (top_of_stk)) - --.macro DO_SPEC_CTRL_COND_VERW -+.macro SPEC_CTRL_COND_VERW \ -+ scf=STK_REL(CPUINFO_spec_ctrl_flags, CPUINFO_error_code), \ -+ sel=STK_REL(CPUINFO_verw_sel, CPUINFO_error_code) - /* -- * Requires %rsp=cpuinfo -+ * Requires \scf and \sel as %rsp-relative expressions -+ * Clobbers eflags -+ * -+ * VERW needs to run after guest GPRs have been restored, where only %rsp is -+ * good to use. Default to expecting %rsp pointing at CPUINFO_error_code. -+ * Contexts where this is not true must provide an alternative \scf and \sel. - * - * Issue a VERW for its flushing side effect, if indicated. This is a Spectre - * v1 gadget, but the IRET/VMEntry is serialising. - */ -- testb $SCF_verw, CPUINFO_spec_ctrl_flags(%rsp) -+ testb $SCF_verw, \scf(%rsp) - jz .L\@_verw_skip -- verw CPUINFO_verw_sel(%rsp) -+ verw \sel(%rsp) - .L\@_verw_skip: - .endm - -@@ -286,8 +293,6 @@ - */ - ALTERNATIVE "", DO_SPEC_CTRL_EXIT_TO_GUEST, X86_FEATURE_SC_MSR_PV - -- DO_SPEC_CTRL_COND_VERW -- - ALTERNATIVE "", DO_SPEC_CTRL_DIV, X86_FEATURE_SC_DIV - .endm - -@@ -367,7 +372,7 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): - */ - .macro SPEC_CTRL_EXIT_TO_XEN - /* -- * Requires %r12=ist_exit, %r14=stack_end -+ * Requires %r12=ist_exit, %r14=stack_end, %rsp=regs - * Clobbers %rax, %rbx, %rcx, %rdx - */ - movzbl STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14), %ebx -@@ -395,11 +400,18 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): - test %r12, %r12 - jz .L\@_skip_ist_exit - -- /* Logically DO_SPEC_CTRL_COND_VERW but without the %rsp=cpuinfo dependency */ -- testb $SCF_verw, %bl -- jz .L\@_skip_verw -- verw STACK_CPUINFO_FIELD(verw_sel)(%r14) --.L\@_skip_verw: -+ /* -+ * Stash SCF and verw_sel above eflags in the case of an IST_exit. The -+ * VERW logic needs to run after guest GPRs have been restored; i.e. where -+ * we cannot use %r12 or %r14 for the purposes they have here. -+ * -+ * When the CPU pushed this exception frame, it zero-extended eflags. -+ * Therefore it is safe for the VERW logic to look at the stashed SCF -+ * outside of the ist_exit condition. Also, this stashing won't influence -+ * any other restore_all_guest() paths. -+ */ -+ or $(__HYPERVISOR_DS32 << 16), %ebx -+ mov %ebx, UREGS_eflags + 4(%rsp) /* EFRAME_shadow_scf/sel */ - - ALTERNATIVE "", DO_SPEC_CTRL_DIV, X86_FEATURE_SC_DIV - -diff --git a/xen/arch/x86/x86_64/asm-offsets.c b/xen/arch/x86/x86_64/asm-offsets.c -index 0d336788989f..85c7d0c98967 100644 ---- a/xen/arch/x86/x86_64/asm-offsets.c -+++ b/xen/arch/x86/x86_64/asm-offsets.c -@@ -55,14 +55,22 @@ void __dummy__(void) - * EFRAME_* is for the entry/exit logic where %rsp is pointing at - * UREGS_error_code and GPRs are still/already guest values. - */ --#define OFFSET_EF(sym, mem) \ -+#define OFFSET_EF(sym, mem, ...) \ - DEFINE(sym, offsetof(struct cpu_user_regs, mem) - \ -- offsetof(struct cpu_user_regs, error_code)) -+ offsetof(struct cpu_user_regs, error_code) __VA_ARGS__) - - OFFSET_EF(EFRAME_entry_vector, entry_vector); - OFFSET_EF(EFRAME_rip, rip); - OFFSET_EF(EFRAME_cs, cs); - OFFSET_EF(EFRAME_eflags, eflags); -+ -+ /* -+ * These aren't real fields. They're spare space, used by the IST -+ * exit-to-xen path. -+ */ -+ OFFSET_EF(EFRAME_shadow_scf, eflags, +4); -+ OFFSET_EF(EFRAME_shadow_sel, eflags, +6); -+ - OFFSET_EF(EFRAME_rsp, rsp); - BLANK(); - -@@ -136,6 +144,7 @@ void __dummy__(void) - - OFFSET(CPUINFO_guest_cpu_user_regs, struct cpu_info, guest_cpu_user_regs); - OFFSET(CPUINFO_error_code, struct cpu_info, guest_cpu_user_regs.error_code); -+ OFFSET(CPUINFO_rip, struct cpu_info, guest_cpu_user_regs.rip); - OFFSET(CPUINFO_verw_sel, struct cpu_info, verw_sel); - OFFSET(CPUINFO_current_vcpu, struct cpu_info, current_vcpu); - OFFSET(CPUINFO_per_cpu_offset, struct cpu_info, per_cpu_offset); -diff --git a/xen/arch/x86/x86_64/compat/entry.S b/xen/arch/x86/x86_64/compat/entry.S -index cb473f08eebd..3bbe3a79a5b7 100644 ---- a/xen/arch/x86/x86_64/compat/entry.S -+++ b/xen/arch/x86/x86_64/compat/entry.S -@@ -161,6 +161,12 @@ ENTRY(compat_restore_all_guest) - SPEC_CTRL_EXIT_TO_PV /* Req: a=spec_ctrl %rsp=regs/cpuinfo, Clob: cd */ - - RESTORE_ALL adj=8 compat=1 -+ -+ /* Account for ev/ec having already been popped off the stack. */ -+ SPEC_CTRL_COND_VERW \ -+ scf=STK_REL(CPUINFO_spec_ctrl_flags, CPUINFO_rip), \ -+ sel=STK_REL(CPUINFO_verw_sel, CPUINFO_rip) -+ - .Lft0: iretq - _ASM_PRE_EXTABLE(.Lft0, handle_exception) - -diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S -index 968da9d727b1..2c7512130f49 100644 ---- a/xen/arch/x86/x86_64/entry.S -+++ b/xen/arch/x86/x86_64/entry.S -@@ -214,6 +214,9 @@ restore_all_guest: - #endif - - mov EFRAME_rip(%rsp), %rcx -+ -+ SPEC_CTRL_COND_VERW /* Req: %rsp=eframe Clob: efl */ -+ - cmpw $FLAT_USER_CS32, EFRAME_cs(%rsp) - mov EFRAME_rsp(%rsp), %rsp - je 1f -@@ -227,6 +230,9 @@ restore_all_guest: - iret_exit_to_guest: - andl $~(X86_EFLAGS_IOPL | X86_EFLAGS_VM), EFRAME_eflags(%rsp) - orl $X86_EFLAGS_IF, EFRAME_eflags(%rsp) -+ -+ SPEC_CTRL_COND_VERW /* Req: %rsp=eframe Clob: efl */ -+ - addq $8,%rsp - .Lft0: iretq - _ASM_PRE_EXTABLE(.Lft0, handle_exception) -@@ -679,9 +685,22 @@ UNLIKELY_START(ne, exit_cr3) - UNLIKELY_END(exit_cr3) - - /* WARNING! `ret`, `call *`, `jmp *` not safe beyond this point. */ -- SPEC_CTRL_EXIT_TO_XEN /* Req: %r12=ist_exit %r14=end, Clob: abcd */ -+ SPEC_CTRL_EXIT_TO_XEN /* Req: %r12=ist_exit %r14=end %rsp=regs, Clob: abcd */ - - RESTORE_ALL adj=8 -+ -+ /* -+ * When the CPU pushed this exception frame, it zero-extended eflags. -+ * For an IST exit, SPEC_CTRL_EXIT_TO_XEN stashed shadow copies of -+ * spec_ctrl_flags and ver_sel above eflags, as we can't use any GPRs, -+ * and we're at a random place on the stack, not in a CPUFINFO block. -+ * -+ * Account for ev/ec having already been popped off the stack. -+ */ -+ SPEC_CTRL_COND_VERW \ -+ scf=STK_REL(EFRAME_shadow_scf, EFRAME_rip), \ -+ sel=STK_REL(EFRAME_shadow_sel, EFRAME_rip) -+ - iretq - - ENTRY(common_interrupt) diff --git a/xsa452-4.18-5.patch b/xsa452-4.18-5.patch deleted file mode 100644 index d55e454..0000000 --- a/xsa452-4.18-5.patch +++ /dev/null @@ -1,239 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Rename VERW related options - -VERW is going to be used for a 3rd purpose, and the existing nomenclature -didn't survive the Stale MMIO issues terribly well. - -Rename the command line option from `md-clear=` to `verw=`. This is more -consistent with other options which tend to be named based on what they're -doing, not which feature enumeration they use behind the scenes. Retain -`md-clear=` as a deprecated alias. - -Rename opt_md_clear_{pv,hvm} and opt_fb_clear_mmio to opt_verw_{pv,hvm,mmio}, -which has a side effect of making spec_ctrl_init_domain() rather clearer to -follow. - -No functional change. - -This is part of XSA-452 / CVE-2023-28746. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -(cherry picked from commit f7603ca252e4226739eb3129a5290ee3da3f8ea4) - -diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc -index 582d6741d182..fbf16839249a 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -2370,7 +2370,7 @@ By default SSBD will be mitigated at runtime (i.e `ssbd=runtime`). - - ### spec-ctrl (x86) - > `= List of [ , xen=, {pv,hvm}=, --> {msr-sc,rsb,md-clear,ibpb-entry}=|{pv,hvm}=, -+> {msr-sc,rsb,verw,ibpb-entry}=|{pv,hvm}=, - > bti-thunk=retpoline|lfence|jmp, {ibrs,ibpb,ssbd,psfd, - > eager-fpu,l1d-flush,branch-harden,srb-lock, - > unpriv-mmio,gds-mit,div-scrub}= ]` -@@ -2395,7 +2395,7 @@ in place for guests to use. - - Use of a positive boolean value for either of these options is invalid. - --The `pv=`, `hvm=`, `msr-sc=`, `rsb=`, `md-clear=` and `ibpb-entry=` options -+The `pv=`, `hvm=`, `msr-sc=`, `rsb=`, `verw=` and `ibpb-entry=` options - offer fine grained control over the primitives by Xen. These impact Xen's - ability to protect itself, and/or Xen's ability to virtualise support for - guests to use. -@@ -2412,11 +2412,12 @@ guests to use. - guests and if disabled, guests will be unable to use IBRS/STIBP/SSBD/etc. - * `rsb=` offers control over whether to overwrite the Return Stack Buffer / - Return Address Stack on entry to Xen and on idle. --* `md-clear=` offers control over whether to use VERW to flush -- microarchitectural buffers on idle and exit from Xen. *Note: For -- compatibility with development versions of this fix, `mds=` is also accepted -- on Xen 4.12 and earlier as an alias. Consult vendor documentation in -- preference to here.* -+* `verw=` offers control over whether to use VERW for its scrubbing side -+ effects at appropriate privilege transitions. The exact side effects are -+ microarchitecture and microcode specific. *Note: `md-clear=` is accepted as -+ a deprecated alias. For compatibility with development versions of XSA-297, -+ `mds=` is also accepted on Xen 4.12 and earlier as an alias. Consult vendor -+ documentation in preference to here.* - * `ibpb-entry=` offers control over whether IBPB (Indirect Branch Prediction - Barrier) is used on entry to Xen. This is used by default on hardware - vulnerable to Branch Type Confusion, and hardware vulnerable to Speculative -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index a965b6db28ba..c42d8cdc22d6 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -25,8 +25,8 @@ static bool __initdata opt_msr_sc_pv = true; - static bool __initdata opt_msr_sc_hvm = true; - static int8_t __initdata opt_rsb_pv = -1; - static bool __initdata opt_rsb_hvm = true; --static int8_t __ro_after_init opt_md_clear_pv = -1; --static int8_t __ro_after_init opt_md_clear_hvm = -1; -+static int8_t __ro_after_init opt_verw_pv = -1; -+static int8_t __ro_after_init opt_verw_hvm = -1; - - static int8_t __ro_after_init opt_ibpb_entry_pv = -1; - static int8_t __ro_after_init opt_ibpb_entry_hvm = -1; -@@ -66,7 +66,7 @@ static bool __initdata cpu_has_bug_mds; /* Any other M{LP,SB,FB}DS combination. - - static int8_t __initdata opt_srb_lock = -1; - static bool __initdata opt_unpriv_mmio; --static bool __ro_after_init opt_fb_clear_mmio; -+static bool __ro_after_init opt_verw_mmio; - static int8_t __initdata opt_gds_mit = -1; - static int8_t __initdata opt_div_scrub = -1; - -@@ -108,8 +108,8 @@ static int __init cf_check parse_spec_ctrl(const char *s) - disable_common: - opt_rsb_pv = false; - opt_rsb_hvm = false; -- opt_md_clear_pv = 0; -- opt_md_clear_hvm = 0; -+ opt_verw_pv = 0; -+ opt_verw_hvm = 0; - opt_ibpb_entry_pv = 0; - opt_ibpb_entry_hvm = 0; - opt_ibpb_entry_dom0 = false; -@@ -140,14 +140,14 @@ static int __init cf_check parse_spec_ctrl(const char *s) - { - opt_msr_sc_pv = val; - opt_rsb_pv = val; -- opt_md_clear_pv = val; -+ opt_verw_pv = val; - opt_ibpb_entry_pv = val; - } - else if ( (val = parse_boolean("hvm", s, ss)) >= 0 ) - { - opt_msr_sc_hvm = val; - opt_rsb_hvm = val; -- opt_md_clear_hvm = val; -+ opt_verw_hvm = val; - opt_ibpb_entry_hvm = val; - } - else if ( (val = parse_boolean("msr-sc", s, ss)) != -1 ) -@@ -192,21 +192,22 @@ static int __init cf_check parse_spec_ctrl(const char *s) - break; - } - } -- else if ( (val = parse_boolean("md-clear", s, ss)) != -1 ) -+ else if ( (val = parse_boolean("verw", s, ss)) != -1 || -+ (val = parse_boolean("md-clear", s, ss)) != -1 ) - { - switch ( val ) - { - case 0: - case 1: -- opt_md_clear_pv = opt_md_clear_hvm = val; -+ opt_verw_pv = opt_verw_hvm = val; - break; - - case -2: -- s += strlen("md-clear="); -+ s += (*s == 'v') ? strlen("verw=") : strlen("md-clear="); - if ( (val = parse_boolean("pv", s, ss)) >= 0 ) -- opt_md_clear_pv = val; -+ opt_verw_pv = val; - else if ( (val = parse_boolean("hvm", s, ss)) >= 0 ) -- opt_md_clear_hvm = val; -+ opt_verw_hvm = val; - else - default: - rc = -EINVAL; -@@ -528,8 +529,8 @@ static void __init print_details(enum ind_thunk thunk) - opt_srb_lock ? " SRB_LOCK+" : " SRB_LOCK-", - opt_ibpb_ctxt_switch ? " IBPB-ctxt" : "", - opt_l1d_flush ? " L1D_FLUSH" : "", -- opt_md_clear_pv || opt_md_clear_hvm || -- opt_fb_clear_mmio ? " VERW" : "", -+ opt_verw_pv || opt_verw_hvm || -+ opt_verw_mmio ? " VERW" : "", - opt_div_scrub ? " DIV" : "", - opt_branch_harden ? " BRANCH_HARDEN" : ""); - -@@ -550,13 +551,13 @@ static void __init print_details(enum ind_thunk thunk) - boot_cpu_has(X86_FEATURE_SC_RSB_HVM) || - boot_cpu_has(X86_FEATURE_IBPB_ENTRY_HVM) || - amd_virt_spec_ctrl || -- opt_eager_fpu || opt_md_clear_hvm) ? "" : " None", -+ opt_eager_fpu || opt_verw_hvm) ? "" : " None", - boot_cpu_has(X86_FEATURE_SC_MSR_HVM) ? " MSR_SPEC_CTRL" : "", - (boot_cpu_has(X86_FEATURE_SC_MSR_HVM) || - amd_virt_spec_ctrl) ? " MSR_VIRT_SPEC_CTRL" : "", - boot_cpu_has(X86_FEATURE_SC_RSB_HVM) ? " RSB" : "", - opt_eager_fpu ? " EAGER_FPU" : "", -- opt_md_clear_hvm ? " MD_CLEAR" : "", -+ opt_verw_hvm ? " VERW" : "", - boot_cpu_has(X86_FEATURE_IBPB_ENTRY_HVM) ? " IBPB-entry" : ""); - - #endif -@@ -565,11 +566,11 @@ static void __init print_details(enum ind_thunk thunk) - (boot_cpu_has(X86_FEATURE_SC_MSR_PV) || - boot_cpu_has(X86_FEATURE_SC_RSB_PV) || - boot_cpu_has(X86_FEATURE_IBPB_ENTRY_PV) || -- opt_eager_fpu || opt_md_clear_pv) ? "" : " None", -+ opt_eager_fpu || opt_verw_pv) ? "" : " None", - boot_cpu_has(X86_FEATURE_SC_MSR_PV) ? " MSR_SPEC_CTRL" : "", - boot_cpu_has(X86_FEATURE_SC_RSB_PV) ? " RSB" : "", - opt_eager_fpu ? " EAGER_FPU" : "", -- opt_md_clear_pv ? " MD_CLEAR" : "", -+ opt_verw_pv ? " VERW" : "", - boot_cpu_has(X86_FEATURE_IBPB_ENTRY_PV) ? " IBPB-entry" : ""); - - printk(" XPTI (64-bit PV only): Dom0 %s, DomU %s (with%s PCID)\n", -@@ -1502,8 +1503,8 @@ void spec_ctrl_init_domain(struct domain *d) - { - bool pv = is_pv_domain(d); - -- bool verw = ((pv ? opt_md_clear_pv : opt_md_clear_hvm) || -- (opt_fb_clear_mmio && is_iommu_enabled(d))); -+ bool verw = ((pv ? opt_verw_pv : opt_verw_hvm) || -+ (opt_verw_mmio && is_iommu_enabled(d))); - - bool ibpb = ((pv ? opt_ibpb_entry_pv : opt_ibpb_entry_hvm) && - (d->domain_id != 0 || opt_ibpb_entry_dom0)); -@@ -1866,19 +1867,20 @@ void __init init_speculation_mitigations(void) - * the return-to-guest path. - */ - if ( opt_unpriv_mmio ) -- opt_fb_clear_mmio = cpu_has_fb_clear; -+ opt_verw_mmio = cpu_has_fb_clear; - - /* - * By default, enable PV and HVM mitigations on MDS-vulnerable hardware. - * This will only be a token effort for MLPDS/MFBDS when HT is enabled, - * but it is somewhat better than nothing. - */ -- if ( opt_md_clear_pv == -1 ) -- opt_md_clear_pv = ((cpu_has_bug_mds || cpu_has_bug_msbds_only) && -- boot_cpu_has(X86_FEATURE_MD_CLEAR)); -- if ( opt_md_clear_hvm == -1 ) -- opt_md_clear_hvm = ((cpu_has_bug_mds || cpu_has_bug_msbds_only) && -- boot_cpu_has(X86_FEATURE_MD_CLEAR)); -+ if ( opt_verw_pv == -1 ) -+ opt_verw_pv = ((cpu_has_bug_mds || cpu_has_bug_msbds_only) && -+ cpu_has_md_clear); -+ -+ if ( opt_verw_hvm == -1 ) -+ opt_verw_hvm = ((cpu_has_bug_mds || cpu_has_bug_msbds_only) && -+ cpu_has_md_clear); - - /* - * Enable MDS/MMIO defences as applicable. The Idle blocks need using if -@@ -1891,12 +1893,12 @@ void __init init_speculation_mitigations(void) - * MDS mitigations. L1D_FLUSH is not safe for MMIO mitigations.) - * - * After calculating the appropriate idle setting, simplify -- * opt_md_clear_hvm to mean just "should we VERW on the way into HVM -+ * opt_verw_hvm to mean just "should we VERW on the way into HVM - * guests", so spec_ctrl_init_domain() can calculate suitable settings. - */ -- if ( opt_md_clear_pv || opt_md_clear_hvm || opt_fb_clear_mmio ) -+ if ( opt_verw_pv || opt_verw_hvm || opt_verw_mmio ) - setup_force_cpu_cap(X86_FEATURE_SC_VERW_IDLE); -- opt_md_clear_hvm &= !cpu_has_skip_l1dfl && !opt_l1d_flush; -+ opt_verw_hvm &= !cpu_has_skip_l1dfl && !opt_l1d_flush; - - /* - * Warn the user if they are on MLPDS/MFBDS-vulnerable hardware with HT diff --git a/xsa452-4.18-6.patch b/xsa452-4.18-6.patch deleted file mode 100644 index 422dc62..0000000 --- a/xsa452-4.18-6.patch +++ /dev/null @@ -1,163 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: VERW-handling adjustments - -... before we add yet more complexity to this logic. Mostly expanded -comments, but with three minor changes. - -1) Introduce cpu_has_useful_md_clear to simplify later logic in this patch and - future ones. - -2) We only ever need SC_VERW_IDLE when SMT is active. If SMT isn't active, - then there's no re-partition of pipeline resources based on thread-idleness - to worry about. - -3) The logic to adjust HVM VERW based on L1D_FLUSH is unmaintainable and, as - it turns out, wrong. SKIP_L1DFL is just a hint bit, whereas opt_l1d_flush - is the relevant decision of whether to use L1D_FLUSH based on - susceptibility and user preference. - - Rewrite the logic so it can be followed, and incorporate the fact that when - FB_CLEAR is visible, L1D_FLUSH isn't a safe substitution. - -This is part of XSA-452 / CVE-2023-28746. - -Signed-off-by: Andrew Cooper -Acked-by: Jan Beulich -(cherry picked from commit 1eb91a8a06230b4b64228c9a380194f8cfe6c5e2) - -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index c42d8cdc22d6..a4afcd8570e2 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -1519,7 +1519,7 @@ void __init init_speculation_mitigations(void) - { - enum ind_thunk thunk = THUNK_DEFAULT; - bool has_spec_ctrl, ibrs = false, hw_smt_enabled; -- bool cpu_has_bug_taa, retpoline_safe; -+ bool cpu_has_bug_taa, cpu_has_useful_md_clear, retpoline_safe; - - hw_smt_enabled = check_smt_enabled(); - -@@ -1855,50 +1855,97 @@ void __init init_speculation_mitigations(void) - "enabled. Please assess your configuration and choose an\n" - "explicit 'smt=' setting. See XSA-273.\n"); - -+ /* -+ * A brief summary of VERW-related changes. -+ * -+ * https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/technical-documentation/intel-analysis-microarchitectural-data-sampling.html -+ * https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/technical-documentation/processor-mmio-stale-data-vulnerabilities.html -+ * -+ * Relevant ucodes: -+ * -+ * - May 2019, for MDS. Introduces the MD_CLEAR CPUID bit and VERW side -+ * effects to scrub Store/Load/Fill buffers as applicable. MD_CLEAR -+ * exists architecturally, even when the side effects have been removed. -+ * -+ * Use VERW to scrub on return-to-guest. Parts with L1D_FLUSH to -+ * mitigate L1TF have the same side effect, so no need to do both. -+ * -+ * Various Atoms suffer from Store-buffer sampling only. Store buffers -+ * are statically partitioned between non-idle threads, so scrubbing is -+ * wanted when going idle too. -+ * -+ * Load ports and Fill buffers are competitively shared between threads. -+ * SMT must be disabled for VERW scrubbing to be fully effective. -+ * -+ * - November 2019, for TAA. Extended VERW side effects to TSX-enabled -+ * MDS_NO parts. -+ * -+ * - February 2022, for Client TSX de-feature. Removed VERW side effects -+ * from Client CPUs only. -+ * -+ * - May 2022, for MMIO Stale Data. (Re)introduced Fill Buffer scrubbing -+ * on all MMIO-affected parts which didn't already have it for MDS -+ * reasons, enumerating FB_CLEAR on those parts only. -+ * -+ * If FB_CLEAR is enumerated, L1D_FLUSH does not have the same scrubbing -+ * side effects as VERW and cannot be used in its place. -+ */ - mds_calculations(); - - /* -- * Parts which enumerate FB_CLEAR are those which are post-MDS_NO and have -- * reintroduced the VERW fill buffer flushing side effect because of a -- * susceptibility to FBSDP. -+ * Parts which enumerate FB_CLEAR are those with now-updated microcode -+ * which weren't susceptible to the original MFBDS (and therefore didn't -+ * have Fill Buffer scrubbing side effects to begin with, or were Client -+ * MDS_NO non-TAA_NO parts where the scrubbing was removed), but have had -+ * the scrubbing reintroduced because of a susceptibility to FBSDP. - * - * If unprivileged guests have (or will have) MMIO mappings, we can - * mitigate cross-domain leakage of fill buffer data by issuing VERW on -- * the return-to-guest path. -+ * the return-to-guest path. This is only a token effort if SMT is -+ * active. - */ - if ( opt_unpriv_mmio ) - opt_verw_mmio = cpu_has_fb_clear; - - /* -- * By default, enable PV and HVM mitigations on MDS-vulnerable hardware. -- * This will only be a token effort for MLPDS/MFBDS when HT is enabled, -- * but it is somewhat better than nothing. -+ * MD_CLEAR is enumerated architecturally forevermore, even after the -+ * scrubbing side effects have been removed. Create ourselves an version -+ * which expressed whether we think MD_CLEAR is having any useful side -+ * effect. -+ */ -+ cpu_has_useful_md_clear = (cpu_has_md_clear && -+ (cpu_has_bug_mds || cpu_has_bug_msbds_only)); -+ -+ /* -+ * By default, use VERW scrubbing on applicable hardware, if we think it's -+ * going to have an effect. This will only be a token effort for -+ * MLPDS/MFBDS when SMT is enabled. - */ - if ( opt_verw_pv == -1 ) -- opt_verw_pv = ((cpu_has_bug_mds || cpu_has_bug_msbds_only) && -- cpu_has_md_clear); -+ opt_verw_pv = cpu_has_useful_md_clear; - - if ( opt_verw_hvm == -1 ) -- opt_verw_hvm = ((cpu_has_bug_mds || cpu_has_bug_msbds_only) && -- cpu_has_md_clear); -+ opt_verw_hvm = cpu_has_useful_md_clear; - - /* -- * Enable MDS/MMIO defences as applicable. The Idle blocks need using if -- * either the PV or HVM MDS defences are used, or if we may give MMIO -- * access to untrusted guests. -- * -- * HVM is more complicated. The MD_CLEAR microcode extends L1D_FLUSH with -- * equivalent semantics to avoid needing to perform both flushes on the -- * HVM path. Therefore, we don't need VERW in addition to L1D_FLUSH (for -- * MDS mitigations. L1D_FLUSH is not safe for MMIO mitigations.) -- * -- * After calculating the appropriate idle setting, simplify -- * opt_verw_hvm to mean just "should we VERW on the way into HVM -- * guests", so spec_ctrl_init_domain() can calculate suitable settings. -+ * If SMT is active, and we're protecting against MDS or MMIO stale data, -+ * we need to scrub before going idle as well as on return to guest. -+ * Various pipeline resources are repartitioned amongst non-idle threads. - */ -- if ( opt_verw_pv || opt_verw_hvm || opt_verw_mmio ) -+ if ( ((cpu_has_useful_md_clear && (opt_verw_pv || opt_verw_hvm)) || -+ opt_verw_mmio) && hw_smt_enabled ) - setup_force_cpu_cap(X86_FEATURE_SC_VERW_IDLE); -- opt_verw_hvm &= !cpu_has_skip_l1dfl && !opt_l1d_flush; -+ -+ /* -+ * After calculating the appropriate idle setting, simplify opt_verw_hvm -+ * to mean just "should we VERW on the way into HVM guests", so -+ * spec_ctrl_init_domain() can calculate suitable settings. -+ * -+ * It is only safe to use L1D_FLUSH in place of VERW when MD_CLEAR is the -+ * only *_CLEAR we can see. -+ */ -+ if ( opt_l1d_flush && cpu_has_md_clear && !cpu_has_fb_clear ) -+ opt_verw_hvm = false; - - /* - * Warn the user if they are on MLPDS/MFBDS-vulnerable hardware with HT diff --git a/xsa452-4.18-7.patch b/xsa452-4.18-7.patch deleted file mode 100644 index a252db0..0000000 --- a/xsa452-4.18-7.patch +++ /dev/null @@ -1,307 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Mitigation Register File Data Sampling - -RFDS affects Atom cores, also branded E-cores, between the Goldmont and -Gracemont microarchitectures. This includes Alder Lake and Raptor Lake hybrid -clien systems which have a mix of Gracemont and other types of cores. - -Two new bits have been defined; RFDS_CLEAR to indicate VERW has more side -effets, and RFDS_NO to incidate that the system is unaffected. Plenty of -unaffected CPUs won't be getting RFDS_NO retrofitted in microcode, so we -synthesise it. Alder Lake and Raptor Lake Xeon-E's are unaffected due to -their platform configuration, and we must use the Hybrid CPUID bit to -distinguish them from their non-Xeon counterparts. - -Like MD_CLEAR and FB_CLEAR, RFDS_CLEAR needs OR-ing across a resource pool, so -set it in the max policies and reflect the host setting in default. - -This is part of XSA-452 / CVE-2023-28746. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -(cherry picked from commit fb5b6f6744713410c74cfc12b7176c108e3c9a31) - -diff --git a/tools/misc/xen-cpuid.c b/tools/misc/xen-cpuid.c -index 7370f1b56ef9..52e451a806c1 100644 ---- a/tools/misc/xen-cpuid.c -+++ b/tools/misc/xen-cpuid.c -@@ -172,7 +172,7 @@ static const char *const str_7d0[32] = - [ 8] = "avx512-vp2intersect", [ 9] = "srbds-ctrl", - [10] = "md-clear", [11] = "rtm-always-abort", - /* 12 */ [13] = "tsx-force-abort", -- [14] = "serialize", -+ [14] = "serialize", [15] = "hybrid", - [16] = "tsxldtrk", - [18] = "pconfig", - [20] = "cet-ibt", -@@ -245,7 +245,8 @@ static const char *const str_m10Al[32] = - [20] = "bhi-no", [21] = "xapic-status", - /* 22 */ [23] = "ovrclk-status", - [24] = "pbrsb-no", [25] = "gds-ctrl", -- [26] = "gds-no", -+ [26] = "gds-no", [27] = "rfds-no", -+ [28] = "rfds-clear", - }; - - static const char *const str_m10Ah[32] = -diff --git a/xen/arch/x86/cpu-policy.c b/xen/arch/x86/cpu-policy.c -index c7c5e99b7b4c..12e621b97de6 100644 ---- a/xen/arch/x86/cpu-policy.c -+++ b/xen/arch/x86/cpu-policy.c -@@ -451,6 +451,7 @@ static void __init guest_common_max_feature_adjustments(uint32_t *fs) - */ - __set_bit(X86_FEATURE_MD_CLEAR, fs); - __set_bit(X86_FEATURE_FB_CLEAR, fs); -+ __set_bit(X86_FEATURE_RFDS_CLEAR, fs); - - /* - * The Gather Data Sampling microcode mitigation (August 2023) has an -@@ -500,6 +501,10 @@ static void __init guest_common_default_feature_adjustments(uint32_t *fs) - if ( cpu_has_fb_clear ) - __set_bit(X86_FEATURE_FB_CLEAR, fs); - -+ __clear_bit(X86_FEATURE_RFDS_CLEAR, fs); -+ if ( cpu_has_rfds_clear ) -+ __set_bit(X86_FEATURE_RFDS_CLEAR, fs); -+ - /* - * The Gather Data Sampling microcode mitigation (August 2023) has an - * adverse performance impact on the CLWB instruction on SKX/CLX/CPX. -diff --git a/xen/arch/x86/include/asm/cpufeature.h b/xen/arch/x86/include/asm/cpufeature.h -index 76ef2aeb1de6..3c57f55de075 100644 ---- a/xen/arch/x86/include/asm/cpufeature.h -+++ b/xen/arch/x86/include/asm/cpufeature.h -@@ -181,6 +181,7 @@ static inline bool boot_cpu_has(unsigned int feat) - #define cpu_has_rtm_always_abort boot_cpu_has(X86_FEATURE_RTM_ALWAYS_ABORT) - #define cpu_has_tsx_force_abort boot_cpu_has(X86_FEATURE_TSX_FORCE_ABORT) - #define cpu_has_serialize boot_cpu_has(X86_FEATURE_SERIALIZE) -+#define cpu_has_hybrid boot_cpu_has(X86_FEATURE_HYBRID) - #define cpu_has_avx512_fp16 boot_cpu_has(X86_FEATURE_AVX512_FP16) - #define cpu_has_arch_caps boot_cpu_has(X86_FEATURE_ARCH_CAPS) - -@@ -208,6 +209,8 @@ static inline bool boot_cpu_has(unsigned int feat) - #define cpu_has_rrsba boot_cpu_has(X86_FEATURE_RRSBA) - #define cpu_has_gds_ctrl boot_cpu_has(X86_FEATURE_GDS_CTRL) - #define cpu_has_gds_no boot_cpu_has(X86_FEATURE_GDS_NO) -+#define cpu_has_rfds_no boot_cpu_has(X86_FEATURE_RFDS_NO) -+#define cpu_has_rfds_clear boot_cpu_has(X86_FEATURE_RFDS_CLEAR) - - /* Synthesized. */ - #define cpu_has_arch_perfmon boot_cpu_has(X86_FEATURE_ARCH_PERFMON) -diff --git a/xen/arch/x86/include/asm/msr-index.h b/xen/arch/x86/include/asm/msr-index.h -index 82a81bd0a232..85ef28a612e0 100644 ---- a/xen/arch/x86/include/asm/msr-index.h -+++ b/xen/arch/x86/include/asm/msr-index.h -@@ -89,6 +89,8 @@ - #define ARCH_CAPS_PBRSB_NO (_AC(1, ULL) << 24) - #define ARCH_CAPS_GDS_CTRL (_AC(1, ULL) << 25) - #define ARCH_CAPS_GDS_NO (_AC(1, ULL) << 26) -+#define ARCH_CAPS_RFDS_NO (_AC(1, ULL) << 27) -+#define ARCH_CAPS_RFDS_CLEAR (_AC(1, ULL) << 28) - - #define MSR_FLUSH_CMD 0x0000010b - #define FLUSH_CMD_L1D (_AC(1, ULL) << 0) -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index a4afcd8570e2..8165379fed94 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -12,6 +12,7 @@ - - #include - #include -+#include - #include - #include - #include -@@ -435,7 +436,7 @@ static void __init print_details(enum ind_thunk thunk) - * Hardware read-only information, stating immunity to certain issues, or - * suggestions of which mitigation to use. - */ -- printk(" Hardware hints:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", -+ printk(" Hardware hints:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", - (caps & ARCH_CAPS_RDCL_NO) ? " RDCL_NO" : "", - (caps & ARCH_CAPS_EIBRS) ? " EIBRS" : "", - (caps & ARCH_CAPS_RSBA) ? " RSBA" : "", -@@ -451,6 +452,7 @@ static void __init print_details(enum ind_thunk thunk) - (caps & ARCH_CAPS_FB_CLEAR) ? " FB_CLEAR" : "", - (caps & ARCH_CAPS_PBRSB_NO) ? " PBRSB_NO" : "", - (caps & ARCH_CAPS_GDS_NO) ? " GDS_NO" : "", -+ (caps & ARCH_CAPS_RFDS_NO) ? " RFDS_NO" : "", - (e8b & cpufeat_mask(X86_FEATURE_IBRS_ALWAYS)) ? " IBRS_ALWAYS" : "", - (e8b & cpufeat_mask(X86_FEATURE_STIBP_ALWAYS)) ? " STIBP_ALWAYS" : "", - (e8b & cpufeat_mask(X86_FEATURE_IBRS_FAST)) ? " IBRS_FAST" : "", -@@ -461,7 +463,7 @@ static void __init print_details(enum ind_thunk thunk) - (e21a & cpufeat_mask(X86_FEATURE_SRSO_NO)) ? " SRSO_NO" : ""); - - /* Hardware features which need driving to mitigate issues. */ -- printk(" Hardware features:%s%s%s%s%s%s%s%s%s%s%s%s%s\n", -+ printk(" Hardware features:%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", - (e8b & cpufeat_mask(X86_FEATURE_IBPB)) || - (_7d0 & cpufeat_mask(X86_FEATURE_IBRSB)) ? " IBPB" : "", - (e8b & cpufeat_mask(X86_FEATURE_IBRS)) || -@@ -479,6 +481,7 @@ static void __init print_details(enum ind_thunk thunk) - (caps & ARCH_CAPS_TSX_CTRL) ? " TSX_CTRL" : "", - (caps & ARCH_CAPS_FB_CLEAR_CTRL) ? " FB_CLEAR_CTRL" : "", - (caps & ARCH_CAPS_GDS_CTRL) ? " GDS_CTRL" : "", -+ (caps & ARCH_CAPS_RFDS_CLEAR) ? " RFDS_CLEAR" : "", - (e21a & cpufeat_mask(X86_FEATURE_SBPB)) ? " SBPB" : ""); - - /* Compiled-in support which pertains to mitigations. */ -@@ -1347,6 +1350,83 @@ static __init void mds_calculations(void) - } - } - -+/* -+ * Register File Data Sampling affects Atom cores from the Goldmont to -+ * Gracemont microarchitectures. The March 2024 microcode adds RFDS_NO to -+ * some but not all unaffected parts, and RFDS_CLEAR to affected parts still -+ * in support. -+ * -+ * Alder Lake and Raptor Lake client CPUs have a mix of P cores -+ * (Golden/Raptor Cove, not vulnerable) and E cores (Gracemont, -+ * vulnerable), and both enumerate RFDS_CLEAR. -+ * -+ * Both exist in a Xeon SKU, which has the E cores (Gracemont) disabled by -+ * platform configuration, and enumerate RFDS_NO. -+ * -+ * With older parts, or with out-of-date microcode, synthesise RFDS_NO when -+ * safe to do so. -+ * -+ * https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/advisory-guidance/register-file-data-sampling.html -+ */ -+static void __init rfds_calculations(void) -+{ -+ /* RFDS is only known to affect Intel Family 6 processors at this time. */ -+ if ( boot_cpu_data.x86_vendor != X86_VENDOR_INTEL || -+ boot_cpu_data.x86 != 6 ) -+ return; -+ -+ /* -+ * If RFDS_NO or RFDS_CLEAR are visible, we've either got suitable -+ * microcode, or an RFDS-aware hypervisor is levelling us in a pool. -+ */ -+ if ( cpu_has_rfds_no || cpu_has_rfds_clear ) -+ return; -+ -+ /* If we're virtualised, don't attempt to synthesise RFDS_NO. */ -+ if ( cpu_has_hypervisor ) -+ return; -+ -+ /* -+ * Not all CPUs are expected to get a microcode update enumerating one of -+ * RFDS_{NO,CLEAR}, or we might have out-of-date microcode. -+ */ -+ switch ( boot_cpu_data.x86_model ) -+ { -+ case INTEL_FAM6_ALDERLAKE: -+ case INTEL_FAM6_RAPTORLAKE: -+ /* -+ * Alder Lake and Raptor Lake might be a client SKU (with the -+ * Gracemont cores active, and therefore vulnerable) or might be a -+ * server SKU (with the Gracemont cores disabled, and therefore not -+ * vulnerable). -+ * -+ * See if the CPU identifies as hybrid to distinguish the two cases. -+ */ -+ if ( !cpu_has_hybrid ) -+ break; -+ fallthrough; -+ case INTEL_FAM6_ALDERLAKE_L: -+ case INTEL_FAM6_RAPTORLAKE_P: -+ case INTEL_FAM6_RAPTORLAKE_S: -+ -+ case INTEL_FAM6_ATOM_GOLDMONT: /* Apollo Lake */ -+ case INTEL_FAM6_ATOM_GOLDMONT_D: /* Denverton */ -+ case INTEL_FAM6_ATOM_GOLDMONT_PLUS: /* Gemini Lake */ -+ case INTEL_FAM6_ATOM_TREMONT_D: /* Snow Ridge / Parker Ridge */ -+ case INTEL_FAM6_ATOM_TREMONT: /* Elkhart Lake */ -+ case INTEL_FAM6_ATOM_TREMONT_L: /* Jasper Lake */ -+ case INTEL_FAM6_ATOM_GRACEMONT: /* Alder Lake N */ -+ return; -+ } -+ -+ /* -+ * We appear to be on an unaffected CPU which didn't enumerate RFDS_NO, -+ * perhaps because of it's age or because of out-of-date microcode. -+ * Synthesise it. -+ */ -+ setup_force_cpu_cap(X86_FEATURE_RFDS_NO); -+} -+ - static bool __init cpu_has_gds(void) - { - /* -@@ -1860,6 +1940,7 @@ void __init init_speculation_mitigations(void) - * - * https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/technical-documentation/intel-analysis-microarchitectural-data-sampling.html - * https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/technical-documentation/processor-mmio-stale-data-vulnerabilities.html -+ * https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/advisory-guidance/register-file-data-sampling.html - * - * Relevant ucodes: - * -@@ -1889,8 +1970,12 @@ void __init init_speculation_mitigations(void) - * - * If FB_CLEAR is enumerated, L1D_FLUSH does not have the same scrubbing - * side effects as VERW and cannot be used in its place. -+ * -+ * - March 2023, for RFDS. Enumerate RFDS_CLEAR to mean that VERW now -+ * scrubs non-architectural entries from certain register files. - */ - mds_calculations(); -+ rfds_calculations(); - - /* - * Parts which enumerate FB_CLEAR are those with now-updated microcode -@@ -1922,15 +2007,19 @@ void __init init_speculation_mitigations(void) - * MLPDS/MFBDS when SMT is enabled. - */ - if ( opt_verw_pv == -1 ) -- opt_verw_pv = cpu_has_useful_md_clear; -+ opt_verw_pv = cpu_has_useful_md_clear || cpu_has_rfds_clear; - - if ( opt_verw_hvm == -1 ) -- opt_verw_hvm = cpu_has_useful_md_clear; -+ opt_verw_hvm = cpu_has_useful_md_clear || cpu_has_rfds_clear; - - /* - * If SMT is active, and we're protecting against MDS or MMIO stale data, - * we need to scrub before going idle as well as on return to guest. - * Various pipeline resources are repartitioned amongst non-idle threads. -+ * -+ * We don't need to scrub on idle for RFDS. There are no affected cores -+ * which support SMT, despite there being affected cores in hybrid systems -+ * which have SMT elsewhere in the platform. - */ - if ( ((cpu_has_useful_md_clear && (opt_verw_pv || opt_verw_hvm)) || - opt_verw_mmio) && hw_smt_enabled ) -@@ -1944,7 +2033,8 @@ void __init init_speculation_mitigations(void) - * It is only safe to use L1D_FLUSH in place of VERW when MD_CLEAR is the - * only *_CLEAR we can see. - */ -- if ( opt_l1d_flush && cpu_has_md_clear && !cpu_has_fb_clear ) -+ if ( opt_l1d_flush && cpu_has_md_clear && !cpu_has_fb_clear && -+ !cpu_has_rfds_clear ) - opt_verw_hvm = false; - - /* -diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h -index 337aaa9c770b..8e17ef670fff 100644 ---- a/xen/include/public/arch-x86/cpufeatureset.h -+++ b/xen/include/public/arch-x86/cpufeatureset.h -@@ -266,6 +266,7 @@ XEN_CPUFEATURE(MD_CLEAR, 9*32+10) /*!A VERW clears microarchitectural buffe - XEN_CPUFEATURE(RTM_ALWAYS_ABORT, 9*32+11) /*! June 2021 TSX defeaturing in microcode. */ - XEN_CPUFEATURE(TSX_FORCE_ABORT, 9*32+13) /* MSR_TSX_FORCE_ABORT.RTM_ABORT */ - XEN_CPUFEATURE(SERIALIZE, 9*32+14) /*A SERIALIZE insn */ -+XEN_CPUFEATURE(HYBRID, 9*32+15) /* Heterogeneous platform */ - XEN_CPUFEATURE(TSXLDTRK, 9*32+16) /*a TSX load tracking suspend/resume insns */ - XEN_CPUFEATURE(CET_IBT, 9*32+20) /* CET - Indirect Branch Tracking */ - XEN_CPUFEATURE(AVX512_FP16, 9*32+23) /*A AVX512 FP16 instructions */ -@@ -338,6 +339,8 @@ XEN_CPUFEATURE(OVRCLK_STATUS, 16*32+23) /* MSR_OVERCLOCKING_STATUS */ - XEN_CPUFEATURE(PBRSB_NO, 16*32+24) /*A No Post-Barrier RSB predictions */ - XEN_CPUFEATURE(GDS_CTRL, 16*32+25) /* MCU_OPT_CTRL.GDS_MIT_{DIS,LOCK} */ - XEN_CPUFEATURE(GDS_NO, 16*32+26) /*A No Gather Data Sampling */ -+XEN_CPUFEATURE(RFDS_NO, 16*32+27) /*A No Register File Data Sampling */ -+XEN_CPUFEATURE(RFDS_CLEAR, 16*32+28) /*!A Register File(s) cleared by VERW */ - - /* Intel-defined CPU features, MSR_ARCH_CAPS 0x10a.edx, word 17 */ - diff --git a/xsa453-4.18-1.patch b/xsa453-4.18-1.patch deleted file mode 100644 index a3c0f05..0000000 --- a/xsa453-4.18-1.patch +++ /dev/null @@ -1,50 +0,0 @@ -From: Andrew Cooper -Subject: xen: Swap order of actions in the FREE*() macros - -Wherever possible, it is a good idea to NULL out the visible reference to an -object prior to freeing it. The FREE*() macros already collect together both -parts, making it easy to adjust. - -This has a marginal code generation improvement, as some of the calls to the -free() function can be tailcall optimised. - -No functional change. - -Signed-off-by: Andrew Cooper -Acked-by: Jan Beulich -(cherry picked from commit c4f427ec879e7c0df6d44d02561e8bee838a293e) - -diff --git a/xen/include/xen/mm.h b/xen/include/xen/mm.h -index 8b9618609f77..8bc5f4249d1b 100644 ---- a/xen/include/xen/mm.h -+++ b/xen/include/xen/mm.h -@@ -91,8 +91,9 @@ bool scrub_free_pages(void); - - /* Free an allocation, and zero the pointer to it. */ - #define FREE_XENHEAP_PAGES(p, o) do { \ -- free_xenheap_pages(p, o); \ -+ void *_ptr_ = (p); \ - (p) = NULL; \ -+ free_xenheap_pages(_ptr_, o); \ - } while ( false ) - #define FREE_XENHEAP_PAGE(p) FREE_XENHEAP_PAGES(p, 0) - -diff --git a/xen/include/xen/xmalloc.h b/xen/include/xen/xmalloc.h -index 16979a117c6a..d857298011c1 100644 ---- a/xen/include/xen/xmalloc.h -+++ b/xen/include/xen/xmalloc.h -@@ -66,9 +66,10 @@ - extern void xfree(void *); - - /* Free an allocation, and zero the pointer to it. */ --#define XFREE(p) do { \ -- xfree(p); \ -- (p) = NULL; \ -+#define XFREE(p) do { \ -+ void *_ptr_ = (p); \ -+ (p) = NULL; \ -+ xfree(_ptr_); \ - } while ( false ) - - /* Underlying functions */ - diff --git a/xsa453-4.18-2.patch b/xsa453-4.18-2.patch deleted file mode 100644 index 01a7e05..0000000 --- a/xsa453-4.18-2.patch +++ /dev/null @@ -1,314 +0,0 @@ -From: =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= -Subject: x86/spinlock: introduce support for blocking speculation into - critical regions - -Introduce a new Kconfig option to block speculation into lock protected -critical regions. The Kconfig option is enabled by default, but the mitigation -won't be engaged unless it's explicitly enabled in the command line using -`spec-ctrl=lock-harden`. - -Convert the spinlock acquire macros into always-inline functions, and introduce -a speculation barrier after the lock has been taken. Note the speculation -barrier is not placed inside the implementation of the spin lock functions, as -to prevent speculation from falling through the call to the lock functions -resulting in the barrier also being skipped. - -trylock variants are protected using a construct akin to the existing -evaluate_nospec(). - -This patch only implements the speculation barrier for x86. - -Note spin locks are the only locking primitive taken care in this change, -further locking primitives will be adjusted by separate changes. - -This is part of XSA-453 / CVE-2024-2193 - -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich -(cherry picked from commit 7ef0084418e188d05f338c3e028fbbe8b6924afa) - -diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc -index fbf16839249a..3f9f9167182f 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -2373,7 +2373,7 @@ By default SSBD will be mitigated at runtime (i.e `ssbd=runtime`). - > {msr-sc,rsb,verw,ibpb-entry}=|{pv,hvm}=, - > bti-thunk=retpoline|lfence|jmp, {ibrs,ibpb,ssbd,psfd, - > eager-fpu,l1d-flush,branch-harden,srb-lock, --> unpriv-mmio,gds-mit,div-scrub}= ]` -+> unpriv-mmio,gds-mit,div-scrub,lock-harden}= ]` - - Controls for speculative execution sidechannel mitigations. By default, Xen - will pick the most appropriate mitigations based on compiled in support, -@@ -2500,6 +2500,11 @@ On all hardware, the `div-scrub=` option can be used to force or prevent Xen - from mitigating the DIV-leakage vulnerability. By default, Xen will mitigate - DIV-leakage on hardware believed to be vulnerable. - -+If Xen is compiled with `CONFIG_SPECULATIVE_HARDEN_LOCK`, the `lock-harden=` -+boolean can be used to force or prevent Xen from using speculation barriers to -+protect lock critical regions. This mitigation won't be engaged by default, -+and needs to be explicitly enabled on the command line. -+ - ### sync_console - > `= ` - -diff --git a/xen/arch/x86/include/asm/cpufeatures.h b/xen/arch/x86/include/asm/cpufeatures.h -index c3aad21c3b43..7e8221fd85dd 100644 ---- a/xen/arch/x86/include/asm/cpufeatures.h -+++ b/xen/arch/x86/include/asm/cpufeatures.h -@@ -24,7 +24,7 @@ XEN_CPUFEATURE(APERFMPERF, X86_SYNTH( 8)) /* APERFMPERF */ - XEN_CPUFEATURE(MFENCE_RDTSC, X86_SYNTH( 9)) /* MFENCE synchronizes RDTSC */ - XEN_CPUFEATURE(XEN_SMEP, X86_SYNTH(10)) /* SMEP gets used by Xen itself */ - XEN_CPUFEATURE(XEN_SMAP, X86_SYNTH(11)) /* SMAP gets used by Xen itself */ --/* Bit 12 unused. */ -+XEN_CPUFEATURE(SC_NO_LOCK_HARDEN, X86_SYNTH(12)) /* (Disable) Lock critical region hardening */ - XEN_CPUFEATURE(IND_THUNK_LFENCE, X86_SYNTH(13)) /* Use IND_THUNK_LFENCE */ - XEN_CPUFEATURE(IND_THUNK_JMP, X86_SYNTH(14)) /* Use IND_THUNK_JMP */ - XEN_CPUFEATURE(SC_NO_BRANCH_HARDEN, X86_SYNTH(15)) /* (Disable) Conditional branch hardening */ -diff --git a/xen/arch/x86/include/asm/nospec.h b/xen/arch/x86/include/asm/nospec.h -index 7150e76b87fb..0725839e1982 100644 ---- a/xen/arch/x86/include/asm/nospec.h -+++ b/xen/arch/x86/include/asm/nospec.h -@@ -38,6 +38,32 @@ static always_inline void block_speculation(void) - barrier_nospec_true(); - } - -+static always_inline void arch_block_lock_speculation(void) -+{ -+ alternative("lfence", "", X86_FEATURE_SC_NO_LOCK_HARDEN); -+} -+ -+/* Allow to insert a read memory barrier into conditionals */ -+static always_inline bool barrier_lock_true(void) -+{ -+ alternative("lfence #nospec-true", "", X86_FEATURE_SC_NO_LOCK_HARDEN); -+ return true; -+} -+ -+static always_inline bool barrier_lock_false(void) -+{ -+ alternative("lfence #nospec-false", "", X86_FEATURE_SC_NO_LOCK_HARDEN); -+ return false; -+} -+ -+static always_inline bool arch_lock_evaluate_nospec(bool condition) -+{ -+ if ( condition ) -+ return barrier_lock_true(); -+ else -+ return barrier_lock_false(); -+} -+ - #endif /* _ASM_X86_NOSPEC_H */ - - /* -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 8165379fed94..5dfc4ed69ec5 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -53,6 +53,7 @@ int8_t __read_mostly opt_eager_fpu = -1; - int8_t __read_mostly opt_l1d_flush = -1; - static bool __initdata opt_branch_harden = - IS_ENABLED(CONFIG_SPECULATIVE_HARDEN_BRANCH); -+static bool __initdata opt_lock_harden; - - bool __initdata bsp_delay_spec_ctrl; - uint8_t __read_mostly default_xen_spec_ctrl; -@@ -121,6 +122,7 @@ static int __init cf_check parse_spec_ctrl(const char *s) - opt_ssbd = false; - opt_l1d_flush = 0; - opt_branch_harden = false; -+ opt_lock_harden = false; - opt_srb_lock = 0; - opt_unpriv_mmio = false; - opt_gds_mit = 0; -@@ -286,6 +288,16 @@ static int __init cf_check parse_spec_ctrl(const char *s) - rc = -EINVAL; - } - } -+ else if ( (val = parse_boolean("lock-harden", s, ss)) >= 0 ) -+ { -+ if ( IS_ENABLED(CONFIG_SPECULATIVE_HARDEN_LOCK) ) -+ opt_lock_harden = val; -+ else -+ { -+ no_config_param("SPECULATIVE_HARDEN_LOCK", "spec-ctrl", s, ss); -+ rc = -EINVAL; -+ } -+ } - else if ( (val = parse_boolean("srb-lock", s, ss)) >= 0 ) - opt_srb_lock = val; - else if ( (val = parse_boolean("unpriv-mmio", s, ss)) >= 0 ) -@@ -488,7 +500,8 @@ static void __init print_details(enum ind_thunk thunk) - if ( IS_ENABLED(CONFIG_INDIRECT_THUNK) || IS_ENABLED(CONFIG_SHADOW_PAGING) || - IS_ENABLED(CONFIG_SPECULATIVE_HARDEN_ARRAY) || - IS_ENABLED(CONFIG_SPECULATIVE_HARDEN_BRANCH) || -- IS_ENABLED(CONFIG_SPECULATIVE_HARDEN_GUEST_ACCESS) ) -+ IS_ENABLED(CONFIG_SPECULATIVE_HARDEN_GUEST_ACCESS) || -+ IS_ENABLED(CONFIG_SPECULATIVE_HARDEN_LOCK) ) - printk(" Compiled-in support:" - #ifdef CONFIG_INDIRECT_THUNK - " INDIRECT_THUNK" -@@ -504,11 +517,14 @@ static void __init print_details(enum ind_thunk thunk) - #endif - #ifdef CONFIG_SPECULATIVE_HARDEN_GUEST_ACCESS - " HARDEN_GUEST_ACCESS" -+#endif -+#ifdef CONFIG_SPECULATIVE_HARDEN_LOCK -+ " HARDEN_LOCK" - #endif - "\n"); - - /* Settings for Xen's protection, irrespective of guests. */ -- printk(" Xen settings: %s%sSPEC_CTRL: %s%s%s%s%s, Other:%s%s%s%s%s%s\n", -+ printk(" Xen settings: %s%sSPEC_CTRL: %s%s%s%s%s, Other:%s%s%s%s%s%s%s\n", - thunk != THUNK_NONE ? "BTI-Thunk: " : "", - thunk == THUNK_NONE ? "" : - thunk == THUNK_RETPOLINE ? "RETPOLINE, " : -@@ -535,7 +551,8 @@ static void __init print_details(enum ind_thunk thunk) - opt_verw_pv || opt_verw_hvm || - opt_verw_mmio ? " VERW" : "", - opt_div_scrub ? " DIV" : "", -- opt_branch_harden ? " BRANCH_HARDEN" : ""); -+ opt_branch_harden ? " BRANCH_HARDEN" : "", -+ opt_lock_harden ? " LOCK_HARDEN" : ""); - - /* L1TF diagnostics, printed if vulnerable or PV shadowing is in use. */ - if ( cpu_has_bug_l1tf || opt_pv_l1tf_hwdom || opt_pv_l1tf_domu ) -@@ -1918,6 +1935,9 @@ void __init init_speculation_mitigations(void) - if ( !opt_branch_harden ) - setup_force_cpu_cap(X86_FEATURE_SC_NO_BRANCH_HARDEN); - -+ if ( !opt_lock_harden ) -+ setup_force_cpu_cap(X86_FEATURE_SC_NO_LOCK_HARDEN); -+ - /* - * We do not disable HT by default on affected hardware. - * -diff --git a/xen/common/Kconfig b/xen/common/Kconfig -index 4d6fe051641d..3361a6d89257 100644 ---- a/xen/common/Kconfig -+++ b/xen/common/Kconfig -@@ -188,6 +188,23 @@ config SPECULATIVE_HARDEN_GUEST_ACCESS - - If unsure, say Y. - -+config SPECULATIVE_HARDEN_LOCK -+ bool "Speculative lock context hardening" -+ default y -+ depends on X86 -+ help -+ Contemporary processors may use speculative execution as a -+ performance optimisation, but this can potentially be abused by an -+ attacker to leak data via speculative sidechannels. -+ -+ One source of data leakage is via speculative accesses to lock -+ critical regions. -+ -+ This option is disabled by default at run time, and needs to be -+ enabled on the command line. -+ -+ If unsure, say Y. -+ - endmenu - - config DIT_DEFAULT -diff --git a/xen/include/xen/nospec.h b/xen/include/xen/nospec.h -index 76255bc46efe..455284640396 100644 ---- a/xen/include/xen/nospec.h -+++ b/xen/include/xen/nospec.h -@@ -70,6 +70,21 @@ static inline unsigned long array_index_mask_nospec(unsigned long index, - #define array_access_nospec(array, index) \ - (array)[array_index_nospec(index, ARRAY_SIZE(array))] - -+static always_inline void block_lock_speculation(void) -+{ -+#ifdef CONFIG_SPECULATIVE_HARDEN_LOCK -+ arch_block_lock_speculation(); -+#endif -+} -+ -+static always_inline bool lock_evaluate_nospec(bool condition) -+{ -+#ifdef CONFIG_SPECULATIVE_HARDEN_LOCK -+ return arch_lock_evaluate_nospec(condition); -+#endif -+ return condition; -+} -+ - #endif /* XEN_NOSPEC_H */ - - /* -diff --git a/xen/include/xen/spinlock.h b/xen/include/xen/spinlock.h -index e7a1c1aa8988..28fce5615e5c 100644 ---- a/xen/include/xen/spinlock.h -+++ b/xen/include/xen/spinlock.h -@@ -1,6 +1,7 @@ - #ifndef __SPINLOCK_H__ - #define __SPINLOCK_H__ - -+#include - #include - #include - -@@ -195,13 +196,30 @@ int _spin_trylock_recursive(spinlock_t *lock); - void _spin_lock_recursive(spinlock_t *lock); - void _spin_unlock_recursive(spinlock_t *lock); - --#define spin_lock(l) _spin_lock(l) --#define spin_lock_cb(l, c, d) _spin_lock_cb(l, c, d) --#define spin_lock_irq(l) _spin_lock_irq(l) -+static always_inline void spin_lock(spinlock_t *l) -+{ -+ _spin_lock(l); -+ block_lock_speculation(); -+} -+ -+static always_inline void spin_lock_cb(spinlock_t *l, void (*c)(void *data), -+ void *d) -+{ -+ _spin_lock_cb(l, c, d); -+ block_lock_speculation(); -+} -+ -+static always_inline void spin_lock_irq(spinlock_t *l) -+{ -+ _spin_lock_irq(l); -+ block_lock_speculation(); -+} -+ - #define spin_lock_irqsave(l, f) \ - ({ \ - BUILD_BUG_ON(sizeof(f) != sizeof(unsigned long)); \ - ((f) = _spin_lock_irqsave(l)); \ -+ block_lock_speculation(); \ - }) - - #define spin_unlock(l) _spin_unlock(l) -@@ -209,7 +227,7 @@ void _spin_unlock_recursive(spinlock_t *lock); - #define spin_unlock_irqrestore(l, f) _spin_unlock_irqrestore(l, f) - - #define spin_is_locked(l) _spin_is_locked(l) --#define spin_trylock(l) _spin_trylock(l) -+#define spin_trylock(l) lock_evaluate_nospec(_spin_trylock(l)) - - #define spin_trylock_irqsave(lock, flags) \ - ({ \ -@@ -230,8 +248,15 @@ void _spin_unlock_recursive(spinlock_t *lock); - * are any critical regions that cannot form part of such a set, they can use - * standard spin_[un]lock(). - */ --#define spin_trylock_recursive(l) _spin_trylock_recursive(l) --#define spin_lock_recursive(l) _spin_lock_recursive(l) -+#define spin_trylock_recursive(l) \ -+ lock_evaluate_nospec(_spin_trylock_recursive(l)) -+ -+static always_inline void spin_lock_recursive(spinlock_t *l) -+{ -+ _spin_lock_recursive(l); -+ block_lock_speculation(); -+} -+ - #define spin_unlock_recursive(l) _spin_unlock_recursive(l) - - #endif /* __SPINLOCK_H__ */ diff --git a/xsa453-4.18-3.patch b/xsa453-4.18-3.patch deleted file mode 100644 index ecaff41..0000000 --- a/xsa453-4.18-3.patch +++ /dev/null @@ -1,113 +0,0 @@ -From: =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= -Subject: rwlock: introduce support for blocking speculation into critical - regions - -Introduce inline wrappers as required and add direct calls to -block_lock_speculation() in order to prevent speculation into the rwlock -protected critical regions. - -Note the rwlock primitives are adjusted to use the non speculation safe variants -of the spinlock handlers, as a speculation barrier is added in the rwlock -calling wrappers. - -trylock variants are protected by using lock_evaluate_nospec(). - -This is part of XSA-453 / CVE-2024-2193 - -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich -(cherry picked from commit a1fb15f61692b1fa9945fc51f55471ace49cdd59) - -diff --git a/xen/common/rwlock.c b/xen/common/rwlock.c -index 18224a4bb5d6..290602936df6 100644 ---- a/xen/common/rwlock.c -+++ b/xen/common/rwlock.c -@@ -34,8 +34,11 @@ void queue_read_lock_slowpath(rwlock_t *lock) - - /* - * Put the reader into the wait queue. -+ * -+ * Use the speculation unsafe helper, as it's the caller responsibility to -+ * issue a speculation barrier if required. - */ -- spin_lock(&lock->lock); -+ _spin_lock(&lock->lock); - - /* - * At the head of the wait queue now, wait until the writer state -@@ -66,8 +69,13 @@ void queue_write_lock_slowpath(rwlock_t *lock) - { - u32 cnts; - -- /* Put the writer into the wait queue. */ -- spin_lock(&lock->lock); -+ /* -+ * Put the writer into the wait queue. -+ * -+ * Use the speculation unsafe helper, as it's the caller responsibility to -+ * issue a speculation barrier if required. -+ */ -+ _spin_lock(&lock->lock); - - /* Try to acquire the lock directly if no reader is present. */ - if ( !atomic_read(&lock->cnts) && -diff --git a/xen/include/xen/rwlock.h b/xen/include/xen/rwlock.h -index e0d2b41c5c7e..9a0d3ec23847 100644 ---- a/xen/include/xen/rwlock.h -+++ b/xen/include/xen/rwlock.h -@@ -259,27 +259,49 @@ static inline int _rw_is_write_locked(const rwlock_t *lock) - return (atomic_read(&lock->cnts) & _QW_WMASK) == _QW_LOCKED; - } - --#define read_lock(l) _read_lock(l) --#define read_lock_irq(l) _read_lock_irq(l) -+static always_inline void read_lock(rwlock_t *l) -+{ -+ _read_lock(l); -+ block_lock_speculation(); -+} -+ -+static always_inline void read_lock_irq(rwlock_t *l) -+{ -+ _read_lock_irq(l); -+ block_lock_speculation(); -+} -+ - #define read_lock_irqsave(l, f) \ - ({ \ - BUILD_BUG_ON(sizeof(f) != sizeof(unsigned long)); \ - ((f) = _read_lock_irqsave(l)); \ -+ block_lock_speculation(); \ - }) - - #define read_unlock(l) _read_unlock(l) - #define read_unlock_irq(l) _read_unlock_irq(l) - #define read_unlock_irqrestore(l, f) _read_unlock_irqrestore(l, f) --#define read_trylock(l) _read_trylock(l) -+#define read_trylock(l) lock_evaluate_nospec(_read_trylock(l)) -+ -+static always_inline void write_lock(rwlock_t *l) -+{ -+ _write_lock(l); -+ block_lock_speculation(); -+} -+ -+static always_inline void write_lock_irq(rwlock_t *l) -+{ -+ _write_lock_irq(l); -+ block_lock_speculation(); -+} - --#define write_lock(l) _write_lock(l) --#define write_lock_irq(l) _write_lock_irq(l) - #define write_lock_irqsave(l, f) \ - ({ \ - BUILD_BUG_ON(sizeof(f) != sizeof(unsigned long)); \ - ((f) = _write_lock_irqsave(l)); \ -+ block_lock_speculation(); \ - }) --#define write_trylock(l) _write_trylock(l) -+#define write_trylock(l) lock_evaluate_nospec(_write_trylock(l)) - - #define write_unlock(l) _write_unlock(l) - #define write_unlock_irq(l) _write_unlock_irq(l) diff --git a/xsa453-4.18-4.patch b/xsa453-4.18-4.patch deleted file mode 100644 index a3c6b3c..0000000 --- a/xsa453-4.18-4.patch +++ /dev/null @@ -1,75 +0,0 @@ -From: =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= -Subject: percpu-rwlock: introduce support for blocking speculation into - critical regions - -Add direct calls to block_lock_speculation() where required in order to prevent -speculation into the lock protected critical regions. Also convert -_percpu_read_lock() from inline to always_inline. - -Note that _percpu_write_lock() has been modified the use the non speculation -safe of the locking primites, as a speculation is added unconditionally by the -calling wrapper. - -This is part of XSA-453 / CVE-2024-2193 - -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich -(cherry picked from commit f218daf6d3a3b847736d37c6a6b76031a0d08441) - -diff --git a/xen/common/rwlock.c b/xen/common/rwlock.c -index 290602936df6..f5a249bcc240 100644 ---- a/xen/common/rwlock.c -+++ b/xen/common/rwlock.c -@@ -129,8 +129,12 @@ void _percpu_write_lock(percpu_rwlock_t **per_cpudata, - /* - * First take the write lock to protect against other writers or slow - * path readers. -+ * -+ * Note we use the speculation unsafe variant of write_lock(), as the -+ * calling wrapper already adds a speculation barrier after the lock has -+ * been taken. - */ -- write_lock(&percpu_rwlock->rwlock); -+ _write_lock(&percpu_rwlock->rwlock); - - /* Now set the global variable so that readers start using read_lock. */ - percpu_rwlock->writer_activating = 1; -diff --git a/xen/include/xen/rwlock.h b/xen/include/xen/rwlock.h -index 9a0d3ec23847..9e35ee2edf8f 100644 ---- a/xen/include/xen/rwlock.h -+++ b/xen/include/xen/rwlock.h -@@ -338,8 +338,8 @@ static inline void _percpu_rwlock_owner_check(percpu_rwlock_t **per_cpudata, - #define percpu_rwlock_resource_init(l, owner) \ - (*(l) = (percpu_rwlock_t)PERCPU_RW_LOCK_UNLOCKED(&get_per_cpu_var(owner))) - --static inline void _percpu_read_lock(percpu_rwlock_t **per_cpudata, -- percpu_rwlock_t *percpu_rwlock) -+static always_inline void _percpu_read_lock(percpu_rwlock_t **per_cpudata, -+ percpu_rwlock_t *percpu_rwlock) - { - /* Validate the correct per_cpudata variable has been provided. */ - _percpu_rwlock_owner_check(per_cpudata, percpu_rwlock); -@@ -374,6 +374,8 @@ static inline void _percpu_read_lock(percpu_rwlock_t **per_cpudata, - } - else - { -+ /* Other branch already has a speculation barrier in read_lock(). */ -+ block_lock_speculation(); - /* All other paths have implicit check_lock() calls via read_lock(). */ - check_lock(&percpu_rwlock->rwlock.lock.debug, false); - } -@@ -430,8 +432,12 @@ static inline void _percpu_write_unlock(percpu_rwlock_t **per_cpudata, - _percpu_read_lock(&get_per_cpu_var(percpu), lock) - #define percpu_read_unlock(percpu, lock) \ - _percpu_read_unlock(&get_per_cpu_var(percpu), lock) --#define percpu_write_lock(percpu, lock) \ -- _percpu_write_lock(&get_per_cpu_var(percpu), lock) -+ -+#define percpu_write_lock(percpu, lock) \ -+({ \ -+ _percpu_write_lock(&get_per_cpu_var(percpu), lock); \ -+ block_lock_speculation(); \ -+}) - #define percpu_write_unlock(percpu, lock) \ - _percpu_write_unlock(&get_per_cpu_var(percpu), lock) - diff --git a/xsa453-4.18-5.patch b/xsa453-4.18-5.patch deleted file mode 100644 index aab46e3..0000000 --- a/xsa453-4.18-5.patch +++ /dev/null @@ -1,382 +0,0 @@ -From: =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= -Subject: locking: attempt to ensure lock wrappers are always inline - -In order to prevent the locking speculation barriers from being inside of -`call`ed functions that could be speculatively bypassed. - -While there also add an extra locking barrier to _mm_write_lock() in the branch -taken when the lock is already held. - -Note some functions are switched to use the unsafe variants (without speculation -barrier) of the locking primitives, but a speculation barrier is always added -to the exposed public lock wrapping helper. That's the case with -sched_spin_lock_double() or pcidevs_lock() for example. - -This is part of XSA-453 / CVE-2024-2193 - -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich -(cherry picked from commit 197ecd838a2aaf959a469df3696d4559c4f8b762) - -diff --git a/xen/arch/x86/hvm/vpt.c b/xen/arch/x86/hvm/vpt.c -index 8f53e88d6706..e1d6845a2844 100644 ---- a/xen/arch/x86/hvm/vpt.c -+++ b/xen/arch/x86/hvm/vpt.c -@@ -150,7 +150,7 @@ static int pt_irq_masked(struct periodic_time *pt) - * pt->vcpu field, because another thread holding the pt_migrate lock - * may already be spinning waiting for your vcpu lock. - */ --static void pt_vcpu_lock(struct vcpu *v) -+static always_inline void pt_vcpu_lock(struct vcpu *v) - { - spin_lock(&v->arch.hvm.tm_lock); - } -@@ -169,9 +169,13 @@ static void pt_vcpu_unlock(struct vcpu *v) - * need to take an additional lock that protects against pt->vcpu - * changing. - */ --static void pt_lock(struct periodic_time *pt) -+static always_inline void pt_lock(struct periodic_time *pt) - { -- read_lock(&pt->vcpu->domain->arch.hvm.pl_time->pt_migrate); -+ /* -+ * Use the speculation unsafe variant for the first lock, as the following -+ * lock taking helper already includes a speculation barrier. -+ */ -+ _read_lock(&pt->vcpu->domain->arch.hvm.pl_time->pt_migrate); - spin_lock(&pt->vcpu->arch.hvm.tm_lock); - } - -diff --git a/xen/arch/x86/include/asm/irq.h b/xen/arch/x86/include/asm/irq.h -index a87af47ece22..465ab39bb041 100644 ---- a/xen/arch/x86/include/asm/irq.h -+++ b/xen/arch/x86/include/asm/irq.h -@@ -174,6 +174,7 @@ void cf_check irq_complete_move(struct irq_desc *desc); - - extern struct irq_desc *irq_desc; - -+/* Not speculation safe, only used for AP bringup. */ - void lock_vector_lock(void); - void unlock_vector_lock(void); - -diff --git a/xen/arch/x86/mm/mm-locks.h b/xen/arch/x86/mm/mm-locks.h -index 5a3f96fbaadd..5ec080c02fd8 100644 ---- a/xen/arch/x86/mm/mm-locks.h -+++ b/xen/arch/x86/mm/mm-locks.h -@@ -74,8 +74,8 @@ static inline void _set_lock_level(int l) - this_cpu(mm_lock_level) = l; - } - --static inline void _mm_lock(const struct domain *d, mm_lock_t *l, -- const char *func, int level, int rec) -+static always_inline void _mm_lock(const struct domain *d, mm_lock_t *l, -+ const char *func, int level, int rec) - { - if ( !((mm_locked_by_me(l)) && rec) ) - _check_lock_level(d, level); -@@ -125,8 +125,8 @@ static inline int mm_write_locked_by_me(mm_rwlock_t *l) - return (l->locker == get_processor_id()); - } - --static inline void _mm_write_lock(const struct domain *d, mm_rwlock_t *l, -- const char *func, int level) -+static always_inline void _mm_write_lock(const struct domain *d, mm_rwlock_t *l, -+ const char *func, int level) - { - if ( !mm_write_locked_by_me(l) ) - { -@@ -137,6 +137,8 @@ static inline void _mm_write_lock(const struct domain *d, mm_rwlock_t *l, - l->unlock_level = _get_lock_level(); - _set_lock_level(_lock_level(d, level)); - } -+ else -+ block_speculation(); - l->recurse_count++; - } - -@@ -150,8 +152,8 @@ static inline void mm_write_unlock(mm_rwlock_t *l) - percpu_write_unlock(p2m_percpu_rwlock, &l->lock); - } - --static inline void _mm_read_lock(const struct domain *d, mm_rwlock_t *l, -- int level) -+static always_inline void _mm_read_lock(const struct domain *d, mm_rwlock_t *l, -+ int level) - { - _check_lock_level(d, level); - percpu_read_lock(p2m_percpu_rwlock, &l->lock); -@@ -166,15 +168,15 @@ static inline void mm_read_unlock(mm_rwlock_t *l) - - /* This wrapper uses the line number to express the locking order below */ - #define declare_mm_lock(name) \ -- static inline void mm_lock_##name(const struct domain *d, mm_lock_t *l, \ -- const char *func, int rec) \ -+ static always_inline void mm_lock_##name( \ -+ const struct domain *d, mm_lock_t *l, const char *func, int rec) \ - { _mm_lock(d, l, func, MM_LOCK_ORDER_##name, rec); } - #define declare_mm_rwlock(name) \ -- static inline void mm_write_lock_##name(const struct domain *d, \ -- mm_rwlock_t *l, const char *func) \ -+ static always_inline void mm_write_lock_##name( \ -+ const struct domain *d, mm_rwlock_t *l, const char *func) \ - { _mm_write_lock(d, l, func, MM_LOCK_ORDER_##name); } \ -- static inline void mm_read_lock_##name(const struct domain *d, \ -- mm_rwlock_t *l) \ -+ static always_inline void mm_read_lock_##name(const struct domain *d, \ -+ mm_rwlock_t *l) \ - { _mm_read_lock(d, l, MM_LOCK_ORDER_##name); } - /* These capture the name of the calling function */ - #define mm_lock(name, d, l) mm_lock_##name(d, l, __func__, 0) -@@ -309,7 +311,7 @@ declare_mm_lock(altp2mlist) - #define MM_LOCK_ORDER_altp2m 40 - declare_mm_rwlock(altp2m); - --static inline void p2m_lock(struct p2m_domain *p) -+static always_inline void p2m_lock(struct p2m_domain *p) - { - if ( p2m_is_altp2m(p) ) - mm_write_lock(altp2m, p->domain, &p->lock); -diff --git a/xen/arch/x86/mm/p2m-pod.c b/xen/arch/x86/mm/p2m-pod.c -index 9969eb45fa8c..9be67b63ce3e 100644 ---- a/xen/arch/x86/mm/p2m-pod.c -+++ b/xen/arch/x86/mm/p2m-pod.c -@@ -24,7 +24,7 @@ - #define superpage_aligned(_x) (((_x)&(SUPERPAGE_PAGES-1))==0) - - /* Enforce lock ordering when grabbing the "external" page_alloc lock */ --static inline void lock_page_alloc(struct p2m_domain *p2m) -+static always_inline void lock_page_alloc(struct p2m_domain *p2m) - { - page_alloc_mm_pre_lock(p2m->domain); - spin_lock(&(p2m->domain->page_alloc_lock)); -diff --git a/xen/common/event_channel.c b/xen/common/event_channel.c -index a7a004a08429..66f924a7b091 100644 ---- a/xen/common/event_channel.c -+++ b/xen/common/event_channel.c -@@ -45,7 +45,7 @@ - * just assume the event channel is free or unbound at the moment when the - * evtchn_read_trylock() returns false. - */ --static inline void evtchn_write_lock(struct evtchn *evtchn) -+static always_inline void evtchn_write_lock(struct evtchn *evtchn) - { - write_lock(&evtchn->lock); - -@@ -351,7 +351,8 @@ int evtchn_alloc_unbound(evtchn_alloc_unbound_t *alloc, evtchn_port_t port) - return rc; - } - --static void double_evtchn_lock(struct evtchn *lchn, struct evtchn *rchn) -+static always_inline void double_evtchn_lock(struct evtchn *lchn, -+ struct evtchn *rchn) - { - ASSERT(lchn != rchn); - -diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c -index 89b7811c51c3..934924cbda66 100644 ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -403,7 +403,7 @@ static inline void act_set_gfn(struct active_grant_entry *act, gfn_t gfn) - - static DEFINE_PERCPU_RWLOCK_GLOBAL(grant_rwlock); - --static inline void grant_read_lock(struct grant_table *gt) -+static always_inline void grant_read_lock(struct grant_table *gt) - { - percpu_read_lock(grant_rwlock, >->lock); - } -@@ -413,7 +413,7 @@ static inline void grant_read_unlock(struct grant_table *gt) - percpu_read_unlock(grant_rwlock, >->lock); - } - --static inline void grant_write_lock(struct grant_table *gt) -+static always_inline void grant_write_lock(struct grant_table *gt) - { - percpu_write_lock(grant_rwlock, >->lock); - } -@@ -450,7 +450,7 @@ nr_active_grant_frames(struct grant_table *gt) - return num_act_frames_from_sha_frames(nr_grant_frames(gt)); - } - --static inline struct active_grant_entry * -+static always_inline struct active_grant_entry * - active_entry_acquire(struct grant_table *t, grant_ref_t e) - { - struct active_grant_entry *act; -diff --git a/xen/common/sched/core.c b/xen/common/sched/core.c -index 901782bbb416..34ad39b9ad0b 100644 ---- a/xen/common/sched/core.c -+++ b/xen/common/sched/core.c -@@ -348,23 +348,28 @@ uint64_t get_cpu_idle_time(unsigned int cpu) - * This avoids dead- or live-locks when this code is running on both - * cpus at the same time. - */ --static void sched_spin_lock_double(spinlock_t *lock1, spinlock_t *lock2, -- unsigned long *flags) -+static always_inline void sched_spin_lock_double( -+ spinlock_t *lock1, spinlock_t *lock2, unsigned long *flags) - { -+ /* -+ * In order to avoid extra overhead, use the locking primitives without the -+ * speculation barrier, and introduce a single barrier here. -+ */ - if ( lock1 == lock2 ) - { -- spin_lock_irqsave(lock1, *flags); -+ *flags = _spin_lock_irqsave(lock1); - } - else if ( lock1 < lock2 ) - { -- spin_lock_irqsave(lock1, *flags); -- spin_lock(lock2); -+ *flags = _spin_lock_irqsave(lock1); -+ _spin_lock(lock2); - } - else - { -- spin_lock_irqsave(lock2, *flags); -- spin_lock(lock1); -+ *flags = _spin_lock_irqsave(lock2); -+ _spin_lock(lock1); - } -+ block_lock_speculation(); - } - - static void sched_spin_unlock_double(spinlock_t *lock1, spinlock_t *lock2, -diff --git a/xen/common/sched/private.h b/xen/common/sched/private.h -index c516976c3740..3b97f1576782 100644 ---- a/xen/common/sched/private.h -+++ b/xen/common/sched/private.h -@@ -207,8 +207,24 @@ DECLARE_PER_CPU(cpumask_t, cpumask_scratch); - #define cpumask_scratch (&this_cpu(cpumask_scratch)) - #define cpumask_scratch_cpu(c) (&per_cpu(cpumask_scratch, c)) - -+/* -+ * Deal with _spin_lock_irqsave() returning the flags value instead of storing -+ * it in a passed parameter. -+ */ -+#define _sched_spinlock0(lock, irq) _spin_lock##irq(lock) -+#define _sched_spinlock1(lock, irq, arg) ({ \ -+ BUILD_BUG_ON(sizeof(arg) != sizeof(unsigned long)); \ -+ (arg) = _spin_lock##irq(lock); \ -+}) -+ -+#define _sched_spinlock__(nr) _sched_spinlock ## nr -+#define _sched_spinlock_(nr) _sched_spinlock__(nr) -+#define _sched_spinlock(lock, irq, args...) \ -+ _sched_spinlock_(count_args(args))(lock, irq, ## args) -+ - #define sched_lock(kind, param, cpu, irq, arg...) \ --static inline spinlock_t *kind##_schedule_lock##irq(param EXTRA_TYPE(arg)) \ -+static always_inline spinlock_t \ -+*kind##_schedule_lock##irq(param EXTRA_TYPE(arg)) \ - { \ - for ( ; ; ) \ - { \ -@@ -220,10 +236,16 @@ static inline spinlock_t *kind##_schedule_lock##irq(param EXTRA_TYPE(arg)) \ - * \ - * It may also be the case that v->processor may change but the \ - * lock may be the same; this will succeed in that case. \ -+ * \ -+ * Use the speculation unsafe locking helper, there's a speculation \ -+ * barrier before returning to the caller. \ - */ \ -- spin_lock##irq(lock, ## arg); \ -+ _sched_spinlock(lock, irq, ## arg); \ - if ( likely(lock == get_sched_res(cpu)->schedule_lock) ) \ -+ { \ -+ block_lock_speculation(); \ - return lock; \ -+ } \ - spin_unlock##irq(lock, ## arg); \ - } \ - } -diff --git a/xen/common/timer.c b/xen/common/timer.c -index 0fddfa74879e..38eb5fd20d36 100644 ---- a/xen/common/timer.c -+++ b/xen/common/timer.c -@@ -239,7 +239,7 @@ static inline void deactivate_timer(struct timer *timer) - list_add(&timer->inactive, &per_cpu(timers, timer->cpu).inactive); - } - --static inline bool_t timer_lock(struct timer *timer) -+static inline bool_t timer_lock_unsafe(struct timer *timer) - { - unsigned int cpu; - -@@ -253,7 +253,8 @@ static inline bool_t timer_lock(struct timer *timer) - rcu_read_unlock(&timer_cpu_read_lock); - return 0; - } -- spin_lock(&per_cpu(timers, cpu).lock); -+ /* Use the speculation unsafe variant, the wrapper has the barrier. */ -+ _spin_lock(&per_cpu(timers, cpu).lock); - if ( likely(timer->cpu == cpu) ) - break; - spin_unlock(&per_cpu(timers, cpu).lock); -@@ -266,8 +267,9 @@ static inline bool_t timer_lock(struct timer *timer) - #define timer_lock_irqsave(t, flags) ({ \ - bool_t __x; \ - local_irq_save(flags); \ -- if ( !(__x = timer_lock(t)) ) \ -+ if ( !(__x = timer_lock_unsafe(t)) ) \ - local_irq_restore(flags); \ -+ block_lock_speculation(); \ - __x; \ - }) - -diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c -index e99837b6e141..2a1e7ee89a5d 100644 ---- a/xen/drivers/passthrough/pci.c -+++ b/xen/drivers/passthrough/pci.c -@@ -52,9 +52,10 @@ struct pci_seg { - - static spinlock_t _pcidevs_lock = SPIN_LOCK_UNLOCKED; - --void pcidevs_lock(void) -+/* Do not use, as it has no speculation barrier, use pcidevs_lock() instead. */ -+void pcidevs_lock_unsafe(void) - { -- spin_lock_recursive(&_pcidevs_lock); -+ _spin_lock_recursive(&_pcidevs_lock); - } - - void pcidevs_unlock(void) -diff --git a/xen/include/xen/event.h b/xen/include/xen/event.h -index 8e509e078475..f1472ea1ebe5 100644 ---- a/xen/include/xen/event.h -+++ b/xen/include/xen/event.h -@@ -114,12 +114,12 @@ void notify_via_xen_event_channel(struct domain *ld, int lport); - #define bucket_from_port(d, p) \ - ((group_from_port(d, p))[((p) % EVTCHNS_PER_GROUP) / EVTCHNS_PER_BUCKET]) - --static inline void evtchn_read_lock(struct evtchn *evtchn) -+static always_inline void evtchn_read_lock(struct evtchn *evtchn) - { - read_lock(&evtchn->lock); - } - --static inline bool evtchn_read_trylock(struct evtchn *evtchn) -+static always_inline bool evtchn_read_trylock(struct evtchn *evtchn) - { - return read_trylock(&evtchn->lock); - } -diff --git a/xen/include/xen/pci.h b/xen/include/xen/pci.h -index 251b8761a8e9..a71bed36be29 100644 ---- a/xen/include/xen/pci.h -+++ b/xen/include/xen/pci.h -@@ -155,8 +155,12 @@ struct pci_dev { - * devices, it also sync the access to the msi capability that is not - * interrupt handling related (the mask bit register). - */ -- --void pcidevs_lock(void); -+void pcidevs_lock_unsafe(void); -+static always_inline void pcidevs_lock(void) -+{ -+ pcidevs_lock_unsafe(); -+ block_lock_speculation(); -+} - void pcidevs_unlock(void); - bool __must_check pcidevs_locked(void); - diff --git a/xsa453-4.18-6.patch b/xsa453-4.18-6.patch deleted file mode 100644 index e8104c5..0000000 --- a/xsa453-4.18-6.patch +++ /dev/null @@ -1,61 +0,0 @@ -From: =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= -Subject: x86/mm: add speculation barriers to open coded locks - -Add a speculation barrier to the clearly identified open-coded lock taking -functions. - -Note that the memory sharing page_lock() replacement (_page_lock()) is left -as-is, as the code is experimental and not security supported. - -This is part of XSA-453 / CVE-2024-2193 - -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich -(cherry picked from commit 42a572a38e22a97d86a4b648a22597628d5b42e4) - -diff --git a/xen/arch/x86/include/asm/mm.h b/xen/arch/x86/include/asm/mm.h -index 05dfe35502c8..d1b1fee99b7d 100644 ---- a/xen/arch/x86/include/asm/mm.h -+++ b/xen/arch/x86/include/asm/mm.h -@@ -399,7 +399,9 @@ const struct platform_bad_page *get_platform_badpages(unsigned int *array_size); - * The use of PGT_locked in mem_sharing does not collide, since mem_sharing is - * only supported for hvm guests, which do not have PV PTEs updated. - */ --int page_lock(struct page_info *page); -+int page_lock_unsafe(struct page_info *page); -+#define page_lock(pg) lock_evaluate_nospec(page_lock_unsafe(pg)) -+ - void page_unlock(struct page_info *page); - - void put_page_type(struct page_info *page); -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index ab0acbfea6e5..000fd0fb558b 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -2017,7 +2017,7 @@ static inline bool current_locked_page_ne_check(struct page_info *page) { - #define current_locked_page_ne_check(x) true - #endif - --int page_lock(struct page_info *page) -+int page_lock_unsafe(struct page_info *page) - { - unsigned long x, nx; - -@@ -2078,7 +2078,7 @@ void page_unlock(struct page_info *page) - * l3t_lock(), so to avoid deadlock we must avoid grabbing them in - * reverse order. - */ --static void l3t_lock(struct page_info *page) -+static always_inline void l3t_lock(struct page_info *page) - { - unsigned long x, nx; - -@@ -2087,6 +2087,8 @@ static void l3t_lock(struct page_info *page) - cpu_relax(); - nx = x | PGT_locked; - } while ( cmpxchg(&page->u.inuse.type_info, x, nx) != x ); -+ -+ block_lock_speculation(); - } - - static void l3t_unlock(struct page_info *page) diff --git a/xsa453-4.18-7.patch b/xsa453-4.18-7.patch deleted file mode 100644 index 48f0bf9..0000000 --- a/xsa453-4.18-7.patch +++ /dev/null @@ -1,201 +0,0 @@ -From: =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= -Subject: x86: protect conditional lock taking from speculative execution - -Conditionally taken locks that use the pattern: - -if ( lock ) - spin_lock(...); - -Need an else branch in order to issue an speculation barrier in the else case, -just like it's done in case the lock needs to be acquired. - -eval_nospec() could be used on the condition itself, but that would result in a -double barrier on the branch where the lock is taken. - -Introduce a new pair of helpers, {gfn,spin}_lock_if() that can be used to -conditionally take a lock in a speculation safe way. - -This is part of XSA-453 / CVE-2024-2193 - -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich -(cherry picked from commit 03cf7ca23e0e876075954c558485b267b7d02406) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 000fd0fb558b..45bfbc2522f7 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -5007,8 +5007,7 @@ static l3_pgentry_t *virt_to_xen_l3e(unsigned long v) - if ( !l3t ) - return NULL; - UNMAP_DOMAIN_PAGE(l3t); -- if ( locking ) -- spin_lock(&map_pgdir_lock); -+ spin_lock_if(locking, &map_pgdir_lock); - if ( !(l4e_get_flags(*pl4e) & _PAGE_PRESENT) ) - { - l4_pgentry_t l4e = l4e_from_mfn(l3mfn, __PAGE_HYPERVISOR); -@@ -5045,8 +5044,7 @@ static l2_pgentry_t *virt_to_xen_l2e(unsigned long v) - return NULL; - } - UNMAP_DOMAIN_PAGE(l2t); -- if ( locking ) -- spin_lock(&map_pgdir_lock); -+ spin_lock_if(locking, &map_pgdir_lock); - if ( !(l3e_get_flags(*pl3e) & _PAGE_PRESENT) ) - { - l3e_write(pl3e, l3e_from_mfn(l2mfn, __PAGE_HYPERVISOR)); -@@ -5084,8 +5082,7 @@ l1_pgentry_t *virt_to_xen_l1e(unsigned long v) - return NULL; - } - UNMAP_DOMAIN_PAGE(l1t); -- if ( locking ) -- spin_lock(&map_pgdir_lock); -+ spin_lock_if(locking, &map_pgdir_lock); - if ( !(l2e_get_flags(*pl2e) & _PAGE_PRESENT) ) - { - l2e_write(pl2e, l2e_from_mfn(l1mfn, __PAGE_HYPERVISOR)); -@@ -5116,6 +5113,8 @@ l1_pgentry_t *virt_to_xen_l1e(unsigned long v) - do { \ - if ( locking ) \ - l3t_lock(page); \ -+ else \ -+ block_lock_speculation(); \ - } while ( false ) - - #define L3T_UNLOCK(page) \ -@@ -5331,8 +5330,7 @@ int map_pages_to_xen( - if ( l3e_get_flags(ol3e) & _PAGE_GLOBAL ) - flush_flags |= FLUSH_TLB_GLOBAL; - -- if ( locking ) -- spin_lock(&map_pgdir_lock); -+ spin_lock_if(locking, &map_pgdir_lock); - if ( (l3e_get_flags(*pl3e) & _PAGE_PRESENT) && - (l3e_get_flags(*pl3e) & _PAGE_PSE) ) - { -@@ -5436,8 +5434,7 @@ int map_pages_to_xen( - if ( l2e_get_flags(*pl2e) & _PAGE_GLOBAL ) - flush_flags |= FLUSH_TLB_GLOBAL; - -- if ( locking ) -- spin_lock(&map_pgdir_lock); -+ spin_lock_if(locking, &map_pgdir_lock); - if ( (l2e_get_flags(*pl2e) & _PAGE_PRESENT) && - (l2e_get_flags(*pl2e) & _PAGE_PSE) ) - { -@@ -5478,8 +5475,7 @@ int map_pages_to_xen( - unsigned long base_mfn; - const l1_pgentry_t *l1t; - -- if ( locking ) -- spin_lock(&map_pgdir_lock); -+ spin_lock_if(locking, &map_pgdir_lock); - - ol2e = *pl2e; - /* -@@ -5533,8 +5529,7 @@ int map_pages_to_xen( - unsigned long base_mfn; - const l2_pgentry_t *l2t; - -- if ( locking ) -- spin_lock(&map_pgdir_lock); -+ spin_lock_if(locking, &map_pgdir_lock); - - ol3e = *pl3e; - /* -@@ -5678,8 +5673,7 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) - l3e_get_flags(*pl3e))); - UNMAP_DOMAIN_PAGE(l2t); - -- if ( locking ) -- spin_lock(&map_pgdir_lock); -+ spin_lock_if(locking, &map_pgdir_lock); - if ( (l3e_get_flags(*pl3e) & _PAGE_PRESENT) && - (l3e_get_flags(*pl3e) & _PAGE_PSE) ) - { -@@ -5738,8 +5732,7 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) - l2e_get_flags(*pl2e) & ~_PAGE_PSE)); - UNMAP_DOMAIN_PAGE(l1t); - -- if ( locking ) -- spin_lock(&map_pgdir_lock); -+ spin_lock_if(locking, &map_pgdir_lock); - if ( (l2e_get_flags(*pl2e) & _PAGE_PRESENT) && - (l2e_get_flags(*pl2e) & _PAGE_PSE) ) - { -@@ -5783,8 +5776,7 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) - */ - if ( (nf & _PAGE_PRESENT) || ((v != e) && (l1_table_offset(v) != 0)) ) - continue; -- if ( locking ) -- spin_lock(&map_pgdir_lock); -+ spin_lock_if(locking, &map_pgdir_lock); - - /* - * L2E may be already cleared, or set to a superpage, by -@@ -5831,8 +5823,7 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) - if ( (nf & _PAGE_PRESENT) || - ((v != e) && (l2_table_offset(v) + l1_table_offset(v) != 0)) ) - continue; -- if ( locking ) -- spin_lock(&map_pgdir_lock); -+ spin_lock_if(locking, &map_pgdir_lock); - - /* - * L3E may be already cleared, or set to a superpage, by -diff --git a/xen/arch/x86/mm/mm-locks.h b/xen/arch/x86/mm/mm-locks.h -index 5ec080c02fd8..b4960fb90eff 100644 ---- a/xen/arch/x86/mm/mm-locks.h -+++ b/xen/arch/x86/mm/mm-locks.h -@@ -335,6 +335,15 @@ static inline void p2m_unlock(struct p2m_domain *p) - #define p2m_locked_by_me(p) mm_write_locked_by_me(&(p)->lock) - #define gfn_locked_by_me(p,g) p2m_locked_by_me(p) - -+static always_inline void gfn_lock_if(bool condition, struct p2m_domain *p2m, -+ gfn_t gfn, unsigned int order) -+{ -+ if ( condition ) -+ gfn_lock(p2m, gfn, order); -+ else -+ block_lock_speculation(); -+} -+ - /* PoD lock (per-p2m-table) - * - * Protects private PoD data structs: entry and cache -diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c -index 0983bd71d9a9..22ab1d606e8a 100644 ---- a/xen/arch/x86/mm/p2m.c -+++ b/xen/arch/x86/mm/p2m.c -@@ -280,9 +280,8 @@ mfn_t p2m_get_gfn_type_access(struct p2m_domain *p2m, gfn_t gfn, - if ( q & P2M_UNSHARE ) - q |= P2M_ALLOC; - -- if ( locked ) -- /* Grab the lock here, don't release until put_gfn */ -- gfn_lock(p2m, gfn, 0); -+ /* Grab the lock here, don't release until put_gfn */ -+ gfn_lock_if(locked, p2m, gfn, 0); - - mfn = p2m->get_entry(p2m, gfn, t, a, q, page_order, NULL); - -diff --git a/xen/include/xen/spinlock.h b/xen/include/xen/spinlock.h -index 28fce5615e5c..c830df3430a3 100644 ---- a/xen/include/xen/spinlock.h -+++ b/xen/include/xen/spinlock.h -@@ -222,6 +222,14 @@ static always_inline void spin_lock_irq(spinlock_t *l) - block_lock_speculation(); \ - }) - -+/* Conditionally take a spinlock in a speculation safe way. */ -+static always_inline void spin_lock_if(bool condition, spinlock_t *l) -+{ -+ if ( condition ) -+ _spin_lock(l); -+ block_lock_speculation(); -+} -+ - #define spin_unlock(l) _spin_unlock(l) - #define spin_unlock_irq(l) _spin_unlock_irq(l) - #define spin_unlock_irqrestore(l, f) _spin_unlock_irqrestore(l, f) From 4dc6f761c8795bd7201c191fec3d30030c63184c Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 9 Apr 2024 19:25:08 +0100 Subject: [PATCH 087/137] 2 security updates x86 HVM hypercalls may trigger Xen bug check [XSA-454, CVE-2023-46842] x86: Incorrect logic for BTC/SRSO mitigations [XSA-455, CVE-2024-31142] --- xen.spec | 12 ++++++- xsa454-4.18-1.patch | 88 +++++++++++++++++++++++++++++++++++++++++++++ xsa454-4.18-2.patch | 68 +++++++++++++++++++++++++++++++++++ xsa455.patch | 41 +++++++++++++++++++++ 4 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 xsa454-4.18-1.patch create mode 100644 xsa454-4.18-2.patch create mode 100644 xsa455.patch diff --git a/xen.spec b/xen.spec index 06a5573..6a7c0c6 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.18.1 -Release: 1%{?dist} +Release: 2%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -114,6 +114,9 @@ Patch49: xen.python3.12.patch Patch50: xen.ocaml5.fixes.patch Patch52: xen.gcc14.fixes.patch Patch53: newlib.gcc14.fixes.patch +Patch54: xsa454-4.18-1.patch +Patch55: xsa454-4.18-2.patch +Patch56: xsa455.patch %if %build_qemutrad @@ -328,6 +331,9 @@ manage Xen virtual machines. %endif %patch 52 -p1 %patch 53 -p1 +%patch 54 -p1 +%patch 55 -p1 +%patch 56 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -934,6 +940,10 @@ fi %endif %changelog +* Tue Apr 09 2024 Michael Young - 4.18.1-2 +- x86 HVM hypercalls may trigger Xen bug check [XSA-454, CVE-2023-46842] +- x86: Incorrect logic for BTC/SRSO mitigations [XSA-455, CVE-2024-31142] + * Wed Mar 20 2024 Michael Young - 4.18.1-1 - update to xen-4.18.1 rebase xen.gcc12.fixes.patch diff --git a/xsa454-4.18-1.patch b/xsa454-4.18-1.patch new file mode 100644 index 0000000..3655cc4 --- /dev/null +++ b/xsa454-4.18-1.patch @@ -0,0 +1,88 @@ +From: Jan Beulich +Subject: x86/HVM: clear upper halves of GPRs upon entry from 32-bit code + +Hypercalls in particular can be the subject of continuations, and logic +there checks updated state against incoming register values. If the +guest manufactured a suitable argument register with a non-zero upper +half before entering compatibility mode and issuing a hypercall from +there, checks in hypercall_xlat_continuation() might trip. + +Since for HVM we want to also be sure to not hit a corner case in the +emulator, initiate the clipping right from the top of +{svm,vmx}_vmexit_handler(). Also rename the invoked function, as it no +longer does only invalidation of fields. + +Note that architecturally the upper halves of registers are undefined +after a switch between compatibility and 64-bit mode (either direction). +Hence once having entered compatibility mode, the guest can't assume +the upper half of any register to retain its value. + +This is part of XSA-454 / CVE-2023-46842. + +Fixes: b8a7efe8528a ("Enable compatibility mode operation for HYPERVISOR_memory_op") +Reported-by: Manuel Andreas +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné + +--- a/xen/arch/x86/hvm/svm/svm.c ++++ b/xen/arch/x86/hvm/svm/svm.c +@@ -2603,7 +2603,8 @@ void svm_vmexit_handler(void) + regs->rsp = vmcb->rsp; + regs->rflags = vmcb->rflags; + +- hvm_invalidate_regs_fields(regs); ++ hvm_sanitize_regs_fields( ++ regs, !(vmcb_get_efer(vmcb) & EFER_LMA) || !(vmcb->cs.l)); + + if ( paging_mode_hap(v->domain) ) + v->arch.hvm.guest_cr[3] = v->arch.hvm.hw_cr[3] = vmcb_get_cr3(vmcb); +--- a/xen/arch/x86/hvm/vmx/vmx.c ++++ b/xen/arch/x86/hvm/vmx/vmx.c +@@ -4041,6 +4041,7 @@ static void undo_nmis_unblocked_by_iret( + void vmx_vmexit_handler(struct cpu_user_regs *regs) + { + unsigned long exit_qualification, exit_reason, idtv_info, intr_info = 0; ++ unsigned long cs_ar_bytes = 0; + unsigned int vector = 0; + struct vcpu *v = current; + struct domain *currd = v->domain; +@@ -4049,7 +4050,10 @@ void vmx_vmexit_handler(struct cpu_user_ + __vmread(GUEST_RSP, ®s->rsp); + __vmread(GUEST_RFLAGS, ®s->rflags); + +- hvm_invalidate_regs_fields(regs); ++ if ( hvm_long_mode_active(v) ) ++ __vmread(GUEST_CS_AR_BYTES, &cs_ar_bytes); ++ ++ hvm_sanitize_regs_fields(regs, !(cs_ar_bytes & X86_SEG_AR_CS_LM_ACTIVE)); + + if ( paging_mode_hap(v->domain) ) + { +--- a/xen/arch/x86/include/asm/hvm/hvm.h ++++ b/xen/arch/x86/include/asm/hvm/hvm.h +@@ -579,8 +579,24 @@ static inline unsigned int hvm_get_insn_ + ? alternative_call(hvm_funcs.get_insn_bytes, v, buf) : 0); + } + +-static inline void hvm_invalidate_regs_fields(struct cpu_user_regs *regs) ++static inline void hvm_sanitize_regs_fields(struct cpu_user_regs *regs, ++ bool compat) + { ++ if ( compat ) ++ { ++ /* Clear GPR upper halves, to counteract guests playing games. */ ++ regs->rbp = regs->ebp; ++ regs->rbx = regs->ebx; ++ regs->rax = regs->eax; ++ regs->rcx = regs->ecx; ++ regs->rdx = regs->edx; ++ regs->rsi = regs->esi; ++ regs->rdi = regs->edi; ++ regs->rip = regs->eip; ++ regs->rflags = regs->eflags; ++ regs->rsp = regs->esp; ++ } ++ + #ifndef NDEBUG + regs->error_code = 0xbeef; + regs->entry_vector = 0xbeef; diff --git a/xsa454-4.18-2.patch b/xsa454-4.18-2.patch new file mode 100644 index 0000000..ff8c350 --- /dev/null +++ b/xsa454-4.18-2.patch @@ -0,0 +1,68 @@ +From: Bjoern Doebel +Subject: hypercall_xlat_continuation: Replace BUG_ON with domain_crash + +Instead of crashing the host in case of unexpected hypercall parameters, +resort to only crashing the calling domain. + +This is part of XSA-454 / CVE-2023-46842. + +Fixes: b8a7efe8528a ("Enable compatibility mode operation for HYPERVISOR_memory_op") +Reported-by: Manuel Andreas +Signed-off-by: Bjoern Doebel +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné + +--- a/xen/arch/x86/hypercall.c ++++ b/xen/arch/x86/hypercall.c +@@ -140,8 +140,10 @@ int hypercall_xlat_continuation(unsigned + cval = va_arg(args, unsigned int); + if ( cval == nval ) + mask &= ~1U; +- else +- BUG_ON(nval == (unsigned int)nval); ++ else if ( nval == (unsigned int)nval ) ++ domain_crash(current->domain, ++ "multicall (op %lu) bogus continuation arg%u (%#lx)\n", ++ mcs->call.op, i, nval); + } + else if ( id && *id == i ) + { +@@ -153,8 +155,10 @@ int hypercall_xlat_continuation(unsigned + mcs->call.args[i] = cval; + ++rc; + } +- else +- BUG_ON(mcs->call.args[i] != (unsigned int)mcs->call.args[i]); ++ else if ( mcs->call.args[i] != (unsigned int)mcs->call.args[i] ) ++ domain_crash(current->domain, ++ "multicall (op %lu) bad continuation arg%u (%#lx)\n", ++ mcs->call.op, i, mcs->call.args[i]); + } + } + else +@@ -180,8 +184,10 @@ int hypercall_xlat_continuation(unsigned + cval = va_arg(args, unsigned int); + if ( cval == nval ) + mask &= ~1U; +- else +- BUG_ON(nval == (unsigned int)nval); ++ else if ( nval == (unsigned int)nval ) ++ domain_crash(current->domain, ++ "hypercall (op %u) bogus continuation arg%u (%#lx)\n", ++ regs->eax, i, nval); + } + else if ( id && *id == i ) + { +@@ -193,8 +199,10 @@ int hypercall_xlat_continuation(unsigned + *reg = cval; + ++rc; + } +- else +- BUG_ON(*reg != (unsigned int)*reg); ++ else if ( *reg != (unsigned int)*reg ) ++ domain_crash(current->domain, ++ "hypercall (op %u) bad continuation arg%u (%#lx)\n", ++ regs->eax, i, *reg); + } + } + diff --git a/xsa455.patch b/xsa455.patch new file mode 100644 index 0000000..637d21f --- /dev/null +++ b/xsa455.patch @@ -0,0 +1,41 @@ +From 5bc561024f81371ff267edae73ae4a768b2f7a91 Mon Sep 17 00:00:00 2001 +From: Andrew Cooper +Date: Tue, 26 Mar 2024 22:47:25 +0000 +Subject: x86/spec-ctrl: Fix BTC/SRSO mitigations + +We were looking for SCF_entry_ibpb in the wrong variable in the top-of-stack +block, and xen_spec_ctrl won't have had bit 5 set because Xen doesn't +understand SPEC_CTRL_RRSBA_DIS_U yet. + +This is XSA-455 / CVE-2024-31142. + +Fixes: 53a570b28569 ("x86/spec-ctrl: Support IBPB-on-entry") +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/hvm/svm/entry.S b/xen/arch/x86/hvm/svm/entry.S +index 60b0b00ed0af..071b3997b1c0 100644 +--- a/xen/arch/x86/hvm/svm/entry.S ++++ b/xen/arch/x86/hvm/svm/entry.S +@@ -101,7 +101,7 @@ __UNLIKELY_END(nsvm_hap) + /* SPEC_CTRL_ENTRY_FROM_SVM Req: %rsp=regs/cpuinfo, %rdx=0 Clob: acd */ + + .macro svm_vmexit_cond_ibpb +- testb $SCF_entry_ibpb, CPUINFO_xen_spec_ctrl(%rsp) ++ testb $SCF_entry_ibpb, CPUINFO_spec_ctrl_flags(%rsp) + jz .L_skip_ibpb + + mov $MSR_PRED_CMD, %ecx +diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h +index 629518cc6925..c19b39d8c200 100644 +--- a/xen/arch/x86/include/asm/spec_ctrl_asm.h ++++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h +@@ -90,7 +90,7 @@ + jz .L\@_skip + testb $3, UREGS_cs(%rsp) + .else +- testb $SCF_entry_ibpb, CPUINFO_xen_spec_ctrl(%rsp) ++ testb $SCF_entry_ibpb, CPUINFO_spec_ctrl_flags(%rsp) + .endif + jz .L\@_skip + From c762409b331479c95fbc74ce9e43931e97f3af17 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 9 Apr 2024 21:43:30 +0100 Subject: [PATCH 088/137] x86: Native Branch History Injection [XSA-456, CVE-2024-2201] update to xen 4.18.2, remove patches now included upstream --- .gitignore | 2 +- sources | 2 +- xen.spec | 14 ++++---- xsa454-4.18-1.patch | 88 --------------------------------------------- xsa454-4.18-2.patch | 68 ----------------------------------- xsa455.patch | 41 --------------------- 6 files changed, 8 insertions(+), 207 deletions(-) delete mode 100644 xsa454-4.18-1.patch delete mode 100644 xsa454-4.18-2.patch delete mode 100644 xsa455.patch diff --git a/.gitignore b/.gitignore index 97a51c2..439b7c0 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.18.1.tar.gz +/xen-4.18.2.tar.gz diff --git a/sources b/sources index 8d4dabc..5b1c173 100644 --- a/sources +++ b/sources @@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.18.1.tar.gz) = 6d67c177a31dae6979c111498f65cff2a7a792299cc72e658ae9f926468c14092807de94b6e269849fafdb6ed5a9d076400ecde0c4fec3b2b4bdb5f5ef39fcbb +SHA512 (xen-4.18.2.tar.gz) = c5feb450155883b5d2e7f43b05a64e7215b661b7d2f438d8f5a0896bd57283379ee11ca8e2e7a1d8787813cc6f1a260253fcb8688ed7d61a2bfb636db1626941 diff --git a/xen.spec b/xen.spec index 6a7c0c6..6441674 100644 --- a/xen.spec +++ b/xen.spec @@ -54,8 +54,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.18.1 -Release: 2%{?dist} +Version: 4.18.2 +Release: 1%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -114,9 +114,6 @@ Patch49: xen.python3.12.patch Patch50: xen.ocaml5.fixes.patch Patch52: xen.gcc14.fixes.patch Patch53: newlib.gcc14.fixes.patch -Patch54: xsa454-4.18-1.patch -Patch55: xsa454-4.18-2.patch -Patch56: xsa455.patch %if %build_qemutrad @@ -331,9 +328,6 @@ manage Xen virtual machines. %endif %patch 52 -p1 %patch 53 -p1 -%patch 54 -p1 -%patch 55 -p1 -%patch 56 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -940,6 +934,10 @@ fi %endif %changelog +* Tue Apr 09 2024 Michael Young - 4.18.2-1 +- x86: Native Branch History Injection [XSA-456, CVE-2024-2201] +- update to xen 4.18.2, remove patches now included upstream + * Tue Apr 09 2024 Michael Young - 4.18.1-2 - x86 HVM hypercalls may trigger Xen bug check [XSA-454, CVE-2023-46842] - x86: Incorrect logic for BTC/SRSO mitigations [XSA-455, CVE-2024-31142] diff --git a/xsa454-4.18-1.patch b/xsa454-4.18-1.patch deleted file mode 100644 index 3655cc4..0000000 --- a/xsa454-4.18-1.patch +++ /dev/null @@ -1,88 +0,0 @@ -From: Jan Beulich -Subject: x86/HVM: clear upper halves of GPRs upon entry from 32-bit code - -Hypercalls in particular can be the subject of continuations, and logic -there checks updated state against incoming register values. If the -guest manufactured a suitable argument register with a non-zero upper -half before entering compatibility mode and issuing a hypercall from -there, checks in hypercall_xlat_continuation() might trip. - -Since for HVM we want to also be sure to not hit a corner case in the -emulator, initiate the clipping right from the top of -{svm,vmx}_vmexit_handler(). Also rename the invoked function, as it no -longer does only invalidation of fields. - -Note that architecturally the upper halves of registers are undefined -after a switch between compatibility and 64-bit mode (either direction). -Hence once having entered compatibility mode, the guest can't assume -the upper half of any register to retain its value. - -This is part of XSA-454 / CVE-2023-46842. - -Fixes: b8a7efe8528a ("Enable compatibility mode operation for HYPERVISOR_memory_op") -Reported-by: Manuel Andreas -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné - ---- a/xen/arch/x86/hvm/svm/svm.c -+++ b/xen/arch/x86/hvm/svm/svm.c -@@ -2603,7 +2603,8 @@ void svm_vmexit_handler(void) - regs->rsp = vmcb->rsp; - regs->rflags = vmcb->rflags; - -- hvm_invalidate_regs_fields(regs); -+ hvm_sanitize_regs_fields( -+ regs, !(vmcb_get_efer(vmcb) & EFER_LMA) || !(vmcb->cs.l)); - - if ( paging_mode_hap(v->domain) ) - v->arch.hvm.guest_cr[3] = v->arch.hvm.hw_cr[3] = vmcb_get_cr3(vmcb); ---- a/xen/arch/x86/hvm/vmx/vmx.c -+++ b/xen/arch/x86/hvm/vmx/vmx.c -@@ -4041,6 +4041,7 @@ static void undo_nmis_unblocked_by_iret( - void vmx_vmexit_handler(struct cpu_user_regs *regs) - { - unsigned long exit_qualification, exit_reason, idtv_info, intr_info = 0; -+ unsigned long cs_ar_bytes = 0; - unsigned int vector = 0; - struct vcpu *v = current; - struct domain *currd = v->domain; -@@ -4049,7 +4050,10 @@ void vmx_vmexit_handler(struct cpu_user_ - __vmread(GUEST_RSP, ®s->rsp); - __vmread(GUEST_RFLAGS, ®s->rflags); - -- hvm_invalidate_regs_fields(regs); -+ if ( hvm_long_mode_active(v) ) -+ __vmread(GUEST_CS_AR_BYTES, &cs_ar_bytes); -+ -+ hvm_sanitize_regs_fields(regs, !(cs_ar_bytes & X86_SEG_AR_CS_LM_ACTIVE)); - - if ( paging_mode_hap(v->domain) ) - { ---- a/xen/arch/x86/include/asm/hvm/hvm.h -+++ b/xen/arch/x86/include/asm/hvm/hvm.h -@@ -579,8 +579,24 @@ static inline unsigned int hvm_get_insn_ - ? alternative_call(hvm_funcs.get_insn_bytes, v, buf) : 0); - } - --static inline void hvm_invalidate_regs_fields(struct cpu_user_regs *regs) -+static inline void hvm_sanitize_regs_fields(struct cpu_user_regs *regs, -+ bool compat) - { -+ if ( compat ) -+ { -+ /* Clear GPR upper halves, to counteract guests playing games. */ -+ regs->rbp = regs->ebp; -+ regs->rbx = regs->ebx; -+ regs->rax = regs->eax; -+ regs->rcx = regs->ecx; -+ regs->rdx = regs->edx; -+ regs->rsi = regs->esi; -+ regs->rdi = regs->edi; -+ regs->rip = regs->eip; -+ regs->rflags = regs->eflags; -+ regs->rsp = regs->esp; -+ } -+ - #ifndef NDEBUG - regs->error_code = 0xbeef; - regs->entry_vector = 0xbeef; diff --git a/xsa454-4.18-2.patch b/xsa454-4.18-2.patch deleted file mode 100644 index ff8c350..0000000 --- a/xsa454-4.18-2.patch +++ /dev/null @@ -1,68 +0,0 @@ -From: Bjoern Doebel -Subject: hypercall_xlat_continuation: Replace BUG_ON with domain_crash - -Instead of crashing the host in case of unexpected hypercall parameters, -resort to only crashing the calling domain. - -This is part of XSA-454 / CVE-2023-46842. - -Fixes: b8a7efe8528a ("Enable compatibility mode operation for HYPERVISOR_memory_op") -Reported-by: Manuel Andreas -Signed-off-by: Bjoern Doebel -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné - ---- a/xen/arch/x86/hypercall.c -+++ b/xen/arch/x86/hypercall.c -@@ -140,8 +140,10 @@ int hypercall_xlat_continuation(unsigned - cval = va_arg(args, unsigned int); - if ( cval == nval ) - mask &= ~1U; -- else -- BUG_ON(nval == (unsigned int)nval); -+ else if ( nval == (unsigned int)nval ) -+ domain_crash(current->domain, -+ "multicall (op %lu) bogus continuation arg%u (%#lx)\n", -+ mcs->call.op, i, nval); - } - else if ( id && *id == i ) - { -@@ -153,8 +155,10 @@ int hypercall_xlat_continuation(unsigned - mcs->call.args[i] = cval; - ++rc; - } -- else -- BUG_ON(mcs->call.args[i] != (unsigned int)mcs->call.args[i]); -+ else if ( mcs->call.args[i] != (unsigned int)mcs->call.args[i] ) -+ domain_crash(current->domain, -+ "multicall (op %lu) bad continuation arg%u (%#lx)\n", -+ mcs->call.op, i, mcs->call.args[i]); - } - } - else -@@ -180,8 +184,10 @@ int hypercall_xlat_continuation(unsigned - cval = va_arg(args, unsigned int); - if ( cval == nval ) - mask &= ~1U; -- else -- BUG_ON(nval == (unsigned int)nval); -+ else if ( nval == (unsigned int)nval ) -+ domain_crash(current->domain, -+ "hypercall (op %u) bogus continuation arg%u (%#lx)\n", -+ regs->eax, i, nval); - } - else if ( id && *id == i ) - { -@@ -193,8 +199,10 @@ int hypercall_xlat_continuation(unsigned - *reg = cval; - ++rc; - } -- else -- BUG_ON(*reg != (unsigned int)*reg); -+ else if ( *reg != (unsigned int)*reg ) -+ domain_crash(current->domain, -+ "hypercall (op %u) bad continuation arg%u (%#lx)\n", -+ regs->eax, i, *reg); - } - } - diff --git a/xsa455.patch b/xsa455.patch deleted file mode 100644 index 637d21f..0000000 --- a/xsa455.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 5bc561024f81371ff267edae73ae4a768b2f7a91 Mon Sep 17 00:00:00 2001 -From: Andrew Cooper -Date: Tue, 26 Mar 2024 22:47:25 +0000 -Subject: x86/spec-ctrl: Fix BTC/SRSO mitigations - -We were looking for SCF_entry_ibpb in the wrong variable in the top-of-stack -block, and xen_spec_ctrl won't have had bit 5 set because Xen doesn't -understand SPEC_CTRL_RRSBA_DIS_U yet. - -This is XSA-455 / CVE-2024-31142. - -Fixes: 53a570b28569 ("x86/spec-ctrl: Support IBPB-on-entry") -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/hvm/svm/entry.S b/xen/arch/x86/hvm/svm/entry.S -index 60b0b00ed0af..071b3997b1c0 100644 ---- a/xen/arch/x86/hvm/svm/entry.S -+++ b/xen/arch/x86/hvm/svm/entry.S -@@ -101,7 +101,7 @@ __UNLIKELY_END(nsvm_hap) - /* SPEC_CTRL_ENTRY_FROM_SVM Req: %rsp=regs/cpuinfo, %rdx=0 Clob: acd */ - - .macro svm_vmexit_cond_ibpb -- testb $SCF_entry_ibpb, CPUINFO_xen_spec_ctrl(%rsp) -+ testb $SCF_entry_ibpb, CPUINFO_spec_ctrl_flags(%rsp) - jz .L_skip_ibpb - - mov $MSR_PRED_CMD, %ecx -diff --git a/xen/arch/x86/include/asm/spec_ctrl_asm.h b/xen/arch/x86/include/asm/spec_ctrl_asm.h -index 629518cc6925..c19b39d8c200 100644 ---- a/xen/arch/x86/include/asm/spec_ctrl_asm.h -+++ b/xen/arch/x86/include/asm/spec_ctrl_asm.h -@@ -90,7 +90,7 @@ - jz .L\@_skip - testb $3, UREGS_cs(%rsp) - .else -- testb $SCF_entry_ibpb, CPUINFO_xen_spec_ctrl(%rsp) -+ testb $SCF_entry_ibpb, CPUINFO_spec_ctrl_flags(%rsp) - .endif - jz .L\@_skip - From 254a3095373afd2c8943e633fdc9e54e8b3a5ea4 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Mon, 3 Jun 2024 21:39:12 +0100 Subject: [PATCH 089/137] x86: Native Branch History Injection [XSA-456 version 3, CVE-2024-2201] --- ...7dca70d64c35c86e5d503f67366ebe2b9138.patch | 69 +++++++++++++++++ ...302c33b117aa9a417056db241aefc840c2f0.patch | 32 ++++++++ ...b0b98b53140102031ceca0611f22190227fd.patch | 49 ++++++++++++ ...9ba44a58c87d6d135d8ffbf468b4ceac0a41.patch | 56 ++++++++++++++ ...59767c82d833ebecdf8106e96482b04f3c40.patch | 36 +++++++++ ...1d9cb96041a84a24857a6464628240deed4f.patch | 75 +++++++++++++++++++ xen.spec | 17 ++++- 7 files changed, 333 insertions(+), 1 deletion(-) create mode 100644 xen.git-0b0c7dca70d64c35c86e5d503f67366ebe2b9138.patch create mode 100644 xen.git-2d38302c33b117aa9a417056db241aefc840c2f0.patch create mode 100644 xen.git-8bdcb0b98b53140102031ceca0611f22190227fd.patch create mode 100644 xen.git-af0e9ba44a58c87d6d135d8ffbf468b4ceac0a41.patch create mode 100644 xen.git-eb7059767c82d833ebecdf8106e96482b04f3c40.patch create mode 100644 xen.git-f0ff1d9cb96041a84a24857a6464628240deed4f.patch diff --git a/xen.git-0b0c7dca70d64c35c86e5d503f67366ebe2b9138.patch b/xen.git-0b0c7dca70d64c35c86e5d503f67366ebe2b9138.patch new file mode 100644 index 0000000..fbaac26 --- /dev/null +++ b/xen.git-0b0c7dca70d64c35c86e5d503f67366ebe2b9138.patch @@ -0,0 +1,69 @@ +From 0b0c7dca70d64c35c86e5d503f67366ebe2b9138 Mon Sep 17 00:00:00 2001 +From: =?utf8?q?Roger=20Pau=20Monn=C3=A9?= +Date: Mon, 29 Apr 2024 09:37:04 +0200 +Subject: [PATCH] x86/spec: fix reporting of BHB clearing usage from guest + entry points +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf8 +Content-Transfer-Encoding: 8bit + +Reporting whether the BHB clearing on entry is done for the different domains +types based on cpu_has_bhb_seq is unhelpful, as that variable signals whether +there's a BHB clearing sequence selected, but that alone doesn't imply that +such sequence is used from the PV and/or HVM entry points. + +Instead use opt_bhb_entry_{pv,hvm} which do signal whether BHB clearing is +performed on entry from PV/HVM. + +Fixes: 689ad48ce9cf ('x86/spec-ctrl: Wire up the Native-BHI software sequences') +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +Reviewed-by: Andrew Cooper +master commit: 049ab0b2c9f1f5edb54b505fef0bc575787dafe9 +master date: 2024-04-25 16:35:56 +0200 +--- + xen/arch/x86/spec_ctrl.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index ba4349a024..8c67d6256a 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -634,7 +634,7 @@ static void __init print_details(enum ind_thunk thunk) + (boot_cpu_has(X86_FEATURE_SC_MSR_HVM) || + boot_cpu_has(X86_FEATURE_SC_RSB_HVM) || + boot_cpu_has(X86_FEATURE_IBPB_ENTRY_HVM) || +- cpu_has_bhb_seq || amd_virt_spec_ctrl || ++ opt_bhb_entry_hvm || amd_virt_spec_ctrl || + opt_eager_fpu || opt_verw_hvm) ? "" : " None", + boot_cpu_has(X86_FEATURE_SC_MSR_HVM) ? " MSR_SPEC_CTRL" : "", + (boot_cpu_has(X86_FEATURE_SC_MSR_HVM) || +@@ -643,7 +643,7 @@ static void __init print_details(enum ind_thunk thunk) + opt_eager_fpu ? " EAGER_FPU" : "", + opt_verw_hvm ? " VERW" : "", + boot_cpu_has(X86_FEATURE_IBPB_ENTRY_HVM) ? " IBPB-entry" : "", +- cpu_has_bhb_seq ? " BHB-entry" : ""); ++ opt_bhb_entry_hvm ? " BHB-entry" : ""); + + #endif + #ifdef CONFIG_PV +@@ -651,14 +651,14 @@ static void __init print_details(enum ind_thunk thunk) + (boot_cpu_has(X86_FEATURE_SC_MSR_PV) || + boot_cpu_has(X86_FEATURE_SC_RSB_PV) || + boot_cpu_has(X86_FEATURE_IBPB_ENTRY_PV) || +- cpu_has_bhb_seq || ++ opt_bhb_entry_pv || + opt_eager_fpu || opt_verw_pv) ? "" : " None", + boot_cpu_has(X86_FEATURE_SC_MSR_PV) ? " MSR_SPEC_CTRL" : "", + boot_cpu_has(X86_FEATURE_SC_RSB_PV) ? " RSB" : "", + opt_eager_fpu ? " EAGER_FPU" : "", + opt_verw_pv ? " VERW" : "", + boot_cpu_has(X86_FEATURE_IBPB_ENTRY_PV) ? " IBPB-entry" : "", +- cpu_has_bhb_seq ? " BHB-entry" : ""); ++ opt_bhb_entry_pv ? " BHB-entry" : ""); + + printk(" XPTI (64-bit PV only): Dom0 %s, DomU %s (with%s PCID)\n", + opt_xpti_hwdom ? "enabled" : "disabled", +-- +2.30.2 + diff --git a/xen.git-2d38302c33b117aa9a417056db241aefc840c2f0.patch b/xen.git-2d38302c33b117aa9a417056db241aefc840c2f0.patch new file mode 100644 index 0000000..4f4ba17 --- /dev/null +++ b/xen.git-2d38302c33b117aa9a417056db241aefc840c2f0.patch @@ -0,0 +1,32 @@ +From 2d38302c33b117aa9a417056db241aefc840c2f0 Mon Sep 17 00:00:00 2001 +From: Andrew Cooper +Date: Tue, 9 Apr 2024 21:39:51 +0100 +Subject: [PATCH] x86/entry: Fix build with older toolchains + +Binutils older than 2.29 doesn't know INCSSPD. + +Fixes: 8e186f98ce0e ("x86: Use indirect calls in reset-stack infrastructure") +Signed-off-by: Andrew Cooper +Reviewed-by: Stefano Stabellini +(cherry picked from commit a9fa82500818a8d8ce5f2843f1577bd2c29d088e) +--- + xen/arch/x86/x86_64/entry.S | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S +index ad7dd3b23b..054fcb225f 100644 +--- a/xen/arch/x86/x86_64/entry.S ++++ b/xen/arch/x86/x86_64/entry.S +@@ -643,7 +643,9 @@ ENTRY(continue_pv_domain) + * JMPed to. Drop the return address. + */ + add $8, %rsp ++#ifdef CONFIG_XEN_SHSTK + ALTERNATIVE "", "mov $2, %eax; incsspd %eax", X86_FEATURE_XEN_SHSTK ++#endif + + call check_wakeup_from_wait + ret_from_intr: +-- +2.30.2 + diff --git a/xen.git-8bdcb0b98b53140102031ceca0611f22190227fd.patch b/xen.git-8bdcb0b98b53140102031ceca0611f22190227fd.patch new file mode 100644 index 0000000..371ebe1 --- /dev/null +++ b/xen.git-8bdcb0b98b53140102031ceca0611f22190227fd.patch @@ -0,0 +1,49 @@ +From 8bdcb0b98b53140102031ceca0611f22190227fd Mon Sep 17 00:00:00 2001 +From: =?utf8?q?Roger=20Pau=20Monn=C3=A9?= +Date: Mon, 29 Apr 2024 09:35:21 +0200 +Subject: [PATCH] altcall: fix __alt_call_maybe_initdata so it's safe for + livepatch +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf8 +Content-Transfer-Encoding: 8bit + +Setting alternative call variables as __init is not safe for use with +livepatch, as livepatches can rightfully introduce new alternative calls to +structures marked as __alt_call_maybe_initdata (possibly just indirectly due to +replacing existing functions that use those). Attempting to resolve those +alternative calls then results in page faults as the variable that holds the +function pointer address has been freed. + +When livepatch is supported use the __ro_after_init attribute instead of +__initdata for __alt_call_maybe_initdata. + +Fixes: f26bb285949b ('xen: Implement xen/alternative-call.h for use in common code') +Signed-off-by: Roger Pau Monné +Reviewed-by: Andrew Cooper +master commit: af4cd0a6a61cdb03bc1afca9478b05b0c9703599 +master date: 2024-04-11 18:51:36 +0100 +--- + xen/include/xen/alternative-call.h | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/xen/include/xen/alternative-call.h b/xen/include/xen/alternative-call.h +index 5c6b9a562b..10f7d7637e 100644 +--- a/xen/include/xen/alternative-call.h ++++ b/xen/include/xen/alternative-call.h +@@ -50,7 +50,12 @@ + + #include + +-#define __alt_call_maybe_initdata __initdata ++#ifdef CONFIG_LIVEPATCH ++/* Must keep for livepatches to resolve alternative calls. */ ++# define __alt_call_maybe_initdata __ro_after_init ++#else ++# define __alt_call_maybe_initdata __initdata ++#endif + + #else + +-- +2.30.2 + diff --git a/xen.git-af0e9ba44a58c87d6d135d8ffbf468b4ceac0a41.patch b/xen.git-af0e9ba44a58c87d6d135d8ffbf468b4ceac0a41.patch new file mode 100644 index 0000000..af34479 --- /dev/null +++ b/xen.git-af0e9ba44a58c87d6d135d8ffbf468b4ceac0a41.patch @@ -0,0 +1,56 @@ +From af0e9ba44a58c87d6d135d8ffbf468b4ceac0a41 Mon Sep 17 00:00:00 2001 +From: Ross Lagerwall +Date: Mon, 29 Apr 2024 09:36:04 +0200 +Subject: [PATCH] x86/rtc: Avoid UIP flag being set for longer than expected + +In a test, OVMF reported an error initializing the RTC without +indicating the precise nature of the error. The only plausible +explanation I can find is as follows: + +As part of the initialization, OVMF reads register C and then reads +register A repatedly until the UIP flag is not set. If this takes longer +than 100 ms, OVMF fails and reports an error. This may happen with the +following sequence of events: + +At guest time=0s, rtc_init() calls check_update_timer() which schedules +update_timer for t=(1 - 244us). + +At t=1s, the update_timer function happens to have been called >= 244us +late. In the timer callback, it sets the UIP flag and schedules +update_timer2 for t=1s. + +Before update_timer2 runs, the guest reads register C which calls +check_update_timer(). check_update_timer() stops the scheduled +update_timer2 and since the guest time is now outside of the update +cycle, it schedules update_timer for t=(2 - 244us). + +The UIP flag will therefore be set for a whole second from t=1 to t=2 +while the guest repeatedly reads register A waiting for the UIP flag to +clear. Fix it by clearing the UIP flag when scheduling update_timer. + +I was able to reproduce this issue with a synthetic test and this +resolves the issue. + +Signed-off-by: Ross Lagerwall +Reviewed-by: Jan Beulich +master commit: 43a07069863b419433dee12c9b58c1f7ce70aa97 +master date: 2024-04-23 14:09:18 +0200 +--- + xen/arch/x86/hvm/rtc.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/xen/arch/x86/hvm/rtc.c b/xen/arch/x86/hvm/rtc.c +index 206b4296e9..4839374352 100644 +--- a/xen/arch/x86/hvm/rtc.c ++++ b/xen/arch/x86/hvm/rtc.c +@@ -202,6 +202,7 @@ static void check_update_timer(RTCState *s) + } + else + { ++ s->hw.cmos_data[RTC_REG_A] &= ~RTC_UIP; + next_update_time = (USEC_PER_SEC - guest_usec - 244) * NS_PER_USEC; + expire_time = NOW() + next_update_time; + s->next_update_time = expire_time; +-- +2.30.2 + diff --git a/xen.git-eb7059767c82d833ebecdf8106e96482b04f3c40.patch b/xen.git-eb7059767c82d833ebecdf8106e96482b04f3c40.patch new file mode 100644 index 0000000..c27b537 --- /dev/null +++ b/xen.git-eb7059767c82d833ebecdf8106e96482b04f3c40.patch @@ -0,0 +1,36 @@ +From eb7059767c82d833ebecdf8106e96482b04f3c40 Mon Sep 17 00:00:00 2001 +From: Jan Beulich +Date: Mon, 29 Apr 2024 09:36:37 +0200 +Subject: [PATCH] x86/MTRR: correct inadvertently inverted WC check +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf8 +Content-Transfer-Encoding: 8bit + +The ! clearly got lost by mistake. + +Fixes: e9e0eb30d4d6 ("x86/MTRR: avoid several indirect calls") +Reported-by: Marek Marczykowski-Górecki +Signed-off-by: Jan Beulich +Acked-by: Roger Pau Monné +master commit: 77e25f0e30ddd11e043e6fce84bf108ce7de5b6f +master date: 2024-04-23 14:13:48 +0200 +--- + xen/arch/x86/cpu/mtrr/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/xen/arch/x86/cpu/mtrr/main.c b/xen/arch/x86/cpu/mtrr/main.c +index 55a4da54a7..90b235f57e 100644 +--- a/xen/arch/x86/cpu/mtrr/main.c ++++ b/xen/arch/x86/cpu/mtrr/main.c +@@ -316,7 +316,7 @@ int mtrr_add_page(unsigned long base, unsigned long size, + } + + /* If the type is WC, check that this processor supports it */ +- if ((type == X86_MT_WC) && mtrr_have_wrcomb()) { ++ if ((type == X86_MT_WC) && !mtrr_have_wrcomb()) { + printk(KERN_WARNING + "mtrr: your processor doesn't support write-combining\n"); + return -EOPNOTSUPP; +-- +2.30.2 + diff --git a/xen.git-f0ff1d9cb96041a84a24857a6464628240deed4f.patch b/xen.git-f0ff1d9cb96041a84a24857a6464628240deed4f.patch new file mode 100644 index 0000000..475a03d --- /dev/null +++ b/xen.git-f0ff1d9cb96041a84a24857a6464628240deed4f.patch @@ -0,0 +1,75 @@ +From f0ff1d9cb96041a84a24857a6464628240deed4f Mon Sep 17 00:00:00 2001 +From: =?utf8?q?Roger=20Pau=20Monn=C3=A9?= +Date: Mon, 29 Apr 2024 09:37:29 +0200 +Subject: [PATCH] x86/spec: adjust logic that elides lfence +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf8 +Content-Transfer-Encoding: 8bit + +It's currently too restrictive by just checking whether there's a BHB clearing +sequence selected. It should instead check whether BHB clearing is used on +entry from PV or HVM specifically. + +Switch to use opt_bhb_entry_{pv,hvm} instead, and then remove cpu_has_bhb_seq +since it no longer has any users. + +Reported-by: Jan Beulich +Fixes: 954c983abcee ('x86/spec-ctrl: Software BHB-clearing sequences') +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +Reviewed-by: Andrew Cooper +master commit: 656ae8f1091bcefec9c46ec3ea3ac2118742d4f6 +master date: 2024-04-25 16:37:01 +0200 +--- + xen/arch/x86/include/asm/cpufeature.h | 3 --- + xen/arch/x86/spec_ctrl.c | 6 +++--- + 2 files changed, 3 insertions(+), 6 deletions(-) + +diff --git a/xen/arch/x86/include/asm/cpufeature.h b/xen/arch/x86/include/asm/cpufeature.h +index 7a312c485e..3c57f55de0 100644 +--- a/xen/arch/x86/include/asm/cpufeature.h ++++ b/xen/arch/x86/include/asm/cpufeature.h +@@ -228,9 +228,6 @@ static inline bool boot_cpu_has(unsigned int feat) + #define cpu_bug_fpu_ptrs boot_cpu_has(X86_BUG_FPU_PTRS) + #define cpu_bug_null_seg boot_cpu_has(X86_BUG_NULL_SEG) + +-#define cpu_has_bhb_seq (boot_cpu_has(X86_SPEC_BHB_TSX) || \ +- boot_cpu_has(X86_SPEC_BHB_LOOPS)) +- + enum _cache_type { + CACHE_TYPE_NULL = 0, + CACHE_TYPE_DATA = 1, +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 8c67d6256a..12c19b7eca 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -2328,7 +2328,7 @@ void __init init_speculation_mitigations(void) + * unconditional WRMSR. If we do have it, or we're not using any + * prior conditional block, then it's safe to drop the LFENCE. + */ +- if ( !cpu_has_bhb_seq && ++ if ( !opt_bhb_entry_pv && + (boot_cpu_has(X86_FEATURE_SC_MSR_PV) || + !boot_cpu_has(X86_FEATURE_IBPB_ENTRY_PV)) ) + setup_force_cpu_cap(X86_SPEC_NO_LFENCE_ENTRY_PV); +@@ -2344,7 +2344,7 @@ void __init init_speculation_mitigations(void) + * active in the block that is skipped when interrupting guest + * context, then it's safe to drop the LFENCE. + */ +- if ( !cpu_has_bhb_seq && ++ if ( !opt_bhb_entry_pv && + (boot_cpu_has(X86_FEATURE_SC_MSR_PV) || + (!boot_cpu_has(X86_FEATURE_IBPB_ENTRY_PV) && + !boot_cpu_has(X86_FEATURE_SC_RSB_PV))) ) +@@ -2356,7 +2356,7 @@ void __init init_speculation_mitigations(void) + * A BHB sequence, if used, is the only conditional action, so if we + * don't have it, we don't need the safety LFENCE. + */ +- if ( !cpu_has_bhb_seq ) ++ if ( !opt_bhb_entry_hvm ) + setup_force_cpu_cap(X86_SPEC_NO_LFENCE_ENTRY_VMX); + } + +-- +2.30.2 + diff --git a/xen.spec b/xen.spec index 6441674..a499993 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.18.2 -Release: 1%{?dist} +Release: 2%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -114,6 +114,12 @@ Patch49: xen.python3.12.patch Patch50: xen.ocaml5.fixes.patch Patch52: xen.gcc14.fixes.patch Patch53: newlib.gcc14.fixes.patch +Patch54: xen.git-2d38302c33b117aa9a417056db241aefc840c2f0.patch +Patch55: xen.git-8bdcb0b98b53140102031ceca0611f22190227fd.patch +Patch56: xen.git-af0e9ba44a58c87d6d135d8ffbf468b4ceac0a41.patch +Patch57: xen.git-eb7059767c82d833ebecdf8106e96482b04f3c40.patch +Patch58: xen.git-0b0c7dca70d64c35c86e5d503f67366ebe2b9138.patch +Patch59: xen.git-f0ff1d9cb96041a84a24857a6464628240deed4f.patch %if %build_qemutrad @@ -328,6 +334,12 @@ manage Xen virtual machines. %endif %patch 52 -p1 %patch 53 -p1 +%patch 54 -p1 +%patch 55 -p1 +%patch 56 -p1 +%patch 57 -p1 +%patch 58 -p1 +%patch 59 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -934,6 +946,9 @@ fi %endif %changelog +* Mon Jun 03 2024 Michael Young - 4.18.2-2 +- x86: Native Branch History Injection [XSA-456 version 3, CVE-2024-2201] + * Tue Apr 09 2024 Michael Young - 4.18.2-1 - x86: Native Branch History Injection [XSA-456, CVE-2024-2201] - update to xen 4.18.2, remove patches now included upstream From bac467190c3e7b9b6aee771ca6366689c9a56a10 Mon Sep 17 00:00:00 2001 From: Python Maint Date: Fri, 7 Jun 2024 09:52:18 +0200 Subject: [PATCH 090/137] Rebuilt for Python 3.13 --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index a499993..fb843ec 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.18.2 -Release: 2%{?dist} +Release: 3%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -946,6 +946,9 @@ fi %endif %changelog +* Fri Jun 07 2024 Python Maint - 4.18.2-3 +- Rebuilt for Python 3.13 + * Mon Jun 03 2024 Michael Young - 4.18.2-2 - x86: Native Branch History Injection [XSA-456 version 3, CVE-2024-2201] From 269fa36c52b4ef61cfd7886f28588807775d4501 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 16 Jul 2024 21:23:30 +0100 Subject: [PATCH 091/137] double unlock in x86 guest IRQ handling [XSA-458, CVE-2024-31143] --- xen.spec | 7 ++++++- xsa458.patch | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 xsa458.patch diff --git a/xen.spec b/xen.spec index fb843ec..2782f96 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.18.2 -Release: 3%{?dist} +Release: 4%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -120,6 +120,7 @@ Patch56: xen.git-af0e9ba44a58c87d6d135d8ffbf468b4ceac0a41.patch Patch57: xen.git-eb7059767c82d833ebecdf8106e96482b04f3c40.patch Patch58: xen.git-0b0c7dca70d64c35c86e5d503f67366ebe2b9138.patch Patch59: xen.git-f0ff1d9cb96041a84a24857a6464628240deed4f.patch +Patch60: xsa458.patch %if %build_qemutrad @@ -340,6 +341,7 @@ manage Xen virtual machines. %patch 57 -p1 %patch 58 -p1 %patch 59 -p1 +%patch 60 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -946,6 +948,9 @@ fi %endif %changelog +* Tue Jul 16 2024 Michael Young - 4.18.2-4 +- double unlock in x86 guest IRQ handling [XSA-458, CVE-2024-31143] + * Fri Jun 07 2024 Python Maint - 4.18.2-3 - Rebuilt for Python 3.13 diff --git a/xsa458.patch b/xsa458.patch new file mode 100644 index 0000000..8be0a90 --- /dev/null +++ b/xsa458.patch @@ -0,0 +1,38 @@ +From: Jan Beulich +Subject: x86/IRQ: avoid double unlock in map_domain_pirq() + +Forever since its introduction the main loop in the function dealing +with multi-vector MSI had error exit points ("break") with different +properties: In one case no IRQ descriptor lock is being held. +Nevertheless the subsequent error cleanup path assumed such a lock would +uniformly need releasing. Identify the case by setting "desc" to NULL, +thus allowing the unlock to be skipped as necessary. + +This is CVE-2024-31143 / XSA-458. + +Coverity ID: 1605298 +Fixes: d1b6d0a02489 ("x86: enable multi-vector MSI") +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné + +--- a/xen/arch/x86/irq.c ++++ b/xen/arch/x86/irq.c +@@ -2273,6 +2273,7 @@ int map_domain_pirq( + + set_domain_irq_pirq(d, irq, info); + spin_unlock_irqrestore(&desc->lock, flags); ++ desc = NULL; + + info = NULL; + irq = create_irq(NUMA_NO_NODE, true); +@@ -2308,7 +2309,9 @@ int map_domain_pirq( + + if ( ret ) + { +- spin_unlock_irqrestore(&desc->lock, flags); ++ if ( desc ) ++ spin_unlock_irqrestore(&desc->lock, flags); ++ + pci_disable_msi(msi_desc); + if ( nr ) + { From 9e3a14be339565ae6ff93f5ec7a9922cfcb0d2aa Mon Sep 17 00:00:00 2001 From: Alejandro Vallejo Date: Sun, 14 Jul 2024 17:21:35 +0100 Subject: [PATCH 092/137] Split xen-runtime posttrans conditional $(ls -A ...) expands before the whole condition is passed to `[`. This means `-d ` does not gate the `ls` command being executed. While it's benign, it causes ls's stderr to propagate to yum/dnf output, which is somewhat disconcerting for the user. Fix it by forcing ls into its own conditional. --- xen.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 2782f96..7e7cf5a 100644 --- a/xen.spec +++ b/xen.spec @@ -612,7 +612,7 @@ fi %endif %posttrans runtime -if [ ! -L /usr/lib/xen -a -d /usr/lib/xen -a -z "$(ls -A /usr/lib/xen)" ]; then +if [ ! -L /usr/lib/xen -a -d /usr/lib/xen ] && [ -z "$(ls -A /usr/lib/xen)" ]; then rmdir /usr/lib/xen fi if [ ! -e /usr/lib/xen ]; then From 1b82c0189440c1c413588871749769aa22a8824d Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 18 Jul 2024 22:27:45 +0100 Subject: [PATCH 093/137] add bugzilla reference --- xen.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/xen.spec b/xen.spec index 7e7cf5a..0f8f439 100644 --- a/xen.spec +++ b/xen.spec @@ -950,6 +950,7 @@ fi %changelog * Tue Jul 16 2024 Michael Young - 4.18.2-4 - double unlock in x86 guest IRQ handling [XSA-458, CVE-2024-31143] + (#2298690) * Fri Jun 07 2024 Python Maint - 4.18.2-3 - Rebuilt for Python 3.13 From 589e86cf5627e92df5ac43147e66835f90ab81f7 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Sat, 20 Jul 2024 09:54:27 +0000 Subject: [PATCH 094/137] Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 0f8f439..32950cd 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.18.2 -Release: 4%{?dist} +Release: 5%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -948,6 +948,9 @@ fi %endif %changelog +* Sat Jul 20 2024 Fedora Release Engineering - 4.18.2-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild + * Tue Jul 16 2024 Michael Young - 4.18.2-4 - double unlock in x86 guest IRQ handling [XSA-458, CVE-2024-31143] (#2298690) From 08778ae14d65ba945d82ef48b52931138025e89e Mon Sep 17 00:00:00 2001 From: Michael Young Date: Sat, 3 Aug 2024 22:01:35 +0100 Subject: [PATCH 095/137] update to xen-4.19.0 --- .gitignore | 2 +- sources | 2 +- xen.efi.build.patch | 8 +- xen.fedora.systemd.patch | 4 +- xen.gcc14.fixes.patch | 12 --- ...7dca70d64c35c86e5d503f67366ebe2b9138.patch | 69 ----------------- ...302c33b117aa9a417056db241aefc840c2f0.patch | 32 -------- ...b0b98b53140102031ceca0611f22190227fd.patch | 49 ------------ ...9ba44a58c87d6d135d8ffbf468b4ceac0a41.patch | 56 -------------- ...59767c82d833ebecdf8106e96482b04f3c40.patch | 36 --------- ...1d9cb96041a84a24857a6464628240deed4f.patch | 75 ------------------- xen.ocaml5.fixes.patch | 11 --- xen.spec | 39 +++++----- xsa458.patch | 38 ---------- 14 files changed, 26 insertions(+), 407 deletions(-) delete mode 100644 xen.git-0b0c7dca70d64c35c86e5d503f67366ebe2b9138.patch delete mode 100644 xen.git-2d38302c33b117aa9a417056db241aefc840c2f0.patch delete mode 100644 xen.git-8bdcb0b98b53140102031ceca0611f22190227fd.patch delete mode 100644 xen.git-af0e9ba44a58c87d6d135d8ffbf468b4ceac0a41.patch delete mode 100644 xen.git-eb7059767c82d833ebecdf8106e96482b04f3c40.patch delete mode 100644 xen.git-f0ff1d9cb96041a84a24857a6464628240deed4f.patch delete mode 100644 xsa458.patch diff --git a/.gitignore b/.gitignore index 439b7c0..a996079 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.18.2.tar.gz +/xen-4.19.0.tar.gz diff --git a/sources b/sources index 5b1c173..e979065 100644 --- a/sources +++ b/sources @@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.18.2.tar.gz) = c5feb450155883b5d2e7f43b05a64e7215b661b7d2f438d8f5a0896bd57283379ee11ca8e2e7a1d8787813cc6f1a260253fcb8688ed7d61a2bfb636db1626941 +SHA512 (xen-4.19.0.tar.gz) = bc0b4257cab66b0f9d4a1fe72e07f4980398b2934989c02ffe488b465eca3443caa0ded903871480aba6e36212921f335b68d67f202f22548c31899225f2d657 diff --git a/xen.efi.build.patch b/xen.efi.build.patch index 06ee249..69d1e93 100644 --- a/xen.efi.build.patch +++ b/xen.efi.build.patch @@ -1,7 +1,7 @@ ---- xen-4.17.0/xen/arch/x86/arch.mk.orig 2022-12-08 18:03:08.000000000 +0000 -+++ xen-4.17.0/xen/arch/x86/arch.mk 2022-12-10 12:13:11.043182208 +0000 -@@ -94,7 +94,9 @@ - XEN_BUILD_EFI := $(call if-success,$(CC) $(CFLAGS) -c $(srctree)/$(efi-check).c -o $(efi-check).o,y) +--- xen-4.19.0-rc1/xen/arch/x86/arch.mk.orig 2024-07-01 18:31:39.000000000 +0100 ++++ xen-4.19.0-rc1/xen/arch/x86/arch.mk 2024-07-02 21:10:00.796753718 +0100 +@@ -91,7 +91,9 @@ + -c $(srctree)/$(efi-check).c -o $(efi-check).o,y) # Check if the linker supports PE. -EFI_LDFLAGS := $(patsubst -m%,-mi386pep,$(LDFLAGS)) --subsystem=10 diff --git a/xen.fedora.systemd.patch b/xen.fedora.systemd.patch index ac56245..5b6a7a3 100644 --- a/xen.fedora.systemd.patch +++ b/xen.fedora.systemd.patch @@ -64,8 +64,8 @@ diff -uN xen-4.5.0/tools/hotplug/Linux/systemd.orig/xen-qemu-dom0-disk-backend.s --- xen-4.17.0/tools/configure.orig 2022-12-08 18:03:08.000000000 +0000 +++ xen-4.17.0/tools/configure 2022-12-09 19:51:43.278708226 +0000 @@ -10081,7 +10081,7 @@ - - if test "x$systemd" = "xy"; then : + if test "x$systemd" = "xy" + then : - ac_config_files="$ac_config_files hotplug/Linux/systemd/proc-xen.mount hotplug/Linux/systemd/xen-init-dom0.service hotplug/Linux/systemd/xen-qemu-dom0-disk-backend.service hotplug/Linux/systemd/xen-watchdog.service hotplug/Linux/systemd/xenconsoled.service hotplug/Linux/systemd/xendomains.service hotplug/Linux/systemd/xendriverdomain.service hotplug/Linux/systemd/xenstored.service" + ac_config_files="$ac_config_files hotplug/Linux/systemd/oxenstored.service hotplug/Linux/systemd/proc-xen.mount hotplug/Linux/systemd/xen-qemu-dom0-disk-backend.service hotplug/Linux/systemd/xen-watchdog.service hotplug/Linux/systemd/xenconsoled.service hotplug/Linux/systemd/xendomains.service hotplug/Linux/systemd/xendriverdomain.service hotplug/Linux/systemd/xenstored.service" diff --git a/xen.gcc14.fixes.patch b/xen.gcc14.fixes.patch index cc27089..a9fb2f3 100644 --- a/xen.gcc14.fixes.patch +++ b/xen.gcc14.fixes.patch @@ -10,15 +10,3 @@ Work around newlib build problems with gcc14 CROSS_ROOT=cross-root-$(GNU_TARGET_ARCH) CROSS_PREFIX=$(CURDIR)/$(CROSS_ROOT) ---- xen-4.18.0/extras/mini-os/include/posix/sys/mman.h.orig 2022-07-04 09:52:38.000000000 +0100 -+++ xen-4.18.0/extras/mini-os/include/posix/sys/mman.h 2024-02-01 23:00:19.923263409 +0000 -@@ -16,7 +16,7 @@ - - void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset) asm("mmap64"); - int munmap(void *start, size_t length); --static inline mlock(const void *addr, size_t len) { return 0; } --static inline munlock(const void *addr, size_t len) { return 0; } -+static inline int mlock(const void *addr, size_t len) { return 0; } -+static inline int munlock(const void *addr, size_t len) { return 0; } - - #endif /* _POSIX_SYS_MMAN_H */ diff --git a/xen.git-0b0c7dca70d64c35c86e5d503f67366ebe2b9138.patch b/xen.git-0b0c7dca70d64c35c86e5d503f67366ebe2b9138.patch deleted file mode 100644 index fbaac26..0000000 --- a/xen.git-0b0c7dca70d64c35c86e5d503f67366ebe2b9138.patch +++ /dev/null @@ -1,69 +0,0 @@ -From 0b0c7dca70d64c35c86e5d503f67366ebe2b9138 Mon Sep 17 00:00:00 2001 -From: =?utf8?q?Roger=20Pau=20Monn=C3=A9?= -Date: Mon, 29 Apr 2024 09:37:04 +0200 -Subject: [PATCH] x86/spec: fix reporting of BHB clearing usage from guest - entry points -MIME-Version: 1.0 -Content-Type: text/plain; charset=utf8 -Content-Transfer-Encoding: 8bit - -Reporting whether the BHB clearing on entry is done for the different domains -types based on cpu_has_bhb_seq is unhelpful, as that variable signals whether -there's a BHB clearing sequence selected, but that alone doesn't imply that -such sequence is used from the PV and/or HVM entry points. - -Instead use opt_bhb_entry_{pv,hvm} which do signal whether BHB clearing is -performed on entry from PV/HVM. - -Fixes: 689ad48ce9cf ('x86/spec-ctrl: Wire up the Native-BHI software sequences') -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich -Reviewed-by: Andrew Cooper -master commit: 049ab0b2c9f1f5edb54b505fef0bc575787dafe9 -master date: 2024-04-25 16:35:56 +0200 ---- - xen/arch/x86/spec_ctrl.c | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) - -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index ba4349a024..8c67d6256a 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -634,7 +634,7 @@ static void __init print_details(enum ind_thunk thunk) - (boot_cpu_has(X86_FEATURE_SC_MSR_HVM) || - boot_cpu_has(X86_FEATURE_SC_RSB_HVM) || - boot_cpu_has(X86_FEATURE_IBPB_ENTRY_HVM) || -- cpu_has_bhb_seq || amd_virt_spec_ctrl || -+ opt_bhb_entry_hvm || amd_virt_spec_ctrl || - opt_eager_fpu || opt_verw_hvm) ? "" : " None", - boot_cpu_has(X86_FEATURE_SC_MSR_HVM) ? " MSR_SPEC_CTRL" : "", - (boot_cpu_has(X86_FEATURE_SC_MSR_HVM) || -@@ -643,7 +643,7 @@ static void __init print_details(enum ind_thunk thunk) - opt_eager_fpu ? " EAGER_FPU" : "", - opt_verw_hvm ? " VERW" : "", - boot_cpu_has(X86_FEATURE_IBPB_ENTRY_HVM) ? " IBPB-entry" : "", -- cpu_has_bhb_seq ? " BHB-entry" : ""); -+ opt_bhb_entry_hvm ? " BHB-entry" : ""); - - #endif - #ifdef CONFIG_PV -@@ -651,14 +651,14 @@ static void __init print_details(enum ind_thunk thunk) - (boot_cpu_has(X86_FEATURE_SC_MSR_PV) || - boot_cpu_has(X86_FEATURE_SC_RSB_PV) || - boot_cpu_has(X86_FEATURE_IBPB_ENTRY_PV) || -- cpu_has_bhb_seq || -+ opt_bhb_entry_pv || - opt_eager_fpu || opt_verw_pv) ? "" : " None", - boot_cpu_has(X86_FEATURE_SC_MSR_PV) ? " MSR_SPEC_CTRL" : "", - boot_cpu_has(X86_FEATURE_SC_RSB_PV) ? " RSB" : "", - opt_eager_fpu ? " EAGER_FPU" : "", - opt_verw_pv ? " VERW" : "", - boot_cpu_has(X86_FEATURE_IBPB_ENTRY_PV) ? " IBPB-entry" : "", -- cpu_has_bhb_seq ? " BHB-entry" : ""); -+ opt_bhb_entry_pv ? " BHB-entry" : ""); - - printk(" XPTI (64-bit PV only): Dom0 %s, DomU %s (with%s PCID)\n", - opt_xpti_hwdom ? "enabled" : "disabled", --- -2.30.2 - diff --git a/xen.git-2d38302c33b117aa9a417056db241aefc840c2f0.patch b/xen.git-2d38302c33b117aa9a417056db241aefc840c2f0.patch deleted file mode 100644 index 4f4ba17..0000000 --- a/xen.git-2d38302c33b117aa9a417056db241aefc840c2f0.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 2d38302c33b117aa9a417056db241aefc840c2f0 Mon Sep 17 00:00:00 2001 -From: Andrew Cooper -Date: Tue, 9 Apr 2024 21:39:51 +0100 -Subject: [PATCH] x86/entry: Fix build with older toolchains - -Binutils older than 2.29 doesn't know INCSSPD. - -Fixes: 8e186f98ce0e ("x86: Use indirect calls in reset-stack infrastructure") -Signed-off-by: Andrew Cooper -Reviewed-by: Stefano Stabellini -(cherry picked from commit a9fa82500818a8d8ce5f2843f1577bd2c29d088e) ---- - xen/arch/x86/x86_64/entry.S | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S -index ad7dd3b23b..054fcb225f 100644 ---- a/xen/arch/x86/x86_64/entry.S -+++ b/xen/arch/x86/x86_64/entry.S -@@ -643,7 +643,9 @@ ENTRY(continue_pv_domain) - * JMPed to. Drop the return address. - */ - add $8, %rsp -+#ifdef CONFIG_XEN_SHSTK - ALTERNATIVE "", "mov $2, %eax; incsspd %eax", X86_FEATURE_XEN_SHSTK -+#endif - - call check_wakeup_from_wait - ret_from_intr: --- -2.30.2 - diff --git a/xen.git-8bdcb0b98b53140102031ceca0611f22190227fd.patch b/xen.git-8bdcb0b98b53140102031ceca0611f22190227fd.patch deleted file mode 100644 index 371ebe1..0000000 --- a/xen.git-8bdcb0b98b53140102031ceca0611f22190227fd.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 8bdcb0b98b53140102031ceca0611f22190227fd Mon Sep 17 00:00:00 2001 -From: =?utf8?q?Roger=20Pau=20Monn=C3=A9?= -Date: Mon, 29 Apr 2024 09:35:21 +0200 -Subject: [PATCH] altcall: fix __alt_call_maybe_initdata so it's safe for - livepatch -MIME-Version: 1.0 -Content-Type: text/plain; charset=utf8 -Content-Transfer-Encoding: 8bit - -Setting alternative call variables as __init is not safe for use with -livepatch, as livepatches can rightfully introduce new alternative calls to -structures marked as __alt_call_maybe_initdata (possibly just indirectly due to -replacing existing functions that use those). Attempting to resolve those -alternative calls then results in page faults as the variable that holds the -function pointer address has been freed. - -When livepatch is supported use the __ro_after_init attribute instead of -__initdata for __alt_call_maybe_initdata. - -Fixes: f26bb285949b ('xen: Implement xen/alternative-call.h for use in common code') -Signed-off-by: Roger Pau Monné -Reviewed-by: Andrew Cooper -master commit: af4cd0a6a61cdb03bc1afca9478b05b0c9703599 -master date: 2024-04-11 18:51:36 +0100 ---- - xen/include/xen/alternative-call.h | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - -diff --git a/xen/include/xen/alternative-call.h b/xen/include/xen/alternative-call.h -index 5c6b9a562b..10f7d7637e 100644 ---- a/xen/include/xen/alternative-call.h -+++ b/xen/include/xen/alternative-call.h -@@ -50,7 +50,12 @@ - - #include - --#define __alt_call_maybe_initdata __initdata -+#ifdef CONFIG_LIVEPATCH -+/* Must keep for livepatches to resolve alternative calls. */ -+# define __alt_call_maybe_initdata __ro_after_init -+#else -+# define __alt_call_maybe_initdata __initdata -+#endif - - #else - --- -2.30.2 - diff --git a/xen.git-af0e9ba44a58c87d6d135d8ffbf468b4ceac0a41.patch b/xen.git-af0e9ba44a58c87d6d135d8ffbf468b4ceac0a41.patch deleted file mode 100644 index af34479..0000000 --- a/xen.git-af0e9ba44a58c87d6d135d8ffbf468b4ceac0a41.patch +++ /dev/null @@ -1,56 +0,0 @@ -From af0e9ba44a58c87d6d135d8ffbf468b4ceac0a41 Mon Sep 17 00:00:00 2001 -From: Ross Lagerwall -Date: Mon, 29 Apr 2024 09:36:04 +0200 -Subject: [PATCH] x86/rtc: Avoid UIP flag being set for longer than expected - -In a test, OVMF reported an error initializing the RTC without -indicating the precise nature of the error. The only plausible -explanation I can find is as follows: - -As part of the initialization, OVMF reads register C and then reads -register A repatedly until the UIP flag is not set. If this takes longer -than 100 ms, OVMF fails and reports an error. This may happen with the -following sequence of events: - -At guest time=0s, rtc_init() calls check_update_timer() which schedules -update_timer for t=(1 - 244us). - -At t=1s, the update_timer function happens to have been called >= 244us -late. In the timer callback, it sets the UIP flag and schedules -update_timer2 for t=1s. - -Before update_timer2 runs, the guest reads register C which calls -check_update_timer(). check_update_timer() stops the scheduled -update_timer2 and since the guest time is now outside of the update -cycle, it schedules update_timer for t=(2 - 244us). - -The UIP flag will therefore be set for a whole second from t=1 to t=2 -while the guest repeatedly reads register A waiting for the UIP flag to -clear. Fix it by clearing the UIP flag when scheduling update_timer. - -I was able to reproduce this issue with a synthetic test and this -resolves the issue. - -Signed-off-by: Ross Lagerwall -Reviewed-by: Jan Beulich -master commit: 43a07069863b419433dee12c9b58c1f7ce70aa97 -master date: 2024-04-23 14:09:18 +0200 ---- - xen/arch/x86/hvm/rtc.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/xen/arch/x86/hvm/rtc.c b/xen/arch/x86/hvm/rtc.c -index 206b4296e9..4839374352 100644 ---- a/xen/arch/x86/hvm/rtc.c -+++ b/xen/arch/x86/hvm/rtc.c -@@ -202,6 +202,7 @@ static void check_update_timer(RTCState *s) - } - else - { -+ s->hw.cmos_data[RTC_REG_A] &= ~RTC_UIP; - next_update_time = (USEC_PER_SEC - guest_usec - 244) * NS_PER_USEC; - expire_time = NOW() + next_update_time; - s->next_update_time = expire_time; --- -2.30.2 - diff --git a/xen.git-eb7059767c82d833ebecdf8106e96482b04f3c40.patch b/xen.git-eb7059767c82d833ebecdf8106e96482b04f3c40.patch deleted file mode 100644 index c27b537..0000000 --- a/xen.git-eb7059767c82d833ebecdf8106e96482b04f3c40.patch +++ /dev/null @@ -1,36 +0,0 @@ -From eb7059767c82d833ebecdf8106e96482b04f3c40 Mon Sep 17 00:00:00 2001 -From: Jan Beulich -Date: Mon, 29 Apr 2024 09:36:37 +0200 -Subject: [PATCH] x86/MTRR: correct inadvertently inverted WC check -MIME-Version: 1.0 -Content-Type: text/plain; charset=utf8 -Content-Transfer-Encoding: 8bit - -The ! clearly got lost by mistake. - -Fixes: e9e0eb30d4d6 ("x86/MTRR: avoid several indirect calls") -Reported-by: Marek Marczykowski-Górecki -Signed-off-by: Jan Beulich -Acked-by: Roger Pau Monné -master commit: 77e25f0e30ddd11e043e6fce84bf108ce7de5b6f -master date: 2024-04-23 14:13:48 +0200 ---- - xen/arch/x86/cpu/mtrr/main.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/xen/arch/x86/cpu/mtrr/main.c b/xen/arch/x86/cpu/mtrr/main.c -index 55a4da54a7..90b235f57e 100644 ---- a/xen/arch/x86/cpu/mtrr/main.c -+++ b/xen/arch/x86/cpu/mtrr/main.c -@@ -316,7 +316,7 @@ int mtrr_add_page(unsigned long base, unsigned long size, - } - - /* If the type is WC, check that this processor supports it */ -- if ((type == X86_MT_WC) && mtrr_have_wrcomb()) { -+ if ((type == X86_MT_WC) && !mtrr_have_wrcomb()) { - printk(KERN_WARNING - "mtrr: your processor doesn't support write-combining\n"); - return -EOPNOTSUPP; --- -2.30.2 - diff --git a/xen.git-f0ff1d9cb96041a84a24857a6464628240deed4f.patch b/xen.git-f0ff1d9cb96041a84a24857a6464628240deed4f.patch deleted file mode 100644 index 475a03d..0000000 --- a/xen.git-f0ff1d9cb96041a84a24857a6464628240deed4f.patch +++ /dev/null @@ -1,75 +0,0 @@ -From f0ff1d9cb96041a84a24857a6464628240deed4f Mon Sep 17 00:00:00 2001 -From: =?utf8?q?Roger=20Pau=20Monn=C3=A9?= -Date: Mon, 29 Apr 2024 09:37:29 +0200 -Subject: [PATCH] x86/spec: adjust logic that elides lfence -MIME-Version: 1.0 -Content-Type: text/plain; charset=utf8 -Content-Transfer-Encoding: 8bit - -It's currently too restrictive by just checking whether there's a BHB clearing -sequence selected. It should instead check whether BHB clearing is used on -entry from PV or HVM specifically. - -Switch to use opt_bhb_entry_{pv,hvm} instead, and then remove cpu_has_bhb_seq -since it no longer has any users. - -Reported-by: Jan Beulich -Fixes: 954c983abcee ('x86/spec-ctrl: Software BHB-clearing sequences') -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich -Reviewed-by: Andrew Cooper -master commit: 656ae8f1091bcefec9c46ec3ea3ac2118742d4f6 -master date: 2024-04-25 16:37:01 +0200 ---- - xen/arch/x86/include/asm/cpufeature.h | 3 --- - xen/arch/x86/spec_ctrl.c | 6 +++--- - 2 files changed, 3 insertions(+), 6 deletions(-) - -diff --git a/xen/arch/x86/include/asm/cpufeature.h b/xen/arch/x86/include/asm/cpufeature.h -index 7a312c485e..3c57f55de0 100644 ---- a/xen/arch/x86/include/asm/cpufeature.h -+++ b/xen/arch/x86/include/asm/cpufeature.h -@@ -228,9 +228,6 @@ static inline bool boot_cpu_has(unsigned int feat) - #define cpu_bug_fpu_ptrs boot_cpu_has(X86_BUG_FPU_PTRS) - #define cpu_bug_null_seg boot_cpu_has(X86_BUG_NULL_SEG) - --#define cpu_has_bhb_seq (boot_cpu_has(X86_SPEC_BHB_TSX) || \ -- boot_cpu_has(X86_SPEC_BHB_LOOPS)) -- - enum _cache_type { - CACHE_TYPE_NULL = 0, - CACHE_TYPE_DATA = 1, -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 8c67d6256a..12c19b7eca 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -2328,7 +2328,7 @@ void __init init_speculation_mitigations(void) - * unconditional WRMSR. If we do have it, or we're not using any - * prior conditional block, then it's safe to drop the LFENCE. - */ -- if ( !cpu_has_bhb_seq && -+ if ( !opt_bhb_entry_pv && - (boot_cpu_has(X86_FEATURE_SC_MSR_PV) || - !boot_cpu_has(X86_FEATURE_IBPB_ENTRY_PV)) ) - setup_force_cpu_cap(X86_SPEC_NO_LFENCE_ENTRY_PV); -@@ -2344,7 +2344,7 @@ void __init init_speculation_mitigations(void) - * active in the block that is skipped when interrupting guest - * context, then it's safe to drop the LFENCE. - */ -- if ( !cpu_has_bhb_seq && -+ if ( !opt_bhb_entry_pv && - (boot_cpu_has(X86_FEATURE_SC_MSR_PV) || - (!boot_cpu_has(X86_FEATURE_IBPB_ENTRY_PV) && - !boot_cpu_has(X86_FEATURE_SC_RSB_PV))) ) -@@ -2356,7 +2356,7 @@ void __init init_speculation_mitigations(void) - * A BHB sequence, if used, is the only conditional action, so if we - * don't have it, we don't need the safety LFENCE. - */ -- if ( !cpu_has_bhb_seq ) -+ if ( !opt_bhb_entry_hvm ) - setup_force_cpu_cap(X86_SPEC_NO_LFENCE_ENTRY_VMX); - } - --- -2.30.2 - diff --git a/xen.ocaml5.fixes.patch b/xen.ocaml5.fixes.patch index bdffd4c..fc85a41 100644 --- a/xen.ocaml5.fixes.patch +++ b/xen.ocaml5.fixes.patch @@ -27,14 +27,3 @@ # define a library target .cmxa and .cma define OCAML_LIBRARY_template ---- xen-4.17.1/tools/ocaml/xenstored/disk.ml.orig 2023-04-27 06:53:19.000000000 -0600 -+++ xen-4.17.1/tools/ocaml/xenstored/disk.ml 2023-06-20 09:49:44.361963710 -0600 -@@ -30,7 +30,7 @@ let undec c = - | _ -> raise (Failure "undecify") - - let unhex c = -- let c = Char.lowercase c in -+ let c = Char.lowercase_ascii c in - match c with - | '0' .. '9' -> (Char.code c) - (Char.code '0') - | 'a' .. 'f' -> (Char.code c) - (Char.code 'a') + 10 diff --git a/xen.spec b/xen.spec index 32950cd..cd4b475 100644 --- a/xen.spec +++ b/xen.spec @@ -50,12 +50,12 @@ %endif # Hypervisor ABI -%define hv_abi 4.18 +%define hv_abi 4.19 Summary: Xen is a virtual machine monitor Name: xen -Version: 4.18.2 -Release: 5%{?dist} +Version: 4.19.0 +Release: 1%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -114,13 +114,6 @@ Patch49: xen.python3.12.patch Patch50: xen.ocaml5.fixes.patch Patch52: xen.gcc14.fixes.patch Patch53: newlib.gcc14.fixes.patch -Patch54: xen.git-2d38302c33b117aa9a417056db241aefc840c2f0.patch -Patch55: xen.git-8bdcb0b98b53140102031ceca0611f22190227fd.patch -Patch56: xen.git-af0e9ba44a58c87d6d135d8ffbf468b4ceac0a41.patch -Patch57: xen.git-eb7059767c82d833ebecdf8106e96482b04f3c40.patch -Patch58: xen.git-0b0c7dca70d64c35c86e5d503f67366ebe2b9138.patch -Patch59: xen.git-f0ff1d9cb96041a84a24857a6464628240deed4f.patch -Patch60: xsa458.patch %if %build_qemutrad @@ -335,13 +328,6 @@ manage Xen virtual machines. %endif %patch 52 -p1 %patch 53 -p1 -%patch 54 -p1 -%patch 55 -p1 -%patch 56 -p1 -%patch 57 -p1 -%patch 58 -p1 -%patch 59 -p1 -%patch 60 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -403,6 +389,9 @@ CONFIG_EXTRA="$CONFIG_EXTRA --with-system-seabios=/usr/share/seabios/bios-256k.b %else CONFIG_EXTRA="$CONFIG_EXTRA --disable-seabios" %endif +%if %with_systemd_presets +CONFIG_EXTRA="$CONFIG_EXTRA --enable-systemd" +%endif ./configure --prefix=%{_prefix} --libdir=%{_libdir} --libexecdir=%{_libexecdir} --with-system-qemu=/usr/bin/qemu-system-i386 --with-linux-backend-modules="xen-evtchn xen-gntdev xen-gntalloc xen-blkback xen-netback xen-pciback xen-scsiback xen-acpi-processor" $CONFIG_EXTRA unset CFLAGS CXXFLAGS FFLAGS LDFLAGS export LDFLAGS="$LDFLAGS_SAVE" @@ -553,7 +542,7 @@ ln -s %{_libexecdir}/%{name} %{buildroot}/%{_libdir}/%{name} %endif ############ create symlink to qemu-system-i386 in /usr/bin ############ -ln -s /usr/bin/qemu-system-i386 %{buildroot}/%{_libexecdir}/%{name}/bin/qemu-system-i386 +ln -s ../../../bin/qemu-system-i386 %{buildroot}/%{_libexecdir}/%{name}/bin/qemu-system-i386 ############ debug packaging: list files ############ @@ -773,7 +762,6 @@ fi # man pages %if %build_docs %{_mandir}/man1/xentop.1* -%{_mandir}/man1/xentrace_format.1* %{_mandir}/man8/xentrace.8* %{_mandir}/man1/xl.1* %{_mandir}/man5/xl.cfg.5* @@ -791,6 +779,7 @@ fi %{_mandir}/man1/xenhypfs.1.gz %{_mandir}/man7/xen-vbd-interface.7.gz %{_mandir}/man5/xl-pci-configuration.5.gz +%{_mandir}/man8/xenwatchdogd.8.gz %endif %{python3_sitearch}/xenfsimage*.so @@ -828,8 +817,6 @@ fi %{_bindir}/qemu-*-xen %{_bindir}/xenstore %{_bindir}/xenstore-* -%{_bindir}/pygrub -%{_bindir}/xentrace* #%#{_bindir}/remus # XSM %{_sbindir}/flask-* @@ -948,6 +935,16 @@ fi %endif %changelog +* Sat Aug 03 2024 Michael Young - 4.19.0-1 +- update to xen-4.19.0 + rebase xen.fedora.systemd.patch, xen.efi.build.patch + xen.ocaml5.fixes.patch and xen.gcc14.fixes.patch + remove patches now included or superceded upstream + now need to enable systemd explicitly + xentrace_format has gone, pygrub is now only in /usr/libexec/xen/bin/ + package xenwatchdogd.8.gz + use relative links for /usr/bin/qemu-system-i386 + * Sat Jul 20 2024 Fedora Release Engineering - 4.18.2-5 - Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild diff --git a/xsa458.patch b/xsa458.patch deleted file mode 100644 index 8be0a90..0000000 --- a/xsa458.patch +++ /dev/null @@ -1,38 +0,0 @@ -From: Jan Beulich -Subject: x86/IRQ: avoid double unlock in map_domain_pirq() - -Forever since its introduction the main loop in the function dealing -with multi-vector MSI had error exit points ("break") with different -properties: In one case no IRQ descriptor lock is being held. -Nevertheless the subsequent error cleanup path assumed such a lock would -uniformly need releasing. Identify the case by setting "desc" to NULL, -thus allowing the unlock to be skipped as necessary. - -This is CVE-2024-31143 / XSA-458. - -Coverity ID: 1605298 -Fixes: d1b6d0a02489 ("x86: enable multi-vector MSI") -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné - ---- a/xen/arch/x86/irq.c -+++ b/xen/arch/x86/irq.c -@@ -2273,6 +2273,7 @@ int map_domain_pirq( - - set_domain_irq_pirq(d, irq, info); - spin_unlock_irqrestore(&desc->lock, flags); -+ desc = NULL; - - info = NULL; - irq = create_irq(NUMA_NO_NODE, true); -@@ -2308,7 +2309,9 @@ int map_domain_pirq( - - if ( ret ) - { -- spin_unlock_irqrestore(&desc->lock, flags); -+ if ( desc ) -+ spin_unlock_irqrestore(&desc->lock, flags); -+ - pci_disable_msi(msi_desc); - if ( nr ) - { From ef4574608ae2807bd37e8bf7415278594da7a781 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 14 Aug 2024 22:07:13 +0100 Subject: [PATCH 096/137] 2 security fixes error handling in x86 IOMMU identity mapping [XSA-460, CVE-2024-31145] PCI device pass-through with shared resources [XSA-461, CVE-2024-31146] --- xen.spec | 10 +++++- xsa460.patch | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++ xsa461.patch | 33 +++++++++++++++++++ 3 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 xsa460.patch create mode 100644 xsa461.patch diff --git a/xen.spec b/xen.spec index cd4b475..371da8d 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.19.0 -Release: 1%{?dist} +Release: 2%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -114,6 +114,8 @@ Patch49: xen.python3.12.patch Patch50: xen.ocaml5.fixes.patch Patch52: xen.gcc14.fixes.patch Patch53: newlib.gcc14.fixes.patch +Patch54: xsa460.patch +Patch55: xsa461.patch %if %build_qemutrad @@ -328,6 +330,8 @@ manage Xen virtual machines. %endif %patch 52 -p1 %patch 53 -p1 +%patch 54 -p1 +%patch 55 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -935,6 +939,10 @@ fi %endif %changelog +* Wed Aug 14 2024 Michael Young - 4.19.0-2 +- error handling in x86 IOMMU identity mapping [XSA-460, CVE-2024-31145] +- PCI device pass-through with shared resources [XSA-461, CVE-2024-31146] + * Sat Aug 03 2024 Michael Young - 4.19.0-1 - update to xen-4.19.0 rebase xen.fedora.systemd.patch, xen.efi.build.patch diff --git a/xsa460.patch b/xsa460.patch new file mode 100644 index 0000000..270df8d --- /dev/null +++ b/xsa460.patch @@ -0,0 +1,93 @@ +From: Teddy Astie +Subject: x86/IOMMU: move tracking in iommu_identity_mapping() + +If for some reason xmalloc() fails after having mapped the reserved +regions, an error is reported, but the regions remain mapped in the P2M. + +Similarly if an error occurs during set_identity_p2m_entry() (except on +the first call), the partial mappings of the region would be retained +without being tracked anywhere, and hence without there being a way to +remove them again from the domain's P2M. + +Move the setting up of the list entry ahead of trying to map the region. +In cases other than the first mapping failing, keep record of the full +region, such that a subsequent unmapping request can be properly torn +down. + +To compensate for the potentially excess unmapping requests, don't log a +warning from p2m_remove_identity_entry() when there really was nothing +mapped at a given GFN. + +This is XSA-460 / CVE-2024-31145. + +Fixes: 2201b67b9128 ("VT-d: improve RMRR region handling") +Fixes: c0e19d7c6c42 ("IOMMU: generalize VT-d's tracking of mapped RMRR regions") +Signed-off-by: Teddy Astie +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné + +--- a/xen/arch/x86/mm/p2m.c ++++ b/xen/arch/x86/mm/p2m.c +@@ -1267,9 +1267,11 @@ int p2m_remove_identity_entry(struct dom + else + { + gfn_unlock(p2m, gfn, 0); +- printk(XENLOG_G_WARNING +- "non-identity map d%d:%lx not cleared (mapped to %lx)\n", +- d->domain_id, gfn_l, mfn_x(mfn)); ++ if ( (p2mt != p2m_invalid && p2mt != p2m_mmio_dm) || ++ a != p2m_access_n || !mfn_eq(mfn, INVALID_MFN) ) ++ printk(XENLOG_G_WARNING ++ "non-identity map %pd:%lx not cleared (mapped to %lx)\n", ++ d, gfn_l, mfn_x(mfn)); + ret = 0; + } + +--- a/xen/drivers/passthrough/x86/iommu.c ++++ b/xen/drivers/passthrough/x86/iommu.c +@@ -267,24 +267,36 @@ int iommu_identity_mapping(struct domain + if ( p2ma == p2m_access_x ) + return -ENOENT; + +- while ( base_pfn < end_pfn ) +- { +- int err = set_identity_p2m_entry(d, base_pfn, p2ma, flag); +- +- if ( err ) +- return err; +- base_pfn++; +- } +- + map = xmalloc(struct identity_map); + if ( !map ) + return -ENOMEM; ++ + map->base = base; + map->end = end; + map->access = p2ma; + map->count = 1; ++ ++ /* ++ * Insert into list ahead of mapping, so the range can be found when ++ * trying to clean up. ++ */ + list_add_tail(&map->list, &hd->arch.identity_maps); + ++ for ( ; base_pfn < end_pfn; ++base_pfn ) ++ { ++ int err = set_identity_p2m_entry(d, base_pfn, p2ma, flag); ++ ++ if ( !err ) ++ continue; ++ ++ if ( (map->base >> PAGE_SHIFT_4K) == base_pfn ) ++ { ++ list_del(&map->list); ++ xfree(map); ++ } ++ return err; ++ } ++ + return 0; + } + diff --git a/xsa461.patch b/xsa461.patch new file mode 100644 index 0000000..82c1f29 --- /dev/null +++ b/xsa461.patch @@ -0,0 +1,33 @@ +From: Jan Beulich +Subject: x86/pass-through: documents as security-unsupported when sharing resources + +When multiple devices share resources and one of them is to be passed +through to a guest, security of the entire system and of respective +guests individually cannot really be guaranteed without knowing +internals of any of the involved guests. Therefore such a configuration +cannot really be security-supported, yet making that explicit was so far +missing. + +This is XSA-461 / CVE-2024-31146. + +Signed-off-by: Jan Beulich +Reviewed-by: Juergen Gross +--- +TBD: Of course the system bus(es) is a / are shared resource(s), too. + I'm afraid I don't know the low level details of PCI to be able to + tell whether there are any fairness guarantees there. + +--- a/SUPPORT.md ++++ b/SUPPORT.md +@@ -841,6 +841,11 @@ This feature is not security supported: + + Only systems using IOMMUs are supported. + ++Passing through of devices sharing resources with another device is not ++security supported. Such sharing could e.g. be the same line interrupt being ++used by multiple devices, one of which is to be passed through, or two such ++devices having memory BARs within the same 4k page. ++ + Not compatible with migration, populate-on-demand, altp2m, + introspection, memory sharing, or memory paging. + From b5342a4b6777071a3fab2faaf6989cb07935f11a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Such=C3=BD?= Date: Wed, 4 Sep 2024 22:52:13 +0200 Subject: [PATCH 097/137] convert license to SPDX This is part of https://fedoraproject.org/wiki/Changes/SPDX_Licenses_Phase_4 --- xen.spec | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/xen.spec b/xen.spec index 371da8d..8ebb09e 100644 --- a/xen.spec +++ b/xen.spec @@ -55,8 +55,9 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.19.0 -Release: 2%{?dist} -License: GPLv2+ and LGPLv2+ and BSD +Release: 3%{?dist} +# Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. +License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz Source2: %{name}.logrotate @@ -939,6 +940,9 @@ fi %endif %changelog +* Wed Sep 04 2024 Miroslav Suchý - 4.19.0-3 +- convert license to SPDX + * Wed Aug 14 2024 Michael Young - 4.19.0-2 - error handling in x86 IOMMU identity mapping [XSA-460, CVE-2024-31145] - PCI device pass-through with shared resources [XSA-461, CVE-2024-31146] From 417c2eb41aad8ddbadd3acb2aee775291cb7959b Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 24 Sep 2024 16:48:07 +0100 Subject: [PATCH 098/137] x86: Deadlock in vlapic_error() [XSA-462, CVE-2024-45817] --- xen.spec | 7 ++++++- xsa462.patch | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 xsa462.patch diff --git a/xen.spec b/xen.spec index 8ebb09e..7b24ac4 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.19.0 -Release: 3%{?dist} +Release: 4%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -117,6 +117,7 @@ Patch52: xen.gcc14.fixes.patch Patch53: newlib.gcc14.fixes.patch Patch54: xsa460.patch Patch55: xsa461.patch +Patch56: xsa462.patch %if %build_qemutrad @@ -333,6 +334,7 @@ manage Xen virtual machines. %patch 53 -p1 %patch 54 -p1 %patch 55 -p1 +%patch 56 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -940,6 +942,9 @@ fi %endif %changelog +* Tue Sep 24 2024 Michael Young - 4.19.0-4 +- x86: Deadlock in vlapic_error() [XSA-462, CVE-2024-45817] + * Wed Sep 04 2024 Miroslav Suchý - 4.19.0-3 - convert license to SPDX diff --git a/xsa462.patch b/xsa462.patch new file mode 100644 index 0000000..be538f9 --- /dev/null +++ b/xsa462.patch @@ -0,0 +1,47 @@ +From: Jan Beulich +Subject: x86/vLAPIC: prevent undue recursion of vlapic_error() + +With the error vector set to an illegal value, the function invoking +vlapic_set_irq() would bring execution back here, with the non-recursive +lock already held. Avoid the call in this case, merely further updating +ESR (if necessary). + +This is XSA-462 / CVE-2024-45817. + +Fixes: 5f32d186a8b1 ("x86/vlapic: don't silently accept bad vectors") +Reported-by: Federico Serafini +Reported-by: Andrew Cooper +Signed-off-by: Jan Beulich +Signed-off-by: Andrew Cooper +Reviewed-by: Andrew Cooper + +diff --git a/xen/arch/x86/hvm/vlapic.c b/xen/arch/x86/hvm/vlapic.c +index 2ec95942713e..8758c4217fab 100644 +--- a/xen/arch/x86/hvm/vlapic.c ++++ b/xen/arch/x86/hvm/vlapic.c +@@ -112,9 +112,24 @@ static void vlapic_error(struct vlapic *vlapic, unsigned int errmask) + if ( (esr & errmask) != errmask ) + { + uint32_t lvterr = vlapic_get_reg(vlapic, APIC_LVTERR); ++ bool inj = false; + +- vlapic_set_reg(vlapic, APIC_ESR, esr | errmask); + if ( !(lvterr & APIC_LVT_MASKED) ) ++ { ++ /* ++ * If LVTERR is unmasked and has an illegal vector, vlapic_set_irq() ++ * will end up back here. Break the cycle by only injecting LVTERR ++ * if it will succeed, and folding in RECVILL otherwise. ++ */ ++ if ( (lvterr & APIC_VECTOR_MASK) >= 16 ) ++ inj = true; ++ else ++ errmask |= APIC_ESR_RECVILL; ++ } ++ ++ vlapic_set_reg(vlapic, APIC_ESR, esr | errmask); ++ ++ if ( inj ) + vlapic_set_irq(vlapic, lvterr & APIC_VECTOR_MASK, 0); + } + spin_unlock_irqrestore(&vlapic->esr_lock, flags); From a322535e9ffceca01cb37dd3aefe60a8ad826356 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 25 Sep 2024 22:02:12 +0100 Subject: [PATCH 099/137] add bugzilla references --- xen.spec | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 7b24ac4..d79eba9 100644 --- a/xen.spec +++ b/xen.spec @@ -943,14 +943,16 @@ fi %changelog * Tue Sep 24 2024 Michael Young - 4.19.0-4 -- x86: Deadlock in vlapic_error() [XSA-462, CVE-2024-45817] +- x86: Deadlock in vlapic_error() [XSA-462, CVE-2024-45817] (#2314782) * Wed Sep 04 2024 Miroslav Suchý - 4.19.0-3 - convert license to SPDX * Wed Aug 14 2024 Michael Young - 4.19.0-2 - error handling in x86 IOMMU identity mapping [XSA-460, CVE-2024-31145] + (#2314784) - PCI device pass-through with shared resources [XSA-461, CVE-2024-31146] + (#2314783) * Sat Aug 03 2024 Michael Young - 4.19.0-1 - update to xen-4.19.0 From 2b88083cfcffc5bd9f7d70a6256a4756ecdb1112 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Fri, 6 Dec 2024 19:19:08 +0000 Subject: [PATCH 100/137] update to xen-4.19.1 --- .gitignore | 2 +- newlib.gcc14.fixes.patch | 388 --------------------------------------- sources | 2 +- xen.gcc13.fixes.patch | 10 - xen.gcc14.fixes.patch | 12 -- xen.ocaml5.fixes.patch | 29 --- xen.spec | 29 ++- xsa460.patch | 93 ---------- xsa461.patch | 33 ---- xsa462.patch | 47 ----- 10 files changed, 13 insertions(+), 632 deletions(-) delete mode 100644 newlib.gcc14.fixes.patch delete mode 100644 xen.gcc13.fixes.patch delete mode 100644 xen.gcc14.fixes.patch delete mode 100644 xen.ocaml5.fixes.patch delete mode 100644 xsa460.patch delete mode 100644 xsa461.patch delete mode 100644 xsa462.patch diff --git a/.gitignore b/.gitignore index a996079..69cde46 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.19.0.tar.gz +/xen-4.19.1.tar.gz diff --git a/newlib.gcc14.fixes.patch b/newlib.gcc14.fixes.patch deleted file mode 100644 index bdb3594..0000000 --- a/newlib.gcc14.fixes.patch +++ /dev/null @@ -1,388 +0,0 @@ ---- xen-4.18.0/stubdom/newlib-makedoc.patch.orig 2023-11-16 21:44:21.000000000 +0000 -+++ xen-4.18.0/stubdom/newlib-makedoc.patch 2024-02-01 20:32:40.565785056 +0000 -@@ -33,3 +33,385 @@ - #include - - #define DEF_SIZE 5000 -+--- -+ -+patch makedoc.c for gcc14 build -+ -+--- newlib-1.16.0/newlib/doc/makedoc.c.orig 2007-01-02 16:59:59.000000000 +0000 -++++ newlib-1.16.0/newlib/doc/makedoc.c 2024-02-01 20:26:32.415396758 +0000 -+@@ -63,25 +63,20 @@ -+ -+ -+ -+-static void DEFUN(init_string_with_size,(buffer, size), -+- string_type *buffer AND -+- unsigned int size ) -++static void init_string_with_size (string_type *buffer, unsigned int size) -+ { -+ buffer->write_idx = 0; -+ buffer->size = size; -+ buffer->ptr = malloc(size); -+ } -+ -+-static void DEFUN(init_string,(buffer), -+- string_type *buffer) -++static void init_string (string_type *buffer) -+ { -+ init_string_with_size(buffer, DEF_SIZE); -+ -+ } -+ -+-static int DEFUN(find, (str, what), -+- string_type *str AND -+- char *what) -++static int find (string_type *str, char *what) -+ { -+ unsigned int i; -+ char *p; -+@@ -97,30 +92,24 @@ -+ -+ } -+ -+-static void DEFUN(write_buffer,(buffer), -+- string_type *buffer) -++static void write_buffer (string_type *buffer) -+ { -+ fwrite(buffer->ptr, buffer->write_idx, 1, stdout); -+ } -+ -+ -+-static void DEFUN(delete_string,(buffer), -+- string_type *buffer) -++static void delete_string (string_type *buffer) -+ { -+ free(buffer->ptr); -+ } -+ -+ -+-static char *DEFUN(addr, (buffer, idx), -+- string_type *buffer AND -+- unsigned int idx) -++static char * addr (string_type *buffer, unsigned int idx) -+ { -+ return buffer->ptr + idx; -+ } -+ -+-static char DEFUN(at,(buffer, pos), -+- string_type *buffer AND -+- unsigned int pos) -++static char at (string_type *buffer, unsigned int pos) -+ { -+ if ( pos >= buffer->write_idx) -+ { -+@@ -129,9 +118,7 @@ -+ return buffer->ptr[pos]; -+ } -+ -+-static void DEFUN(catchar,(buffer, ch), -+- string_type *buffer AND -+- char ch) -++static void catchar (string_type *buffer, char ch) -+ { -+ if (buffer->write_idx == buffer->size) -+ { -+@@ -143,9 +130,7 @@ -+ } -+ -+ -+-static void DEFUN(overwrite_string,(dst, src), -+- string_type *dst AND -+- string_type *src) -++static void overwrite_string (string_type *dst, string_type *src) -+ { -+ free(dst->ptr); -+ dst->size = src->size; -+@@ -153,9 +138,7 @@ -+ dst->ptr = src->ptr; -+ } -+ -+-static void DEFUN(catstr,(dst, src), -+- string_type *dst AND -+- string_type *src) -++static void catstr ( string_type *dst, string_type *src) -+ { -+ unsigned int i; -+ for (i = 0; i < src->write_idx; i++) -+@@ -165,9 +148,7 @@ -+ } -+ -+ -+-static void DEFUN(cattext,(buffer, string), -+- string_type *buffer AND -+- char *string) -++static void cattext (string_type *buffer, char *string) -+ { -+ -+ while (*string) -+@@ -177,10 +158,7 @@ -+ } -+ } -+ -+-static void DEFUN(catbuf,(buffer, buf, len), -+- string_type *buffer AND -+- char *buf AND -+- unsigned int len) -++static void catbuf ( string_type *buffer, char *buf, unsigned int len) -+ { -+ -+ while (len--) -+@@ -192,10 +170,7 @@ -+ -+ -+ -+-static unsigned int -+-DEFUN(skip_white_and_stars,(src, idx), -+- string_type *src AND -+- unsigned int idx) -++static unsigned int skip_white_and_stars (string_type *src, unsigned int idx) -+ { -+ while (isspace(at(src,idx)) -+ || (at(src,idx) == '*' && at(src,idx +1) !='/')) -+@@ -216,10 +191,23 @@ -+ stinst_type *pc; -+ stinst_type sstack[STACK]; -+ stinst_type *ssp = &sstack[0]; -+-int istack[STACK]; -+-int *isp = &istack[0]; -+ -+-typedef int *word_type; -++#if defined(__PTRDIFF_TYPE__) -++typedef signed __PTRDIFF_TYPE__ intptr_t; -++typedef unsigned __PTRDIFF_TYPE__ uintptr_t; -++#else -++/* -++ * Fallback to hardcoded values, -++ * should be valid on cpu's with 32bit int/32bit void* -++ */ -++typedef signed long intptr_t; -++typedef unsigned long uintptr_t; -++#endif -++ -++uintptr_t istack[STACK]; -++uintptr_t *isp = &istack[0]; -++ -++typedef uintptr_t *word_type; -+ -+ -+ -+@@ -236,8 +224,7 @@ -+ typedef struct dict_struct dict_type; -+ #define WORD(x) static void x() -+ -+-static void DEFUN(exec,(word), -+- dict_type *word) -++static void exec (dict_type *word) -+ { -+ pc = word->code; -+ while (*pc) -+@@ -267,7 +254,7 @@ -+ { -+ isp++; -+ pc++; -+- *isp = (int)(*pc); -++ *isp = (uintptr_t)(*pc); -+ pc++; -+ -+ } -+@@ -294,10 +281,7 @@ -+ Blank lines are turned into one blank line -+ */ -+ -+-static void -+-DEFUN(remove_noncomments,(src,dst), -+- string_type *src AND -+- string_type *dst) -++static void remove_noncomments (string_type *src, string_type *dst) -+ { -+ unsigned int idx = 0; -+ -+@@ -349,8 +333,7 @@ -+ -+ */ -+ -+-static void -+-DEFUN_VOID(exfunstuff) -++static void exfunstuff (void) -+ { -+ unsigned int openp; -+ unsigned int fname; -+@@ -522,8 +505,7 @@ -+ } -+ -+ /* Mod tos so that only lines with leading dots remain */ -+-static void -+-DEFUN_VOID(outputdots) -++static void (outputdots (void)) -+ { -+ unsigned int idx = 0; -+ string_type out; -+@@ -772,10 +754,7 @@ -+ -+ } -+ /* A command is all upper case,and alone on a line */ -+-static int -+-DEFUN( iscommand,(ptr, idx), -+- string_type *ptr AND -+- unsigned int idx) -++static int iscommand (string_type *ptr, unsigned int idx) -+ { -+ unsigned int len = 0; -+ while (at(ptr,idx)) { -+@@ -797,10 +776,7 @@ -+ } -+ -+ -+-DEFUN(copy_past_newline,(ptr, idx, dst), -+- string_type *ptr AND -+- unsigned int idx AND -+- string_type *dst) -++unsigned int copy_past_newline (string_type *ptr, unsigned int idx, string_type *dst) -+ { -+ while (at(ptr, idx) && at(ptr, idx) != '\n') -+ { -+@@ -1060,10 +1036,7 @@ -+ -+ } -+ -+-char * -+-DEFUN(nextword,(string, word), -+- char *string AND -+- char **word) -++char * nextword (char *string, char **word) -+ { -+ char *word_start; -+ int idx; -+@@ -1138,9 +1111,7 @@ -+ -+ } -+ dict_type *root; -+-dict_type * -+-DEFUN(lookup_word,(word), -+- char *word) -++dict_type * lookup_word (char *word) -+ { -+ dict_type *ptr = root; -+ while (ptr) { -+@@ -1154,7 +1125,7 @@ -+ -+ } -+ -+-static void DEFUN_VOID(perform) -++static int perform (void) -+ { -+ tos = stack; -+ -+@@ -1192,9 +1163,7 @@ -+ } -+ } -+ -+-dict_type * -+-DEFUN(newentry,(word), -+- char *word) -++dict_type * newentry (char *word) -+ { -+ dict_type *new = (dict_type *)malloc(sizeof(dict_type)); -+ new->word = word; -+@@ -1208,10 +1177,7 @@ -+ } -+ -+ -+-unsigned int -+-DEFUN(add_to_definition,(entry, word), -+- dict_type *entry AND -+- stinst_type word) -++unsigned int add_to_definition (dict_type *entry, stinst_type word) -+ { -+ if (entry->code_end == entry->code_length) -+ { -+@@ -1232,9 +1198,7 @@ -+ -+ -+ void -+-DEFUN(add_intrinsic,(name, func), -+- char *name AND -+- void (*func)()) -++add_intrinsic (char *name, void (*func)(void)) -+ { -+ dict_type *new = newentry(name); -+ add_to_definition(new, func); -+@@ -1247,9 +1211,7 @@ -+ -+ } -+ -+-void -+-DEFUN(add_var,(name), -+- char *name) -++void add_var (char *name) -+ { -+ dict_type *new = newentry(name); -+ add_to_definition(new, push_number); -+@@ -1262,8 +1224,7 @@ -+ -+ -+ void -+-DEFUN(compile, (string), -+- char *string) -++compile (char *string) -+ -+ { -+ int jstack[STACK]; -+@@ -1315,11 +1276,11 @@ -+ /* Got a number, embedd the magic push number -+ function */ -+ add_to_definition(ptr, push_number); -+- add_to_definition(ptr, atol(word)); -++ add_to_definition(ptr, (stinst_type)atol(word)); -+ break; -+ default: -+ add_to_definition(ptr, call); -+- add_to_definition(ptr, lookup_word(word)); -++ add_to_definition(ptr, (stinst_type)lookup_word(word)); -+ } -+ -+ string = nextword(string, &word); -+@@ -1336,9 +1297,9 @@ -+ } -+ -+ -+-static void DEFUN_VOID(bang) -++static void bang (void) -+ { -+-*(int *)((isp[0])) = isp[-1]; -++*(uintptr_t *)((isp[0])) = isp[-1]; -+ isp-=2; -+ pc++; -+ -+@@ -1359,9 +1320,7 @@ -+ -+ -+ -+-static void DEFUN(read_in, (str, file), -+- string_type *str AND -+- FILE *file) -++static void read_in (string_type *str, FILE *file) -+ { -+ char buff[10000]; -+ unsigned int r; -+@@ -1378,15 +1337,13 @@ -+ } -+ -+ -+-static void DEFUN_VOID(usage) -++static void usage (void) -+ { -+ fprintf(stderr,"usage: -[d|i|g] file\n"); -+ exit(33); -+ } -+ -+-int DEFUN(main,(ac,av), -+-int ac AND -+-char *av[]) -++int main (int ac, char *av[]) -+ { -+ unsigned int i; -+ diff --git a/sources b/sources index e979065..595852d 100644 --- a/sources +++ b/sources @@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.19.0.tar.gz) = bc0b4257cab66b0f9d4a1fe72e07f4980398b2934989c02ffe488b465eca3443caa0ded903871480aba6e36212921f335b68d67f202f22548c31899225f2d657 +SHA512 (xen-4.19.1.tar.gz) = 6971d07d4eafd40186f35cf6de235badbdf0e1640974693b8fbd415876c46634094178a6157e6dfdc16af885c70ff3b7a72be35a88dcc63daf36e6eb08f9b17b diff --git a/xen.gcc13.fixes.patch b/xen.gcc13.fixes.patch deleted file mode 100644 index d9800e2..0000000 --- a/xen.gcc13.fixes.patch +++ /dev/null @@ -1,10 +0,0 @@ ---- xen-4.17.0/xen/common/bunzip2.c.orig 2022-12-08 18:03:08.000000000 +0000 -+++ xen-4.17.0/xen/common/bunzip2.c 2023-01-17 21:35:39.251292015 +0000 -@@ -223,6 +223,7 @@ - for (j = 0; j < groupCount; j++) { - unsigned char length[MAX_SYMBOLS], temp[MAX_HUFCODE_BITS+1]; - int minLen, maxLen, pp; -+ length[0]=0; - /* Read Huffman code lengths for each symbol. They're - stored in a way similar to mtf; record a starting - value for the first symbol, and an offset from the diff --git a/xen.gcc14.fixes.patch b/xen.gcc14.fixes.patch deleted file mode 100644 index a9fb2f3..0000000 --- a/xen.gcc14.fixes.patch +++ /dev/null @@ -1,12 +0,0 @@ -Work around newlib build problems with gcc14 - ---- xen-4.18.0/stubdom/Makefile.orig 2024-01-15 20:35:36.941740548 +0000 -+++ xen-4.18.0/stubdom/Makefile 2024-01-29 22:29:29.067367523 +0000 -@@ -45,6 +45,7 @@ - NEWLIB_CFLAGS+=-D_I386MACH_ALLOW_HW_INTERRUPTS - STUBDOM_SUPPORTED=1 - endif -+NEWLIB_CFLAGS+=-Wno-error=implicit-function-declaration - - CROSS_ROOT=cross-root-$(GNU_TARGET_ARCH) - CROSS_PREFIX=$(CURDIR)/$(CROSS_ROOT) diff --git a/xen.ocaml5.fixes.patch b/xen.ocaml5.fixes.patch deleted file mode 100644 index fc85a41..0000000 --- a/xen.ocaml5.fixes.patch +++ /dev/null @@ -1,29 +0,0 @@ ---- xen-4.17.1/tools/ocaml/common.make.orig 2023-04-27 06:53:19.000000000 -0600 -+++ xen-4.17.1/tools/ocaml/common.make 2023-06-20 09:47:25.250969763 -0600 -@@ -11,9 +11,9 @@ OCAMLFIND ?= ocamlfind - - CFLAGS += -fPIC -I$(shell ocamlc -where) - --OCAMLOPTFLAG_G := $(shell $(OCAMLOPT) -h 2>&1 | sed -n 's/^ *\(-g\) .*/\1/p') --OCAMLOPTFLAGS = $(OCAMLOPTFLAG_G) -ccopt "$(LDFLAGS)" -dtypes $(OCAMLINCLUDE) -cc $(CC) -w F -warn-error F --OCAMLCFLAGS += -g $(OCAMLINCLUDE) -w F -warn-error F -+OCAMLOPTFLAG_G := -g -+OCAMLOPTFLAGS = $(OCAMLOPTFLAG_G) -ccopt "$(LDFLAGS)" -dtypes $(OCAMLINCLUDE) -cc $(CC) -w F -warn-error F -I +unix -+OCAMLCFLAGS += -g $(OCAMLINCLUDE) -w F -warn-error F -I +unix - - VERSION := 4.1 - ---- xen-4.17.1/tools/ocaml/Makefile.rules.orig 2023-04-27 06:53:19.000000000 -0600 -+++ xen-4.17.1/tools/ocaml/Makefile.rules 2023-06-20 10:00:58.769235173 -0600 -@@ -59,9 +59,9 @@ quiet-command = $(if $(V),$1,@printf " % - mk-caml-lib-native = $(call quiet-command, $(OCAMLOPT) $(OCAMLOPTFLAGS) -a -o $1 $2 $3,MLA,$1) - mk-caml-lib-bytecode = $(call quiet-command, $(OCAMLC) $(OCAMLCFLAGS) -a -o $1 $2 $3,MLA,$1) - --mk-caml-stubs = $(call quiet-command, $(OCAMLMKLIB) -o `basename $1 .a` $2,MKLIB,$1) -+mk-caml-stubs = $(call quiet-command, $(OCAMLMKLIB) -g -o `basename $1 .a` $2,MKLIB,$1) - mk-caml-lib-stubs = \ -- $(call quiet-command, $(AR) rcs $1 $2 && $(OCAMLMKLIB) -o `basename $1 .a | sed -e 's/^lib//'` $2,MKLIB,$1) -+ $(call quiet-command, $(AR) rcs $1 $2 && $(OCAMLMKLIB) -g -o `basename $1 .a | sed -e 's/^lib//'` $2,MKLIB,$1) - - # define a library target .cmxa and .cma - define OCAML_LIBRARY_template diff --git a/xen.spec b/xen.spec index d79eba9..b5f39ca 100644 --- a/xen.spec +++ b/xen.spec @@ -54,8 +54,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.19.0 -Release: 4%{?dist} +Version: 4.19.1 +Release: 1%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -110,14 +110,7 @@ Patch41: xen.gcc9.fixes.patch Patch43: xen.gcc11.fixes.patch Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch -Patch47: xen.gcc13.fixes.patch Patch49: xen.python3.12.patch -Patch50: xen.ocaml5.fixes.patch -Patch52: xen.gcc14.fixes.patch -Patch53: newlib.gcc14.fixes.patch -Patch54: xsa460.patch -Patch55: xsa461.patch -Patch56: xsa462.patch %if %build_qemutrad @@ -325,16 +318,7 @@ manage Xen virtual machines. %patch 43 -p1 %patch 45 -p1 %patch 46 -p1 -%patch 47 -p1 %patch 49 -p1 -%if "%dist" != ".fc38" -%patch 50 -p1 -%endif -%patch 52 -p1 -%patch 53 -p1 -%patch 54 -p1 -%patch 55 -p1 -%patch 56 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -942,6 +926,15 @@ fi %endif %changelog +* Thu Dec 05 2024 Michael Young - 4.19.1-1 +- update to xen-4.19.1 + remove patches now included or superceded upstream + +* Tue Nov 12 2024 Michael Young - 4.19.0-5 +- Deadlock in x86 HVM standard VGA handling [XSA-463, CVE-2024-45818] +- libxl leaks data to PVH guests via ACPI tables [XSA-464, CVE-2024-45819] +- additional patches so above applies cleanly + * Tue Sep 24 2024 Michael Young - 4.19.0-4 - x86: Deadlock in vlapic_error() [XSA-462, CVE-2024-45817] (#2314782) diff --git a/xsa460.patch b/xsa460.patch deleted file mode 100644 index 270df8d..0000000 --- a/xsa460.patch +++ /dev/null @@ -1,93 +0,0 @@ -From: Teddy Astie -Subject: x86/IOMMU: move tracking in iommu_identity_mapping() - -If for some reason xmalloc() fails after having mapped the reserved -regions, an error is reported, but the regions remain mapped in the P2M. - -Similarly if an error occurs during set_identity_p2m_entry() (except on -the first call), the partial mappings of the region would be retained -without being tracked anywhere, and hence without there being a way to -remove them again from the domain's P2M. - -Move the setting up of the list entry ahead of trying to map the region. -In cases other than the first mapping failing, keep record of the full -region, such that a subsequent unmapping request can be properly torn -down. - -To compensate for the potentially excess unmapping requests, don't log a -warning from p2m_remove_identity_entry() when there really was nothing -mapped at a given GFN. - -This is XSA-460 / CVE-2024-31145. - -Fixes: 2201b67b9128 ("VT-d: improve RMRR region handling") -Fixes: c0e19d7c6c42 ("IOMMU: generalize VT-d's tracking of mapped RMRR regions") -Signed-off-by: Teddy Astie -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné - ---- a/xen/arch/x86/mm/p2m.c -+++ b/xen/arch/x86/mm/p2m.c -@@ -1267,9 +1267,11 @@ int p2m_remove_identity_entry(struct dom - else - { - gfn_unlock(p2m, gfn, 0); -- printk(XENLOG_G_WARNING -- "non-identity map d%d:%lx not cleared (mapped to %lx)\n", -- d->domain_id, gfn_l, mfn_x(mfn)); -+ if ( (p2mt != p2m_invalid && p2mt != p2m_mmio_dm) || -+ a != p2m_access_n || !mfn_eq(mfn, INVALID_MFN) ) -+ printk(XENLOG_G_WARNING -+ "non-identity map %pd:%lx not cleared (mapped to %lx)\n", -+ d, gfn_l, mfn_x(mfn)); - ret = 0; - } - ---- a/xen/drivers/passthrough/x86/iommu.c -+++ b/xen/drivers/passthrough/x86/iommu.c -@@ -267,24 +267,36 @@ int iommu_identity_mapping(struct domain - if ( p2ma == p2m_access_x ) - return -ENOENT; - -- while ( base_pfn < end_pfn ) -- { -- int err = set_identity_p2m_entry(d, base_pfn, p2ma, flag); -- -- if ( err ) -- return err; -- base_pfn++; -- } -- - map = xmalloc(struct identity_map); - if ( !map ) - return -ENOMEM; -+ - map->base = base; - map->end = end; - map->access = p2ma; - map->count = 1; -+ -+ /* -+ * Insert into list ahead of mapping, so the range can be found when -+ * trying to clean up. -+ */ - list_add_tail(&map->list, &hd->arch.identity_maps); - -+ for ( ; base_pfn < end_pfn; ++base_pfn ) -+ { -+ int err = set_identity_p2m_entry(d, base_pfn, p2ma, flag); -+ -+ if ( !err ) -+ continue; -+ -+ if ( (map->base >> PAGE_SHIFT_4K) == base_pfn ) -+ { -+ list_del(&map->list); -+ xfree(map); -+ } -+ return err; -+ } -+ - return 0; - } - diff --git a/xsa461.patch b/xsa461.patch deleted file mode 100644 index 82c1f29..0000000 --- a/xsa461.patch +++ /dev/null @@ -1,33 +0,0 @@ -From: Jan Beulich -Subject: x86/pass-through: documents as security-unsupported when sharing resources - -When multiple devices share resources and one of them is to be passed -through to a guest, security of the entire system and of respective -guests individually cannot really be guaranteed without knowing -internals of any of the involved guests. Therefore such a configuration -cannot really be security-supported, yet making that explicit was so far -missing. - -This is XSA-461 / CVE-2024-31146. - -Signed-off-by: Jan Beulich -Reviewed-by: Juergen Gross ---- -TBD: Of course the system bus(es) is a / are shared resource(s), too. - I'm afraid I don't know the low level details of PCI to be able to - tell whether there are any fairness guarantees there. - ---- a/SUPPORT.md -+++ b/SUPPORT.md -@@ -841,6 +841,11 @@ This feature is not security supported: - - Only systems using IOMMUs are supported. - -+Passing through of devices sharing resources with another device is not -+security supported. Such sharing could e.g. be the same line interrupt being -+used by multiple devices, one of which is to be passed through, or two such -+devices having memory BARs within the same 4k page. -+ - Not compatible with migration, populate-on-demand, altp2m, - introspection, memory sharing, or memory paging. - diff --git a/xsa462.patch b/xsa462.patch deleted file mode 100644 index be538f9..0000000 --- a/xsa462.patch +++ /dev/null @@ -1,47 +0,0 @@ -From: Jan Beulich -Subject: x86/vLAPIC: prevent undue recursion of vlapic_error() - -With the error vector set to an illegal value, the function invoking -vlapic_set_irq() would bring execution back here, with the non-recursive -lock already held. Avoid the call in this case, merely further updating -ESR (if necessary). - -This is XSA-462 / CVE-2024-45817. - -Fixes: 5f32d186a8b1 ("x86/vlapic: don't silently accept bad vectors") -Reported-by: Federico Serafini -Reported-by: Andrew Cooper -Signed-off-by: Jan Beulich -Signed-off-by: Andrew Cooper -Reviewed-by: Andrew Cooper - -diff --git a/xen/arch/x86/hvm/vlapic.c b/xen/arch/x86/hvm/vlapic.c -index 2ec95942713e..8758c4217fab 100644 ---- a/xen/arch/x86/hvm/vlapic.c -+++ b/xen/arch/x86/hvm/vlapic.c -@@ -112,9 +112,24 @@ static void vlapic_error(struct vlapic *vlapic, unsigned int errmask) - if ( (esr & errmask) != errmask ) - { - uint32_t lvterr = vlapic_get_reg(vlapic, APIC_LVTERR); -+ bool inj = false; - -- vlapic_set_reg(vlapic, APIC_ESR, esr | errmask); - if ( !(lvterr & APIC_LVT_MASKED) ) -+ { -+ /* -+ * If LVTERR is unmasked and has an illegal vector, vlapic_set_irq() -+ * will end up back here. Break the cycle by only injecting LVTERR -+ * if it will succeed, and folding in RECVILL otherwise. -+ */ -+ if ( (lvterr & APIC_VECTOR_MASK) >= 16 ) -+ inj = true; -+ else -+ errmask |= APIC_ESR_RECVILL; -+ } -+ -+ vlapic_set_reg(vlapic, APIC_ESR, esr | errmask); -+ -+ if ( inj ) - vlapic_set_irq(vlapic, lvterr & APIC_VECTOR_MASK, 0); - } - spin_unlock_irqrestore(&vlapic->esr_lock, flags); From ebbbc5ccb727feedec4f730c33c56f14f96b7d6d Mon Sep 17 00:00:00 2001 From: Andrea Perotti Date: Sat, 4 Jan 2025 14:12:34 +0100 Subject: [PATCH 101/137] FIX #2335558: xen-hypervisor %post doesn't load all needed grub2 modules (elf.mod is missing) --- xen.spec | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/xen.spec b/xen.spec index b5f39ca..8a04075 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.19.1 -Release: 1%{?dist} +Release: 2%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -610,7 +610,7 @@ do_it() { if [ ! -d $TARGET ]; then mkdir $TARGET fi - for m in relocator.mod multiboot2.mod; do + for m in relocator.mod multiboot2.mod elf.mod; do if [ -f $DIR/$m ]; then if [ ! -f $TARGET/$m ] || ! cmp -s $DIR/$m $TARGET/$m; then cp -p $DIR/$m $TARGET/$m @@ -926,6 +926,10 @@ fi %endif %changelog +* Sat Jan 04 2025 Andrea Perotti - 4.19.1-2 +- xen-hypervisor %post doesn't load all needed grub2 modules + (#2335558) + * Thu Dec 05 2024 Michael Young - 4.19.1-1 - update to xen-4.19.1 remove patches now included or superceded upstream From d458004d8bff14222254bf36c0f76673b6d73a3b Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 9 Jan 2025 21:24:58 +0000 Subject: [PATCH 102/137] work around debugedit bug to fix aarch64 builds --- xen.spec | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 8a04075..1068616 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.19.1 -Release: 2%{?dist} +Release: 3%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -551,6 +551,11 @@ find . -path licensedir -prune -o -path stubdom/ioemu -prune -o \ install -m 644 $file licensedir/$file done +############ workaround debugedit bug +%ifarch aarch64 +/bin/rm -r dist/install/usr/{bin,lib64,libexec,sbin} +%endif + ############ all done now ############ %post @@ -926,6 +931,9 @@ fi %endif %changelog +* Thu Jan 09 2025 Michael Young - 4.19.1-3 +- work around debugedit bug to fix aarch64 builds + * Sat Jan 04 2025 Andrea Perotti - 4.19.1-2 - xen-hypervisor %post doesn't load all needed grub2 modules (#2335558) From 5c740ce63a9ca0526725f40283d0091ce327ae19 Mon Sep 17 00:00:00 2001 From: Jerry James Date: Fri, 10 Jan 2025 13:42:27 -0700 Subject: [PATCH 103/137] OCaml 5.3.0 rebuild for Fedora 42 --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 1068616..558c54a 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.19.1 -Release: 3%{?dist} +Release: 4%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -931,6 +931,9 @@ fi %endif %changelog +* Fri Jan 10 2025 Jerry James - 4.19.1-4 +- OCaml 5.3.0 rebuild for Fedora 42 + * Thu Jan 09 2025 Michael Young - 4.19.1-3 - work around debugedit bug to fix aarch64 builds From 0d1f7da88aaa0b824a1f7ebc8ddc2a8527abedf6 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Sun, 19 Jan 2025 15:38:51 +0000 Subject: [PATCH 104/137] Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 558c54a..5126983 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.19.1 -Release: 4%{?dist} +Release: 5%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -931,6 +931,9 @@ fi %endif %changelog +* Sun Jan 19 2025 Fedora Release Engineering - 4.19.1-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild + * Fri Jan 10 2025 Jerry James - 4.19.1-4 - OCaml 5.3.0 rebuild for Fedora 42 From 0e81fe4bff17648fc345e3f47449eedf4314413d Mon Sep 17 00:00:00 2001 From: Michael Young Date: Fri, 24 Jan 2025 22:32:04 +0000 Subject: [PATCH 105/137] adjust file locations now /usr/sbin is a symlink to /usr/bin remove debugedit fix as no longer needed --- xen.spec | 79 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/xen.spec b/xen.spec index 5126983..212efa4 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.19.1 -Release: 5%{?dist} +Release: 6%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -463,7 +463,7 @@ rm -rf %{buildroot}/boot rm -fr %{buildroot}%{_datadir}/doc/xen # Pointless helper -rm -f %{buildroot}%{_sbindir}/xen-python-path +rm -f %{buildroot}%{_bindir}/xen-python-path # qemu stuff (unused or available from upstream) rm -rf %{buildroot}/usr/share/xen/man @@ -551,10 +551,9 @@ find . -path licensedir -prune -o -path stubdom/ioemu -prune -o \ install -m 644 $file licensedir/$file done -############ workaround debugedit bug -%ifarch aarch64 -/bin/rm -r dist/install/usr/{bin,lib64,libexec,sbin} -%endif +############ move sbin files to bin + +mv %{buildroot}/usr/sbin/* %{buildroot}/usr/bin/ ############ all done now ############ @@ -815,57 +814,57 @@ fi %{_bindir}/xenstore-* #%#{_bindir}/remus # XSM -%{_sbindir}/flask-* +%{_bindir}/flask-* # Misc stuff %ifnarch armv7hl aarch64 %{_bindir}/xen-detect %endif %{_bindir}/xencov_split %ifnarch armv7hl aarch64 -%{_sbindir}/gdbsx -%{_sbindir}/xen-kdd +%{_bindir}/gdbsx +%{_bindir}/xen-kdd %endif %ifnarch armv7hl aarch64 -%{_sbindir}/xen-hptool -%{_sbindir}/xen-hvmcrash -%{_sbindir}/xen-hvmctx +%{_bindir}/xen-hptool +%{_bindir}/xen-hvmcrash +%{_bindir}/xen-hvmctx %endif -%{_sbindir}/xenconsoled -%{_sbindir}/xenlockprof -%{_sbindir}/xenmon -%{_sbindir}/xentop -%{_sbindir}/xentrace_setmask -%{_sbindir}/xenbaked -%{_sbindir}/xenstored -%{_sbindir}/xenpm -%{_sbindir}/xenpmd -%{_sbindir}/xenperf -%{_sbindir}/xenwatchdogd -%{_sbindir}/xl +%{_bindir}/xenconsoled +%{_bindir}/xenlockprof +%{_bindir}/xenmon +%{_bindir}/xentop +%{_bindir}/xentrace_setmask +%{_bindir}/xenbaked +%{_bindir}/xenstored +%{_bindir}/xenpm +%{_bindir}/xenpmd +%{_bindir}/xenperf +%{_bindir}/xenwatchdogd +%{_bindir}/xl %ifnarch armv7hl aarch64 -%{_sbindir}/xen-lowmemd +%{_bindir}/xen-lowmemd %endif -%{_sbindir}/xencov +%{_bindir}/xencov %ifnarch armv7hl aarch64 -%{_sbindir}/xen-mfndump +%{_bindir}/xen-mfndump %endif %{_bindir}/xenalyze -%{_sbindir}/xentrace -%{_sbindir}/xentrace_setsize +%{_bindir}/xentrace +%{_bindir}/xentrace_setsize %ifnarch armv7hl aarch64 %{_bindir}/xen-cpuid %endif -%{_sbindir}/xen-livepatch -%{_sbindir}/xen-diag +%{_bindir}/xen-livepatch +%{_bindir}/xen-diag %ifnarch armv7hl aarch64 -%{_sbindir}/xen-ucode -%{_sbindir}/xen-memshare -%{_sbindir}/xen-mceinj -%{_sbindir}/xen-vmtrace +%{_bindir}/xen-ucode +%{_bindir}/xen-memshare +%{_bindir}/xen-mceinj +%{_bindir}/xen-vmtrace %endif %{_bindir}/vchan-socket-proxy -%{_sbindir}/xenhypfs -%{_sbindir}/xen-access +%{_bindir}/xenhypfs +%{_bindir}/xen-access # Xen logfiles %dir %attr(0700,root,root) %{_localstatedir}/log/xen @@ -920,7 +919,7 @@ fi %exclude %{_libdir}/ocaml/xen*/*.cmx %{_libdir}/ocaml/stublibs/*.so %{_libdir}/ocaml/stublibs/*.so.owner -%{_sbindir}/oxenstored +%{_bindir}/oxenstored %config(noreplace) %{_sysconfdir}/xen/oxenstored.conf %{_unitdir}/oxenstored.service @@ -931,6 +930,10 @@ fi %endif %changelog +* Thu Jan 23 2025 Michael Young - 4.19.1-6 +- adjust file locations now /usr/sbin is a symlink to /usr/bin +- remove debugedit fix as no longer needed + * Sun Jan 19 2025 Fedora Release Engineering - 4.19.1-5 - Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild From 59fe5ad165212ec0c53c231b3a0edf8e88b5c5d9 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 27 Feb 2025 18:20:39 +0000 Subject: [PATCH 106/137] deadlock potential with VT-d and legacy PCI device pass-through [XSA-467, CVE-2025-1713] --- xen.spec | 8 ++++- xsa467.patch | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 xsa467.patch diff --git a/xen.spec b/xen.spec index 212efa4..d24a639 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.19.1 -Release: 6%{?dist} +Release: 7%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -111,6 +111,7 @@ Patch43: xen.gcc11.fixes.patch Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch Patch49: xen.python3.12.patch +Patch50: xsa467.patch %if %build_qemutrad @@ -319,6 +320,7 @@ manage Xen virtual machines. %patch 45 -p1 %patch 46 -p1 %patch 49 -p1 +%patch 50 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -930,6 +932,10 @@ fi %endif %changelog +* Thu Feb 27 2025 Michael Young - 4.19.1-7 +- deadlock potential with VT-d and legacy PCI device pass-through + [XSA-467, CVE-2025-1713] + * Thu Jan 23 2025 Michael Young - 4.19.1-6 - adjust file locations now /usr/sbin is a symlink to /usr/bin - remove debugedit fix as no longer needed diff --git a/xsa467.patch b/xsa467.patch new file mode 100644 index 0000000..baadd4c --- /dev/null +++ b/xsa467.patch @@ -0,0 +1,98 @@ +From: Jan Beulich +Subject: IOMMU/x86: the bus-to-bridge lock needs to be acquired IRQ-safe + +The function's use from set_msi_source_id() is guaranteed to be in an +IRQs-off region. While the invocation of that function could be moved +ahead in msi_msg_to_remap_entry() (doesn't need to be in the IOMMU- +intremap-locked region), the call tree from map_domain_pirq() holds an +IRQ descriptor lock. Hence all use sites of the lock need become IRQ- +safe ones. + +In find_upstream_bridge() do a tiny bit of tidying in adjacent code: +Change a variable's type to unsigned and merge a redundant assignment +into another variable's initializer. + +This is XSA-467 / CVE-2025-1713. + +Fixes: 476bbccc811c ("VT-d: fix MSI source-id of interrupt remapping") +Signed-off-by: Jan Beulich +Reviewed-by: Juergen Gross +Reviewed-by: Roger Pau Monné + +--- a/xen/drivers/passthrough/pci.c ++++ b/xen/drivers/passthrough/pci.c +@@ -354,20 +354,21 @@ static struct pci_dev *alloc_pdev(struct + switch ( pdev->type = pdev_type(pseg->nr, bus, devfn) ) + { + unsigned int cap, sec_bus, sub_bus; ++ unsigned long flags; + + case DEV_TYPE_PCIe2PCI_BRIDGE: + case DEV_TYPE_LEGACY_PCI_BRIDGE: + sec_bus = pci_conf_read8(pdev->sbdf, PCI_SECONDARY_BUS); + sub_bus = pci_conf_read8(pdev->sbdf, PCI_SUBORDINATE_BUS); + +- spin_lock(&pseg->bus2bridge_lock); ++ spin_lock_irqsave(&pseg->bus2bridge_lock, flags); + for ( ; sec_bus <= sub_bus; sec_bus++ ) + { + pseg->bus2bridge[sec_bus].map = 1; + pseg->bus2bridge[sec_bus].bus = bus; + pseg->bus2bridge[sec_bus].devfn = devfn; + } +- spin_unlock(&pseg->bus2bridge_lock); ++ spin_unlock_irqrestore(&pseg->bus2bridge_lock, flags); + break; + + case DEV_TYPE_PCIe_ENDPOINT: +@@ -437,16 +438,17 @@ static void free_pdev(struct pci_seg *ps + switch ( pdev->type ) + { + unsigned int sec_bus, sub_bus; ++ unsigned long flags; + + case DEV_TYPE_PCIe2PCI_BRIDGE: + case DEV_TYPE_LEGACY_PCI_BRIDGE: + sec_bus = pci_conf_read8(pdev->sbdf, PCI_SECONDARY_BUS); + sub_bus = pci_conf_read8(pdev->sbdf, PCI_SUBORDINATE_BUS); + +- spin_lock(&pseg->bus2bridge_lock); ++ spin_lock_irqsave(&pseg->bus2bridge_lock, flags); + for ( ; sec_bus <= sub_bus; sec_bus++ ) + pseg->bus2bridge[sec_bus] = pseg->bus2bridge[pdev->bus]; +- spin_unlock(&pseg->bus2bridge_lock); ++ spin_unlock_irqrestore(&pseg->bus2bridge_lock, flags); + break; + + default: +@@ -1053,8 +1055,9 @@ enum pdev_type pdev_type(u16 seg, u8 bus + int find_upstream_bridge(u16 seg, u8 *bus, u8 *devfn, u8 *secbus) + { + struct pci_seg *pseg = get_pseg(seg); +- int ret = 0; +- int cnt = 0; ++ int ret = 1; ++ unsigned long flags; ++ unsigned int cnt = 0; + + if ( *bus == 0 ) + return 0; +@@ -1065,8 +1068,7 @@ int find_upstream_bridge(u16 seg, u8 *bu + if ( !pseg->bus2bridge[*bus].map ) + return 0; + +- ret = 1; +- spin_lock(&pseg->bus2bridge_lock); ++ spin_lock_irqsave(&pseg->bus2bridge_lock, flags); + while ( pseg->bus2bridge[*bus].map ) + { + *secbus = *bus; +@@ -1080,7 +1082,7 @@ int find_upstream_bridge(u16 seg, u8 *bu + } + + out: +- spin_unlock(&pseg->bus2bridge_lock); ++ spin_unlock_irqrestore(&pseg->bus2bridge_lock, flags); + return ret; + } + From 88c9f05c391958000cbf4243edd6ce4335380ed1 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Mon, 7 Apr 2025 21:00:32 +0100 Subject: [PATCH 107/137] update to xen-4.19.2 remove patches now included or superceded upstream remove xen*.efi.elf files to avoid debuginfo failure --- .gitignore | 2 +- sources | 2 +- xen.spec | 18 +++++++--- xsa467.patch | 98 ---------------------------------------------------- 4 files changed, 16 insertions(+), 104 deletions(-) delete mode 100644 xsa467.patch diff --git a/.gitignore b/.gitignore index 69cde46..85e99f7 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.19.1.tar.gz +/xen-4.19.2.tar.gz diff --git a/sources b/sources index 595852d..1e984f8 100644 --- a/sources +++ b/sources @@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.19.1.tar.gz) = 6971d07d4eafd40186f35cf6de235badbdf0e1640974693b8fbd415876c46634094178a6157e6dfdc16af885c70ff3b7a72be35a88dcc63daf36e6eb08f9b17b +SHA512 (xen-4.19.2.tar.gz) = 35147b0d755b5a603915fa5017bc021d4a238ab96ee1139dfee8e0f6ba76e64b89d5f331fbd60549682f45ee983d3d8f1cb95c53eed22373a7fa06074ca8da09 diff --git a/xen.spec b/xen.spec index d24a639..f9977fc 100644 --- a/xen.spec +++ b/xen.spec @@ -54,8 +54,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.19.1 -Release: 7%{?dist} +Version: 4.19.2 +Release: 2%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -111,7 +111,6 @@ Patch43: xen.gcc11.fixes.patch Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch Patch49: xen.python3.12.patch -Patch50: xsa467.patch %if %build_qemutrad @@ -320,7 +319,6 @@ manage Xen virtual machines. %patch 45 -p1 %patch 46 -p1 %patch 49 -p1 -%patch 50 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -557,6 +555,13 @@ done mv %{buildroot}/usr/sbin/* %{buildroot}/usr/bin/ +############ remove xen*.efi.elf files to avoid debuginfo failure + +%ifarch x86_64 +rm dist/install/usr/lib/debug/xen-*.efi.elf +rm %{buildroot}/usr/lib/debug/xen-*.efi.elf +%endif + ############ all done now ############ %post @@ -932,6 +937,11 @@ fi %endif %changelog +* Mon Apr 07 2025 Michael Young - 4.19.2-2 +- update to xen-4.19.2 + remove patches now included or superceded upstream + remove xen*.efi.elf files to avoid debuginfo failure + * Thu Feb 27 2025 Michael Young - 4.19.1-7 - deadlock potential with VT-d and legacy PCI device pass-through [XSA-467, CVE-2025-1713] diff --git a/xsa467.patch b/xsa467.patch deleted file mode 100644 index baadd4c..0000000 --- a/xsa467.patch +++ /dev/null @@ -1,98 +0,0 @@ -From: Jan Beulich -Subject: IOMMU/x86: the bus-to-bridge lock needs to be acquired IRQ-safe - -The function's use from set_msi_source_id() is guaranteed to be in an -IRQs-off region. While the invocation of that function could be moved -ahead in msi_msg_to_remap_entry() (doesn't need to be in the IOMMU- -intremap-locked region), the call tree from map_domain_pirq() holds an -IRQ descriptor lock. Hence all use sites of the lock need become IRQ- -safe ones. - -In find_upstream_bridge() do a tiny bit of tidying in adjacent code: -Change a variable's type to unsigned and merge a redundant assignment -into another variable's initializer. - -This is XSA-467 / CVE-2025-1713. - -Fixes: 476bbccc811c ("VT-d: fix MSI source-id of interrupt remapping") -Signed-off-by: Jan Beulich -Reviewed-by: Juergen Gross -Reviewed-by: Roger Pau Monné - ---- a/xen/drivers/passthrough/pci.c -+++ b/xen/drivers/passthrough/pci.c -@@ -354,20 +354,21 @@ static struct pci_dev *alloc_pdev(struct - switch ( pdev->type = pdev_type(pseg->nr, bus, devfn) ) - { - unsigned int cap, sec_bus, sub_bus; -+ unsigned long flags; - - case DEV_TYPE_PCIe2PCI_BRIDGE: - case DEV_TYPE_LEGACY_PCI_BRIDGE: - sec_bus = pci_conf_read8(pdev->sbdf, PCI_SECONDARY_BUS); - sub_bus = pci_conf_read8(pdev->sbdf, PCI_SUBORDINATE_BUS); - -- spin_lock(&pseg->bus2bridge_lock); -+ spin_lock_irqsave(&pseg->bus2bridge_lock, flags); - for ( ; sec_bus <= sub_bus; sec_bus++ ) - { - pseg->bus2bridge[sec_bus].map = 1; - pseg->bus2bridge[sec_bus].bus = bus; - pseg->bus2bridge[sec_bus].devfn = devfn; - } -- spin_unlock(&pseg->bus2bridge_lock); -+ spin_unlock_irqrestore(&pseg->bus2bridge_lock, flags); - break; - - case DEV_TYPE_PCIe_ENDPOINT: -@@ -437,16 +438,17 @@ static void free_pdev(struct pci_seg *ps - switch ( pdev->type ) - { - unsigned int sec_bus, sub_bus; -+ unsigned long flags; - - case DEV_TYPE_PCIe2PCI_BRIDGE: - case DEV_TYPE_LEGACY_PCI_BRIDGE: - sec_bus = pci_conf_read8(pdev->sbdf, PCI_SECONDARY_BUS); - sub_bus = pci_conf_read8(pdev->sbdf, PCI_SUBORDINATE_BUS); - -- spin_lock(&pseg->bus2bridge_lock); -+ spin_lock_irqsave(&pseg->bus2bridge_lock, flags); - for ( ; sec_bus <= sub_bus; sec_bus++ ) - pseg->bus2bridge[sec_bus] = pseg->bus2bridge[pdev->bus]; -- spin_unlock(&pseg->bus2bridge_lock); -+ spin_unlock_irqrestore(&pseg->bus2bridge_lock, flags); - break; - - default: -@@ -1053,8 +1055,9 @@ enum pdev_type pdev_type(u16 seg, u8 bus - int find_upstream_bridge(u16 seg, u8 *bus, u8 *devfn, u8 *secbus) - { - struct pci_seg *pseg = get_pseg(seg); -- int ret = 0; -- int cnt = 0; -+ int ret = 1; -+ unsigned long flags; -+ unsigned int cnt = 0; - - if ( *bus == 0 ) - return 0; -@@ -1065,8 +1068,7 @@ int find_upstream_bridge(u16 seg, u8 *bu - if ( !pseg->bus2bridge[*bus].map ) - return 0; - -- ret = 1; -- spin_lock(&pseg->bus2bridge_lock); -+ spin_lock_irqsave(&pseg->bus2bridge_lock, flags); - while ( pseg->bus2bridge[*bus].map ) - { - *secbus = *bus; -@@ -1080,7 +1082,7 @@ int find_upstream_bridge(u16 seg, u8 *bu - } - - out: -- spin_unlock(&pseg->bus2bridge_lock); -+ spin_unlock_irqrestore(&pseg->bus2bridge_lock, flags); - return ret; - } - From 3a0543c8d6015dfb9426d46ca7859c63ad83b719 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Mon, 12 May 2025 21:42:55 +0100 Subject: [PATCH 108/137] x86: Indirect Target Selection [XSA-469, CVE-2024-28956] --- xen.spec | 19 +- xsa469-4.19-01.patch | 83 +++++++ xsa469-4.19-02.patch | 314 +++++++++++++++++++++++++++ xsa469-4.19-03.patch | 46 ++++ xsa469-4.19-04.patch | 64 ++++++ xsa469-4.19-05.patch | 506 +++++++++++++++++++++++++++++++++++++++++++ xsa469-4.19-06.patch | 375 ++++++++++++++++++++++++++++++++ xsa469-4.19-07.patch | 154 +++++++++++++ 8 files changed, 1560 insertions(+), 1 deletion(-) create mode 100644 xsa469-4.19-01.patch create mode 100644 xsa469-4.19-02.patch create mode 100644 xsa469-4.19-03.patch create mode 100644 xsa469-4.19-04.patch create mode 100644 xsa469-4.19-05.patch create mode 100644 xsa469-4.19-06.patch create mode 100644 xsa469-4.19-07.patch diff --git a/xen.spec b/xen.spec index f9977fc..650144d 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.19.2 -Release: 2%{?dist} +Release: 4%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -111,6 +111,13 @@ Patch43: xen.gcc11.fixes.patch Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch Patch49: xen.python3.12.patch +Patch50: xsa469-4.19-01.patch +Patch51: xsa469-4.19-02.patch +Patch52: xsa469-4.19-03.patch +Patch53: xsa469-4.19-04.patch +Patch54: xsa469-4.19-05.patch +Patch55: xsa469-4.19-06.patch +Patch56: xsa469-4.19-07.patch %if %build_qemutrad @@ -319,6 +326,13 @@ manage Xen virtual machines. %patch 45 -p1 %patch 46 -p1 %patch 49 -p1 +%patch 50 -p1 +%patch 51 -p1 +%patch 52 -p1 +%patch 53 -p1 +%patch 54 -p1 +%patch 55 -p1 +%patch 56 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -937,6 +951,9 @@ fi %endif %changelog +* Mon May 12 2025 Michael Young - 4.19.2-4 +- x86: Indirect Target Selection [XSA-469, CVE-2024-28956] + * Mon Apr 07 2025 Michael Young - 4.19.2-2 - update to xen-4.19.2 remove patches now included or superceded upstream diff --git a/xsa469-4.19-01.patch b/xsa469-4.19-01.patch new file mode 100644 index 0000000..cbbb14f --- /dev/null +++ b/xsa469-4.19-01.patch @@ -0,0 +1,83 @@ +From: Andrew Cooper +Subject: x86/alternative: Support replacements when a feature is not present + +Use the top bit of a->cpuid to express inverted polarity. This requires +stripping the top bit back out when performing the sanity checks. + +Despite only being used once, create a replace boolean to express the decision +more clearly in _apply_alternatives(). + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/alternative.c b/xen/arch/x86/alternative.c +index 1ba35cb9ede9..88c90044c20d 100644 +--- a/xen/arch/x86/alternative.c ++++ b/xen/arch/x86/alternative.c +@@ -197,6 +197,8 @@ static int init_or_livepatch _apply_alternatives(struct alt_instr *start, + uint8_t *repl = ALT_REPL_PTR(a); + uint8_t buf[MAX_PATCH_LEN]; + unsigned int total_len = a->orig_len + a->pad_len; ++ unsigned int feat = a->cpuid & ~ALT_FLAG_NOT; ++ bool inv = a->cpuid & ALT_FLAG_NOT, replace; + + if ( a->repl_len > total_len ) + { +@@ -214,11 +216,11 @@ static int init_or_livepatch _apply_alternatives(struct alt_instr *start, + return -ENOSPC; + } + +- if ( a->cpuid >= NCAPINTS * 32 ) ++ if ( feat >= NCAPINTS * 32 ) + { + printk(XENLOG_ERR + "Alt for %ps, feature %#x outside of featureset range %#x\n", +- ALT_ORIG_PTR(a), a->cpuid, NCAPINTS * 32); ++ ALT_ORIG_PTR(a), feat, NCAPINTS * 32); + return -ERANGE; + } + +@@ -243,8 +245,14 @@ static int init_or_livepatch _apply_alternatives(struct alt_instr *start, + continue; + } + ++ /* ++ * Should a replacement be performed? Most replacements have positive ++ * polarity, but we support negative polarity too. ++ */ ++ replace = boot_cpu_has(feat) ^ inv; ++ + /* If there is no replacement to make, see about optimising the nops. */ +- if ( !boot_cpu_has(a->cpuid) ) ++ if ( !replace ) + { + /* Origin site site already touched? Don't nop anything. */ + if ( base->priv ) +diff --git a/xen/arch/x86/include/asm/alternative.h b/xen/arch/x86/include/asm/alternative.h +index 69555d781ef9..89b7bdcb82e5 100644 +--- a/xen/arch/x86/include/asm/alternative.h ++++ b/xen/arch/x86/include/asm/alternative.h +@@ -1,6 +1,13 @@ + #ifndef __X86_ALTERNATIVE_H__ + #define __X86_ALTERNATIVE_H__ + ++/* ++ * Common to both C and ASM. Express a replacement when a feature is not ++ * available. ++ */ ++#define ALT_FLAG_NOT (1 << 15) ++#define ALT_NOT(x) (ALT_FLAG_NOT | (x)) ++ + #ifdef __ASSEMBLY__ + #include + #else +@@ -11,7 +18,7 @@ + struct __packed alt_instr { + int32_t orig_offset; /* original instruction */ + int32_t repl_offset; /* offset to replacement instruction */ +- uint16_t cpuid; /* cpuid bit set for replacement */ ++ uint16_t cpuid; /* cpuid bit set for replacement (top bit is polarity) */ + uint8_t orig_len; /* length of original instruction */ + uint8_t repl_len; /* length of new instruction */ + uint8_t pad_len; /* length of build-time padding */ + diff --git a/xsa469-4.19-02.patch b/xsa469-4.19-02.patch new file mode 100644 index 0000000..5fd285f --- /dev/null +++ b/xsa469-4.19-02.patch @@ -0,0 +1,314 @@ +From: Andrew Cooper +Subject: x86/guest: Remove use of the Xen hypercall_page + +In order to protect against ITS, Xen needs to start using return thunks. +Therefore the advice in XSA-466 becomes relevant, and the hypercall_page needs +to be removed. + +Implement early_hypercall(), with infrastructure to figure out the correct +instruction on first use. Use ALTERNATIVE()s to result in inline hypercalls, +including the ALT_NOT() form so we only need a single synthetic feature bit. + +No overall change. + +This is part of XSA-469 / CVE-2024-28956 + +Signed-off-by: Andrew Cooper +Reviewed-by: Roger Pau Monné + +diff --git a/xen/arch/x86/guest/xen/Makefile b/xen/arch/x86/guest/xen/Makefile +index 26fb4b1007c0..8b3250aa8886 100644 +--- a/xen/arch/x86/guest/xen/Makefile ++++ b/xen/arch/x86/guest/xen/Makefile +@@ -1,4 +1,4 @@ +-obj-y += hypercall_page.o ++obj-bin-y += hypercall.init.o + obj-y += xen.o + + obj-bin-$(CONFIG_PVH_GUEST) += pvh-boot.init.o +diff --git a/xen/arch/x86/guest/xen/hypercall.S b/xen/arch/x86/guest/xen/hypercall.S +new file mode 100644 +index 000000000000..05e429794cc4 +--- /dev/null ++++ b/xen/arch/x86/guest/xen/hypercall.S +@@ -0,0 +1,50 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++ ++#include ++ ++ .section .init.text, "ax", @progbits ++ ++ /* ++ * Used during early boot, before alternatives have run and inlined ++ * the appropriate instruction. Called using the hypercall ABI. ++ */ ++FUNC(early_hypercall) ++ cmpb $0, early_hypercall_insn(%rip) ++ jl .L_setup ++ je 1f ++ ++ vmmcall ++ ret ++ ++1: vmcall ++ ret ++ ++.L_setup: ++ /* ++ * When setting up the first time around, all registers need ++ * preserving. Save the non-callee-saved ones. ++ */ ++ push %r11 ++ push %r10 ++ push %r9 ++ push %r8 ++ push %rdi ++ push %rsi ++ push %rdx ++ push %rcx ++ push %rax ++ ++ call early_hypercall_setup ++ ++ pop %rax ++ pop %rcx ++ pop %rdx ++ pop %rsi ++ pop %rdi ++ pop %r8 ++ pop %r9 ++ pop %r10 ++ pop %r11 ++ ++ jmp early_hypercall ++END(early_hypercall) +diff --git a/xen/arch/x86/guest/xen/hypercall_page.S b/xen/arch/x86/guest/xen/hypercall_page.S +deleted file mode 100644 +index 7ab55fc1f6e6..000000000000 +--- a/xen/arch/x86/guest/xen/hypercall_page.S ++++ /dev/null +@@ -1,76 +0,0 @@ +-#include +-#include +-#include +- +- .section ".text.page_aligned", "ax", @progbits +- +-DATA(hypercall_page, PAGE_SIZE) +- /* Poisoned with `ret` for safety before hypercalls are set up. */ +- .fill PAGE_SIZE, 1, 0xc3 +-END(hypercall_page) +- +-/* +- * Identify a specific hypercall in the hypercall page +- * @param name Hypercall name. +- */ +-#define DECLARE_HYPERCALL(name) \ +- .globl HYPERCALL_ ## name; \ +- .type HYPERCALL_ ## name, STT_FUNC; \ +- .size HYPERCALL_ ## name, 32; \ +- .set HYPERCALL_ ## name, hypercall_page + __HYPERVISOR_ ## name * 32 +- +-DECLARE_HYPERCALL(set_trap_table) +-DECLARE_HYPERCALL(mmu_update) +-DECLARE_HYPERCALL(set_gdt) +-DECLARE_HYPERCALL(stack_switch) +-DECLARE_HYPERCALL(set_callbacks) +-DECLARE_HYPERCALL(fpu_taskswitch) +-DECLARE_HYPERCALL(sched_op_compat) +-DECLARE_HYPERCALL(platform_op) +-DECLARE_HYPERCALL(set_debugreg) +-DECLARE_HYPERCALL(get_debugreg) +-DECLARE_HYPERCALL(update_descriptor) +-DECLARE_HYPERCALL(memory_op) +-DECLARE_HYPERCALL(multicall) +-DECLARE_HYPERCALL(update_va_mapping) +-DECLARE_HYPERCALL(set_timer_op) +-DECLARE_HYPERCALL(event_channel_op_compat) +-DECLARE_HYPERCALL(xen_version) +-DECLARE_HYPERCALL(console_io) +-DECLARE_HYPERCALL(physdev_op_compat) +-DECLARE_HYPERCALL(grant_table_op) +-DECLARE_HYPERCALL(vm_assist) +-DECLARE_HYPERCALL(update_va_mapping_otherdomain) +-DECLARE_HYPERCALL(iret) +-DECLARE_HYPERCALL(vcpu_op) +-DECLARE_HYPERCALL(set_segment_base) +-DECLARE_HYPERCALL(mmuext_op) +-DECLARE_HYPERCALL(xsm_op) +-DECLARE_HYPERCALL(nmi_op) +-DECLARE_HYPERCALL(sched_op) +-DECLARE_HYPERCALL(callback_op) +-DECLARE_HYPERCALL(xenoprof_op) +-DECLARE_HYPERCALL(event_channel_op) +-DECLARE_HYPERCALL(physdev_op) +-DECLARE_HYPERCALL(hvm_op) +-DECLARE_HYPERCALL(sysctl) +-DECLARE_HYPERCALL(domctl) +-DECLARE_HYPERCALL(kexec_op) +-DECLARE_HYPERCALL(argo_op) +-DECLARE_HYPERCALL(xenpmu_op) +- +-DECLARE_HYPERCALL(arch_0) +-DECLARE_HYPERCALL(arch_1) +-DECLARE_HYPERCALL(arch_2) +-DECLARE_HYPERCALL(arch_3) +-DECLARE_HYPERCALL(arch_4) +-DECLARE_HYPERCALL(arch_5) +-DECLARE_HYPERCALL(arch_6) +-DECLARE_HYPERCALL(arch_7) +- +-/* +- * Local variables: +- * tab-width: 8 +- * indent-tabs-mode: nil +- * End: +- */ +diff --git a/xen/arch/x86/guest/xen/xen.c b/xen/arch/x86/guest/xen/xen.c +index 7484b3f73ad3..2c30db05dfa7 100644 +--- a/xen/arch/x86/guest/xen/xen.c ++++ b/xen/arch/x86/guest/xen/xen.c +@@ -26,7 +26,6 @@ + bool __read_mostly xen_guest; + + uint32_t __read_mostly xen_cpuid_base; +-extern char hypercall_page[]; + static struct rangeset *mem; + + DEFINE_PER_CPU(unsigned int, vcpu_id); +@@ -35,6 +34,50 @@ static struct vcpu_info *vcpu_info; + static unsigned long vcpu_info_mapped[BITS_TO_LONGS(NR_CPUS)]; + DEFINE_PER_CPU(struct vcpu_info *, vcpu_info); + ++/* ++ * Which instruction to use for early hypercalls: ++ * < 0 setup ++ * 0 vmcall ++ * > 0 vmmcall ++ */ ++int8_t __initdata early_hypercall_insn = -1; ++ ++/* ++ * Called once during the first hypercall to figure out which instruction to ++ * use. Error handling options are limited. ++ */ ++void asmlinkage __init early_hypercall_setup(void) ++{ ++ BUG_ON(early_hypercall_insn != -1); ++ ++ if ( !boot_cpu_data.x86_vendor ) ++ { ++ unsigned int eax, ebx, ecx, edx; ++ ++ cpuid(0, &eax, &ebx, &ecx, &edx); ++ ++ boot_cpu_data.x86_vendor = x86_cpuid_lookup_vendor(ebx, ecx, edx); ++ } ++ ++ switch ( boot_cpu_data.x86_vendor ) ++ { ++ case X86_VENDOR_INTEL: ++ case X86_VENDOR_CENTAUR: ++ case X86_VENDOR_SHANGHAI: ++ early_hypercall_insn = 0; ++ setup_force_cpu_cap(X86_FEATURE_USE_VMCALL); ++ break; ++ ++ case X86_VENDOR_AMD: ++ case X86_VENDOR_HYGON: ++ early_hypercall_insn = 1; ++ break; ++ ++ default: ++ BUG(); ++ } ++} ++ + static void __init find_xen_leaves(void) + { + uint32_t eax, ebx, ecx, edx, base; +@@ -337,9 +380,6 @@ const struct hypervisor_ops *__init xg_probe(void) + if ( !xen_cpuid_base ) + return NULL; + +- /* Fill the hypercall page. */ +- wrmsrl(cpuid_ebx(xen_cpuid_base + 2), __pa(hypercall_page)); +- + xen_guest = true; + + return &ops; +diff --git a/xen/arch/x86/include/asm/cpufeatures.h b/xen/arch/x86/include/asm/cpufeatures.h +index ba3df174b76e..9e3ed21c026d 100644 +--- a/xen/arch/x86/include/asm/cpufeatures.h ++++ b/xen/arch/x86/include/asm/cpufeatures.h +@@ -42,6 +42,7 @@ XEN_CPUFEATURE(XEN_SHSTK, X86_SYNTH(26)) /* Xen uses CET Shadow Stacks * + XEN_CPUFEATURE(XEN_IBT, X86_SYNTH(27)) /* Xen uses CET Indirect Branch Tracking */ + XEN_CPUFEATURE(IBPB_ENTRY_PV, X86_SYNTH(28)) /* MSR_PRED_CMD used by Xen for PV */ + XEN_CPUFEATURE(IBPB_ENTRY_HVM, X86_SYNTH(29)) /* MSR_PRED_CMD used by Xen for HVM */ ++XEN_CPUFEATURE(USE_VMCALL, X86_SYNTH(30)) /* Use VMCALL instead of VMMCALL */ + + /* Bug words follow the synthetic words. */ + #define X86_NR_BUG 1 +diff --git a/xen/arch/x86/include/asm/guest/xen-hcall.h b/xen/arch/x86/include/asm/guest/xen-hcall.h +index 665b472d05ac..96004dec9909 100644 +--- a/xen/arch/x86/include/asm/guest/xen-hcall.h ++++ b/xen/arch/x86/include/asm/guest/xen-hcall.h +@@ -30,9 +30,11 @@ + ({ \ + long res, tmp__; \ + asm volatile ( \ +- "call hypercall_page + %c[offset]" \ ++ ALTERNATIVE_2("call early_hypercall", \ ++ "vmmcall", ALT_NOT(X86_FEATURE_USE_VMCALL), \ ++ "vmcall", X86_FEATURE_USE_VMCALL) \ + : "=a" (res), "=D" (tmp__) ASM_CALL_CONSTRAINT \ +- : [offset] "i" (hcall * 32), \ ++ : "0" (hcall), \ + "1" ((long)(a1)) \ + : "memory" ); \ + (type)res; \ +@@ -42,10 +44,12 @@ + ({ \ + long res, tmp__; \ + asm volatile ( \ +- "call hypercall_page + %c[offset]" \ ++ ALTERNATIVE_2("call early_hypercall", \ ++ "vmmcall", ALT_NOT(X86_FEATURE_USE_VMCALL), \ ++ "vmcall", X86_FEATURE_USE_VMCALL) \ + : "=a" (res), "=D" (tmp__), "=S" (tmp__) \ + ASM_CALL_CONSTRAINT \ +- : [offset] "i" (hcall * 32), \ ++ : "0" (hcall), \ + "1" ((long)(a1)), "2" ((long)(a2)) \ + : "memory" ); \ + (type)res; \ +@@ -55,10 +59,12 @@ + ({ \ + long res, tmp__; \ + asm volatile ( \ +- "call hypercall_page + %c[offset]" \ ++ ALTERNATIVE_2("call early_hypercall", \ ++ "vmmcall", ALT_NOT(X86_FEATURE_USE_VMCALL), \ ++ "vmcall", X86_FEATURE_USE_VMCALL) \ + : "=a" (res), "=D" (tmp__), "=S" (tmp__), "=d" (tmp__) \ + ASM_CALL_CONSTRAINT \ +- : [offset] "i" (hcall * 32), \ ++ : "0" (hcall), \ + "1" ((long)(a1)), "2" ((long)(a2)), "3" ((long)(a3)) \ + : "memory" ); \ + (type)res; \ +@@ -69,10 +75,12 @@ + long res, tmp__; \ + register long _a4 asm ("r10") = ((long)(a4)); \ + asm volatile ( \ +- "call hypercall_page + %c[offset]" \ ++ ALTERNATIVE_2("call early_hypercall", \ ++ "vmmcall", ALT_NOT(X86_FEATURE_USE_VMCALL), \ ++ "vmcall", X86_FEATURE_USE_VMCALL) \ + : "=a" (res), "=D" (tmp__), "=S" (tmp__), "=d" (tmp__), \ + "=&r" (tmp__) ASM_CALL_CONSTRAINT \ +- : [offset] "i" (hcall * 32), \ ++ : "0" (hcall), \ + "1" ((long)(a1)), "2" ((long)(a2)), "3" ((long)(a3)), \ + "4" (_a4) \ + : "memory" ); \ diff --git a/xsa469-4.19-03.patch b/xsa469-4.19-03.patch new file mode 100644 index 0000000..2e944f9 --- /dev/null +++ b/xsa469-4.19-03.patch @@ -0,0 +1,46 @@ +From: Jan Beulich +Subject: x86/thunk: (Mis)align __x86_indirect_thunk_* to mitigate ITS + +The Indirect Target Selection speculative vulnerability means that indirect +branches (including RETs) are unsafe when in the first half of a cacheline. + +Arrange for __x86_indirect_thunk_* to always be in the second half. + +This is part of XSA-469 / CVE-2024-28956 + +Signed-off-by: Jan Beulich +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/indirect-thunk.S b/xen/arch/x86/indirect-thunk.S +index fd5493c22b16..c4b978d67b8e 100644 +--- a/xen/arch/x86/indirect-thunk.S ++++ b/xen/arch/x86/indirect-thunk.S +@@ -11,6 +11,10 @@ + + #include + ++/* Alignment is dealt with explicitly here; override the respective macro. */ ++#undef SYM_ALIGN ++#define SYM_ALIGN(align...) ++ + .macro IND_THUNK_RETPOLINE reg:req + call 1f + int3 +@@ -35,6 +39,16 @@ + .macro GEN_INDIRECT_THUNK reg:req + .section .text.__x86_indirect_thunk_\reg, "ax", @progbits + ++ /* ++ * The Indirect Target Selection speculative vulnerability means that ++ * indirect branches (including RETs) are unsafe when in the first ++ * half of a cacheline. Arrange for them to be in the second half. ++ * ++ * Align to 64, then skip 32. ++ */ ++ .balign 64 ++ .fill 32, 1, 0xcc ++ + FUNC(__x86_indirect_thunk_\reg) + ALTERNATIVE_2 __stringify(IND_THUNK_RETPOLINE \reg), \ + __stringify(IND_THUNK_LFENCE \reg), X86_FEATURE_IND_THUNK_LFENCE, \ diff --git a/xsa469-4.19-04.patch b/xsa469-4.19-04.patch new file mode 100644 index 0000000..81d77a9 --- /dev/null +++ b/xsa469-4.19-04.patch @@ -0,0 +1,64 @@ +From: Andrew Cooper +Subject: x86/thunk: (Mis)align the RETs in clear_bhb_loops() to mitigate ITS + +The Indirect Target Selection speculative vulnerability means that indirect +branches (including RETs) are unsafe when in the first half of a cacheline. + +clear_bhb_loops() has a precise layout of branches. The alignment for +performance cause the RETs to always be in an unsafe position, and converting +those to return thunks changes the branching pattern. While such a conversion +is believed to be safe, clear_bhb_loops() is also a performance-relevant +fastpath, so (mis)align the RETs to be in a safe position. + +No functional change. + +This is part of XSA-469 / CVE-2024-28956 + +Signed-off-by: Andrew Cooper +Reviewed-by: Roger Pau Monné + +diff --git a/xen/arch/x86/bhb-thunk.S b/xen/arch/x86/bhb-thunk.S +index 678c00c5d06f..52625f4e2c17 100644 +--- a/xen/arch/x86/bhb-thunk.S ++++ b/xen/arch/x86/bhb-thunk.S +@@ -50,7 +50,12 @@ END(clear_bhb_tsx) + * ret + * + * The CALL/RETs are necessary to prevent the Loop Stream Detector from +- * interfering. The alignment is for performance and not safety. ++ * interfering. ++ * ++ * The .balign's are for performance, but they cause the RETs to be in unsafe ++ * positions with respect to Indirect Target Selection. The .skips are to ++ * move the RETs into ITS-safe positions, rather than using the slowpath ++ * through __x86_return_thunk. + * + * The "short" sequence (5 and 5) is for CPUs prior to Alder Lake / Sapphire + * Rapids (i.e. Cores prior to Golden Cove and/or Gracemont). +@@ -66,12 +71,14 @@ FUNC(clear_bhb_loops) + jmp 5f + int3 + +- .align 64 ++ .balign 64 ++ .skip 32 - (.Lr1 - 1f), 0xcc + 1: call 2f +- ret ++.Lr1: ret + int3 + +- .align 64 ++ .balign 64 ++ .skip 32 - 18 /* (.Lr2 - 2f) but Clang IAS doesn't like this */, 0xcc + 2: ALTERNATIVE "mov $5, %eax", "mov $7, %eax", X86_SPEC_BHB_LOOPS_LONG + + 3: jmp 4f +@@ -83,7 +90,7 @@ FUNC(clear_bhb_loops) + sub $1, %ecx + jnz 1b + +- ret ++.Lr2: ret + 5: + /* + * The Intel sequence has an LFENCE here. The purpose is to ensure diff --git a/xsa469-4.19-05.patch b/xsa469-4.19-05.patch new file mode 100644 index 0000000..51a135d --- /dev/null +++ b/xsa469-4.19-05.patch @@ -0,0 +1,506 @@ +From: Andrew Cooper +Subject: x86/stubs: Introduce place_ret() to abstract away raw 0xc3's + +The Indirect Target Selection speculative vulnerability means that indirect +branches (including RETs) are unsafe when in the first half of a cacheline. +This means it's not safe for logic using the stubs to write raw 0xc3's. + +Introduce place_ret() which, for now, writes a raw 0xc3 but will contain +additional logic when return thunks are in use. + +stub_selftest() doesn't strictly need to be converted as they only run on +boot, but doing so gets us a partial test of place_ret() too. + +No functional change. + +This is part of XSA-469 / CVE-2024-28956 + +Signed-off-by: Andrew Cooper +Reviewed-by: Roger Pau Monné + +diff --git a/tools/tests/x86_emulator/x86-emulate.h b/tools/tests/x86_emulator/x86-emulate.h +index 8f8accfe3e70..946aaa9d660b 100644 +--- a/tools/tests/x86_emulator/x86-emulate.h ++++ b/tools/tests/x86_emulator/x86-emulate.h +@@ -68,6 +68,12 @@ + + #define is_canonical_address(x) (((int64_t)(x) >> 47) == ((int64_t)(x) >> 63)) + ++static inline void *place_ret(void *ptr) ++{ ++ *(uint8_t *)ptr = 0xc3; ++ return ptr + 1; ++} ++ + extern uint32_t mxcsr_mask; + extern struct cpu_policy cp; + +diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile +index c1e64278ce85..a7e5a82689de 100644 +--- a/xen/arch/x86/Makefile ++++ b/xen/arch/x86/Makefile +@@ -11,9 +11,7 @@ obj-$(CONFIG_PV) += pv/ + obj-y += x86_64/ + obj-y += x86_emulate/ + +-alternative-y := alternative.init.o +-alternative-$(CONFIG_LIVEPATCH) := +-obj-bin-y += $(alternative-y) ++obj-y += alternative.o + obj-y += apic.o + obj-y += bhb-thunk.o + obj-y += bitops.o +@@ -41,7 +39,7 @@ obj-y += hypercall.o + obj-y += i387.o + obj-y += i8259.o + obj-y += io_apic.o +-obj-$(CONFIG_LIVEPATCH) += alternative.o livepatch.o ++obj-$(CONFIG_LIVEPATCH) += livepatch.o + obj-y += msi.o + obj-y += msr.o + obj-$(CONFIG_INDIRECT_THUNK) += indirect-thunk.o +diff --git a/xen/arch/x86/alternative.c b/xen/arch/x86/alternative.c +index 88c90044c20d..ec451d962c10 100644 +--- a/xen/arch/x86/alternative.c ++++ b/xen/arch/x86/alternative.c +@@ -137,6 +137,20 @@ void init_or_livepatch add_nops(void *insns, unsigned int len) + } + } + ++/* ++ * Place a return at @ptr. @ptr must be in the writable alias of a stub. ++ * ++ * Returns the next position to write into the stub. ++ */ ++void *place_ret(void *ptr) ++{ ++ uint8_t *p = ptr; ++ ++ *p++ = 0xc3; ++ ++ return p; ++} ++ + /* + * text_poke - Update instructions on a live kernel or non-executed code. + * @addr: address to modify +diff --git a/xen/arch/x86/extable.c b/xen/arch/x86/extable.c +index 705cf9eb94ca..1572efa69a00 100644 +--- a/xen/arch/x86/extable.c ++++ b/xen/arch/x86/extable.c +@@ -151,20 +151,20 @@ search_exception_table(const struct cpu_user_regs *regs, unsigned long *stub_ra) + int __init cf_check stub_selftest(void) + { + static const struct { +- uint8_t opc[8]; ++ uint8_t opc[7]; + uint64_t rax; + union stub_exception_token res; + } tests[] __initconst = { + #define endbr64 0xf3, 0x0f, 0x1e, 0xfa +- { .opc = { endbr64, 0x0f, 0xb9, 0xc3, 0xc3 }, /* ud1 */ ++ { .opc = { endbr64, 0x0f, 0xb9, 0x90 }, /* ud1 */ + .res.fields.trapnr = X86_EXC_UD }, +- { .opc = { endbr64, 0x90, 0x02, 0x00, 0xc3 }, /* nop; add (%rax),%al */ ++ { .opc = { endbr64, 0x90, 0x02, 0x00 }, /* nop; add (%rax),%al */ + .rax = 0x0123456789abcdef, + .res.fields.trapnr = X86_EXC_GP }, +- { .opc = { endbr64, 0x02, 0x04, 0x04, 0xc3 }, /* add (%rsp,%rax),%al */ ++ { .opc = { endbr64, 0x02, 0x04, 0x04 }, /* add (%rsp,%rax),%al */ + .rax = 0xfedcba9876543210UL, + .res.fields.trapnr = X86_EXC_SS }, +- { .opc = { endbr64, 0xcc, 0xc3, 0xc3, 0xc3 }, /* int3 */ ++ { .opc = { endbr64, 0xcc, 0x90, 0x90 }, /* int3 */ + .res.fields.trapnr = X86_EXC_BP }, + #undef endbr64 + }; +@@ -183,6 +183,7 @@ int __init cf_check stub_selftest(void) + + memset(ptr, 0xcc, STUB_BUF_SIZE / 2); + memcpy(ptr, tests[i].opc, ARRAY_SIZE(tests[i].opc)); ++ place_ret(ptr + ARRAY_SIZE(tests[i].opc)); + unmap_domain_page(ptr); + + asm volatile ( "INDIRECT_CALL %[stb]\n" +diff --git a/xen/arch/x86/include/asm/alternative.h b/xen/arch/x86/include/asm/alternative.h +index 89b7bdcb82e5..841a63ebf1b6 100644 +--- a/xen/arch/x86/include/asm/alternative.h ++++ b/xen/arch/x86/include/asm/alternative.h +@@ -30,6 +30,8 @@ struct __packed alt_instr { + #define ALT_REPL_PTR(a) __ALT_PTR(a, repl_offset) + + extern void add_nops(void *insns, unsigned int len); ++void *place_ret(void *ptr); ++ + /* Similar to alternative_instructions except it can be run with IRQs enabled. */ + extern int apply_alternatives(struct alt_instr *start, struct alt_instr *end); + extern void alternative_instructions(void); +diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c +index 70150c272276..ff5d1c9f8634 100644 +--- a/xen/arch/x86/pv/emul-priv-op.c ++++ b/xen/arch/x86/pv/emul-priv-op.c +@@ -76,7 +76,6 @@ static io_emul_stub_t *io_emul_stub_setup(struct priv_op_ctxt *ctxt, u8 opcode, + 0x41, 0x5c, /* pop %r12 */ + 0x5d, /* pop %rbp */ + 0x5b, /* pop %rbx */ +- 0xc3, /* ret */ + }; + + const struct stubs *this_stubs = &this_cpu(stubs); +@@ -126,11 +125,13 @@ static io_emul_stub_t *io_emul_stub_setup(struct priv_op_ctxt *ctxt, u8 opcode, + + APPEND_CALL(save_guest_gprs); + APPEND_BUFF(epilogue); ++ p = place_ret(p); + + /* Build-time best effort attempt to catch problems. */ + BUILD_BUG_ON(STUB_BUF_SIZE / 2 < + (sizeof(prologue) + sizeof(epilogue) + 10 /* 2x call */ + +- MAX(3 /* default stub */, IOEMUL_QUIRK_STUB_BYTES))); ++ MAX(3 /* default stub */, IOEMUL_QUIRK_STUB_BYTES) + ++ 1 /* ret */)); + /* Runtime confirmation that we haven't clobbered an adjacent stub. */ + BUG_ON(STUB_BUF_SIZE / 2 < (p - ctxt->io_emul_stub)); + +diff --git a/xen/arch/x86/x86_emulate/fpu.c b/xen/arch/x86/x86_emulate/fpu.c +index 480d87965705..03612d00a2ce 100644 +--- a/xen/arch/x86/x86_emulate/fpu.c ++++ b/xen/arch/x86/x86_emulate/fpu.c +@@ -32,36 +32,42 @@ static inline bool fpu_check_write(void) + + #define emulate_fpu_insn_memdst(opc, ext, arg) \ + do { \ ++ void *_p = get_stub(stub); \ + /* ModRM: mod=0, reg=ext, rm=0, i.e. a (%rax) operand */ \ + *insn_bytes = 2; \ +- memcpy(get_stub(stub), \ +- ((uint8_t[]){ opc, ((ext) & 7) << 3, 0xc3 }), 3); \ ++ memcpy(_p, ((uint8_t[]){ opc, ((ext) & 7) << 3 }), 2); _p += 2; \ ++ place_ret(_p); \ + invoke_stub("", "", "+m" (arg) : "a" (&(arg))); \ + put_stub(stub); \ + } while (0) + + #define emulate_fpu_insn_memsrc(opc, ext, arg) \ + do { \ ++ void *_p = get_stub(stub); \ + /* ModRM: mod=0, reg=ext, rm=0, i.e. a (%rax) operand */ \ +- memcpy(get_stub(stub), \ +- ((uint8_t[]){ opc, ((ext) & 7) << 3, 0xc3 }), 3); \ ++ memcpy(_p, ((uint8_t[]){ opc, ((ext) & 7) << 3 }), 2); _p += 2; \ ++ place_ret(_p); \ + invoke_stub("", "", "=m" (dummy) : "m" (arg), "a" (&(arg))); \ + put_stub(stub); \ + } while (0) + + #define emulate_fpu_insn_stub(bytes...) \ + do { \ ++ void *_p = get_stub(stub); \ + unsigned int nr_ = sizeof((uint8_t[]){ bytes }); \ +- memcpy(get_stub(stub), ((uint8_t[]){ bytes, 0xc3 }), nr_ + 1); \ ++ memcpy(_p, ((uint8_t[]){ bytes }), nr_); _p += nr_; \ ++ place_ret(_p); \ + invoke_stub("", "", "=m" (dummy) : "i" (0)); \ + put_stub(stub); \ + } while (0) + + #define emulate_fpu_insn_stub_eflags(bytes...) \ + do { \ ++ void *_p = get_stub(stub); \ + unsigned int nr_ = sizeof((uint8_t[]){ bytes }); \ + unsigned long tmp_; \ +- memcpy(get_stub(stub), ((uint8_t[]){ bytes, 0xc3 }), nr_ + 1); \ ++ memcpy(_p, ((uint8_t[]){ bytes }), nr_); _p += nr_; \ ++ place_ret(_p); \ + invoke_stub(_PRE_EFLAGS("[eflags]", "[mask]", "[tmp]"), \ + _POST_EFLAGS("[eflags]", "[mask]", "[tmp]"), \ + [eflags] "+g" (regs->eflags), [tmp] "=&r" (tmp_) \ +diff --git a/xen/arch/x86/x86_emulate/x86_emulate.c b/xen/arch/x86/x86_emulate/x86_emulate.c +index b1d192cbbf1e..f40709682484 100644 +--- a/xen/arch/x86/x86_emulate/x86_emulate.c ++++ b/xen/arch/x86/x86_emulate/x86_emulate.c +@@ -1396,7 +1396,7 @@ x86_emulate( + stb[3] = 0x91; + stb[4] = evex.opmsk << 3; + insn_bytes = 5; +- stb[5] = 0xc3; ++ place_ret(&stb[5]); + + invoke_stub("", "", "+m" (op_mask) : "a" (&op_mask)); + +@@ -3627,7 +3627,7 @@ x86_emulate( + } + opc[1] = (modrm & 0x38) | 0xc0; + insn_bytes = EVEX_PFX_BYTES + 2; +- opc[2] = 0xc3; ++ place_ret(&opc[2]); + + copy_EVEX(opc, evex); + invoke_stub("", "", "=g" (dummy) : "a" (src.val)); +@@ -3694,7 +3694,7 @@ x86_emulate( + insn_bytes = PFX_BYTES + 2; + copy_REX_VEX(opc, rex_prefix, vex); + } +- opc[2] = 0xc3; ++ place_ret(&opc[2]); + + ea.reg = decode_gpr(&_regs, modrm_reg); + invoke_stub("", "", "=a" (*ea.reg) : "c" (mmvalp), "m" (*mmvalp)); +@@ -3768,7 +3768,7 @@ x86_emulate( + insn_bytes = PFX_BYTES + 2; + copy_REX_VEX(opc, rex_prefix, vex); + } +- opc[2] = 0xc3; ++ place_ret(&opc[2]); + + _regs.eflags &= ~EFLAGS_MASK; + invoke_stub("", +@@ -4004,7 +4004,7 @@ x86_emulate( + opc[1] = modrm & 0xc7; + insn_bytes = PFX_BYTES + 2; + simd_0f_to_gpr: +- opc[insn_bytes - PFX_BYTES] = 0xc3; ++ place_ret(&opc[insn_bytes - PFX_BYTES]); + + generate_exception_if(ea.type != OP_REG, X86_EXC_UD); + +@@ -4401,7 +4401,7 @@ x86_emulate( + vex.w = 0; + opc[1] = modrm & 0x38; + insn_bytes = PFX_BYTES + 2; +- opc[2] = 0xc3; ++ place_ret(&opc[2]); + + copy_REX_VEX(opc, rex_prefix, vex); + invoke_stub("", "", "+m" (src.val) : "a" (&src.val)); +@@ -4438,7 +4438,7 @@ x86_emulate( + evex.w = 0; + opc[1] = modrm & 0x38; + insn_bytes = EVEX_PFX_BYTES + 2; +- opc[2] = 0xc3; ++ place_ret(&opc[2]); + + copy_EVEX(opc, evex); + invoke_stub("", "", "+m" (src.val) : "a" (&src.val)); +@@ -4633,7 +4633,7 @@ x86_emulate( + #endif /* X86EMUL_NO_SIMD */ + + simd_0f_reg_only: +- opc[insn_bytes - PFX_BYTES] = 0xc3; ++ place_ret(&opc[insn_bytes - PFX_BYTES]); + + copy_REX_VEX(opc, rex_prefix, vex); + invoke_stub("", "", [dummy_out] "=g" (dummy) : [dummy_in] "i" (0) ); +@@ -4967,7 +4967,7 @@ x86_emulate( + if ( !mode_64bit() ) + vex.w = 0; + opc[1] = modrm & 0xf8; +- opc[2] = 0xc3; ++ place_ret(&opc[2]); + + copy_VEX(opc, vex); + ea.reg = decode_gpr(&_regs, modrm_rm); +@@ -5010,7 +5010,7 @@ x86_emulate( + if ( !mode_64bit() ) + vex.w = 0; + opc[1] = modrm & 0xc7; +- opc[2] = 0xc3; ++ place_ret(&opc[2]); + + copy_VEX(opc, vex); + invoke_stub("", "", "=a" (dst.val) : [dummy] "i" (0)); +@@ -5040,7 +5040,7 @@ x86_emulate( + opc = init_prefixes(stub); + opc[0] = b; + opc[1] = modrm; +- opc[2] = 0xc3; ++ place_ret(&opc[2]); + + copy_VEX(opc, vex); + _regs.eflags &= ~EFLAGS_MASK; +@@ -5608,7 +5608,7 @@ x86_emulate( + if ( !mode_64bit() ) + vex.w = 0; + opc[1] = modrm & 0xc7; +- opc[2] = 0xc3; ++ place_ret(&opc[2]); + + copy_REX_VEX(opc, rex_prefix, vex); + invoke_stub("", "", "=a" (ea.val) : [dummy] "i" (0)); +@@ -5726,7 +5726,7 @@ x86_emulate( + opc[1] &= 0x38; + } + insn_bytes = PFX_BYTES + 2; +- opc[2] = 0xc3; ++ place_ret(&opc[2]); + if ( vex.opcx == vex_none ) + { + /* Cover for extra prefix byte. */ +@@ -6006,7 +6006,7 @@ x86_emulate( + pvex->b = !mode_64bit() || (vex.reg >> 3); + opc[1] = 0xc0 | (~vex.reg & 7); + pvex->reg = 0xf; +- opc[2] = 0xc3; ++ place_ret(&opc[2]); + + invoke_stub("", "", "=a" (ea.val) : [dummy] "i" (0)); + put_stub(stub); +@@ -6290,7 +6290,7 @@ x86_emulate( + evex.w = 0; + opc[1] = modrm & 0xf8; + insn_bytes = EVEX_PFX_BYTES + 2; +- opc[2] = 0xc3; ++ place_ret(&opc[2]); + + copy_EVEX(opc, evex); + invoke_stub("", "", "=g" (dummy) : "a" (src.val)); +@@ -6389,7 +6389,7 @@ x86_emulate( + pvex->b = 1; + opc[1] = (modrm_reg & 7) << 3; + pvex->reg = 0xf; +- opc[2] = 0xc3; ++ place_ret(&opc[2]); + + invoke_stub("", "", "=m" (*mmvalp) : "a" (mmvalp)); + +@@ -6459,7 +6459,7 @@ x86_emulate( + pvex->b = 1; + opc[1] = (modrm_reg & 7) << 3; + pvex->reg = 0xf; +- opc[2] = 0xc3; ++ place_ret(&opc[2]); + + invoke_stub("", "", "+m" (*mmvalp) : "a" (mmvalp)); + +@@ -6515,7 +6515,7 @@ x86_emulate( + pevex->b = 1; + opc[1] = (modrm_reg & 7) << 3; + pevex->RX = 1; +- opc[2] = 0xc3; ++ place_ret(&opc[2]); + + invoke_stub("", "", "=m" (*mmvalp) : "a" (mmvalp)); + +@@ -6580,7 +6580,7 @@ x86_emulate( + pevex->b = 1; + opc[1] = (modrm_reg & 7) << 3; + pevex->RX = 1; +- opc[2] = 0xc3; ++ place_ret(&opc[2]); + + invoke_stub("", "", "+m" (*mmvalp) : "a" (mmvalp)); + +@@ -6594,7 +6594,7 @@ x86_emulate( + opc[2] = 0x90; + /* Use (%rax) as source. */ + opc[3] = evex.opmsk << 3; +- opc[4] = 0xc3; ++ place_ret(&opc[4]); + + invoke_stub("", "", "+m" (op_mask) : "a" (&op_mask)); + put_stub(stub); +@@ -6688,7 +6688,7 @@ x86_emulate( + pevex->b = 1; + opc[1] = (modrm_reg & 7) << 3; + pevex->RX = 1; +- opc[2] = 0xc3; ++ place_ret(&opc[2]); + + invoke_stub("", "", "=m" (*mmvalp) : "a" (mmvalp)); + +@@ -6766,7 +6766,7 @@ x86_emulate( + opc[2] = 0x90; + /* Use (%rax) as source. */ + opc[3] = evex.opmsk << 3; +- opc[4] = 0xc3; ++ place_ret(&opc[4]); + + invoke_stub("", "", "+m" (op_mask) : "a" (&op_mask)); + put_stub(stub); +@@ -6848,7 +6848,7 @@ x86_emulate( + pevex->r = !mode_64bit() || !(state->sib_index & 0x08); + pevex->R = !mode_64bit() || !(state->sib_index & 0x10); + pevex->RX = 1; +- opc[2] = 0xc3; ++ place_ret(&opc[2]); + + invoke_stub("", "", "=m" (index) : "a" (&index)); + put_stub(stub); +@@ -7058,7 +7058,7 @@ x86_emulate( + pvex->reg = 0xf; /* rAX */ + buf[3] = b; + buf[4] = 0x09; /* reg=rCX r/m=(%rCX) */ +- buf[5] = 0xc3; ++ place_ret(&buf[5]); + + src.reg = decode_vex_gpr(vex.reg, &_regs, ctxt); + emulate_stub([dst] "=&c" (dst.val), "[dst]" (&src.val), "a" (*src.reg)); +@@ -7094,7 +7094,7 @@ x86_emulate( + pvex->reg = 0xf; /* rAX */ + buf[3] = b; + buf[4] = (modrm & 0x38) | 0x01; /* r/m=(%rCX) */ +- buf[5] = 0xc3; ++ place_ret(&buf[5]); + + dst.reg = decode_vex_gpr(vex.reg, &_regs, ctxt); + emulate_stub("=&a" (dst.val), "c" (&src.val)); +@@ -7335,7 +7335,7 @@ x86_emulate( + evex.w = vex.w = 0; + opc[1] = modrm & 0x38; + opc[2] = imm1; +- opc[3] = 0xc3; ++ place_ret(&opc[3]); + if ( vex.opcx == vex_none ) + { + /* Cover for extra prefix byte. */ +@@ -7502,7 +7502,7 @@ x86_emulate( + insn_bytes = PFX_BYTES + 3; + copy_VEX(opc, vex); + } +- opc[3] = 0xc3; ++ place_ret(&opc[3]); + + /* Latch MXCSR - we may need to restore it below. */ + invoke_stub("stmxcsr %[mxcsr]", "", +@@ -7748,7 +7748,7 @@ x86_emulate( + } + opc[2] = imm1; + insn_bytes = PFX_BYTES + 3; +- opc[3] = 0xc3; ++ place_ret(&opc[3]); + if ( vex.opcx == vex_none ) + { + /* Cover for extra prefix byte. */ +@@ -8094,7 +8094,7 @@ x86_emulate( + pxop->reg = 0xf; /* rAX */ + buf[3] = b; + buf[4] = (modrm & 0x38) | 0x01; /* r/m=(%rCX) */ +- buf[5] = 0xc3; ++ place_ret(&buf[5]); + + dst.reg = decode_vex_gpr(vex.reg, &_regs, ctxt); + emulate_stub([dst] "=&a" (dst.val), "c" (&src.val)); +@@ -8203,7 +8203,7 @@ x86_emulate( + buf[3] = b; + buf[4] = 0x09; /* reg=rCX r/m=(%rCX) */ + *(uint32_t *)(buf + 5) = imm1; +- buf[9] = 0xc3; ++ place_ret(&buf[9]); + + emulate_stub([dst] "=&c" (dst.val), "[dst]" (&src.val)); + +@@ -8293,12 +8293,12 @@ x86_emulate( + BUG(); + if ( evex_encoded() ) + { +- opc[insn_bytes - EVEX_PFX_BYTES] = 0xc3; ++ place_ret(&opc[insn_bytes - EVEX_PFX_BYTES]); + copy_EVEX(opc, evex); + } + else + { +- opc[insn_bytes - PFX_BYTES] = 0xc3; ++ place_ret(&opc[insn_bytes - PFX_BYTES]); + copy_REX_VEX(opc, rex_prefix, vex); + } + diff --git a/xsa469-4.19-06.patch b/xsa469-4.19-06.patch new file mode 100644 index 0000000..724790e --- /dev/null +++ b/xsa469-4.19-06.patch @@ -0,0 +1,375 @@ +From: Jan Beulich +Subject: x86/thunk: Build Xen with Return Thunks + +The Indirect Target Selection speculative vulnerability means that indirect +branches (including RETs) are unsafe when in the first half of a cacheline. + +In order to mitigate this, build with return thunks and arrange for +__x86_return_thunk to be (mis)aligned in the same manner as +__x86_indirect_thunk_* so the RET instruction is placed in a safe location. + +place_ret() needs to conditionally emit JMP __x86_return_thunk instead of RET. + +This is part of XSA-469 / CVE-2024-28956 + +Signed-off-by: Jan Beulich +Signed-off-by: Andrew Cooper +Reviewed-by: Roger Pau Monné + +diff --git a/xen/arch/x86/Kconfig b/xen/arch/x86/Kconfig +index 7e03e4bc5546..4542ea8408c7 100644 +--- a/xen/arch/x86/Kconfig ++++ b/xen/arch/x86/Kconfig +@@ -37,9 +37,14 @@ config ARCH_DEFCONFIG + default "arch/x86/configs/x86_64_defconfig" + + config CC_HAS_INDIRECT_THUNK ++ # GCC >= 8 or Clang >= 6 + def_bool $(cc-option,-mindirect-branch-register) || \ + $(cc-option,-mretpoline-external-thunk) + ++config CC_HAS_RETURN_THUNK ++ # GCC >= 8 or Clang >= 15 ++ def_bool $(cc-option,-mfunction-return=thunk-extern) ++ + config HAS_AS_CET_SS + # binutils >= 2.29 or LLVM >= 6 + def_bool $(as-instr,wrssq %rax$(comma)0;setssbsy) +diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile +index a7e5a82689de..27806a81aca8 100644 +--- a/xen/arch/x86/Makefile ++++ b/xen/arch/x86/Makefile +@@ -43,6 +43,7 @@ obj-$(CONFIG_LIVEPATCH) += livepatch.o + obj-y += msi.o + obj-y += msr.o + obj-$(CONFIG_INDIRECT_THUNK) += indirect-thunk.o ++obj-$(CONFIG_RETURN_THUNK) += indirect-thunk.o + obj-$(CONFIG_PV) += ioport_emulate.o + obj-y += irq.o + obj-$(CONFIG_KEXEC) += machine_kexec.o +diff --git a/xen/arch/x86/acpi/wakeup_prot.S b/xen/arch/x86/acpi/wakeup_prot.S +index 66f799339913..97bd676aaee2 100644 +--- a/xen/arch/x86/acpi/wakeup_prot.S ++++ b/xen/arch/x86/acpi/wakeup_prot.S +@@ -133,7 +133,7 @@ ENTRY(s3_resume) + pop %r12 + pop %rbx + pop %rbp +- ret ++ RET + + .data + .align 16 +diff --git a/xen/arch/x86/alternative.c b/xen/arch/x86/alternative.c +index ec451d962c10..1b71ae959abe 100644 +--- a/xen/arch/x86/alternative.c ++++ b/xen/arch/x86/alternative.c +@@ -137,16 +137,45 @@ void init_or_livepatch add_nops(void *insns, unsigned int len) + } + } + ++void nocall __x86_return_thunk(void); ++ + /* + * Place a return at @ptr. @ptr must be in the writable alias of a stub. + * ++ * When CONFIG_RETURN_THUNK is active, this may be a JMP __x86_return_thunk ++ * instead, depending on the safety of @ptr with respect to Indirect Target ++ * Selection. ++ * + * Returns the next position to write into the stub. + */ + void *place_ret(void *ptr) + { ++ unsigned long addr = (unsigned long)ptr; + uint8_t *p = ptr; + +- *p++ = 0xc3; ++ /* ++ * When Return Thunks are used, if a RET would be unsafe at this location ++ * with respect to Indirect Target Selection (i.e. if addr is in the first ++ * half of a cacheline), insert a JMP __x86_return_thunk instead. ++ * ++ * The displacement needs to be relative to the executable alias of the ++ * stub, not to @ptr which is the writeable alias. ++ */ ++ if ( IS_ENABLED(CONFIG_RETURN_THUNK) && !(addr & 0x20) ) ++ { ++ long stub_va = (this_cpu(stubs.addr) & PAGE_MASK) + (addr & ~PAGE_MASK); ++ long disp = (long)__x86_return_thunk - (stub_va + 5); ++ ++ BUG_ON((int32_t)disp != disp); ++ ++ *p++ = 0xe9; ++ *(int32_t *)p = disp; ++ p += 4; ++ } ++ else ++ { ++ *p++ = 0xc3; ++ } + + return p; + } +diff --git a/xen/arch/x86/arch.mk b/xen/arch/x86/arch.mk +index b88d097a844b..85d3e7cbfeeb 100644 +--- a/xen/arch/x86/arch.mk ++++ b/xen/arch/x86/arch.mk +@@ -46,6 +46,9 @@ CFLAGS-$(CONFIG_CC_IS_GCC) += -fno-jump-tables + CFLAGS-$(CONFIG_CC_IS_CLANG) += -mretpoline-external-thunk + endif + ++# Compile with return thunk support if selected. ++CFLAGS-$(CONFIG_RETURN_THUNK) += -mfunction-return=thunk-extern ++ + # Disable the addition of a .note.gnu.property section to object files when + # livepatch support is enabled. The contents of that section can change + # depending on the instructions used, and livepatch-build-tools doesn't know +diff --git a/xen/arch/x86/bhb-thunk.S b/xen/arch/x86/bhb-thunk.S +index 52625f4e2c17..7f92201a3cbb 100644 +--- a/xen/arch/x86/bhb-thunk.S ++++ b/xen/arch/x86/bhb-thunk.S +@@ -23,7 +23,7 @@ FUNC(clear_bhb_tsx) + 0: .byte 0xc6, 0xf8, 0 /* xabort $0 */ + int3 + 1: +- ret ++ RET + END(clear_bhb_tsx) + + /* +diff --git a/xen/arch/x86/clear_page.S b/xen/arch/x86/clear_page.S +index d6c076f1d8bc..dc3c3c26bfb7 100644 +--- a/xen/arch/x86/clear_page.S ++++ b/xen/arch/x86/clear_page.S +@@ -1,6 +1,8 @@ + .file __FILE__ + + #include ++ ++#include + #include + + FUNC(clear_page_sse2) +@@ -16,5 +18,5 @@ FUNC(clear_page_sse2) + jnz 0b + + sfence +- ret ++ RET + END(clear_page_sse2) +diff --git a/xen/arch/x86/copy_page.S b/xen/arch/x86/copy_page.S +index c3c436545bac..e43e5370c815 100644 +--- a/xen/arch/x86/copy_page.S ++++ b/xen/arch/x86/copy_page.S +@@ -1,6 +1,8 @@ + .file __FILE__ + + #include ++ ++#include + #include + + #define src_reg %rsi +@@ -41,5 +43,5 @@ FUNC(copy_page_sse2) + movnti tmp4_reg, 3*WORD_SIZE(dst_reg) + + sfence +- ret ++ RET + END(copy_page_sse2) +diff --git a/xen/arch/x86/efi/check.c b/xen/arch/x86/efi/check.c +index 9e473faad3c9..23ba30abf330 100644 +--- a/xen/arch/x86/efi/check.c ++++ b/xen/arch/x86/efi/check.c +@@ -3,6 +3,9 @@ int __attribute__((__ms_abi__)) test(int i) + return i; + } + ++/* In case -mfunction-return is in use. */ ++void __x86_return_thunk(void) {}; ++ + /* + * Populate an array with "addresses" of relocatable and absolute values. + * This is to probe ld for (a) emitting base relocations at all and (b) not +diff --git a/xen/arch/x86/include/asm/asm-defns.h b/xen/arch/x86/include/asm/asm-defns.h +index 32d6b4491063..97ebe21298a2 100644 +--- a/xen/arch/x86/include/asm/asm-defns.h ++++ b/xen/arch/x86/include/asm/asm-defns.h +@@ -58,6 +58,12 @@ + .endif + .endm + ++#ifdef CONFIG_RETURN_THUNK ++# define RET jmp __x86_return_thunk ++#else ++# define RET ret ++#endif ++ + #ifdef CONFIG_XEN_IBT + # define ENDBR64 endbr64 + #else +diff --git a/xen/arch/x86/indirect-thunk.S b/xen/arch/x86/indirect-thunk.S +index c4b978d67b8e..26dad15f12c9 100644 +--- a/xen/arch/x86/indirect-thunk.S ++++ b/xen/arch/x86/indirect-thunk.S +@@ -15,6 +15,8 @@ + #undef SYM_ALIGN + #define SYM_ALIGN(align...) + ++#ifdef CONFIG_INDIRECT_THUNK ++ + .macro IND_THUNK_RETPOLINE reg:req + call 1f + int3 +@@ -62,3 +64,25 @@ END(__x86_indirect_thunk_\reg) + .irp reg, ax, cx, dx, bx, bp, si, di, 8, 9, 10, 11, 12, 13, 14, 15 + GEN_INDIRECT_THUNK reg=r\reg + .endr ++ ++#endif /* CONFIG_INDIRECT_THUNK */ ++ ++#ifdef CONFIG_RETURN_THUNK ++ .section .text.entry.__x86_return_thunk, "ax", @progbits ++ ++ /* ++ * The Indirect Target Selection speculative vulnerability means that ++ * indirect branches (including RETs) are unsafe when in the first ++ * half of a cacheline. Arrange for them to be in the second half. ++ * ++ * Align to 64, then skip 32. ++ */ ++ .balign 64 ++ .fill 32, 1, 0xcc ++ ++FUNC(__x86_return_thunk) ++ ret ++ int3 /* Halt straight-line speculation */ ++END(__x86_return_thunk) ++ ++#endif /* CONFIG_RETURN_THUNK */ +diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c +index ff5d1c9f8634..295d847ea24c 100644 +--- a/xen/arch/x86/pv/emul-priv-op.c ++++ b/xen/arch/x86/pv/emul-priv-op.c +@@ -131,7 +131,7 @@ static io_emul_stub_t *io_emul_stub_setup(struct priv_op_ctxt *ctxt, u8 opcode, + BUILD_BUG_ON(STUB_BUF_SIZE / 2 < + (sizeof(prologue) + sizeof(epilogue) + 10 /* 2x call */ + + MAX(3 /* default stub */, IOEMUL_QUIRK_STUB_BYTES) + +- 1 /* ret */)); ++ (IS_ENABLED(CONFIG_RETURN_THUNK) ? 5 : 1) /* ret */)); + /* Runtime confirmation that we haven't clobbered an adjacent stub. */ + BUG_ON(STUB_BUF_SIZE / 2 < (p - ctxt->io_emul_stub)); + +diff --git a/xen/arch/x86/pv/gpr_switch.S b/xen/arch/x86/pv/gpr_switch.S +index 5409ad3b1447..362b5d241623 100644 +--- a/xen/arch/x86/pv/gpr_switch.S ++++ b/xen/arch/x86/pv/gpr_switch.S +@@ -26,7 +26,7 @@ FUNC(load_guest_gprs) + movq UREGS_r15(%rdi), %r15 + movq UREGS_rcx(%rdi), %rcx + movq UREGS_rdi(%rdi), %rdi +- ret ++ RET + END(load_guest_gprs) + + /* Save guest GPRs. Parameter on the stack above the return address. */ +@@ -48,5 +48,5 @@ FUNC(save_guest_gprs) + movq %rbx, UREGS_rbx(%rdi) + movq %rdx, UREGS_rdx(%rdi) + movq %rcx, UREGS_rcx(%rdi) +- ret ++ RET + END(save_guest_gprs) +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 35351044f901..019a0a81f4a7 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -569,6 +569,9 @@ static void __init print_details(enum ind_thunk thunk) + #ifdef CONFIG_INDIRECT_THUNK + " INDIRECT_THUNK" + #endif ++#ifdef CONFIG_RETURN_THUNK ++ " RETURN_THUNK" ++#endif + #ifdef CONFIG_SHADOW_PAGING + " SHADOW_PAGING" + #endif +diff --git a/xen/arch/x86/x86_64/compat/entry.S b/xen/arch/x86/x86_64/compat/entry.S +index a99646c0cd4e..18f46c78cfbe 100644 +--- a/xen/arch/x86/x86_64/compat/entry.S ++++ b/xen/arch/x86/x86_64/compat/entry.S +@@ -180,7 +180,7 @@ FUNC(cr4_pv32_restore) + or cr4_pv32_mask(%rip), %rax + mov %rax, %cr4 + mov %rax, (%rcx) +- ret ++ RET + 0: + #ifndef NDEBUG + /* Check that _all_ of the bits intended to be set actually are. */ +@@ -198,7 +198,7 @@ FUNC(cr4_pv32_restore) + 1: + #endif + xor %eax, %eax +- ret ++ RET + END(cr4_pv32_restore) + + FUNC(compat_syscall) +@@ -329,7 +329,7 @@ __UNLIKELY_END(compat_bounce_null_selector) + xor %eax, %eax + mov %ax, TRAPBOUNCE_cs(%rdx) + mov %al, TRAPBOUNCE_flags(%rdx) +- ret ++ RET + + .section .fixup,"ax" + .Lfx13: +diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S +index 9b0cdb76408b..eb62e7c329bd 100644 +--- a/xen/arch/x86/x86_64/entry.S ++++ b/xen/arch/x86/x86_64/entry.S +@@ -604,7 +604,7 @@ __UNLIKELY_END(create_bounce_frame_bad_bounce_ip) + xor %eax, %eax + mov %rax, TRAPBOUNCE_eip(%rdx) + mov %al, TRAPBOUNCE_flags(%rdx) +- ret ++ RET + + .pushsection .fixup, "ax", @progbits + # Numeric tags below represent the intended overall %rsi adjustment. +diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S +index 9a1dfe1b340a..506993867502 100644 +--- a/xen/arch/x86/xen.lds.S ++++ b/xen/arch/x86/xen.lds.S +@@ -82,6 +82,7 @@ SECTIONS + . = ALIGN(PAGE_SIZE); + _stextentry = .; + *(.text.entry) ++ *(.text.entry.*) + . = ALIGN(PAGE_SIZE); + _etextentry = .; + +diff --git a/xen/common/Kconfig b/xen/common/Kconfig +index 565ceda741b9..da0fa7527643 100644 +--- a/xen/common/Kconfig ++++ b/xen/common/Kconfig +@@ -130,6 +130,17 @@ config INDIRECT_THUNK + When enabled, indirect branches are implemented using a new construct + called "retpoline" that prevents speculation. + ++config RETURN_THUNK ++ bool "Out-of-line Returns" ++ depends on CC_HAS_RETURN_THUNK ++ default INDIRECT_THUNK ++ help ++ Compile Xen with out-of-line returns. ++ ++ This allows Xen to mitigate a variety of speculative vulnerabilities ++ by choosing a hardware-dependent instruction sequence to implement ++ function returns safely. ++ + config SPECULATIVE_HARDEN_ARRAY + bool "Speculative Array Hardening" + default y diff --git a/xsa469-4.19-07.patch b/xsa469-4.19-07.patch new file mode 100644 index 0000000..2ba9d48 --- /dev/null +++ b/xsa469-4.19-07.patch @@ -0,0 +1,154 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Synthesise ITS_NO to guests on unaffected hardware + +It is easier to express feature word 17 in terms of word 16 + [32, 64) as +that's how the layout is given in documentation. + +This is part of XSA-469 / CVE-2024-28956 + +Signed-off-by: Andrew Cooper +Reviewed-by: Roger Pau Monné + +diff --git a/xen/arch/x86/include/asm/cpufeature.h b/xen/arch/x86/include/asm/cpufeature.h +index 9bc553681f4a..1729ba0c3097 100644 +--- a/xen/arch/x86/include/asm/cpufeature.h ++++ b/xen/arch/x86/include/asm/cpufeature.h +@@ -216,6 +216,7 @@ static inline bool boot_cpu_has(unsigned int feat) + #define cpu_has_gds_no boot_cpu_has(X86_FEATURE_GDS_NO) + #define cpu_has_rfds_no boot_cpu_has(X86_FEATURE_RFDS_NO) + #define cpu_has_rfds_clear boot_cpu_has(X86_FEATURE_RFDS_CLEAR) ++#define cpu_has_its_no boot_cpu_has(X86_FEATURE_ITS_NO) + + /* Synthesized. */ + #define cpu_has_arch_perfmon boot_cpu_has(X86_FEATURE_ARCH_PERFMON) +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 019a0a81f4a7..94cdbd521c4d 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -1781,6 +1781,90 @@ static void __init bhi_calculations(void) + } + } + ++/* ++ * https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/advisory-guidance/indirect-target-selection.html ++ */ ++static void __init its_calculations(void) ++{ ++ /* ++ * Indirect Target Selection is a Branch Prediction bug whereby certain ++ * indirect branches (including RETs) get predicted using a direct branch ++ * target, rather than a suitable indirect target, bypassing hardware ++ * isolation protections. ++ * ++ * ITS affects Core (but not Atom) processors starting from the ++ * introduction of eIBRS, up to but not including Golden Cove cores ++ * (checked here with BHI_CTRL). ++ * ++ * The ITS_NO feature is not expected to be enumerated by hardware, and is ++ * only for VMMs to synthesise for guests. ++ * ++ * ITS comes in 3 flavours: ++ * ++ * 1) Across-IBPB. Indirect branches after the IBPB can be controlled ++ * by direct targets which existed prior to the IBPB. This is ++ * addressed in the IPU 2025.1 microcode drop, and has no other ++ * software interaction. ++ * ++ * 2) Guest/Host. Indirect branches in the VMM can be controlled by ++ * direct targets from the guest. This applies equally to PV guests ++ * (Ring3) and HVM guests (VMX), and applies to all Skylake-uarch ++ * cores with eIBRS. ++ * ++ * 3) Intra-mode. Indirect branches in the VMM can be controlled by ++ * other execution in the same mode. ++ */ ++ ++ /* ++ * If we can see ITS_NO, or we're virtualised, do nothing. We are or may ++ * migrate somewhere unsafe. ++ */ ++ if ( cpu_has_its_no || cpu_has_hypervisor ) ++ return; ++ ++ /* ITS is only known to affect Intel processors at this time. */ ++ if ( boot_cpu_data.x86_vendor != X86_VENDOR_INTEL ) ++ return; ++ ++ /* ++ * ITS does not exist on: ++ * - non-Family 6 CPUs ++ * - those without eIBRS ++ * - those with BHI_CTRL ++ * but we still need to synthesise ITS_NO. ++ */ ++ if ( boot_cpu_data.x86 != 6 || !cpu_has_eibrs || ++ boot_cpu_has(X86_FEATURE_BHI_CTRL) ) ++ goto synthesise; ++ ++ switch ( boot_cpu_data.x86_model ) ++ { ++ /* These Skylake-uarch cores suffer cases #2 and #3. */ ++ case INTEL_FAM6_SKYLAKE_X: ++ case INTEL_FAM6_KABYLAKE_L: ++ case INTEL_FAM6_KABYLAKE: ++ case INTEL_FAM6_COMETLAKE: ++ case INTEL_FAM6_COMETLAKE_L: ++ return; ++ ++ /* These Sunny/Willow/Cypress Cove cores suffer case #3. */ ++ case INTEL_FAM6_ICELAKE_X: ++ case INTEL_FAM6_ICELAKE_D: ++ case INTEL_FAM6_ICELAKE_L: ++ case INTEL_FAM6_TIGERLAKE_L: ++ case INTEL_FAM6_TIGERLAKE: ++ case INTEL_FAM6_ROCKETLAKE: ++ return; ++ ++ default: ++ break; ++ } ++ ++ /* Platforms remaining are not believed to be vulnerable to ITS. */ ++ synthesise: ++ setup_force_cpu_cap(X86_FEATURE_ITS_NO); ++} ++ + void spec_ctrl_init_domain(struct domain *d) + { + bool pv = is_pv_domain(d); +@@ -2331,6 +2415,8 @@ void __init init_speculation_mitigations(void) + + bhi_calculations(); + ++ its_calculations(); ++ + print_details(thunk); + + /* +diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h +index 9c98e4992861..4d9e468af653 100644 +--- a/xen/include/public/arch-x86/cpufeatureset.h ++++ b/xen/include/public/arch-x86/cpufeatureset.h +@@ -365,7 +365,8 @@ XEN_CPUFEATURE(GDS_NO, 16*32+26) /*A No Gather Data Sampling */ + XEN_CPUFEATURE(RFDS_NO, 16*32+27) /*A No Register File Data Sampling */ + XEN_CPUFEATURE(RFDS_CLEAR, 16*32+28) /*!A| Register File(s) cleared by VERW */ + +-/* Intel-defined CPU features, MSR_ARCH_CAPS 0x10a.edx, word 17 */ ++/* Intel-defined CPU features, MSR_ARCH_CAPS 0x10a.edx, word 17 (express in terms of word 16) */ ++XEN_CPUFEATURE(ITS_NO, 16*32+62) /*!A No Indirect Target Selection */ + + #endif /* XEN_CPUFEATURE */ + +diff --git a/xen/tools/gen-cpuid.py b/xen/tools/gen-cpuid.py +index 601eec608983..dc33ca3181b1 100755 +--- a/xen/tools/gen-cpuid.py ++++ b/xen/tools/gen-cpuid.py +@@ -51,7 +51,7 @@ def parse_definitions(state): + r"\s+/\*([\w!|]*) .*$") + + word_regex = re.compile( +- r"^/\* .* word (\d*) \*/$") ++ r"^/\* .* word (\d*) .*\*/$") + last_word = -1 + + this = sys.modules[__name__] From 41f5d9097f54c7b963e079d3261159e075c07bd6 Mon Sep 17 00:00:00 2001 From: Python Maint Date: Mon, 2 Jun 2025 21:24:59 +0200 Subject: [PATCH 109/137] Rebuilt for Python 3.14 --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 650144d..3801543 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.19.2 -Release: 4%{?dist} +Release: 5%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -951,6 +951,9 @@ fi %endif %changelog +* Mon Jun 02 2025 Python Maint - 4.19.2-5 +- Rebuilt for Python 3.14 + * Mon May 12 2025 Michael Young - 4.19.2-4 - x86: Indirect Target Selection [XSA-469, CVE-2024-28956] From 80e497a2951d965b7a45a1ca611cbdeae2d0b126 Mon Sep 17 00:00:00 2001 From: Jerry James Date: Fri, 11 Jul 2025 14:54:03 -0600 Subject: [PATCH 110/137] Rebuild to fix OCaml dependencies --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 3801543..0ea5f41 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.19.2 -Release: 5%{?dist} +Release: 6%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -951,6 +951,9 @@ fi %endif %changelog +* Fri Jul 11 2025 Jerry James - 4.19.2-6 +- Rebuild to fix OCaml dependencies + * Mon Jun 02 2025 Python Maint - 4.19.2-5 - Rebuilt for Python 3.14 From d382b68b39e4ff3d899e8cd1d4e95baacab95766 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 15 Jul 2025 21:03:56 +0100 Subject: [PATCH 111/137] update to xen 4.20.1 includes fixes for x86: Incorrect stubs exception handling for flags recovery [XSA-470, CVE-2025-27465] x86: Transitive Scheduler Attacks [XSA-471, CVE-2024-36350, CVE-2024-36357] --- .gitignore | 3 +- CVE-2014-0150.patch | 11 - qemu.trad.CVE-2015-5278.patch | 11 - qemu.trad.CVE-2015-5279.patch | 48 ---- qemu.trad.CVE-2015-6815.patch | 12 - qemu.trad.CVE-2015-7295.patch | 63 ----- qemu.trad.CVE-2015-7512.patch | 37 --- qemu.trad.CVE-2015-8345.patch | 38 --- qemu.trad.CVE-2015-8504.patch | 44 --- qemu.trad.CVE-2016-1714.patch | 30 -- qemu.trad.CVE-2016-1981.patch | 104 ------- qemu.trad.CVE-2016-2538.patch | 56 ---- qemu.trad.CVE-2016-2841.patch | 34 --- qemu.trad.CVE-2016-2857.patch | 45 --- qemu.trad.CVE-2016-4001.patch | 46 ---- qemu.trad.CVE-2016-4002.patch | 31 --- qemu.trad.CVE-2016-4439.patch | 44 --- qemu.trad.CVE-2016-4441.patch | 68 ----- qemu.trad.CVE-2016-5238.patch | 65 ----- qemu.trad.CVE-2016-5338.patch | 76 ----- qemu.trad.CVE-2016-6351.patch | 81 ------ qemu.trad.CVE-2016-8669.patch | 37 --- qemu.trad.CVE-2016-8910.patch | 29 -- qemu.trad.CVE-2016-9776.patch | 34 --- qemu.trad.CVE-2017-6505.patch | 51 ---- qemu.trad.CVE-2017-7718.patch | 51 ---- qemu.trad.CVE-2017-8309.patch | 38 --- qemu.trad.CVE-2017-9330.patch | 31 --- qemu.trad.bug1399055.patch | 76 ----- sources | 3 +- xen.drop.brctl.patch | 8 - xen.efi.build.patch | 12 +- xen.fedora.crypt.patch | 11 - xen.gcc7.fix.patch | 12 - xen.hypervisor.config | 109 ++++++-- xen.spec | 210 +++----------- xsa469-4.19-01.patch | 83 ------ xsa469-4.19-02.patch | 314 --------------------- xsa469-4.19-03.patch | 46 ---- xsa469-4.19-04.patch | 64 ----- xsa469-4.19-05.patch | 506 ---------------------------------- xsa469-4.19-06.patch | 375 ------------------------- xsa469-4.19-07.patch | 154 ----------- 43 files changed, 138 insertions(+), 3063 deletions(-) delete mode 100644 CVE-2014-0150.patch delete mode 100644 qemu.trad.CVE-2015-5278.patch delete mode 100644 qemu.trad.CVE-2015-5279.patch delete mode 100644 qemu.trad.CVE-2015-6815.patch delete mode 100644 qemu.trad.CVE-2015-7295.patch delete mode 100644 qemu.trad.CVE-2015-7512.patch delete mode 100644 qemu.trad.CVE-2015-8345.patch delete mode 100644 qemu.trad.CVE-2015-8504.patch delete mode 100644 qemu.trad.CVE-2016-1714.patch delete mode 100644 qemu.trad.CVE-2016-1981.patch delete mode 100644 qemu.trad.CVE-2016-2538.patch delete mode 100644 qemu.trad.CVE-2016-2841.patch delete mode 100644 qemu.trad.CVE-2016-2857.patch delete mode 100644 qemu.trad.CVE-2016-4001.patch delete mode 100644 qemu.trad.CVE-2016-4002.patch delete mode 100644 qemu.trad.CVE-2016-4439.patch delete mode 100644 qemu.trad.CVE-2016-4441.patch delete mode 100644 qemu.trad.CVE-2016-5238.patch delete mode 100644 qemu.trad.CVE-2016-5338.patch delete mode 100644 qemu.trad.CVE-2016-6351.patch delete mode 100644 qemu.trad.CVE-2016-8669.patch delete mode 100644 qemu.trad.CVE-2016-8910.patch delete mode 100644 qemu.trad.CVE-2016-9776.patch delete mode 100644 qemu.trad.CVE-2017-6505.patch delete mode 100644 qemu.trad.CVE-2017-7718.patch delete mode 100644 qemu.trad.CVE-2017-8309.patch delete mode 100644 qemu.trad.CVE-2017-9330.patch delete mode 100644 qemu.trad.bug1399055.patch delete mode 100644 xen.drop.brctl.patch delete mode 100644 xen.fedora.crypt.patch delete mode 100644 xen.gcc7.fix.patch delete mode 100644 xsa469-4.19-01.patch delete mode 100644 xsa469-4.19-02.patch delete mode 100644 xsa469-4.19-03.patch delete mode 100644 xsa469-4.19-04.patch delete mode 100644 xsa469-4.19-05.patch delete mode 100644 xsa469-4.19-06.patch delete mode 100644 xsa469-4.19-07.patch diff --git a/.gitignore b/.gitignore index 85e99f7..5d9b74e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.19.2.tar.gz +/xen-4.20.1.tar.gz +/mini-os-4.20.0.tar.xz diff --git a/CVE-2014-0150.patch b/CVE-2014-0150.patch deleted file mode 100644 index adcbcc7..0000000 --- a/CVE-2014-0150.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- xen-4.4.1/tools/qemu-xen-traditional/hw/virtio-net.c.orig 2014-07-02 15:54:37.000000000 +0100 -+++ xen-4.4.1/tools/qemu-xen-traditional/hw/virtio-net.c 2014-11-18 20:50:13.593122915 +0000 -@@ -192,7 +192,7 @@ - return VIRTIO_NET_ERR; - - if (mac_data.entries) { -- if (n->mac_table.in_use + mac_data.entries <= MAC_TABLE_ENTRIES) { -+ if (n->mac_table.in_use <= MAC_TABLE_ENTRIES - mac_data.entries) { - memcpy(n->mac_table.macs + (n->mac_table.in_use * ETH_ALEN), - elem->out_sg[2].iov_base + sizeof(mac_data), - mac_data.entries * ETH_ALEN); diff --git a/qemu.trad.CVE-2015-5278.patch b/qemu.trad.CVE-2015-5278.patch deleted file mode 100644 index 950817a..0000000 --- a/qemu.trad.CVE-2015-5278.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- xen-4.5.1/tools/qemu-xen-traditional/hw/ne2000.c.orig 2015-09-26 17:27:49.494334726 +0100 -+++ xen-4.5.1/tools/qemu-xen-traditional/hw/ne2000.c 2015-09-26 17:31:53.107474932 +0100 -@@ -331,7 +331,7 @@ - if (index <= s->stop) - avail = s->stop - index; - else -- avail = 0; -+ break; - len = size; - if (len > avail) - len = avail; diff --git a/qemu.trad.CVE-2015-5279.patch b/qemu.trad.CVE-2015-5279.patch deleted file mode 100644 index ea08067..0000000 --- a/qemu.trad.CVE-2015-5279.patch +++ /dev/null @@ -1,48 +0,0 @@ ---- xen-4.5.1/tools/qemu-xen-traditional/hw/ne2000.c.orig 2015-06-09 16:32:24.000000000 +0100 -+++ xen-4.5.1/tools/qemu-xen-traditional/hw/ne2000.c 2015-09-26 17:27:49.494334726 +0100 -@@ -304,6 +304,9 @@ - } - - index = s->curpag << 8; -+ if (index >= NE2000_PMEM_END) { -+ index = s->start; -+ } - /* 4 bytes for header */ - total_len = size + 4; - /* address for next packet (4 bytes for CRC) */ -@@ -387,15 +390,21 @@ - offset = addr | (page << 4); - switch(offset) { - case EN0_STARTPG: -- s->start = val << 8; -+ if (val << 8 <= NE2000_PMEM_END) { -+ s->start = val << 8; -+ } - s->tainted = 1; - break; - case EN0_STOPPG: -- s->stop = val << 8; -+ if (val << 8 <= NE2000_PMEM_END) { -+ s->stop = val << 8; -+ } - s->tainted = 1; - break; - case EN0_BOUNDARY: -- s->boundary = val; -+ if (val << 8 < NE2000_PMEM_END) { -+ s->boundary = val; -+ } - break; - case EN0_IMR: - s->imr = val; -@@ -436,7 +445,9 @@ - s->phys[offset - EN1_PHYS] = val; - break; - case EN1_CURPAG: -- s->curpag = val; -+ if (val << 8 < NE2000_PMEM_END) { -+ s->curpag = val; -+ } - s->tainted = 1; - break; - case EN1_MULT ... EN1_MULT + 7: diff --git a/qemu.trad.CVE-2015-6815.patch b/qemu.trad.CVE-2015-6815.patch deleted file mode 100644 index 7386d6c..0000000 --- a/qemu.trad.CVE-2015-6815.patch +++ /dev/null @@ -1,12 +0,0 @@ ---- xen-4.5.1/tools/qemu-xen-traditional/hw/e1000.c.orig 2015-06-09 16:32:24.000000000 +0100 -+++ xen-4.5.1/tools/qemu-xen-traditional/hw/e1000.c 2015-09-26 17:16:36.406544380 +0100 -@@ -461,7 +461,8 @@ - memmove(tp->data, tp->header, hdr); - tp->size = hdr; - } -- } while (split_size -= bytes); -+ split_size -= bytes; -+ } while (bytes && split_size); - } else if (!tp->tse && tp->cptse) { - // context descriptor TSE is not set, while data descriptor TSE is set - DBGOUT(TXERR, "TCP segmentaion Error\n"); diff --git a/qemu.trad.CVE-2015-7295.patch b/qemu.trad.CVE-2015-7295.patch deleted file mode 100644 index 1c74270..0000000 --- a/qemu.trad.CVE-2015-7295.patch +++ /dev/null @@ -1,63 +0,0 @@ ---- xen-4.5.1/tools/qemu-xen-traditional/hw/virtio.c.orig 2015-06-09 16:32:24.000000000 +0100 -+++ xen-4.5.1/tools/qemu-xen-traditional/hw/virtio.c 2015-10-10 16:57:01.806370020 +0100 -@@ -268,8 +268,8 @@ - return vring_avail_idx(vq) == vq->last_avail_idx; - } - --void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem, -- unsigned int len, unsigned int idx) -+static void virtqueue_unmap_sg(VirtQueue *vq, const VirtQueueElement *elem, -+ unsigned int len) - { - unsigned int offset; - int i; -@@ -302,7 +302,19 @@ - - offset += size; - } -+} - -+void virtqueue_discard(VirtQueue *vq, const VirtQueueElement *elem, -+ unsigned int len) -+{ -+ vq->last_avail_idx--; -+ virtqueue_unmap_sg(vq, elem, len); -+} -+ -+void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem, -+ unsigned int len, unsigned int idx) -+{ -+ virtqueue_unmap_sg(vq, elem, len); - idx = (idx + vring_used_idx(vq)) % vq->vring.num; - - /* Get a pointer to the next entry in the used ring. */ ---- xen-4.5.1/tools/qemu-xen-traditional/hw/virtio.h.orig 2015-06-09 16:32:24.000000000 +0100 -+++ xen-4.5.1/tools/qemu-xen-traditional/hw/virtio.h 2015-10-10 16:57:53.146216039 +0100 -@@ -105,6 +105,8 @@ - void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem, - unsigned int len); - void virtqueue_flush(VirtQueue *vq, unsigned int count); -+void virtqueue_discard(VirtQueue *vq, const VirtQueueElement *elem, -+ unsigned int len); - void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem, - unsigned int len, unsigned int idx); - ---- xen-4.5.1/tools/qemu-xen-traditional/hw/virtio-net.c.orig 2015-10-10 16:10:05.071786348 +0100 -+++ xen-4.5.1/tools/qemu-xen-traditional/hw/virtio-net.c 2015-10-10 19:05:34.510029916 +0100 -@@ -424,11 +424,15 @@ - len = iov_fill(sg, elem.in_num, - buf + offset, size - offset); - total += len; -+ offset += len; -+ if (!n->mergeable_rx_bufs && offset < size) { -+ virtqueue_discard(n->rx_vq, &elem, total); -+ return; -+ } - - /* signal other side */ - virtqueue_fill(n->rx_vq, &elem, total, i++); - -- offset += len; - } - - if (mhdr) diff --git a/qemu.trad.CVE-2015-7512.patch b/qemu.trad.CVE-2015-7512.patch deleted file mode 100644 index 6a1f33f..0000000 --- a/qemu.trad.CVE-2015-7512.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 8b98a2f07175d46c3f7217639bd5e03f2ec56343 Mon Sep 17 00:00:00 2001 -From: Jason Wang -Date: Mon, 30 Nov 2015 15:00:06 +0800 -Subject: [PATCH] pcnet: fix rx buffer overflow(CVE-2015-7512) - -Backends could provide a packet whose length is greater than buffer -size. Check for this and truncate the packet to avoid rx buffer -overflow in this case. - -Cc: Prasad J Pandit -Cc: qemu-stable@nongnu.org -Reviewed-by: Michael S. Tsirkin -Signed-off-by: Jason Wang ---- - tools/qemu-xen-traditional/hw/pcnet.c | 6 ++++++ - 1 files changed, 6 insertions(+), 0 deletions(-) - -diff --git a/tools/qemu-xen-traditional/hw/pcnet.c b/tools/qemu-xen-traditional/hw/pcnet.c -index 309c40b..1f4a3db 100644 ---- a/tools/qemu-xen-traditional/hw/pcnet.c -+++ b/tools/qemu-xen-traditional/hw/pcnet.c -@@ -1064,6 +1064,12 @@ ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_) - int pktcount = 0; - - if (!s->looptest) { -+ if (size > 4092) { -+#ifdef PCNET_DEBUG_RMD -+ fprintf(stderr, "pcnet: truncates rx packet.\n"); -+#endif -+ size = 4092; -+ } - memcpy(src, buf, size); - /* no need to compute the CRC */ - src[size] = 0; --- -1.7.0.4 - diff --git a/qemu.trad.CVE-2015-8345.patch b/qemu.trad.CVE-2015-8345.patch deleted file mode 100644 index 73215ca..0000000 --- a/qemu.trad.CVE-2015-8345.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 00837731d254908a841d69298a4f9f077babaf24 Mon Sep 17 00:00:00 2001 -From: Stefan Weil -Date: Fri, 20 Nov 2015 08:42:33 +0100 -Subject: [PATCH] eepro100: Prevent two endless loops - -http://lists.nongnu.org/archive/html/qemu-devel/2015-11/msg04592.html -shows an example how an endless loop in function action_command can -be achieved. - -During my code review, I noticed a 2nd case which can result in an -endless loop. - -Reported-by: Qinghao Tang -Signed-off-by: Stefan Weil -Signed-off-by: Jason Wang ---- - tools/qemu-xen-traditional/hw/eepro100.c | 16 ++++++++++++++++ - 1 files changed, 16 insertions(+), 0 deletions(-) - -diff --git a/tools/qemu-xen-traditional/hw/eepro100.c b/tools/qemu-xen-traditional/hw/eepro100.c -index 60333b7..685a478 100644 ---- a/tools/qemu-xen-traditional/hw/eepro100.c -+++ b/tools/qemu-xen-traditional/hw/eepro100.c -@@ -774,6 +774,11 @@ static void tx_command(EEPRO100State *s) - uint32_t tx_buffer_address = ldl_phys(tbd_address); - uint16_t tx_buffer_size = lduw_phys(tbd_address + 4); - //~ uint16_t tx_buffer_el = lduw_phys(tbd_address + 6); -+ if (tx_buffer_size == 0) { -+ /* Prevent an endless loop. */ -+ logout("loop in %s:%u\n", __FILE__, __LINE__); -+ break; -+ } - tbd_address += 8; - logout - ("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n", --- -1.7.0.4 - diff --git a/qemu.trad.CVE-2015-8504.patch b/qemu.trad.CVE-2015-8504.patch deleted file mode 100644 index 3620d40..0000000 --- a/qemu.trad.CVE-2015-8504.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 4c65fed8bdf96780735dbdb92a8bd0d6b6526cc3 Mon Sep 17 00:00:00 2001 -From: Prasad J Pandit -Date: Thu, 3 Dec 2015 18:54:17 +0530 -Subject: [PATCH] ui: vnc: avoid floating point exception - -While sending 'SetPixelFormat' messages to a VNC server, -the client could set the 'red-max', 'green-max' and 'blue-max' -values to be zero. This leads to a floating point exception in -write_png_palette while doing frame buffer updates. - -Reported-by: Lian Yihan -Signed-off-by: Prasad J Pandit -Reviewed-by: Gerd Hoffmann -Signed-off-by: Peter Maydell ---- - tools/qemu-xen-traditional/vnc.c | 6 +++--- - 1 files changed, 3 insertions(+), 3 deletions(-) - -diff --git a/tools/qemu-xen-traditional/vnc.c b/tools/qemu-xen-traditional/vnc.c -index 7538405..cbe4d33 100644 ---- a/tools/qemu-xen-traditional/vnc.c -+++ b/tools/qemu-xen-traditional/vnc.c -@@ -2198,15 +2198,15 @@ static void set_pixel_format(VncState *vs, - } - - vs->clientds = vs->serverds; -- vs->clientds.pf.rmax = red_max; -+ vs->clientds.pf.rmax = red_max ? red_max : 0xFF; - count_bits(vs->clientds.pf.rbits, red_max); - vs->clientds.pf.rshift = red_shift; - vs->clientds.pf.rmask = red_max << red_shift; -- vs->clientds.pf.gmax = green_max; -+ vs->clientds.pf.gmax = green_max ? green_max : 0xFF; - count_bits(vs->clientds.pf.gbits, green_max); - vs->clientds.pf.gshift = green_shift; - vs->clientds.pf.gmask = green_max << green_shift; -- vs->clientds.pf.bmax = blue_max; -+ vs->clientds.pf.bmax = blue_max ? blue_max : 0xFF; - count_bits(vs->clientds.pf.bbits, blue_max); - vs->clientds.pf.bshift = blue_shift; - vs->clientds.pf.bmask = blue_max << blue_shift; --- -1.7.0.4 - diff --git a/qemu.trad.CVE-2016-1714.patch b/qemu.trad.CVE-2016-1714.patch deleted file mode 100644 index 59b840b..0000000 --- a/qemu.trad.CVE-2016-1714.patch +++ /dev/null @@ -1,30 +0,0 @@ ---- xen-4.6.1/tools/qemu-xen-traditional/hw/fw_cfg.c.orig 2016-01-04 15:35:42.000000000 +0000 -+++ xen-4.6.1/tools/qemu-xen-traditional/hw/fw_cfg.c 2016-03-06 16:42:33.464296362 +0000 -@@ -54,11 +54,15 @@ - static void fw_cfg_write(FWCfgState *s, uint8_t value) - { - int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL); -- FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK]; -+ FWCfgEntry *e = (s->cur_entry == FW_CFG_INVALID) ? NULL : -+ &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK]; - - FW_CFG_DPRINTF("write %d\n", value); - -- if (s->cur_entry & FW_CFG_WRITE_CHANNEL && s->cur_offset < e->len) { -+ if (s->cur_entry & FW_CFG_WRITE_CHANNEL -+ && e != NULL -+ && e->callback -+ && s->cur_offset < e->len) { - e->data[s->cur_offset++] = value; - if (s->cur_offset == e->len) { - e->callback(e->callback_opaque, e->data); -@@ -88,7 +92,8 @@ - static uint8_t fw_cfg_read(FWCfgState *s) - { - int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL); -- FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK]; -+ FWCfgEntry *e = (s->cur_entry == FW_CFG_INVALID) ? NULL : -+ &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK]; - uint8_t ret; - - if (s->cur_entry == FW_CFG_INVALID || !e->data || s->cur_offset >= e->len) diff --git a/qemu.trad.CVE-2016-1981.patch b/qemu.trad.CVE-2016-1981.patch deleted file mode 100644 index cd2a8c1..0000000 --- a/qemu.trad.CVE-2016-1981.patch +++ /dev/null @@ -1,104 +0,0 @@ ------------------------------------------------------------------------- -*From*: Laszlo Ersek -*Subject*: [Qemu-devel] [PATCH] e1000: eliminate infinite loops on -out-of-bounds transfer start -*Date*: Tue, 19 Jan 2016 14:17:20 +0100 - ------------------------------------------------------------------------- - -The start_xmit() and e1000_receive_iov() functions implement DMA transfers -iterating over a set of descriptors that the guest's e1000 driver -prepares: - -- the TDLEN and RDLEN registers store the total size of the descriptor - area, - -- while the TDH and RDH registers store the offset (in whole tx / rx - descriptors) into the area where the transfer is supposed to start. - -Each time a descriptor is processed, the TDH and RDH register is bumped -(as appropriate for the transfer direction). - -QEMU already contains logic to deal with bogus transfers submitted by the -guest: - -- Normally, the transmit case wants to increase TDH from its initial value - to TDT. (TDT is allowed to be numerically smaller than the initial TDH - value; wrapping at or above TDLEN bytes to zero is normal.) The failsafe - that QEMU currently has here is a check against reaching the original - TDH value again -- a complete wraparound, which should never happen. - -- In the receive case RDH is increased from its initial value until - "total_size" bytes have been received; preferably in a single step, or - in "s->rxbuf_size" byte steps, if the latter is smaller. However, null - RX descriptors are skipped without receiving data, while RDH is - incremented just the same. QEMU tries to prevent an infinite loop - (processing only null RX descriptors) by detecting whether RDH assumes - its original value during the loop. (Again, wrapping from RDLEN to 0 is - normal.) - -What both directions miss is that the guest could program TDLEN and RDLEN -so low, and the initial TDH and RDH so high, that these registers will -immediately be truncated to zero, and then never reassume their initial -values in the loop -- a full wraparound will never occur. - -The condition that expresses this is: - - xdh_start >= s->mac_reg[XDLEN] / sizeof(desc) - -i.e., TDH or RDH start out after the last whole rx or tx descriptor that -fits into the TDLEN or RDLEN sized area. - -This condition could be checked before we enter the loops, but -pci_dma_read() / pci_dma_write() knows how to fill in buffers safely for -bogus DMA addresses, so we just extend the existing failsafes with the -above condition. - -Cc: "Michael S. Tsirkin" -Cc: Petr Matousek -Cc: Stefano Stabellini -Cc: Prasad Pandit -Cc: Michael Roth -Cc: Jason Wang -RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=1296044 -Signed-off-by: Laszlo Ersek -Reviewed-by: Jason Wang ---- - -Notes: - Regarding the public posting: we made an honest effort to vet this - vulnerability, and the impact seems low -- no host side reads/writes, - "just" a DoS (infinite loop). We decided the patch could be posted - publicly, for the usual review process. Jason and Prasad checked the - patch in the internal discussion already, but comments, improvements - etc. are clearly welcome. The CVE request is underway. Thanks. - - hw/net/e1000.c | 6 ++++-- - 1 file changed, 4 insertions(+), 2 deletions(-) - -diff --git a/hw/net/e1000.c b/hw/net/e1000.c -index bec06e9..34d0823 100644 ---- a/tools/qemu-xen-traditional/hw/e1000.c -+++ b/tools/qemu-xen-traditional/hw/e1000.c -@@ -908,7 +908,8 @@ start_xmit(E1000State *s) - * bogus values to TDT/TDLEN. - * there's nothing too intelligent we could do about this. - */ -- if (s->mac_reg[TDH] == tdh_start) { -+ if (s->mac_reg[TDH] == tdh_start || -+ tdh_start >= s->mac_reg[TDLEN] / sizeof(desc)) { - DBGOUT(TXERR, "TDH wraparound @%x, TDT %x, TDLEN %x\n", - tdh_start, s->mac_reg[TDT], s->mac_reg[TDLEN]); - break; -@@ -1165,7 +1166,8 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) - s->mac_reg[RDH] = 0; - s->check_rxov = 1; - /* see comment in start_xmit; same here */ -- if (s->mac_reg[RDH] == rdh_start) { -+ if (s->mac_reg[RDH] == rdh_start || -+ rdh_start >= s->mac_reg[RDLEN] / sizeof(desc)) { - DBGOUT(RXERR, "RDH wraparound @%x, RDT %x, RDLEN %x\n", - rdh_start, s->mac_reg[RDT], s->mac_reg[RDLEN]); - set_ics(s, 0, E1000_ICS_RXO); --- -1.8.3.1 diff --git a/qemu.trad.CVE-2016-2538.patch b/qemu.trad.CVE-2016-2538.patch deleted file mode 100644 index be05dd7..0000000 --- a/qemu.trad.CVE-2016-2538.patch +++ /dev/null @@ -1,56 +0,0 @@ -From: Prasad J Pandit - -When processing remote NDIS control message packets, -the USB Net device emulator uses a fixed length(4096) data buffer. -The incoming informationBufferOffset & Length combination could -overflow and cross that range. Check control message buffer -offsets and length to avoid it. - -Reported-by: Qinghao Tang -Signed-off-by: Prasad J Pandit ---- - hw/usb/dev-network.c | 9 ++++++--- - 1 file changed, 6 insertions(+), 3 deletions(-) - -Update as per review - -> https://lists.gnu.org/archive/html/qemu-devel/2016-02/msg03475.html - -diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c -index 8a4ff49..180adce 100644 ---- a/tools/qemu-xen-traditional/hw/usb-net.c -+++ b/tools/qemu-xen-traditional/hw/usb-net.c -@@ -915,8 +915,9 @@ static int rndis_query_response(USBNetState *s, - - bufoffs = le32_to_cpu(buf->InformationBufferOffset) + 8; - buflen = le32_to_cpu(buf->InformationBufferLength); -- if (bufoffs + buflen > length) -+ if (buflen > length || bufoffs >= length || bufoffs + buflen > length) { - return USB_RET_STALL; -+ } - - infobuflen = ndis_query(s, le32_to_cpu(buf->OID), - bufoffs + (uint8_t *) buf, buflen, infobuf, -@@ -961,8 +962,9 @@ static int rndis_set_response(USBNetState *s, - - bufoffs = le32_to_cpu(buf->InformationBufferOffset) + 8; - buflen = le32_to_cpu(buf->InformationBufferLength); -- if (bufoffs + buflen > length) -+ if (buflen > length || bufoffs >= length || bufoffs + buflen > length) { - return USB_RET_STALL; -+ } - - ret = ndis_set(s, le32_to_cpu(buf->OID), - bufoffs + (uint8_t *) buf, buflen); -@@ -1212,8 +1214,9 @@ static void usb_net_handle_dataout(USBNetState *s, USBPacket *p) - if (le32_to_cpu(msg->MessageType) == RNDIS_PACKET_MSG) { - uint32_t offs = 8 + le32_to_cpu(msg->DataOffset); - uint32_t size = le32_to_cpu(msg->DataLength); -- if (offs + size <= len) -+ if (offs < len && size < len && offs + size <= len) { - qemu_send_packet(s->vc, s->out_buf + offs, size); -+ } - } - s->out_ptr -= len; - memmove(s->out_buf, &s->out_buf[len], s->out_ptr); --- -2.5.0 diff --git a/qemu.trad.CVE-2016-2841.patch b/qemu.trad.CVE-2016-2841.patch deleted file mode 100644 index 6979fbc..0000000 --- a/qemu.trad.CVE-2016-2841.patch +++ /dev/null @@ -1,34 +0,0 @@ -From: Prasad J Pandit - -Ne2000 NIC uses ring buffer of NE2000_MEM_SIZE(49152) -bytes to process network packets. Registers PSTART & PSTOP -define ring buffer size & location. Setting these registers -to invalid values could lead to infinite loop or OOB r/w -access issues. Add check to avoid it. - -Reported-by: Yang Hongke -Signed-off-by: Prasad J Pandit ---- - hw/net/ne2000.c | 4 ++++ - 1 file changed, 4 insertions(+) - -Update per review: - -> https://lists.gnu.org/archive/html/qemu-devel/2016-02/msg05522.html - -diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c -index b032212..ced4666 100644 ---- a/tools/qemu-xen-traditional/hw/ne2000.c -+++ b/tools/qemu-xen-traditional/hw/ne2000.c -@@ -154,6 +154,10 @@ static int ne2000_buffer_full(NE2000State *s) - { - int avail, index, boundary; - -+ if (s->stop <= s->start) { -+ return 1; -+ } -+ - index = s->curpag << 8; - boundary = s->boundary << 8; - if (index < boundary) --- -2.5.0 diff --git a/qemu.trad.CVE-2016-2857.patch b/qemu.trad.CVE-2016-2857.patch deleted file mode 100644 index 5bef1a7..0000000 --- a/qemu.trad.CVE-2016-2857.patch +++ /dev/null @@ -1,45 +0,0 @@ -From: Prasad J Pandit - -While computing IP checksum, 'net_checksum_calculate' reads -payload length from the packet. It could exceed the given 'data' -buffer size. Add a check to avoid it. - -Reported-by: Liu Ling -Signed-off-by: Prasad J Pandit ---- - net/checksum.c | 10 ++++++++-- - 1 file changed, 8 insertions(+), 2 deletions(-) - -Update as per review: - -> https://lists.gnu.org/archive/html/qemu-devel/2016-02/msg06121.html - -diff --git a/net/checksum.c b/net/checksum.c -index 14c0855..0942437 100644 ---- a/tools/qemu-xen-traditional/net-checksum.c -+++ b/tools/qemu-xen-traditional/net-checksum.c -@@ -59,6 +59,11 @@ void net_checksum_calculate(uint8_t *data, int length) - int hlen, plen, proto, csum_offset; - uint16_t csum; - -+ /* Ensure data has complete L2 & L3 headers. */ -+ if (length < 14 + 20) { -+ return; -+ } -+ - if ((data[14] & 0xf0) != 0x40) - return; /* not IPv4 */ - hlen = (data[14] & 0x0f) * 4; -@@ -76,8 +81,9 @@ void net_checksum_calculate(uint8_t *data, int length) - return; - } - -- if (plen < csum_offset+2) -- return; -+ if (plen < csum_offset + 2 || 14 + hlen + plen > length) { -+ return; -+ } - - data[14+hlen+csum_offset] = 0; - data[14+hlen+csum_offset+1] = 0; --- -2.5.0 diff --git a/qemu.trad.CVE-2016-4001.patch b/qemu.trad.CVE-2016-4001.patch deleted file mode 100644 index 9ca362f..0000000 --- a/qemu.trad.CVE-2016-4001.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 3a15cc0e1ee7168db0782133d2607a6bfa422d66 Mon Sep 17 00:00:00 2001 -From: Prasad J Pandit -Date: Fri, 8 Apr 2016 11:33:48 +0530 -Subject: [PATCH] net: stellaris_enet: check packet length against receive buffer - -When receiving packets over Stellaris ethernet controller, it -uses receive buffer of size 2048 bytes. In case the controller -accepts large(MTU) packets, it could lead to memory corruption. -Add check to avoid it. - -Reported-by: Oleksandr Bazhaniuk -Signed-off-by: Prasad J Pandit -Message-id: 1460095428-22698-1-git-send-email-ppandit@redhat.com -Reviewed-by: Peter Maydell -Signed-off-by: Peter Maydell ---- - tools/qemu-xen-traditional/hw/stellaris_enet.c | 12 +++++++++++- - 1 files changed, 11 insertions(+), 1 deletions(-) - -diff --git a/tools/qemu-xen-traditional/hw/stellaris_enet.c b/tools/qemu-xen-traditional/hw/stellaris_enet.c -index 84cf60b..6880894 100644 ---- a/tools/qemu-xen-traditional/hw/stellaris_enet.c -+++ b/tools/qemu-xen-traditional/hw/stellaris_enet.c -@@ -236,8 +236,18 @@ static ssize_t stellaris_enet_receive(NetClientState *nc, const uint8_t *buf, si - n = s->next_packet + s->np; - if (n >= 31) - n -= 31; -- s->np++; - -+ if (size >= sizeof(s->rx[n].data) - 6) { -+ /* If the packet won't fit into the -+ * emulated 2K RAM, this is reported -+ * as a FIFO overrun error. -+ */ -+ s->ris |= SE_INT_FOV; -+ stellaris_enet_update(s); -+ return -1; -+ } -+ -+ s->np++; - s->rx[n].len = size + 6; - p = s->rx[n].data; - *(p++) = (size + 6); --- -1.7.0.4 - diff --git a/qemu.trad.CVE-2016-4002.patch b/qemu.trad.CVE-2016-4002.patch deleted file mode 100644 index e122297..0000000 --- a/qemu.trad.CVE-2016-4002.patch +++ /dev/null @@ -1,31 +0,0 @@ -From: Prasad J Pandit - -When receiving packets over MIPSnet network device, it uses - receive buffer of size 1514 bytes. In case the controller -accepts large(MTU) packets, it could lead to memory corruption. -Add check to avoid it. - -Reported by: Oleksandr Bazhaniuk - -Signed-off-by: Prasad J Pandit ---- - tools/qemu-xen-traditional/hw/mipsnet.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/tools/qemu-xen-traditional/hw/mipsnet.c b/tools/qemu-xen-traditional/hw/mipsnet.c -index f261011..e134b31 100644 ---- a/tools/qemu-xen-traditional/hw/mipsnet.c -+++ b/tools/qemu-xen-traditional/hw/mipsnet.c -@@ -82,6 +82,9 @@ static ssize_t mipsnet_receive(NetClientState *nc, const uint8_t *buf, size_t si - if (!mipsnet_can_receive(opaque)) - return; - -+ if (size >= sizeof(s->rx_buffer)) { -+ return; -+ } - s->busy = 1; - - /* Just accept everything. */ --- -2.5.5 - diff --git a/qemu.trad.CVE-2016-4439.patch b/qemu.trad.CVE-2016-4439.patch deleted file mode 100644 index 6816695..0000000 --- a/qemu.trad.CVE-2016-4439.patch +++ /dev/null @@ -1,44 +0,0 @@ ------------------------------------------------------------------------- -*From*: P J P -*Subject*: [Qemu-devel] [PATCH 1/2] scsi: check command buffer length -before write(CVE-2016-4439) -*Date*: Thu, 19 May 2016 16:09:30 +0530 - ------------------------------------------------------------------------- - -From: Prasad J Pandit - -The 53C9X Fast SCSI Controller(FSC) comes with an internal 16-byte -FIFO buffer. It is used to handle command and data transfer. While -writing to this command buffer 's->cmdbuf[TI_BUFSZ=16]', a check -was missing to validate input length. Add check to avoid OOB write -access. - -Fixes CVE-2016-4439 -Reported-by: Li Qiang - -Signed-off-by: Prasad J Pandit ---- - hw/scsi/esp.c | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/tools/qemu-xen-traditional/hw/esp.c b/tools/qemu-xen-traditional/hw/esp.c -index 8961be2..01497e6 100644 ---- a/tools/qemu-xen-traditional/hw/esp.c -+++ b/tools/qemu-xen-traditional/hw/esp.c -@@ -448,7 +448,11 @@ void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val) - break; - case ESP_FIFO: - if (s->do_cmd) { -- s->cmdbuf[s->cmdlen++] = val & 0xff; -+ if (s->cmdlen < TI_BUFSZ) { -+ s->cmdbuf[s->cmdlen++] = val & 0xff; -+ } else { -+ ESP_ERROR("fifo overrun\n"); -+ } - } else if (s->ti_size == TI_BUFSZ - 1) { - ESP_ERROR("fifo overrun\n"); - } else { --- -2.5.5 - diff --git a/qemu.trad.CVE-2016-4441.patch b/qemu.trad.CVE-2016-4441.patch deleted file mode 100644 index fab6a35..0000000 --- a/qemu.trad.CVE-2016-4441.patch +++ /dev/null @@ -1,68 +0,0 @@ ------------------------------------------------------------------------- -*From*: P J P -*Subject*: [Qemu-devel] [PATCH 2/2] scsi: check dma length before -reading scsi command(CVE-2016-4441) -*Date*: Thu, 19 May 2016 16:09:31 +0530 - ------------------------------------------------------------------------- - -From: Prasad J Pandit - -The 53C9X Fast SCSI Controller(FSC) comes with an internal 16-byte -FIFO buffer. It is used to handle command and data transfer. -Routine get_cmd() uses DMA to read scsi commands into this buffer. -Add check to validate DMA length against buffer size to avoid any -overrun. - -Fixes CVE-2016-4441 -Reported-by: Li Qiang - -Signed-off-by: Prasad J Pandit ---- - hw/scsi/esp.c | 11 +++++++---- - 1 file changed, 7 insertions(+), 4 deletions(-) - -diff --git a/tools/qemu-xen-traditional/hw/esp.c b/tools/qemu-xen-traditional/hw/esp.c -index 01497e6..591c817 100644 ---- a/tools/qemu-xen-traditional/hw/esp.c -+++ b/tools/qemu-xen-traditional/hw/esp.c -@@ -82,7 +82,7 @@ void esp_request_cancelled(SCSIRequest *req) - } - } - --static uint32_t get_cmd(ESPState *s, uint8_t *buf) -+static uint32_t get_cmd(ESPState *s, uint8_t *buf, uint8_t buflen) - { - uint32_t dmalen; - int target; -@@ -92,6 +92,9 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf) - target = s->wregs[ESP_WBUSID] & BUSID_DID; - if (s->dma) { - dmalen = s->rregs[ESP_TCLO] | (s->rregs[ESP_TCMID] << 8); -+ if (dmalen > buflen) { -+ return 0; -+ } - s->dma_memory_read(s->dma_opaque, buf, dmalen); - } else { - dmalen = s->ti_size; -@@ -166,7 +169,7 @@ static void handle_satn(ESPState *s) - uint8_t buf[32]; - int len; - -- len = get_cmd(s, buf); -+ len = get_cmd(s, buf, sizeof(buf)); - if (len) - do_cmd(s, buf); - } -@@ -192,7 +195,7 @@ static void handle_satn_stop(ESPState *s) - - static void handle_satn_stop(ESPState *s) - { -- s->cmdlen = get_cmd(s, s->cmdbuf); -+ s->cmdlen = get_cmd(s, s->cmdbuf, sizeof(s->cmdbuf)); - if (s->cmdlen) { - DPRINTF("Set ATN & Stop: cmdlen %d\n", s->cmdlen); - s->do_cmd = 1; --- -2.5.5 - diff --git a/qemu.trad.CVE-2016-5238.patch b/qemu.trad.CVE-2016-5238.patch deleted file mode 100644 index f6767de..0000000 --- a/qemu.trad.CVE-2016-5238.patch +++ /dev/null @@ -1,65 +0,0 @@ ------------------------------------------------------------------------- -*From*: Paolo Bonzini -*Subject*: Re: [Qemu-devel] [PATCH] scsi: check buffer length before -reading scsi command -*Date*: Wed, 1 Jun 2016 15:10:16 +0200 -*User-agent*: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 -Thunderbird/45.1.0 - ------------------------------------------------------------------------- - - -On 31/05/2016 19:53, P J P wrote: ->/ From: Prasad J Pandit / ->/ / ->/ The 53C9X Fast SCSI Controller(FSC) comes with an internal 16-byte/ ->/ FIFO buffer. It is used to handle command and data transfer./ ->/ Routine get_cmd() in non-DMA mode, uses 'ti_size' to read scsi/ ->/ command into a buffer. Add check to validate command length against/ ->/ buffer size to avoid any overrun./ ->/ / ->/ Reported-by: Li Qiang / ->/ Signed-off-by: Prasad J Pandit / ->/ ---/ ->/ hw/scsi/esp.c | 3 +++/ ->/ 1 file changed, 3 insertions(+)/ ->/ / ->/ diff --git a/tools/qemu-xen-traditional/hw/esp.c b/tools/qemu-xen-traditional/hw/esp.c/ ->/ index 60c1b28..953027a 100644/ ->/ --- a/tools/qemu-xen-traditional/hw/esp.c/ ->/ +++ b/tools/qemu-xen-traditional/hw/esp.c/ ->/ @@ -98,6 +98,9 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf, uint8_t / ->/ buflen)/ ->/ s->dma_memory_read(s->dma_opaque, buf, dmalen);/ ->/ } else {/ ->/ dmalen = s->ti_size;/ ->/ + if (dmalen > TI_BUFSZ) {/ ->/ + return 0;/ ->/ + }/ ->/ memcpy(buf, s->ti_buf, dmalen);/ ->/ buf[0] = buf[2] >> 5;/ ->/ }/ ->/ / - -In theory this shouldn't happen, but I agree that it is better to be -defensive. I'm queuing this patch. - -At least the following patch is needed to ensure that ti_size always -matches ti_rptr/ti_wptr (Hervé, what do you think about it? should I -resubmit it formally?). Also, things are more complicated than -necessary due to ti_size being used for both DMA and FIFO transfers. - -diff --git a/tools/qemu-xen-traditional/hw/esp.c b/tools/qemu-xen-traditional/hw/esp.c -index c2f6f8f..6407844 100644 ---- a/tools/qemu-xen-traditional/hw/esp.c -+++ b/tools/qemu-xen-traditional/hw/esp.c -@@ -222,7 +222,7 @@ static void write_response(ESPState *s) - } else { - s->ti_size = 2; - s->ti_rptr = 0; -- s->ti_wptr = 0; -+ s->ti_wptr = 2; - s->rregs[ESP_RFLAGS] = 2; - } - esp_raise_irq(s); - diff --git a/qemu.trad.CVE-2016-5338.patch b/qemu.trad.CVE-2016-5338.patch deleted file mode 100644 index be36dca..0000000 --- a/qemu.trad.CVE-2016-5338.patch +++ /dev/null @@ -1,76 +0,0 @@ ------------------------------------------------------------------------- -*From*: P J P -*Subject*: [Qemu-devel] [PATCH v3] scsi: esp: check TI buffer index -before read/write -*Date*: Mon, 6 Jun 2016 22:04:43 +0530 - ------------------------------------------------------------------------- - -From: Prasad J Pandit - -The 53C9X Fast SCSI Controller(FSC) comes with internal 16-byte -FIFO buffers. One is used to handle commands and other is for -information transfer. Three control variables 'ti_rptr', -'ti_wptr' and 'ti_size' are used to control r/w access to the -information transfer buffer ti_buf[TI_BUFSZ=16]. In that, - -'ti_rptr' is used as read index, where read occurs. -'ti_wptr' is a write index, where write would occur. -'ti_size' indicates total bytes to be read from the buffer. - -While reading/writing to this buffer, index could exceed its -size. Add check to avoid OOB r/w access. - -Reported-by: Huawei PSIRT -Reported-by: Li Qiang -Signed-off-by: Prasad J Pandit ---- - hw/scsi/esp.c | 20 +++++++++----------- - 1 file changed, 9 insertions(+), 11 deletions(-) - -Update as per: - -> https://lists.gnu.org/archive/html/qemu-devel/2016-06/msg01326.html - -diff --git a/tools/qemu-xen-traditional/hw/esp.c b/tools/qemu-xen-traditional/hw/esp.c -index c2f6f8f..4b94bbc 100644 ---- a/tools/qemu-xen-traditional/hw/esp.c -+++ b/tools/qemu-xen-traditional/hw/esp.c -@@ -403,18 +403,17 @@ uint64_t esp_reg_read(ESPState *s, uint32_t saddr) - DPRINTF("read reg[%d]: 0x%2.2x\n", saddr, s->rregs[saddr]); - switch (saddr) { - case ESP_FIFO: -- if (s->ti_size > 0) { -+ if ((s->rregs[ESP_RSTAT] & STAT_PIO_MASK) == 0) { -+ /* Data out. */ -+ ESP_ERROR("PIO data read not implemented\n"); -+ s->rregs[ESP_FIFO] = 0; -+ esp_raise_irq(s); -+ } else if (s->ti_rptr < s->ti_wptr) { - s->ti_size--; -- if ((s->rregs[ESP_RSTAT] & STAT_PIO_MASK) == 0) { -- /* Data out. */ -- ESP_ERROR("PIO data read not implemented\n"); -- s->rregs[ESP_FIFO] = 0; -- } else { -- s->rregs[ESP_FIFO] = s->ti_buf[s->ti_rptr++]; -- } -+ s->rregs[ESP_FIFO] = s->ti_buf[s->ti_rptr++]; - esp_raise_irq(s); - } -- if (s->ti_size == 0) { -+ if (s->ti_rptr == s->ti_wptr) { - s->ti_rptr = 0; - s->ti_wptr = 0; - } -@@ -459,7 +457,7 @@ void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val) - } else { - ESP_ERROR("fifo overrun\n"); - } -- } else if (s->ti_size == TI_BUFSZ - 1) { -+ } else if (s->ti_wptr == TI_BUFSZ - 1) { - ESP_ERROR("fifo overrun\n"); - } else { - s->ti_size++; --- -2.5.5 - diff --git a/qemu.trad.CVE-2016-6351.patch b/qemu.trad.CVE-2016-6351.patch deleted file mode 100644 index 10f1ab3..0000000 --- a/qemu.trad.CVE-2016-6351.patch +++ /dev/null @@ -1,81 +0,0 @@ -From 926cde5f3e4d2504ed161ed0cb771ac7cad6fd11 Mon Sep 17 00:00:00 2001 -From: Prasad J Pandit -Date: Thu, 16 Jun 2016 00:22:35 +0200 -Subject: [PATCH] scsi: esp: make cmdbuf big enough for maximum CDB size - -While doing DMA read into ESP command buffer 's->cmdbuf', it could -write past the 's->cmdbuf' area, if it was transferring more than 16 -bytes. Increase the command buffer size to 32, which is maximum when -'s->do_cmd' is set, and add a check on 'len' to avoid OOB access. - -Reported-by: Li Qiang -Signed-off-by: Prasad J Pandit -Signed-off-by: Paolo Bonzini ---- - hw/esp.c | 6 ++++-- - hw/esp.c | 3 ++- - 2 files changed, 6 insertions(+), 3 deletions(-) - -diff --git a/hw/esp.c b/hw/esp.c -index 64680b3..baa0a2c 100644 ---- a/hw/esp.c -+++ b/hw/esp.c -@@ -25,6 +25,7 @@ - #include "hw.h" - #include "scsi-disk.h" - #include "scsi.h" -+#include - - /* debug ESP card */ - //#define DEBUG_ESP -@@ -248,6 +248,8 @@ static void esp_do_dma(ESPState *s) - len = s->dma_left; - if (s->do_cmd) { - DPRINTF("command len %d + %d\n", s->cmdlen, len); -+ assert (s->cmdlen <= sizeof(s->cmdbuf) && -+ len <= sizeof(s->cmdbuf) - s->cmdlen); - s->dma_memory_read(s->dma_opaque, &s->cmdbuf[s->cmdlen], len); - s->ti_size = 0; - s->cmdlen = 0; -@@ -345,7 +347,7 @@ static void handle_ti(ESPState *s) - s->dma_counter = dmalen; - - if (s->do_cmd) -- minlen = (dmalen < 32) ? dmalen : 32; -+ minlen = (dmalen < ESP_CMDBUF_SZ) ? dmalen : ESP_CMDBUF_SZ; - else if (s->ti_size < 0) - minlen = (dmalen < -s->ti_size) ? dmalen : -s->ti_size; - else -@@ -449,7 +451,7 @@ void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val) - break; - case ESP_FIFO: - if (s->do_cmd) { -- if (s->cmdlen < TI_BUFSZ) { -+ if (s->cmdlen < ESP_CMDBUF_SZ) { - s->cmdbuf[s->cmdlen++] = val & 0xff; - } else { - ESP_ERROR("fifo overrun\n"); -diff --git a/hw/esp.c b/hw/esp.c -index 6c79527..d2c4886 100644 ---- a/hw/esp.c -+++ b/hw/esp.c -@@ -14,6 +14,7 @@ void esp_init(hwaddr espaddr, int it_shift, - - #define ESP_REGS 16 - #define TI_BUFSZ 16 -+#define ESP_CMDBUF_SZ 32 - - typedef struct ESPState ESPState; - -@@ -31,7 +32,7 @@ struct ESPState { - uint32_t dma; - SCSIDevice *scsi_dev[ESP_MAX_DEVS]; - SCSIDevice *current_dev; -- uint8_t cmdbuf[TI_BUFSZ]; -+ uint8_t cmdbuf[ESP_CMDBUF_SZ]; - uint32_t cmdlen; - uint32_t do_cmd; - --- -1.7.0.4 - diff --git a/qemu.trad.CVE-2016-8669.patch b/qemu.trad.CVE-2016-8669.patch deleted file mode 100644 index 05abe36..0000000 --- a/qemu.trad.CVE-2016-8669.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 3592fe0c919cf27a81d8e9f9b4f269553418bb01 Mon Sep 17 00:00:00 2001 -From: Prasad J Pandit -Date: Wed, 12 Oct 2016 11:28:08 +0530 -Subject: [PATCH] char: serial: check divider value against baud base - -16550A UART device uses an oscillator to generate frequencies -(baud base), which decide communication speed. This speed could -be changed by dividing it by a divider. If the divider is -greater than the baud base, speed is set to zero, leading to a -divide by zero error. Add check to avoid it. - -Reported-by: Huawei PSIRT -Signed-off-by: Prasad J Pandit -Message-Id: <1476251888-20238-1-git-send-email-ppandit@redhat.com> -Signed-off-by: Paolo Bonzini ---- - hw/char/serial.c | 3 ++- - 1 files changed, 2 insertions(+), 1 deletions(-) - -diff --git a/hw/serial.c b/hw/serial.c -index 3442f47..eec72b7 100644 ---- a/hw/serial.c -+++ b/hw/serial.c -@@ -153,8 +153,9 @@ static void serial_update_parameters(SerialState *s) - int speed, parity, data_bits, stop_bits, frame_size; - QEMUSerialSetParams ssp; - -- if (s->divider == 0) -+ if (s->divider == 0 || s->divider > s->baudbase) { - return; -+ } - - frame_size = 1; - if (s->lcr & 0x08) { --- -1.7.0.4 - diff --git a/qemu.trad.CVE-2016-8910.patch b/qemu.trad.CVE-2016-8910.patch deleted file mode 100644 index ddb67b1..0000000 --- a/qemu.trad.CVE-2016-8910.patch +++ /dev/null @@ -1,29 +0,0 @@ -From: Prasad J Pandit - -RTL8139 ethernet controller in C+ mode supports multiple -descriptor rings, each with maximum of 64 descriptors. While -processing transmit descriptor ring in 'rtl8139_cplus_transmit', -it does not limit the descriptor count and runs forever. Add -check to avoid it. - -Reported-by: Andrew Henderson -Signed-off-by: Prasad J Pandit ---- - hw/net/rtl8139.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/hw/rtl8139.c b/hw/rtl8139.c -index 3345bc6..f05e59c 100644 ---- a/hw/rtl8139.c -+++ b/hw/rtl8139.c -@@ -2350,7 +2350,7 @@ static void rtl8139_cplus_transmit(RTL8139State *s) - { - int txcount = 0; - -- while (rtl8139_cplus_transmit_one(s)) -+ while (txcount < 64 && rtl8139_cplus_transmit_one(s)) - { - ++txcount; - } --- -2.7.4 diff --git a/qemu.trad.CVE-2016-9776.patch b/qemu.trad.CVE-2016-9776.patch deleted file mode 100644 index 2098ed3..0000000 --- a/qemu.trad.CVE-2016-9776.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 77d54985b85a0cb760330ec2bd92505e0a2a97a9 Mon Sep 17 00:00:00 2001 -From: Prasad J Pandit -Date: Tue, 29 Nov 2016 00:38:39 +0530 -Subject: [PATCH] net: mcf: check receive buffer size register value - -ColdFire Fast Ethernet Controller uses a receive buffer size -register(EMRBR) to hold maximum size of all receive buffers. -It is set by a user before any operation. If it was set to be -zero, ColdFire emulator would go into an infinite loop while -receiving data in mcf_fec_receive. Add check to avoid it. - -Reported-by: Wjjzhang -Signed-off-by: Prasad J Pandit -Signed-off-by: Jason Wang ---- - hw/net/mcf_fec.c | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/hw/mcf_fec.c b/hw/mcf_fec.c -index dc61bac..4025eb3 100644 ---- a/hw/mcf_fec.c -+++ b/hw/mcf_fec.c -@@ -393,7 +393,7 @@ static void mcf_fec_write(void *opaque, hwaddr addr, - s->tx_descriptor = s->etdsr; - break; - case 0x188: -- s->emrbr = value & 0x7f0; -+ s->emrbr = value > 0 ? value & 0x7F0 : 0x7F0; - break; - default: - cpu_abort(cpu_single_env, "mcf_fec_write Bad address 0x%x\n", --- -1.7.0.4 - diff --git a/qemu.trad.CVE-2017-6505.patch b/qemu.trad.CVE-2017-6505.patch deleted file mode 100644 index b374a3d..0000000 --- a/qemu.trad.CVE-2017-6505.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 95ed56939eb2eaa4e2f349fe6dcd13ca4edfd8fb Mon Sep 17 00:00:00 2001 -From: Li Qiang -Date: Tue, 7 Feb 2017 02:23:33 -0800 -Subject: [PATCH] usb: ohci: limit the number of link eds - -The guest may builds an infinite loop with link eds. This patch -limit the number of linked ed to avoid this. - -Signed-off-by: Li Qiang -Message-id: 5899a02e.45ca240a.6c373.93c1@mx.google.com -Signed-off-by: Gerd Hoffmann ---- - hw/usb-ohci.c | 9 ++++++++- - 1 file changed, 8 insertions(+), 1 deletion(-) - -diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c -index 2cba3e3..21c93e0 100644 ---- a/hw/usb-ohci.c -+++ b/hw/usb-ohci.c -@@ -42,6 +42,8 @@ - - #define OHCI_MAX_PORTS 15 - -+#define ED_LINK_LIMIT 4 -+ - static int64_t usb_frame_time; - static int64_t usb_bit_time; - -@@ -1184,7 +1186,7 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) - uint32_t next_ed; - uint32_t cur; - int active; -- -+ uint32_t link_cnt = 0; - active = 0; - - if (head == 0) -@@ -1199,6 +1201,10 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) - - next_ed = ed.next & OHCI_DPTR_MASK; - -+ if (++link_cnt > ED_LINK_LIMIT) { -+ return 0; -+ } -+ - if ((ed.head & OHCI_ED_H) || (ed.flags & OHCI_ED_K)) { - uint32_t addr; - /* Cancel pending packets for ED that have been paused. */ --- -1.8.3.1 - diff --git a/qemu.trad.CVE-2017-7718.patch b/qemu.trad.CVE-2017-7718.patch deleted file mode 100644 index 70382ab..0000000 --- a/qemu.trad.CVE-2017-7718.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 215902d7b6fb50c6fc216fc74f770858278ed904 Mon Sep 17 00:00:00 2001 -From: hangaohuai -Date: Tue, 14 Mar 2017 14:39:19 +0800 -Subject: [PATCH] fix :cirrus_vga fix OOB read case qemu Segmentation fault - -check the validity of parameters in cirrus_bitblt_rop_fwd_transp_xxx -and cirrus_bitblt_rop_fwd_xxx to avoid the OOB read which causes qemu Segmentation fault. - -After the fix, we will touch the assert in -cirrus_invalidate_region: -assert(off_cur_end >= off_cur); - -Signed-off-by: fangying -Signed-off-by: hangaohuai -Message-id: 20170314063919.16200-1-hangaohuai@huawei.com -Signed-off-by: Gerd Hoffmann ---- - hw/cirrus_vga_rop.h | 10 ++++++++++ - 1 file changed, 10 insertions(+) - -diff --git a/hw/cirrus_vga_rop.h b/hw/cirrus_vga_rop.h -index 0925a00..b7447f8 100644 ---- a/hw/cirrus_vga_rop.h -+++ b/hw/cirrus_vga_rop.h -@@ -97,6 +97,11 @@ glue(glue(cirrus_bitblt_rop_fwd_transp_, ROP_NAME),_8)(CirrusVGAState *s, - src = src_ - src_base; - dstpitch -= bltwidth; - srcpitch -= bltwidth; -+ -+ if (bltheight > 1 && (dstpitch < 0 || srcpitch < 0)) { -+ return; -+ } -+ - for (y = 0; y < bltheight; y++) { - for (x = 0; x < bltwidth; x++) { - p = *(dst_base + m(dst)); -@@ -143,6 +148,11 @@ glue(glue(cirrus_bitblt_rop_fwd_transp_, ROP_NAME),_16)(CirrusVGAState *s, - src = src_ - src_base; - dstpitch -= bltwidth; - srcpitch -= bltwidth; -+ -+ if (bltheight > 1 && (dstpitch < 0 || srcpitch < 0)) { -+ return; -+ } -+ - for (y = 0; y < bltheight; y++) { - for (x = 0; x < bltwidth; x+=2) { - p1 = *(dst_base + m(dst)); --- -1.8.3.1 - diff --git a/qemu.trad.CVE-2017-8309.patch b/qemu.trad.CVE-2017-8309.patch deleted file mode 100644 index 10b5b05..0000000 --- a/qemu.trad.CVE-2017-8309.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 3268a845f41253fb55852a8429c32b50f36f349a Mon Sep 17 00:00:00 2001 -From: Gerd Hoffmann -Date: Fri, 28 Apr 2017 09:56:12 +0200 -Subject: [PATCH] audio: release capture buffers - -AUD_add_capture() allocates two buffers which are never released. -Add the missing calls to AUD_del_capture(). - -Impact: Allows vnc clients to exhaust host memory by repeatedly -starting and stopping audio capture. - -Fixes: CVE-2017-8309 -Cc: P J P -Cc: Huawei PSIRT -Reported-by: "Jiangxin (hunter, SCC)" -Signed-off-by: Gerd Hoffmann -Reviewed-by: Prasad J Pandit -Message-id: 20170428075612.9997-1-kraxel@redhat.com ---- - audio/audio.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/audio/audio.c b/audio/audio.c -index c8898d8..beafed2 100644 ---- a/audio/audio.c -+++ b/audio/audio.c -@@ -2028,6 +2028,8 @@ void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque) - sw = sw1; - } - LIST_REMOVE (cap, entries); -+ qemu_free (cap->hw.mix_buf); -+ qemu_free (cap->buf); - qemu_free (cap); - } - return; --- -1.8.3.1 - diff --git a/qemu.trad.CVE-2017-9330.patch b/qemu.trad.CVE-2017-9330.patch deleted file mode 100644 index 046e3e0..0000000 --- a/qemu.trad.CVE-2017-9330.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 26f670a244982335cc08943fb1ec099a2c81e42d Mon Sep 17 00:00:00 2001 -From: Li Qiang -Date: Tue, 7 Feb 2017 03:15:03 -0800 -Subject: [PATCH] usb: ohci: fix error return code in servicing iso td - -It should return 1 if an error occurs when reading iso td. -This will avoid an infinite loop issue in ohci_service_ed_list. - -Signed-off-by: Li Qiang -Message-id: 5899ac3e.1033240a.944d5.9a2d@mx.google.com -Signed-off-by: Gerd Hoffmann ---- - hw/usb-ohci.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c -index c82a92f..2cba3e3 100644 ---- a/hw/usb-ohci.c -+++ b/hw/usb-ohci.c -@@ -725,7 +725,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, - - if (!ohci_read_iso_td(addr, &iso_td)) { - printf("usb-ohci: ISO_TD read error at %x\n", addr); -- return 0; -+ return 1; - } - - starting_frame = OHCI_BM(iso_td.flags, TD_SF); --- -1.8.3.1 - diff --git a/qemu.trad.bug1399055.patch b/qemu.trad.bug1399055.patch deleted file mode 100644 index 69f8fd6..0000000 --- a/qemu.trad.bug1399055.patch +++ /dev/null @@ -1,76 +0,0 @@ -From 4299b90e9ba9ce5ca9024572804ba751aa1a7e70 Mon Sep 17 00:00:00 2001 -From: Prasad J Pandit -Date: Tue, 18 Oct 2016 13:15:17 +0530 -Subject: [PATCH] display: cirrus: check vga bits per pixel(bpp) value - -In Cirrus CLGD 54xx VGA Emulator, if cirrus graphics mode is VGA, -'cirrus_get_bpp' returns zero(0), which could lead to a divide -by zero error in while copying pixel data. The same could occur -via blit pitch values. Add check to avoid it. - -Reported-by: Huawei PSIRT -Signed-off-by: Prasad J Pandit -Message-id: 1476776717-24807-1-git-send-email-ppandit@redhat.com -Signed-off-by: Gerd Hoffmann ---- - hw/cirrus_vga.c | 14 ++++++++++---- - 1 files changed, 10 insertions(+), 4 deletions(-) - -diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c -index 3d712d5..bdb092e 100644 ---- a/hw/cirrus_vga.c -+++ b/hw/cirrus_vga.c -@@ -272,6 +272,9 @@ static void cirrus_update_memory_access(CirrusVGAState *s); - static bool blit_region_is_unsafe(struct CirrusVGAState *s, - int32_t pitch, int32_t addr) - { -+ if (!pitch) { -+ return true; -+ } - if (pitch < 0) { - int64_t min = addr - + ((int64_t)s->cirrus_blt_height - 1) * pitch -@@ -715,7 +718,7 @@ static int cirrus_bitblt_videotovideo_patterncopy(CirrusVGAState * s) - s->cirrus_addr_mask)); - } - --static void cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h) -+static int cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h) - { - int sx = 0, sy = 0; - int dx = 0, dy = 0; -@@ -729,6 +732,9 @@ static void cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h) - int width, height; - - depth = s->get_bpp((VGAState *)s) / 8; -+ if (!depth) { -+ return 0; -+ } - s->get_resolution((VGAState *)s, &width, &height); - - /* extra x, y */ -@@ -783,6 +789,8 @@ static void cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h) - cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, - s->cirrus_blt_dstpitch, s->cirrus_blt_width, - s->cirrus_blt_height); -+ -+ return 1; - } - - static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s) -@@ -790,11 +798,9 @@ static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s) - if (blit_is_unsafe(s)) - return 0; - -- cirrus_do_copy(s, s->cirrus_blt_dstaddr - s->start_addr, -+ return cirrus_do_copy(s, s->cirrus_blt_dstaddr - s->start_addr, - s->cirrus_blt_srcaddr - s->start_addr, - s->cirrus_blt_width, s->cirrus_blt_height); -- -- return 1; - } - - /*************************************** --- -1.7.0.4 - diff --git a/sources b/sources index 1e984f8..0aa5da3 100644 --- a/sources +++ b/sources @@ -4,4 +4,5 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.19.2.tar.gz) = 35147b0d755b5a603915fa5017bc021d4a238ab96ee1139dfee8e0f6ba76e64b89d5f331fbd60549682f45ee983d3d8f1cb95c53eed22373a7fa06074ca8da09 +SHA512 (xen-4.20.1.tar.gz) = e4502ddaeb66de4c30d59e90af198586683c28b56bf0eb03ca5508f663fef5e6582c009861ff5b66b7f0df6e5f6bc12337eff0ec775d72e7cb189fd2079dbd9d +SHA512 (mini-os-4.20.0.tar.xz) = 52d5d0985e1b4c26b761c94a3f7ca543bc388a96f69710524ebf34dd5b01d8f774fd5a3f6a50739c8b7c7edfdf07b594f5d44de8f389ea4138a62f9c5884c4e6 diff --git a/xen.drop.brctl.patch b/xen.drop.brctl.patch deleted file mode 100644 index f420b8e..0000000 --- a/xen.drop.brctl.patch +++ /dev/null @@ -1,8 +0,0 @@ ---- xen-4.11.0-rc7/tools/qemu-xen-traditional/i386-dm/qemu-ifup-Linux.orig 2017-09-15 19:37:27.000000000 +0100 -+++ xen-4.11.0-rc7/tools/qemu-xen-traditional/i386-dm/qemu-ifup-Linux 2018-07-03 20:17:52.934780235 +0100 -@@ -34,4 +34,4 @@ - fi - - ifconfig $1 0.0.0.0 up --brctl addif $bridge $1 || true -+ip link set $1 master $bridge || true diff --git a/xen.efi.build.patch b/xen.efi.build.patch index 69d1e93..b5455df 100644 --- a/xen.efi.build.patch +++ b/xen.efi.build.patch @@ -1,13 +1,13 @@ ---- xen-4.19.0-rc1/xen/arch/x86/arch.mk.orig 2024-07-01 18:31:39.000000000 +0100 -+++ xen-4.19.0-rc1/xen/arch/x86/arch.mk 2024-07-02 21:10:00.796753718 +0100 -@@ -91,7 +91,9 @@ +--- xen-4.20.0-rc4/xen/arch/x86/arch.mk.orig 2025-02-07 11:56:01.000000000 +0000 ++++ xen-4.20.0-rc4/xen/arch/x86/arch.mk 2025-02-09 22:56:05.579507311 +0000 +@@ -95,7 +95,9 @@ -c $(srctree)/$(efi-check).c -o $(efi-check).o,y) # Check if the linker supports PE. --EFI_LDFLAGS := $(patsubst -m%,-mi386pep,$(LDFLAGS)) --subsystem=10 -+#EFI_LDFLAGS := $(patsubst -m%,-mi386pep,$(LDFLAGS)) --subsystem=10 +-EFI_LDFLAGS := $(patsubst -m%,-mi386pep,$(LDFLAGS)) --subsystem=10 --enable-long-section-names ++#EFI_LDFLAGS := $(patsubst -m%,-mi386pep,$(LDFLAGS)) --subsystem=10 --enable-long-section-names +# use a reduced set of options from LDFLAGS -+EFI_LDFLAGS = --as-needed --build-id=sha1 -mi386pep --subsystem=10 ++EFI_LDFLAGS = --as-needed --build-id=sha1 -mi386pep --subsystem=10 --enable-long-section-names LD_PE_check_cmd = $(call ld-option,$(EFI_LDFLAGS) --image-base=0x100000000 -o $(efi-check).efi $(efi-check).o) XEN_BUILD_PE := $(LD_PE_check_cmd) diff --git a/xen.fedora.crypt.patch b/xen.fedora.crypt.patch deleted file mode 100644 index 7aba2d4..0000000 --- a/xen.fedora.crypt.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- xen-4.5.1/tools/qemu-xen-traditional/vnc.c.orig 2015-07-12 21:55:32.875504811 +0100 -+++ xen-4.5.1/tools/qemu-xen-traditional/vnc.c 2015-07-12 22:03:03.860005391 +0100 -@@ -2140,7 +2140,7 @@ - GNUTLS_VERSION_NUMBER >= 0x020200 /* 2.2.0 */ - static int vnc_set_gnutls_priority(gnutls_session_t s, int x509) - { -- const char *priority = x509 ? "NORMAL" : "NORMAL:+ANON-DH"; -+ const char *priority = x509 ? "@SYSTEM" : "@SYSTEM:+ANON-DH"; - int rc; - - rc = gnutls_priority_set_direct(s, priority, NULL); diff --git a/xen.gcc7.fix.patch b/xen.gcc7.fix.patch deleted file mode 100644 index b18ba2b..0000000 --- a/xen.gcc7.fix.patch +++ /dev/null @@ -1,12 +0,0 @@ ---- xen-4.8.0/extras/mini-os/Makefile.orig 2016-09-28 12:09:38.000000000 +0100 -+++ xen-4.8.0/extras/mini-os/Makefile 2017-02-15 21:15:19.340197960 +0000 -@@ -142,6 +142,9 @@ - APP_LDLIBS += -lz - APP_LDLIBS += -lm - LDLIBS += -lc -+ifeq ($(MINIOS_TARGET_ARCH),x86_32) -+LDLIBS += -L$(shell dirname `gcc -m32 -print-libgcc-file-name`) -lgcc -+endif - endif - - ifneq ($(APP_OBJS)-$(lwip),-y) diff --git a/xen.hypervisor.config b/xen.hypervisor.config index a498382..7f11043 100644 --- a/xen.hypervisor.config +++ b/xen.hypervisor.config @@ -1,32 +1,50 @@ # # Automatically generated file; DO NOT EDIT. -# Xen/x86 4.14.0-rc Configuration +# Xen/x86 4.20 Configuration # CONFIG_CC_IS_GCC=y -CONFIG_GCC_VERSION=100101 +CONFIG_GCC_VERSION=150001 CONFIG_CLANG_VERSION=0 +CONFIG_LD_IS_GNU=y CONFIG_CC_HAS_VISIBILITY_ATTRIBUTE=y +CONFIG_CC_SPLIT_SECTIONS=y +CONFIG_FUNCTION_ALIGNMENT_16B=y +CONFIG_FUNCTION_ALIGNMENT=16 CONFIG_X86_64=y CONFIG_X86=y CONFIG_ARCH_DEFCONFIG="arch/x86/configs/x86_64_defconfig" -CONFIG_INDIRECT_THUNK=y +CONFIG_CC_HAS_INDIRECT_THUNK=y CONFIG_HAS_AS_CET_SS=y +CONFIG_HAS_CC_CET_IBT=y # # Architecture Features # +CONFIG_AMD=y +CONFIG_INTEL=y +CONFIG_64BIT=y CONFIG_NR_CPUS=256 +CONFIG_NR_NUMA_NODES=64 CONFIG_PV=y CONFIG_PV32=y CONFIG_PV_LINEAR_PT=y CONFIG_HVM=y +CONFIG_AMD_SVM=y +CONFIG_INTEL_VMX=y +CONFIG_XEN_SHSTK=y +CONFIG_XEN_IBT=y CONFIG_SHADOW_PAGING=y # CONFIG_BIGMEM is not set -CONFIG_TBOOT=y +CONFIG_HVM_FEP=y +CONFIG_X86_PSR=y CONFIG_XEN_ALIGN_DEFAULT=y # CONFIG_XEN_ALIGN_2M is not set +# CONFIG_X2APIC_PHYSICAL is not set +CONFIG_X2APIC_MIXED=y # CONFIG_XEN_GUEST is not set # CONFIG_HYPERV_GUEST is not set +# CONFIG_REQUIRE_NX is not set +CONFIG_ALTP2M=y # end of Architecture Features # @@ -35,42 +53,55 @@ CONFIG_XEN_ALIGN_DEFAULT=y CONFIG_COMPAT=y CONFIG_CORE_PARKING=y CONFIG_GRANT_TABLE=y +CONFIG_ALTERNATIVE_CALL=y +CONFIG_ARCH_MAP_DOMAIN_PAGE=y +CONFIG_GENERIC_BUG_FRAME=y CONFIG_HAS_ALTERNATIVE=y +CONFIG_HAS_COMPAT=y +CONFIG_HAS_DIT=y CONFIG_HAS_EX_TABLE=y CONFIG_HAS_FAST_MULTIPLY=y +CONFIG_HAS_IOPORTS=y +CONFIG_HAS_KEXEC=y +CONFIG_HAS_PIRQ=y +CONFIG_HAS_SCHED_GRANULARITY=y +CONFIG_HAS_UBSAN=y +CONFIG_HAS_VMAP=y CONFIG_MEM_ACCESS_ALWAYS_ON=y CONFIG_MEM_ACCESS=y -CONFIG_HAS_MEM_PAGING=y -CONFIG_HAS_PDX=y -CONFIG_HAS_UBSAN=y -CONFIG_HAS_KEXEC=y -CONFIG_HAS_IOPORTS=y -CONFIG_HAS_SCHED_GRANULARITY=y CONFIG_NEEDS_LIBELF=y +CONFIG_NUMA=y # # Speculative hardening # +CONFIG_INDIRECT_THUNK=y +CONFIG_RETURN_THUNK=y CONFIG_SPECULATIVE_HARDEN_ARRAY=y CONFIG_SPECULATIVE_HARDEN_BRANCH=y +CONFIG_SPECULATIVE_HARDEN_GUEST_ACCESS=y +CONFIG_SPECULATIVE_HARDEN_LOCK=y # end of Speculative hardening +# CONFIG_DIT_DEFAULT is not set CONFIG_HYPFS=y CONFIG_HYPFS_CONFIG=y +CONFIG_IOREQ_SERVER=y CONFIG_KEXEC=y -CONFIG_XENOPROF=y # CONFIG_XSM is not set CONFIG_SCHED_CREDIT=y CONFIG_SCHED_CREDIT2=y CONFIG_SCHED_RTDS=y +CONFIG_SCHED_ARINC653=y CONFIG_SCHED_NULL=y CONFIG_SCHED_DEFAULT="credit2" -CONFIG_CRYPTO=y +# CONFIG_BOOT_TIME_CPUPOOLS is not set CONFIG_LIVEPATCH=y CONFIG_FAST_SYMBOL_LOOKUP=y CONFIG_ENFORCE_UNIQUE_SYMBOLS=y CONFIG_CMDLINE="" CONFIG_DOM0_MEM="" +CONFIG_DTB_FILE="" CONFIG_TRACEBUFFER=y # end of Common Features @@ -79,35 +110,63 @@ CONFIG_TRACEBUFFER=y # CONFIG_ACPI=y CONFIG_ACPI_LEGACY_TABLES_LOOKUP=y -CONFIG_NUMA=y +CONFIG_ACPI_NUMA=y CONFIG_HAS_NS16550=y CONFIG_HAS_EHCI=y +CONFIG_SERIAL_TX_BUFSIZE=32768 +# CONFIG_XHCI is not set CONFIG_HAS_CPUFREQ=y CONFIG_HAS_PASSTHROUGH=y +CONFIG_AMD_IOMMU=y +CONFIG_INTEL_IOMMU=y +# CONFIG_IOMMU_QUARANTINE_NONE is not set +CONFIG_IOMMU_QUARANTINE_BASIC=y +# CONFIG_IOMMU_QUARANTINE_SCRATCH_PAGE is not set CONFIG_HAS_PCI=y +CONFIG_HAS_PCI_MSI=y CONFIG_VIDEO=y CONFIG_VGA=y CONFIG_HAS_VPCI=y # end of Device Drivers # CONFIG_EXPERT is not set +# CONFIG_UNSUPPORTED is not set CONFIG_ARCH_SUPPORTS_INT128=y +CONFIG_ARCH_VCPU_IOREQ_COMPLETION=y # # Debugging Options # # CONFIG_DEBUG is not set +CONFIG_GDBSX=y +CONFIG_FRAME_POINTER=y +CONFIG_SELF_TESTS=y +# CONFIG_DEBUG_LOCK_PROFILE is not set +CONFIG_DEBUG_LOCKS=y +# CONFIG_PERF_COUNTERS is not set +CONFIG_VERBOSE_DEBUG=y +CONFIG_SCRUB_DEBUG=y +# CONFIG_UBSAN is not set +# CONFIG_DEBUG_TRACE is not set +CONFIG_XMEM_POOL_POISON=y +CONFIG_DEBUG_INFO=y # end of Debugging Options # ARM64 settings -CONFIG_64BIT=y +CONFIG_MMU=y CONFIG_ARM_64=y CONFIG_ARM=y +CONFIG_ARM_EFI=y +CONFIG_GICV2=y CONFIG_GICV3=y +CONFIG_VGICV2=y # CONFIG_NEW_VGIC is not set CONFIG_SBSA_VUART_CONSOLE=y +CONFIG_HWDOM_VUART=y CONFIG_ARM_SSBD=y CONFIG_HARDEN_BRANCH_PREDICTOR=y +CONFIG_STATIC_EVTCHN=y +CONFIG_PARTIAL_EMULATION=y # # ARM errata workaround via the alternative framework @@ -115,8 +174,15 @@ CONFIG_HARDEN_BRANCH_PREDICTOR=y CONFIG_ARM64_ERRATUM_827319=y CONFIG_ARM64_ERRATUM_824069=y CONFIG_ARM64_ERRATUM_819472=y +CONFIG_ARM64_ERRATUM_843419=y CONFIG_ARM64_ERRATUM_832075=y CONFIG_ARM64_ERRATUM_834220=y +CONFIG_ARM_ERRATUM_858921=y +CONFIG_ARM64_WORKAROUND_REPEAT_TLBI=y +CONFIG_ARM64_ERRATUM_1286807=y +CONFIG_ARM64_ERRATUM_1508412=y + +# end of ARM errata workaround via the alternative framework CONFIG_ARM64_HARDEN_BRANCH_PREDICTOR=y CONFIG_ALL_PLAT=y # CONFIG_QEMU is not set @@ -125,17 +191,18 @@ CONFIG_ALL_PLAT=y # CONFIG_NO_PLAT is not set CONFIG_ALL64_PLAT=y CONFIG_MPSOC_PLATFORM=y + +# +# Common Features +# CONFIG_HAS_DEVICE_TREE=y CONFIG_HAS_CADENCE_UART=y +CONFIG_HAS_LINFLEX=y +CONFIG_HAS_IMX_LPUART=y CONFIG_HAS_MVEBU=y CONFIG_HAS_MESON=y CONFIG_HAS_PL011=y +CONFIG_HAS_OMAP=y CONFIG_HAS_SCIF=y CONFIG_ARM_SMMU=y - -# ARM32 settings -CONFIG_ALL32_PLAT=y -CONFIG_ARM32_HARDEN_BRANCH_PREDICTOR=y -CONFIG_ARM_32=y -CONFIG_HAS_EXYNOS4210=y -CONFIG_HAS_OMAP=y +# CONFIG_IPMMU_VMSA is not set diff --git a/xen.spec b/xen.spec index 0ea5f41..8868407 100644 --- a/xen.spec +++ b/xen.spec @@ -6,13 +6,10 @@ %define build_docs %{?_without_docs: 0} %{?!_without_docs: 1} # Build with stubdom unless rpmbuild was run with --without stubdom %define build_stubdom %{?_without_stubdom: 0} %{?!_without_stubdom: 1} -# Only build with qemu-traditional if rpmbuild was run with --with qemutrad -%define build_qemutrad %{?_with_qemutrad: 1} %{?!_with_qemutrad: 0} # build with ovmf from edk2-ovmf unless rpmbuild was run with --without ovmf %define build_ovmf %{?_without_ovmf: 0} %{?!_without_ovmf: 1} -# set to 0 for archs that don't use qemu or ovmf (reduces build dependencies) -%ifnarch x86_64 %{ix86} -%define build_qemutrad 0 +# set to 0 for archs that don't use ovmf (reduces build dependencies) +%ifnarch x86_64 %define build_ovmf 0 %endif # Build with xen hypervisor unless rpmbuild was run with --without hyp @@ -39,8 +36,7 @@ # --without efi %define build_efi %{?_without_efi: 0} %{?!_without_efi: 1} # xen only supports efi boot images on x86_64 or aarch64 -# i686 builds a x86_64 hypervisor so add that as well -%ifnarch x86_64 aarch64 %{ix86} +%ifnarch x86_64 aarch64 %define build_efi 0 %endif %if "%dist" >= ".fc20" @@ -50,12 +46,12 @@ %endif # Hypervisor ABI -%define hv_abi 4.19 +%define hv_abi 4.20 Summary: Xen is a virtual machine monitor Name: xen -Version: 4.19.2 -Release: 6%{?dist} +Version: 4.20.1 +Release: 1%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -70,60 +66,20 @@ Source14: grub-0.97.tar.gz Source15: polarssl-1.1.4-gpl.tgz # .config file for xen hypervisor Source21: xen.hypervisor.config +# mini-os xen-RELEASE-4.20.0 with .git and .gitignore stripped +Source22: mini-os-4.20.0.tar.xz -Patch4: CVE-2014-0150.patch Patch5: xen.fedora.systemd.patch Patch6: xen.ocaml.selinux.fix.patch -Patch7: xen.fedora.crypt.patch -Patch8: qemu.trad.CVE-2015-6815.patch -Patch9: qemu.trad.CVE-2015-5279.patch -Patch10: qemu.trad.CVE-2015-5278.patch -Patch11: qemu.trad.CVE-2015-7295.patch -Patch12: qemu.trad.CVE-2015-8345.patch -Patch13: qemu.trad.CVE-2015-7512.patch -Patch14: qemu.trad.CVE-2015-8504.patch -Patch15: qemu.trad.CVE-2016-1714.patch -Patch16: qemu.trad.CVE-2016-1981.patch -Patch17: qemu.trad.CVE-2016-2841.patch -Patch18: qemu.trad.CVE-2016-2538.patch -Patch19: qemu.trad.CVE-2016-2857.patch -Patch20: qemu.trad.CVE-2016-4001.patch -Patch21: qemu.trad.CVE-2016-4002.patch -Patch22: qemu.trad.CVE-2016-4439.patch -Patch23: qemu.trad.CVE-2016-4441.patch -Patch24: qemu.trad.CVE-2016-5238.patch -Patch25: qemu.trad.CVE-2016-5338.patch -Patch27: qemu.trad.CVE-2016-6351.patch -Patch29: qemu.trad.CVE-2016-8669.patch -Patch30: qemu.trad.CVE-2016-8910.patch -Patch31: qemu.trad.bug1399055.patch -Patch32: qemu.trad.CVE-2016-9776.patch -Patch33: xen.gcc7.fix.patch Patch34: xen.canonicalize.patch -Patch35: qemu.trad.CVE-2017-6505.patch -Patch36: qemu.trad.CVE-2017-7718.patch Patch37: droplibvirtconflict.patch -Patch38: qemu.trad.CVE-2017-8309.patch -Patch39: qemu.trad.CVE-2017-9330.patch -Patch40: xen.drop.brctl.patch Patch41: xen.gcc9.fixes.patch Patch43: xen.gcc11.fixes.patch Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch Patch49: xen.python3.12.patch -Patch50: xsa469-4.19-01.patch -Patch51: xsa469-4.19-02.patch -Patch52: xsa469-4.19-03.patch -Patch53: xsa469-4.19-04.patch -Patch54: xsa469-4.19-05.patch -Patch55: xsa469-4.19-06.patch -Patch56: xsa469-4.19-07.patch -%if %build_qemutrad -BuildRequires: libidn-devel zlib-devel SDL-devel curl-devel -BuildRequires: libX11-devel gtk2-devel libaio-devel -%endif # build using Fedora seabios and ipxe packages for roms BuildRequires: seabios-bin ipxe-roms-qemu %ifarch %{ix86} x86_64 @@ -166,7 +122,6 @@ Requires: xen-runtime = %{version}-%{release} # installs xen. Requires: kpartx ExclusiveArch: x86_64 aarch64 -#ExclusiveArch: %#{ix86} x86_64 ia64 noarch %if %with_ocaml BuildRequires: ocaml, ocaml-findlib BuildRequires: perl(Data::Dumper) @@ -177,7 +132,7 @@ Requires(preun): systemd BuildRequires: systemd %endif BuildRequires: systemd-devel -%ifarch armv7hl aarch64 +%ifarch aarch64 BuildRequires: libfdt-devel %endif %if %build_hyp @@ -209,11 +164,9 @@ Requires: /usr/bin/qemu-img Requires: xen-hypervisor-abi = %{hv_abi} # perl is used in /etc/xen/scripts/locking.sh Recommends: perl -%ifnarch armv7hl aarch64 +%ifnarch aarch64 # use /usr/bin/qemu-system-i386 in Fedora instead of qemu-xen Recommends: qemu-system-x86-core -# rom file for qemu-xen-traditional -Recommends: ipxe-roms-qemu %endif %if %build_ovmf Recommends: edk2-ovmf-xen @@ -295,67 +248,23 @@ manage Xen virtual machines. %prep %setup -q -%patch 4 -p1 %patch 5 -p1 %patch 6 -p1 -%patch 7 -p1 -%patch 8 -p1 -%patch 9 -p1 -%patch 10 -p1 -%patch 11 -p1 -%patch 12 -p1 -%patch 13 -p1 -%patch 14 -p1 -%patch 15 -p1 -%patch 16 -p1 -%patch 17 -p1 -%patch 18 -p1 -%patch 19 -p1 -%patch 20 -p1 -%patch 21 -p1 -%patch 22 -p1 -%patch 23 -p1 -%patch 24 -p1 -%patch 25 -p1 -%patch 33 -p1 %patch 34 -p1 %patch 37 -p1 -%patch 40 -p1 %patch 41 -p1 %patch 43 -p1 %patch 45 -p1 %patch 46 -p1 %patch 49 -p1 -%patch 50 -p1 -%patch 51 -p1 -%patch 52 -p1 -%patch 53 -p1 -%patch 54 -p1 -%patch 55 -p1 -%patch 56 -p1 - -# qemu-xen-traditional patches -pushd tools/qemu-xen-traditional -%patch 27 -p1 -%patch 29 -p1 -%patch 30 -p1 -%patch 31 -p1 -%patch 32 -p1 -%patch 35 -p1 -%patch 36 -p1 -%patch 38 -p1 -%patch 39 -p1 -popd - -# qemu-xen patches -pushd tools/qemu-xen -popd # stubdom sources cp -v %{SOURCE10} %{SOURCE11} %{SOURCE12} %{SOURCE13} %{SOURCE14} %{SOURCE15} stubdom # copy xen hypervisor .config file to change settings cp -v %{SOURCE21} xen/.config - +# mini-os is now separate file +mkdir extras +tar -C extras -xf %{SOURCE22} %build # This package calls binutils components directly and would need to pass @@ -373,20 +282,14 @@ mkdir -p dist/install/boot/efi/efi/fedora mkdir -p dist/install%{_libdir}/ocaml/stublibs %endif export EXTRA_CFLAGS_XEN_TOOLS="$RPM_OPT_FLAGS -Wno-error=use-after-free $LDFLAGS" -export EXTRA_CFLAGS_QEMU_TRADITIONAL="$RPM_OPT_FLAGS" -export EXTRA_CFLAGS_QEMU_XEN="$RPM_OPT_FLAGS" export PYTHON="/usr/bin/python3" export LDFLAGS_SAVE=`echo $LDFLAGS | sed -e 's/-Wl,//g' -e 's/,/ /g' -e 's? -specs=[-a-z/0-9]*??g'` export CFLAGS_SAVE="$CFLAGS" -%if %build_qemutrad -CONFIG_EXTRA="--enable-qemu-traditional" -%else CONFIG_EXTRA="" -%endif %if %build_ovmf CONFIG_EXTRA="$CONFIG_EXTRA --with-system-ovmf=/usr/share/edk2/xen/OVMF.fd" %endif -%ifnarch armv7hl aarch64 +%ifarch aarch64 CONFIG_EXTRA="$CONFIG_EXTRA --with-system-ipxe=/usr/share/ipxe/10ec8139.rom" %endif %if %(test -f /usr/share/seabios/bios-256k.bin && echo 1|| echo 0) @@ -400,20 +303,11 @@ CONFIG_EXTRA="$CONFIG_EXTRA --enable-systemd" ./configure --prefix=%{_prefix} --libdir=%{_libdir} --libexecdir=%{_libexecdir} --with-system-qemu=/usr/bin/qemu-system-i386 --with-linux-backend-modules="xen-evtchn xen-gntdev xen-gntalloc xen-blkback xen-netback xen-pciback xen-scsiback xen-acpi-processor" $CONFIG_EXTRA unset CFLAGS CXXFLAGS FFLAGS LDFLAGS export LDFLAGS="$LDFLAGS_SAVE" -export CFLAGS="$CFLAGS_SAVE -Wno-error=address" +export CFLAGS=`echo "$CFLAGS_SAVE -Wno-error=address" | sed -e s/-specs=\/usr\/lib\/rpm\/redhat/redhat-annobin-cc1//g` %if %build_hyp -%if %build_crosshyp -export CFLAGS=`echo $CFLAGS | sed -e 's/-m32//g' -e 's/-march=i686//g' 's/-specs=\/usr\/lib\/rpm\/redhat\/redhat-annobin-cc1//g'` -XEN_TARGET_ARCH=x86_64 %make_build prefix=/usr xen CC="/usr/bin/x86_64-linux-gnu-gcc" -%else -%ifarch armv7hl -export CFLAGS=`echo $CFLAGS | sed -e 's/-mfloat-abi=hard//g' -e 's/-march=armv7-a//g'` -%endif -# armv7hl aarch64 or x86_64 %make_build prefix=/usr xen %endif -%endif unset CFLAGS CXXFLAGS FFLAGS LDFLAGS %make_build %{?ocaml_flags} prefix=/usr tools @@ -421,9 +315,6 @@ unset CFLAGS CXXFLAGS FFLAGS LDFLAGS make prefix=/usr docs %endif export RPM_OPT_FLAGS_RED=`echo $RPM_OPT_FLAGS | sed -e 's/-m64//g' -e 's/--param=ssp-buffer-size=4//g' -e's/-fstack-protector-strong//'` -%ifarch %{ix86} -export EXTRA_CFLAGS_XEN_TOOLS="$RPM_OPT_FLAGS_RED" -%endif %if %build_stubdom %ifnarch armv7hl aarch64 make mini-os-dir @@ -479,18 +370,6 @@ rm -fr %{buildroot}%{_datadir}/doc/xen # Pointless helper rm -f %{buildroot}%{_bindir}/xen-python-path -# qemu stuff (unused or available from upstream) -rm -rf %{buildroot}/usr/share/xen/man -rm -rf %{buildroot}/usr/bin/qemu-*-xen -ln -s qemu-img %{buildroot}/%{_bindir}/qemu-img-xen -ln -s qemu-img %{buildroot}/%{_bindir}/qemu-nbd-xen -for file in bios.bin openbios-sparc32 openbios-sparc64 ppc_rom.bin \ - pxe-e1000.bin pxe-ne2k_pci.bin pxe-pcnet.bin pxe-rtl8139.bin \ - vgabios.bin vgabios-cirrus.bin video.x openbios-ppc bamboo.dtb -do - rm -f %{buildroot}/%{_datadir}/xen/qemu/$file -done - # README's not intended for end users rm -f %{buildroot}/%{_sysconfdir}/xen/README* @@ -502,20 +381,12 @@ rm -rf %{buildroot}/%{_libdir}/*.a %if %build_efi # clean up extra efi files -%ifarch %{ix86} -rm -f %{buildroot}/usr/lib64/efi/xen-%{hv_abi}.efi -rm -f %{buildroot}/usr/lib64/efi/xen-4.efi -rm -f %{buildroot}/usr/lib64/efi/xen.efi -cp -p %{buildroot}/usr/lib64/efi/xen-%{version}{,.notstripped}.efi -strip -s %{buildroot}/usr/lib64/efi/xen-%{version}.efi -%else rm -f %{buildroot}/%{_libdir}/efi/xen-%{hv_abi}.efi rm -f %{buildroot}/%{_libdir}/efi/xen-4.efi rm -f %{buildroot}/%{_libdir}/efi/xen.efi cp -p %{buildroot}/%{_libdir}/efi/xen-%{version}{,.notstripped}.efi strip -s %{buildroot}/%{_libdir}/efi/xen-%{version}.efi %endif -%endif %if ! %build_ocaml rm -rf %{buildroot}/%{_unitdir}/oxenstored.service @@ -766,14 +637,6 @@ fi %dir %{_libexecdir}/%{name} %dir %{_libexecdir}/%{name}/bin %attr(0700,root,root) %{_libexecdir}/%{name}/bin/* -# QEMU runtime files -%if %build_qemutrad -%ifnarch armv7hl aarch64 -%dir %{_datadir}/%{name}/qemu -%dir %{_datadir}/%{name}/qemu/keymaps -%{_datadir}/%{name}/qemu/keymaps/* -%endif -%endif # man pages %if %build_docs @@ -803,17 +666,12 @@ fi %{python3_sitearch}/pygrub-*.egg-info # The firmware -%ifarch %{ix86} x86_64 +%ifarch x86_64 %dir %{_libexecdir}/%{name}/boot %{_libexecdir}/xen/boot/hvmloader -%ifnarch %{ix86} %{_libexecdir}/%{name}/boot/xen-shim /usr/lib/debug%{_libexecdir}/xen/boot/xen-shim-syms -%endif %if %build_stubdom -%if %build_qemutrad -%{_libexecdir}/xen/boot/ioemu-stubdom.gz -%endif %{_libexecdir}/xen/boot/xenstore-stubdom.gz %{_libexecdir}/xen/boot/xenstorepvh-stubdom.gz %endif @@ -830,22 +688,21 @@ fi %ghost %{_localstatedir}/run/xenstored # All xenstore CLI tools -%{_bindir}/qemu-*-xen %{_bindir}/xenstore %{_bindir}/xenstore-* #%#{_bindir}/remus # XSM %{_bindir}/flask-* # Misc stuff -%ifnarch armv7hl aarch64 +%ifnarch aarch64 %{_bindir}/xen-detect %endif %{_bindir}/xencov_split -%ifnarch armv7hl aarch64 +%ifnarch aarch64 %{_bindir}/gdbsx %{_bindir}/xen-kdd %endif -%ifnarch armv7hl aarch64 +%ifnarch aarch64 %{_bindir}/xen-hptool %{_bindir}/xen-hvmcrash %{_bindir}/xen-hvmctx @@ -862,17 +719,17 @@ fi %{_bindir}/xenperf %{_bindir}/xenwatchdogd %{_bindir}/xl -%ifnarch armv7hl aarch64 +%ifnarch aarch64 %{_bindir}/xen-lowmemd %endif %{_bindir}/xencov -%ifnarch armv7hl aarch64 +%ifnarch aarch64 %{_bindir}/xen-mfndump %endif %{_bindir}/xenalyze %{_bindir}/xentrace %{_bindir}/xentrace_setsize -%ifnarch armv7hl aarch64 +%ifnarch aarch64 %{_bindir}/xen-cpuid %endif %{_bindir}/xen-livepatch @@ -894,7 +751,7 @@ fi %files hypervisor %if %build_hyp -%ifnarch armv7hl aarch64 +%ifnarch aarch64 /boot/xen-*.gz /boot/xen*.config %else @@ -907,12 +764,8 @@ fi /usr/lib/debug/xen* %endif %if %build_efi -%ifarch %{ix86} -/usr/lib64/efi/*.efi -%else %{_libdir}/efi/*.efi %endif -%endif %if %build_docs %files doc @@ -948,9 +801,26 @@ fi %{_libdir}/ocaml/xen*/*.a %{_libdir}/ocaml/xen*/*.cmxa %{_libdir}/ocaml/xen*/*.cmx +%{_libdir}/ocaml/xsd_glue/* +%{_libexecdir}/xen/ocaml/xsd_glue/xenctrl_plugin/domain_getinfo_v1.cmxs %endif %changelog +* Sun Jul 13 2025 Michael Young - 4.20.1-1 +- update to xen 4.20.1 + remove old qemu code for spac file + remove armv7hl and ix86 code from spec file + update configuration in xen.hypervisor.config + minios is now a separate file + package extra ocaml files + unset -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 for hypervisor build + rebase xen.efi.build.patch + includes fixes for security vulnerabilites + x86: Incorrect stubs exception handling for flags recovery [XSA-470, + CVE-2025-27465] + x86: Transitive Scheduler Attacks [XSA-471, CVE-2024-36350, + CVE-2024-36357] + * Fri Jul 11 2025 Jerry James - 4.19.2-6 - Rebuild to fix OCaml dependencies diff --git a/xsa469-4.19-01.patch b/xsa469-4.19-01.patch deleted file mode 100644 index cbbb14f..0000000 --- a/xsa469-4.19-01.patch +++ /dev/null @@ -1,83 +0,0 @@ -From: Andrew Cooper -Subject: x86/alternative: Support replacements when a feature is not present - -Use the top bit of a->cpuid to express inverted polarity. This requires -stripping the top bit back out when performing the sanity checks. - -Despite only being used once, create a replace boolean to express the decision -more clearly in _apply_alternatives(). - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/alternative.c b/xen/arch/x86/alternative.c -index 1ba35cb9ede9..88c90044c20d 100644 ---- a/xen/arch/x86/alternative.c -+++ b/xen/arch/x86/alternative.c -@@ -197,6 +197,8 @@ static int init_or_livepatch _apply_alternatives(struct alt_instr *start, - uint8_t *repl = ALT_REPL_PTR(a); - uint8_t buf[MAX_PATCH_LEN]; - unsigned int total_len = a->orig_len + a->pad_len; -+ unsigned int feat = a->cpuid & ~ALT_FLAG_NOT; -+ bool inv = a->cpuid & ALT_FLAG_NOT, replace; - - if ( a->repl_len > total_len ) - { -@@ -214,11 +216,11 @@ static int init_or_livepatch _apply_alternatives(struct alt_instr *start, - return -ENOSPC; - } - -- if ( a->cpuid >= NCAPINTS * 32 ) -+ if ( feat >= NCAPINTS * 32 ) - { - printk(XENLOG_ERR - "Alt for %ps, feature %#x outside of featureset range %#x\n", -- ALT_ORIG_PTR(a), a->cpuid, NCAPINTS * 32); -+ ALT_ORIG_PTR(a), feat, NCAPINTS * 32); - return -ERANGE; - } - -@@ -243,8 +245,14 @@ static int init_or_livepatch _apply_alternatives(struct alt_instr *start, - continue; - } - -+ /* -+ * Should a replacement be performed? Most replacements have positive -+ * polarity, but we support negative polarity too. -+ */ -+ replace = boot_cpu_has(feat) ^ inv; -+ - /* If there is no replacement to make, see about optimising the nops. */ -- if ( !boot_cpu_has(a->cpuid) ) -+ if ( !replace ) - { - /* Origin site site already touched? Don't nop anything. */ - if ( base->priv ) -diff --git a/xen/arch/x86/include/asm/alternative.h b/xen/arch/x86/include/asm/alternative.h -index 69555d781ef9..89b7bdcb82e5 100644 ---- a/xen/arch/x86/include/asm/alternative.h -+++ b/xen/arch/x86/include/asm/alternative.h -@@ -1,6 +1,13 @@ - #ifndef __X86_ALTERNATIVE_H__ - #define __X86_ALTERNATIVE_H__ - -+/* -+ * Common to both C and ASM. Express a replacement when a feature is not -+ * available. -+ */ -+#define ALT_FLAG_NOT (1 << 15) -+#define ALT_NOT(x) (ALT_FLAG_NOT | (x)) -+ - #ifdef __ASSEMBLY__ - #include - #else -@@ -11,7 +18,7 @@ - struct __packed alt_instr { - int32_t orig_offset; /* original instruction */ - int32_t repl_offset; /* offset to replacement instruction */ -- uint16_t cpuid; /* cpuid bit set for replacement */ -+ uint16_t cpuid; /* cpuid bit set for replacement (top bit is polarity) */ - uint8_t orig_len; /* length of original instruction */ - uint8_t repl_len; /* length of new instruction */ - uint8_t pad_len; /* length of build-time padding */ - diff --git a/xsa469-4.19-02.patch b/xsa469-4.19-02.patch deleted file mode 100644 index 5fd285f..0000000 --- a/xsa469-4.19-02.patch +++ /dev/null @@ -1,314 +0,0 @@ -From: Andrew Cooper -Subject: x86/guest: Remove use of the Xen hypercall_page - -In order to protect against ITS, Xen needs to start using return thunks. -Therefore the advice in XSA-466 becomes relevant, and the hypercall_page needs -to be removed. - -Implement early_hypercall(), with infrastructure to figure out the correct -instruction on first use. Use ALTERNATIVE()s to result in inline hypercalls, -including the ALT_NOT() form so we only need a single synthetic feature bit. - -No overall change. - -This is part of XSA-469 / CVE-2024-28956 - -Signed-off-by: Andrew Cooper -Reviewed-by: Roger Pau Monné - -diff --git a/xen/arch/x86/guest/xen/Makefile b/xen/arch/x86/guest/xen/Makefile -index 26fb4b1007c0..8b3250aa8886 100644 ---- a/xen/arch/x86/guest/xen/Makefile -+++ b/xen/arch/x86/guest/xen/Makefile -@@ -1,4 +1,4 @@ --obj-y += hypercall_page.o -+obj-bin-y += hypercall.init.o - obj-y += xen.o - - obj-bin-$(CONFIG_PVH_GUEST) += pvh-boot.init.o -diff --git a/xen/arch/x86/guest/xen/hypercall.S b/xen/arch/x86/guest/xen/hypercall.S -new file mode 100644 -index 000000000000..05e429794cc4 ---- /dev/null -+++ b/xen/arch/x86/guest/xen/hypercall.S -@@ -0,0 +1,50 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#include -+ -+ .section .init.text, "ax", @progbits -+ -+ /* -+ * Used during early boot, before alternatives have run and inlined -+ * the appropriate instruction. Called using the hypercall ABI. -+ */ -+FUNC(early_hypercall) -+ cmpb $0, early_hypercall_insn(%rip) -+ jl .L_setup -+ je 1f -+ -+ vmmcall -+ ret -+ -+1: vmcall -+ ret -+ -+.L_setup: -+ /* -+ * When setting up the first time around, all registers need -+ * preserving. Save the non-callee-saved ones. -+ */ -+ push %r11 -+ push %r10 -+ push %r9 -+ push %r8 -+ push %rdi -+ push %rsi -+ push %rdx -+ push %rcx -+ push %rax -+ -+ call early_hypercall_setup -+ -+ pop %rax -+ pop %rcx -+ pop %rdx -+ pop %rsi -+ pop %rdi -+ pop %r8 -+ pop %r9 -+ pop %r10 -+ pop %r11 -+ -+ jmp early_hypercall -+END(early_hypercall) -diff --git a/xen/arch/x86/guest/xen/hypercall_page.S b/xen/arch/x86/guest/xen/hypercall_page.S -deleted file mode 100644 -index 7ab55fc1f6e6..000000000000 ---- a/xen/arch/x86/guest/xen/hypercall_page.S -+++ /dev/null -@@ -1,76 +0,0 @@ --#include --#include --#include -- -- .section ".text.page_aligned", "ax", @progbits -- --DATA(hypercall_page, PAGE_SIZE) -- /* Poisoned with `ret` for safety before hypercalls are set up. */ -- .fill PAGE_SIZE, 1, 0xc3 --END(hypercall_page) -- --/* -- * Identify a specific hypercall in the hypercall page -- * @param name Hypercall name. -- */ --#define DECLARE_HYPERCALL(name) \ -- .globl HYPERCALL_ ## name; \ -- .type HYPERCALL_ ## name, STT_FUNC; \ -- .size HYPERCALL_ ## name, 32; \ -- .set HYPERCALL_ ## name, hypercall_page + __HYPERVISOR_ ## name * 32 -- --DECLARE_HYPERCALL(set_trap_table) --DECLARE_HYPERCALL(mmu_update) --DECLARE_HYPERCALL(set_gdt) --DECLARE_HYPERCALL(stack_switch) --DECLARE_HYPERCALL(set_callbacks) --DECLARE_HYPERCALL(fpu_taskswitch) --DECLARE_HYPERCALL(sched_op_compat) --DECLARE_HYPERCALL(platform_op) --DECLARE_HYPERCALL(set_debugreg) --DECLARE_HYPERCALL(get_debugreg) --DECLARE_HYPERCALL(update_descriptor) --DECLARE_HYPERCALL(memory_op) --DECLARE_HYPERCALL(multicall) --DECLARE_HYPERCALL(update_va_mapping) --DECLARE_HYPERCALL(set_timer_op) --DECLARE_HYPERCALL(event_channel_op_compat) --DECLARE_HYPERCALL(xen_version) --DECLARE_HYPERCALL(console_io) --DECLARE_HYPERCALL(physdev_op_compat) --DECLARE_HYPERCALL(grant_table_op) --DECLARE_HYPERCALL(vm_assist) --DECLARE_HYPERCALL(update_va_mapping_otherdomain) --DECLARE_HYPERCALL(iret) --DECLARE_HYPERCALL(vcpu_op) --DECLARE_HYPERCALL(set_segment_base) --DECLARE_HYPERCALL(mmuext_op) --DECLARE_HYPERCALL(xsm_op) --DECLARE_HYPERCALL(nmi_op) --DECLARE_HYPERCALL(sched_op) --DECLARE_HYPERCALL(callback_op) --DECLARE_HYPERCALL(xenoprof_op) --DECLARE_HYPERCALL(event_channel_op) --DECLARE_HYPERCALL(physdev_op) --DECLARE_HYPERCALL(hvm_op) --DECLARE_HYPERCALL(sysctl) --DECLARE_HYPERCALL(domctl) --DECLARE_HYPERCALL(kexec_op) --DECLARE_HYPERCALL(argo_op) --DECLARE_HYPERCALL(xenpmu_op) -- --DECLARE_HYPERCALL(arch_0) --DECLARE_HYPERCALL(arch_1) --DECLARE_HYPERCALL(arch_2) --DECLARE_HYPERCALL(arch_3) --DECLARE_HYPERCALL(arch_4) --DECLARE_HYPERCALL(arch_5) --DECLARE_HYPERCALL(arch_6) --DECLARE_HYPERCALL(arch_7) -- --/* -- * Local variables: -- * tab-width: 8 -- * indent-tabs-mode: nil -- * End: -- */ -diff --git a/xen/arch/x86/guest/xen/xen.c b/xen/arch/x86/guest/xen/xen.c -index 7484b3f73ad3..2c30db05dfa7 100644 ---- a/xen/arch/x86/guest/xen/xen.c -+++ b/xen/arch/x86/guest/xen/xen.c -@@ -26,7 +26,6 @@ - bool __read_mostly xen_guest; - - uint32_t __read_mostly xen_cpuid_base; --extern char hypercall_page[]; - static struct rangeset *mem; - - DEFINE_PER_CPU(unsigned int, vcpu_id); -@@ -35,6 +34,50 @@ static struct vcpu_info *vcpu_info; - static unsigned long vcpu_info_mapped[BITS_TO_LONGS(NR_CPUS)]; - DEFINE_PER_CPU(struct vcpu_info *, vcpu_info); - -+/* -+ * Which instruction to use for early hypercalls: -+ * < 0 setup -+ * 0 vmcall -+ * > 0 vmmcall -+ */ -+int8_t __initdata early_hypercall_insn = -1; -+ -+/* -+ * Called once during the first hypercall to figure out which instruction to -+ * use. Error handling options are limited. -+ */ -+void asmlinkage __init early_hypercall_setup(void) -+{ -+ BUG_ON(early_hypercall_insn != -1); -+ -+ if ( !boot_cpu_data.x86_vendor ) -+ { -+ unsigned int eax, ebx, ecx, edx; -+ -+ cpuid(0, &eax, &ebx, &ecx, &edx); -+ -+ boot_cpu_data.x86_vendor = x86_cpuid_lookup_vendor(ebx, ecx, edx); -+ } -+ -+ switch ( boot_cpu_data.x86_vendor ) -+ { -+ case X86_VENDOR_INTEL: -+ case X86_VENDOR_CENTAUR: -+ case X86_VENDOR_SHANGHAI: -+ early_hypercall_insn = 0; -+ setup_force_cpu_cap(X86_FEATURE_USE_VMCALL); -+ break; -+ -+ case X86_VENDOR_AMD: -+ case X86_VENDOR_HYGON: -+ early_hypercall_insn = 1; -+ break; -+ -+ default: -+ BUG(); -+ } -+} -+ - static void __init find_xen_leaves(void) - { - uint32_t eax, ebx, ecx, edx, base; -@@ -337,9 +380,6 @@ const struct hypervisor_ops *__init xg_probe(void) - if ( !xen_cpuid_base ) - return NULL; - -- /* Fill the hypercall page. */ -- wrmsrl(cpuid_ebx(xen_cpuid_base + 2), __pa(hypercall_page)); -- - xen_guest = true; - - return &ops; -diff --git a/xen/arch/x86/include/asm/cpufeatures.h b/xen/arch/x86/include/asm/cpufeatures.h -index ba3df174b76e..9e3ed21c026d 100644 ---- a/xen/arch/x86/include/asm/cpufeatures.h -+++ b/xen/arch/x86/include/asm/cpufeatures.h -@@ -42,6 +42,7 @@ XEN_CPUFEATURE(XEN_SHSTK, X86_SYNTH(26)) /* Xen uses CET Shadow Stacks * - XEN_CPUFEATURE(XEN_IBT, X86_SYNTH(27)) /* Xen uses CET Indirect Branch Tracking */ - XEN_CPUFEATURE(IBPB_ENTRY_PV, X86_SYNTH(28)) /* MSR_PRED_CMD used by Xen for PV */ - XEN_CPUFEATURE(IBPB_ENTRY_HVM, X86_SYNTH(29)) /* MSR_PRED_CMD used by Xen for HVM */ -+XEN_CPUFEATURE(USE_VMCALL, X86_SYNTH(30)) /* Use VMCALL instead of VMMCALL */ - - /* Bug words follow the synthetic words. */ - #define X86_NR_BUG 1 -diff --git a/xen/arch/x86/include/asm/guest/xen-hcall.h b/xen/arch/x86/include/asm/guest/xen-hcall.h -index 665b472d05ac..96004dec9909 100644 ---- a/xen/arch/x86/include/asm/guest/xen-hcall.h -+++ b/xen/arch/x86/include/asm/guest/xen-hcall.h -@@ -30,9 +30,11 @@ - ({ \ - long res, tmp__; \ - asm volatile ( \ -- "call hypercall_page + %c[offset]" \ -+ ALTERNATIVE_2("call early_hypercall", \ -+ "vmmcall", ALT_NOT(X86_FEATURE_USE_VMCALL), \ -+ "vmcall", X86_FEATURE_USE_VMCALL) \ - : "=a" (res), "=D" (tmp__) ASM_CALL_CONSTRAINT \ -- : [offset] "i" (hcall * 32), \ -+ : "0" (hcall), \ - "1" ((long)(a1)) \ - : "memory" ); \ - (type)res; \ -@@ -42,10 +44,12 @@ - ({ \ - long res, tmp__; \ - asm volatile ( \ -- "call hypercall_page + %c[offset]" \ -+ ALTERNATIVE_2("call early_hypercall", \ -+ "vmmcall", ALT_NOT(X86_FEATURE_USE_VMCALL), \ -+ "vmcall", X86_FEATURE_USE_VMCALL) \ - : "=a" (res), "=D" (tmp__), "=S" (tmp__) \ - ASM_CALL_CONSTRAINT \ -- : [offset] "i" (hcall * 32), \ -+ : "0" (hcall), \ - "1" ((long)(a1)), "2" ((long)(a2)) \ - : "memory" ); \ - (type)res; \ -@@ -55,10 +59,12 @@ - ({ \ - long res, tmp__; \ - asm volatile ( \ -- "call hypercall_page + %c[offset]" \ -+ ALTERNATIVE_2("call early_hypercall", \ -+ "vmmcall", ALT_NOT(X86_FEATURE_USE_VMCALL), \ -+ "vmcall", X86_FEATURE_USE_VMCALL) \ - : "=a" (res), "=D" (tmp__), "=S" (tmp__), "=d" (tmp__) \ - ASM_CALL_CONSTRAINT \ -- : [offset] "i" (hcall * 32), \ -+ : "0" (hcall), \ - "1" ((long)(a1)), "2" ((long)(a2)), "3" ((long)(a3)) \ - : "memory" ); \ - (type)res; \ -@@ -69,10 +75,12 @@ - long res, tmp__; \ - register long _a4 asm ("r10") = ((long)(a4)); \ - asm volatile ( \ -- "call hypercall_page + %c[offset]" \ -+ ALTERNATIVE_2("call early_hypercall", \ -+ "vmmcall", ALT_NOT(X86_FEATURE_USE_VMCALL), \ -+ "vmcall", X86_FEATURE_USE_VMCALL) \ - : "=a" (res), "=D" (tmp__), "=S" (tmp__), "=d" (tmp__), \ - "=&r" (tmp__) ASM_CALL_CONSTRAINT \ -- : [offset] "i" (hcall * 32), \ -+ : "0" (hcall), \ - "1" ((long)(a1)), "2" ((long)(a2)), "3" ((long)(a3)), \ - "4" (_a4) \ - : "memory" ); \ diff --git a/xsa469-4.19-03.patch b/xsa469-4.19-03.patch deleted file mode 100644 index 2e944f9..0000000 --- a/xsa469-4.19-03.patch +++ /dev/null @@ -1,46 +0,0 @@ -From: Jan Beulich -Subject: x86/thunk: (Mis)align __x86_indirect_thunk_* to mitigate ITS - -The Indirect Target Selection speculative vulnerability means that indirect -branches (including RETs) are unsafe when in the first half of a cacheline. - -Arrange for __x86_indirect_thunk_* to always be in the second half. - -This is part of XSA-469 / CVE-2024-28956 - -Signed-off-by: Jan Beulich -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/indirect-thunk.S b/xen/arch/x86/indirect-thunk.S -index fd5493c22b16..c4b978d67b8e 100644 ---- a/xen/arch/x86/indirect-thunk.S -+++ b/xen/arch/x86/indirect-thunk.S -@@ -11,6 +11,10 @@ - - #include - -+/* Alignment is dealt with explicitly here; override the respective macro. */ -+#undef SYM_ALIGN -+#define SYM_ALIGN(align...) -+ - .macro IND_THUNK_RETPOLINE reg:req - call 1f - int3 -@@ -35,6 +39,16 @@ - .macro GEN_INDIRECT_THUNK reg:req - .section .text.__x86_indirect_thunk_\reg, "ax", @progbits - -+ /* -+ * The Indirect Target Selection speculative vulnerability means that -+ * indirect branches (including RETs) are unsafe when in the first -+ * half of a cacheline. Arrange for them to be in the second half. -+ * -+ * Align to 64, then skip 32. -+ */ -+ .balign 64 -+ .fill 32, 1, 0xcc -+ - FUNC(__x86_indirect_thunk_\reg) - ALTERNATIVE_2 __stringify(IND_THUNK_RETPOLINE \reg), \ - __stringify(IND_THUNK_LFENCE \reg), X86_FEATURE_IND_THUNK_LFENCE, \ diff --git a/xsa469-4.19-04.patch b/xsa469-4.19-04.patch deleted file mode 100644 index 81d77a9..0000000 --- a/xsa469-4.19-04.patch +++ /dev/null @@ -1,64 +0,0 @@ -From: Andrew Cooper -Subject: x86/thunk: (Mis)align the RETs in clear_bhb_loops() to mitigate ITS - -The Indirect Target Selection speculative vulnerability means that indirect -branches (including RETs) are unsafe when in the first half of a cacheline. - -clear_bhb_loops() has a precise layout of branches. The alignment for -performance cause the RETs to always be in an unsafe position, and converting -those to return thunks changes the branching pattern. While such a conversion -is believed to be safe, clear_bhb_loops() is also a performance-relevant -fastpath, so (mis)align the RETs to be in a safe position. - -No functional change. - -This is part of XSA-469 / CVE-2024-28956 - -Signed-off-by: Andrew Cooper -Reviewed-by: Roger Pau Monné - -diff --git a/xen/arch/x86/bhb-thunk.S b/xen/arch/x86/bhb-thunk.S -index 678c00c5d06f..52625f4e2c17 100644 ---- a/xen/arch/x86/bhb-thunk.S -+++ b/xen/arch/x86/bhb-thunk.S -@@ -50,7 +50,12 @@ END(clear_bhb_tsx) - * ret - * - * The CALL/RETs are necessary to prevent the Loop Stream Detector from -- * interfering. The alignment is for performance and not safety. -+ * interfering. -+ * -+ * The .balign's are for performance, but they cause the RETs to be in unsafe -+ * positions with respect to Indirect Target Selection. The .skips are to -+ * move the RETs into ITS-safe positions, rather than using the slowpath -+ * through __x86_return_thunk. - * - * The "short" sequence (5 and 5) is for CPUs prior to Alder Lake / Sapphire - * Rapids (i.e. Cores prior to Golden Cove and/or Gracemont). -@@ -66,12 +71,14 @@ FUNC(clear_bhb_loops) - jmp 5f - int3 - -- .align 64 -+ .balign 64 -+ .skip 32 - (.Lr1 - 1f), 0xcc - 1: call 2f -- ret -+.Lr1: ret - int3 - -- .align 64 -+ .balign 64 -+ .skip 32 - 18 /* (.Lr2 - 2f) but Clang IAS doesn't like this */, 0xcc - 2: ALTERNATIVE "mov $5, %eax", "mov $7, %eax", X86_SPEC_BHB_LOOPS_LONG - - 3: jmp 4f -@@ -83,7 +90,7 @@ FUNC(clear_bhb_loops) - sub $1, %ecx - jnz 1b - -- ret -+.Lr2: ret - 5: - /* - * The Intel sequence has an LFENCE here. The purpose is to ensure diff --git a/xsa469-4.19-05.patch b/xsa469-4.19-05.patch deleted file mode 100644 index 51a135d..0000000 --- a/xsa469-4.19-05.patch +++ /dev/null @@ -1,506 +0,0 @@ -From: Andrew Cooper -Subject: x86/stubs: Introduce place_ret() to abstract away raw 0xc3's - -The Indirect Target Selection speculative vulnerability means that indirect -branches (including RETs) are unsafe when in the first half of a cacheline. -This means it's not safe for logic using the stubs to write raw 0xc3's. - -Introduce place_ret() which, for now, writes a raw 0xc3 but will contain -additional logic when return thunks are in use. - -stub_selftest() doesn't strictly need to be converted as they only run on -boot, but doing so gets us a partial test of place_ret() too. - -No functional change. - -This is part of XSA-469 / CVE-2024-28956 - -Signed-off-by: Andrew Cooper -Reviewed-by: Roger Pau Monné - -diff --git a/tools/tests/x86_emulator/x86-emulate.h b/tools/tests/x86_emulator/x86-emulate.h -index 8f8accfe3e70..946aaa9d660b 100644 ---- a/tools/tests/x86_emulator/x86-emulate.h -+++ b/tools/tests/x86_emulator/x86-emulate.h -@@ -68,6 +68,12 @@ - - #define is_canonical_address(x) (((int64_t)(x) >> 47) == ((int64_t)(x) >> 63)) - -+static inline void *place_ret(void *ptr) -+{ -+ *(uint8_t *)ptr = 0xc3; -+ return ptr + 1; -+} -+ - extern uint32_t mxcsr_mask; - extern struct cpu_policy cp; - -diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile -index c1e64278ce85..a7e5a82689de 100644 ---- a/xen/arch/x86/Makefile -+++ b/xen/arch/x86/Makefile -@@ -11,9 +11,7 @@ obj-$(CONFIG_PV) += pv/ - obj-y += x86_64/ - obj-y += x86_emulate/ - --alternative-y := alternative.init.o --alternative-$(CONFIG_LIVEPATCH) := --obj-bin-y += $(alternative-y) -+obj-y += alternative.o - obj-y += apic.o - obj-y += bhb-thunk.o - obj-y += bitops.o -@@ -41,7 +39,7 @@ obj-y += hypercall.o - obj-y += i387.o - obj-y += i8259.o - obj-y += io_apic.o --obj-$(CONFIG_LIVEPATCH) += alternative.o livepatch.o -+obj-$(CONFIG_LIVEPATCH) += livepatch.o - obj-y += msi.o - obj-y += msr.o - obj-$(CONFIG_INDIRECT_THUNK) += indirect-thunk.o -diff --git a/xen/arch/x86/alternative.c b/xen/arch/x86/alternative.c -index 88c90044c20d..ec451d962c10 100644 ---- a/xen/arch/x86/alternative.c -+++ b/xen/arch/x86/alternative.c -@@ -137,6 +137,20 @@ void init_or_livepatch add_nops(void *insns, unsigned int len) - } - } - -+/* -+ * Place a return at @ptr. @ptr must be in the writable alias of a stub. -+ * -+ * Returns the next position to write into the stub. -+ */ -+void *place_ret(void *ptr) -+{ -+ uint8_t *p = ptr; -+ -+ *p++ = 0xc3; -+ -+ return p; -+} -+ - /* - * text_poke - Update instructions on a live kernel or non-executed code. - * @addr: address to modify -diff --git a/xen/arch/x86/extable.c b/xen/arch/x86/extable.c -index 705cf9eb94ca..1572efa69a00 100644 ---- a/xen/arch/x86/extable.c -+++ b/xen/arch/x86/extable.c -@@ -151,20 +151,20 @@ search_exception_table(const struct cpu_user_regs *regs, unsigned long *stub_ra) - int __init cf_check stub_selftest(void) - { - static const struct { -- uint8_t opc[8]; -+ uint8_t opc[7]; - uint64_t rax; - union stub_exception_token res; - } tests[] __initconst = { - #define endbr64 0xf3, 0x0f, 0x1e, 0xfa -- { .opc = { endbr64, 0x0f, 0xb9, 0xc3, 0xc3 }, /* ud1 */ -+ { .opc = { endbr64, 0x0f, 0xb9, 0x90 }, /* ud1 */ - .res.fields.trapnr = X86_EXC_UD }, -- { .opc = { endbr64, 0x90, 0x02, 0x00, 0xc3 }, /* nop; add (%rax),%al */ -+ { .opc = { endbr64, 0x90, 0x02, 0x00 }, /* nop; add (%rax),%al */ - .rax = 0x0123456789abcdef, - .res.fields.trapnr = X86_EXC_GP }, -- { .opc = { endbr64, 0x02, 0x04, 0x04, 0xc3 }, /* add (%rsp,%rax),%al */ -+ { .opc = { endbr64, 0x02, 0x04, 0x04 }, /* add (%rsp,%rax),%al */ - .rax = 0xfedcba9876543210UL, - .res.fields.trapnr = X86_EXC_SS }, -- { .opc = { endbr64, 0xcc, 0xc3, 0xc3, 0xc3 }, /* int3 */ -+ { .opc = { endbr64, 0xcc, 0x90, 0x90 }, /* int3 */ - .res.fields.trapnr = X86_EXC_BP }, - #undef endbr64 - }; -@@ -183,6 +183,7 @@ int __init cf_check stub_selftest(void) - - memset(ptr, 0xcc, STUB_BUF_SIZE / 2); - memcpy(ptr, tests[i].opc, ARRAY_SIZE(tests[i].opc)); -+ place_ret(ptr + ARRAY_SIZE(tests[i].opc)); - unmap_domain_page(ptr); - - asm volatile ( "INDIRECT_CALL %[stb]\n" -diff --git a/xen/arch/x86/include/asm/alternative.h b/xen/arch/x86/include/asm/alternative.h -index 89b7bdcb82e5..841a63ebf1b6 100644 ---- a/xen/arch/x86/include/asm/alternative.h -+++ b/xen/arch/x86/include/asm/alternative.h -@@ -30,6 +30,8 @@ struct __packed alt_instr { - #define ALT_REPL_PTR(a) __ALT_PTR(a, repl_offset) - - extern void add_nops(void *insns, unsigned int len); -+void *place_ret(void *ptr); -+ - /* Similar to alternative_instructions except it can be run with IRQs enabled. */ - extern int apply_alternatives(struct alt_instr *start, struct alt_instr *end); - extern void alternative_instructions(void); -diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c -index 70150c272276..ff5d1c9f8634 100644 ---- a/xen/arch/x86/pv/emul-priv-op.c -+++ b/xen/arch/x86/pv/emul-priv-op.c -@@ -76,7 +76,6 @@ static io_emul_stub_t *io_emul_stub_setup(struct priv_op_ctxt *ctxt, u8 opcode, - 0x41, 0x5c, /* pop %r12 */ - 0x5d, /* pop %rbp */ - 0x5b, /* pop %rbx */ -- 0xc3, /* ret */ - }; - - const struct stubs *this_stubs = &this_cpu(stubs); -@@ -126,11 +125,13 @@ static io_emul_stub_t *io_emul_stub_setup(struct priv_op_ctxt *ctxt, u8 opcode, - - APPEND_CALL(save_guest_gprs); - APPEND_BUFF(epilogue); -+ p = place_ret(p); - - /* Build-time best effort attempt to catch problems. */ - BUILD_BUG_ON(STUB_BUF_SIZE / 2 < - (sizeof(prologue) + sizeof(epilogue) + 10 /* 2x call */ + -- MAX(3 /* default stub */, IOEMUL_QUIRK_STUB_BYTES))); -+ MAX(3 /* default stub */, IOEMUL_QUIRK_STUB_BYTES) + -+ 1 /* ret */)); - /* Runtime confirmation that we haven't clobbered an adjacent stub. */ - BUG_ON(STUB_BUF_SIZE / 2 < (p - ctxt->io_emul_stub)); - -diff --git a/xen/arch/x86/x86_emulate/fpu.c b/xen/arch/x86/x86_emulate/fpu.c -index 480d87965705..03612d00a2ce 100644 ---- a/xen/arch/x86/x86_emulate/fpu.c -+++ b/xen/arch/x86/x86_emulate/fpu.c -@@ -32,36 +32,42 @@ static inline bool fpu_check_write(void) - - #define emulate_fpu_insn_memdst(opc, ext, arg) \ - do { \ -+ void *_p = get_stub(stub); \ - /* ModRM: mod=0, reg=ext, rm=0, i.e. a (%rax) operand */ \ - *insn_bytes = 2; \ -- memcpy(get_stub(stub), \ -- ((uint8_t[]){ opc, ((ext) & 7) << 3, 0xc3 }), 3); \ -+ memcpy(_p, ((uint8_t[]){ opc, ((ext) & 7) << 3 }), 2); _p += 2; \ -+ place_ret(_p); \ - invoke_stub("", "", "+m" (arg) : "a" (&(arg))); \ - put_stub(stub); \ - } while (0) - - #define emulate_fpu_insn_memsrc(opc, ext, arg) \ - do { \ -+ void *_p = get_stub(stub); \ - /* ModRM: mod=0, reg=ext, rm=0, i.e. a (%rax) operand */ \ -- memcpy(get_stub(stub), \ -- ((uint8_t[]){ opc, ((ext) & 7) << 3, 0xc3 }), 3); \ -+ memcpy(_p, ((uint8_t[]){ opc, ((ext) & 7) << 3 }), 2); _p += 2; \ -+ place_ret(_p); \ - invoke_stub("", "", "=m" (dummy) : "m" (arg), "a" (&(arg))); \ - put_stub(stub); \ - } while (0) - - #define emulate_fpu_insn_stub(bytes...) \ - do { \ -+ void *_p = get_stub(stub); \ - unsigned int nr_ = sizeof((uint8_t[]){ bytes }); \ -- memcpy(get_stub(stub), ((uint8_t[]){ bytes, 0xc3 }), nr_ + 1); \ -+ memcpy(_p, ((uint8_t[]){ bytes }), nr_); _p += nr_; \ -+ place_ret(_p); \ - invoke_stub("", "", "=m" (dummy) : "i" (0)); \ - put_stub(stub); \ - } while (0) - - #define emulate_fpu_insn_stub_eflags(bytes...) \ - do { \ -+ void *_p = get_stub(stub); \ - unsigned int nr_ = sizeof((uint8_t[]){ bytes }); \ - unsigned long tmp_; \ -- memcpy(get_stub(stub), ((uint8_t[]){ bytes, 0xc3 }), nr_ + 1); \ -+ memcpy(_p, ((uint8_t[]){ bytes }), nr_); _p += nr_; \ -+ place_ret(_p); \ - invoke_stub(_PRE_EFLAGS("[eflags]", "[mask]", "[tmp]"), \ - _POST_EFLAGS("[eflags]", "[mask]", "[tmp]"), \ - [eflags] "+g" (regs->eflags), [tmp] "=&r" (tmp_) \ -diff --git a/xen/arch/x86/x86_emulate/x86_emulate.c b/xen/arch/x86/x86_emulate/x86_emulate.c -index b1d192cbbf1e..f40709682484 100644 ---- a/xen/arch/x86/x86_emulate/x86_emulate.c -+++ b/xen/arch/x86/x86_emulate/x86_emulate.c -@@ -1396,7 +1396,7 @@ x86_emulate( - stb[3] = 0x91; - stb[4] = evex.opmsk << 3; - insn_bytes = 5; -- stb[5] = 0xc3; -+ place_ret(&stb[5]); - - invoke_stub("", "", "+m" (op_mask) : "a" (&op_mask)); - -@@ -3627,7 +3627,7 @@ x86_emulate( - } - opc[1] = (modrm & 0x38) | 0xc0; - insn_bytes = EVEX_PFX_BYTES + 2; -- opc[2] = 0xc3; -+ place_ret(&opc[2]); - - copy_EVEX(opc, evex); - invoke_stub("", "", "=g" (dummy) : "a" (src.val)); -@@ -3694,7 +3694,7 @@ x86_emulate( - insn_bytes = PFX_BYTES + 2; - copy_REX_VEX(opc, rex_prefix, vex); - } -- opc[2] = 0xc3; -+ place_ret(&opc[2]); - - ea.reg = decode_gpr(&_regs, modrm_reg); - invoke_stub("", "", "=a" (*ea.reg) : "c" (mmvalp), "m" (*mmvalp)); -@@ -3768,7 +3768,7 @@ x86_emulate( - insn_bytes = PFX_BYTES + 2; - copy_REX_VEX(opc, rex_prefix, vex); - } -- opc[2] = 0xc3; -+ place_ret(&opc[2]); - - _regs.eflags &= ~EFLAGS_MASK; - invoke_stub("", -@@ -4004,7 +4004,7 @@ x86_emulate( - opc[1] = modrm & 0xc7; - insn_bytes = PFX_BYTES + 2; - simd_0f_to_gpr: -- opc[insn_bytes - PFX_BYTES] = 0xc3; -+ place_ret(&opc[insn_bytes - PFX_BYTES]); - - generate_exception_if(ea.type != OP_REG, X86_EXC_UD); - -@@ -4401,7 +4401,7 @@ x86_emulate( - vex.w = 0; - opc[1] = modrm & 0x38; - insn_bytes = PFX_BYTES + 2; -- opc[2] = 0xc3; -+ place_ret(&opc[2]); - - copy_REX_VEX(opc, rex_prefix, vex); - invoke_stub("", "", "+m" (src.val) : "a" (&src.val)); -@@ -4438,7 +4438,7 @@ x86_emulate( - evex.w = 0; - opc[1] = modrm & 0x38; - insn_bytes = EVEX_PFX_BYTES + 2; -- opc[2] = 0xc3; -+ place_ret(&opc[2]); - - copy_EVEX(opc, evex); - invoke_stub("", "", "+m" (src.val) : "a" (&src.val)); -@@ -4633,7 +4633,7 @@ x86_emulate( - #endif /* X86EMUL_NO_SIMD */ - - simd_0f_reg_only: -- opc[insn_bytes - PFX_BYTES] = 0xc3; -+ place_ret(&opc[insn_bytes - PFX_BYTES]); - - copy_REX_VEX(opc, rex_prefix, vex); - invoke_stub("", "", [dummy_out] "=g" (dummy) : [dummy_in] "i" (0) ); -@@ -4967,7 +4967,7 @@ x86_emulate( - if ( !mode_64bit() ) - vex.w = 0; - opc[1] = modrm & 0xf8; -- opc[2] = 0xc3; -+ place_ret(&opc[2]); - - copy_VEX(opc, vex); - ea.reg = decode_gpr(&_regs, modrm_rm); -@@ -5010,7 +5010,7 @@ x86_emulate( - if ( !mode_64bit() ) - vex.w = 0; - opc[1] = modrm & 0xc7; -- opc[2] = 0xc3; -+ place_ret(&opc[2]); - - copy_VEX(opc, vex); - invoke_stub("", "", "=a" (dst.val) : [dummy] "i" (0)); -@@ -5040,7 +5040,7 @@ x86_emulate( - opc = init_prefixes(stub); - opc[0] = b; - opc[1] = modrm; -- opc[2] = 0xc3; -+ place_ret(&opc[2]); - - copy_VEX(opc, vex); - _regs.eflags &= ~EFLAGS_MASK; -@@ -5608,7 +5608,7 @@ x86_emulate( - if ( !mode_64bit() ) - vex.w = 0; - opc[1] = modrm & 0xc7; -- opc[2] = 0xc3; -+ place_ret(&opc[2]); - - copy_REX_VEX(opc, rex_prefix, vex); - invoke_stub("", "", "=a" (ea.val) : [dummy] "i" (0)); -@@ -5726,7 +5726,7 @@ x86_emulate( - opc[1] &= 0x38; - } - insn_bytes = PFX_BYTES + 2; -- opc[2] = 0xc3; -+ place_ret(&opc[2]); - if ( vex.opcx == vex_none ) - { - /* Cover for extra prefix byte. */ -@@ -6006,7 +6006,7 @@ x86_emulate( - pvex->b = !mode_64bit() || (vex.reg >> 3); - opc[1] = 0xc0 | (~vex.reg & 7); - pvex->reg = 0xf; -- opc[2] = 0xc3; -+ place_ret(&opc[2]); - - invoke_stub("", "", "=a" (ea.val) : [dummy] "i" (0)); - put_stub(stub); -@@ -6290,7 +6290,7 @@ x86_emulate( - evex.w = 0; - opc[1] = modrm & 0xf8; - insn_bytes = EVEX_PFX_BYTES + 2; -- opc[2] = 0xc3; -+ place_ret(&opc[2]); - - copy_EVEX(opc, evex); - invoke_stub("", "", "=g" (dummy) : "a" (src.val)); -@@ -6389,7 +6389,7 @@ x86_emulate( - pvex->b = 1; - opc[1] = (modrm_reg & 7) << 3; - pvex->reg = 0xf; -- opc[2] = 0xc3; -+ place_ret(&opc[2]); - - invoke_stub("", "", "=m" (*mmvalp) : "a" (mmvalp)); - -@@ -6459,7 +6459,7 @@ x86_emulate( - pvex->b = 1; - opc[1] = (modrm_reg & 7) << 3; - pvex->reg = 0xf; -- opc[2] = 0xc3; -+ place_ret(&opc[2]); - - invoke_stub("", "", "+m" (*mmvalp) : "a" (mmvalp)); - -@@ -6515,7 +6515,7 @@ x86_emulate( - pevex->b = 1; - opc[1] = (modrm_reg & 7) << 3; - pevex->RX = 1; -- opc[2] = 0xc3; -+ place_ret(&opc[2]); - - invoke_stub("", "", "=m" (*mmvalp) : "a" (mmvalp)); - -@@ -6580,7 +6580,7 @@ x86_emulate( - pevex->b = 1; - opc[1] = (modrm_reg & 7) << 3; - pevex->RX = 1; -- opc[2] = 0xc3; -+ place_ret(&opc[2]); - - invoke_stub("", "", "+m" (*mmvalp) : "a" (mmvalp)); - -@@ -6594,7 +6594,7 @@ x86_emulate( - opc[2] = 0x90; - /* Use (%rax) as source. */ - opc[3] = evex.opmsk << 3; -- opc[4] = 0xc3; -+ place_ret(&opc[4]); - - invoke_stub("", "", "+m" (op_mask) : "a" (&op_mask)); - put_stub(stub); -@@ -6688,7 +6688,7 @@ x86_emulate( - pevex->b = 1; - opc[1] = (modrm_reg & 7) << 3; - pevex->RX = 1; -- opc[2] = 0xc3; -+ place_ret(&opc[2]); - - invoke_stub("", "", "=m" (*mmvalp) : "a" (mmvalp)); - -@@ -6766,7 +6766,7 @@ x86_emulate( - opc[2] = 0x90; - /* Use (%rax) as source. */ - opc[3] = evex.opmsk << 3; -- opc[4] = 0xc3; -+ place_ret(&opc[4]); - - invoke_stub("", "", "+m" (op_mask) : "a" (&op_mask)); - put_stub(stub); -@@ -6848,7 +6848,7 @@ x86_emulate( - pevex->r = !mode_64bit() || !(state->sib_index & 0x08); - pevex->R = !mode_64bit() || !(state->sib_index & 0x10); - pevex->RX = 1; -- opc[2] = 0xc3; -+ place_ret(&opc[2]); - - invoke_stub("", "", "=m" (index) : "a" (&index)); - put_stub(stub); -@@ -7058,7 +7058,7 @@ x86_emulate( - pvex->reg = 0xf; /* rAX */ - buf[3] = b; - buf[4] = 0x09; /* reg=rCX r/m=(%rCX) */ -- buf[5] = 0xc3; -+ place_ret(&buf[5]); - - src.reg = decode_vex_gpr(vex.reg, &_regs, ctxt); - emulate_stub([dst] "=&c" (dst.val), "[dst]" (&src.val), "a" (*src.reg)); -@@ -7094,7 +7094,7 @@ x86_emulate( - pvex->reg = 0xf; /* rAX */ - buf[3] = b; - buf[4] = (modrm & 0x38) | 0x01; /* r/m=(%rCX) */ -- buf[5] = 0xc3; -+ place_ret(&buf[5]); - - dst.reg = decode_vex_gpr(vex.reg, &_regs, ctxt); - emulate_stub("=&a" (dst.val), "c" (&src.val)); -@@ -7335,7 +7335,7 @@ x86_emulate( - evex.w = vex.w = 0; - opc[1] = modrm & 0x38; - opc[2] = imm1; -- opc[3] = 0xc3; -+ place_ret(&opc[3]); - if ( vex.opcx == vex_none ) - { - /* Cover for extra prefix byte. */ -@@ -7502,7 +7502,7 @@ x86_emulate( - insn_bytes = PFX_BYTES + 3; - copy_VEX(opc, vex); - } -- opc[3] = 0xc3; -+ place_ret(&opc[3]); - - /* Latch MXCSR - we may need to restore it below. */ - invoke_stub("stmxcsr %[mxcsr]", "", -@@ -7748,7 +7748,7 @@ x86_emulate( - } - opc[2] = imm1; - insn_bytes = PFX_BYTES + 3; -- opc[3] = 0xc3; -+ place_ret(&opc[3]); - if ( vex.opcx == vex_none ) - { - /* Cover for extra prefix byte. */ -@@ -8094,7 +8094,7 @@ x86_emulate( - pxop->reg = 0xf; /* rAX */ - buf[3] = b; - buf[4] = (modrm & 0x38) | 0x01; /* r/m=(%rCX) */ -- buf[5] = 0xc3; -+ place_ret(&buf[5]); - - dst.reg = decode_vex_gpr(vex.reg, &_regs, ctxt); - emulate_stub([dst] "=&a" (dst.val), "c" (&src.val)); -@@ -8203,7 +8203,7 @@ x86_emulate( - buf[3] = b; - buf[4] = 0x09; /* reg=rCX r/m=(%rCX) */ - *(uint32_t *)(buf + 5) = imm1; -- buf[9] = 0xc3; -+ place_ret(&buf[9]); - - emulate_stub([dst] "=&c" (dst.val), "[dst]" (&src.val)); - -@@ -8293,12 +8293,12 @@ x86_emulate( - BUG(); - if ( evex_encoded() ) - { -- opc[insn_bytes - EVEX_PFX_BYTES] = 0xc3; -+ place_ret(&opc[insn_bytes - EVEX_PFX_BYTES]); - copy_EVEX(opc, evex); - } - else - { -- opc[insn_bytes - PFX_BYTES] = 0xc3; -+ place_ret(&opc[insn_bytes - PFX_BYTES]); - copy_REX_VEX(opc, rex_prefix, vex); - } - diff --git a/xsa469-4.19-06.patch b/xsa469-4.19-06.patch deleted file mode 100644 index 724790e..0000000 --- a/xsa469-4.19-06.patch +++ /dev/null @@ -1,375 +0,0 @@ -From: Jan Beulich -Subject: x86/thunk: Build Xen with Return Thunks - -The Indirect Target Selection speculative vulnerability means that indirect -branches (including RETs) are unsafe when in the first half of a cacheline. - -In order to mitigate this, build with return thunks and arrange for -__x86_return_thunk to be (mis)aligned in the same manner as -__x86_indirect_thunk_* so the RET instruction is placed in a safe location. - -place_ret() needs to conditionally emit JMP __x86_return_thunk instead of RET. - -This is part of XSA-469 / CVE-2024-28956 - -Signed-off-by: Jan Beulich -Signed-off-by: Andrew Cooper -Reviewed-by: Roger Pau Monné - -diff --git a/xen/arch/x86/Kconfig b/xen/arch/x86/Kconfig -index 7e03e4bc5546..4542ea8408c7 100644 ---- a/xen/arch/x86/Kconfig -+++ b/xen/arch/x86/Kconfig -@@ -37,9 +37,14 @@ config ARCH_DEFCONFIG - default "arch/x86/configs/x86_64_defconfig" - - config CC_HAS_INDIRECT_THUNK -+ # GCC >= 8 or Clang >= 6 - def_bool $(cc-option,-mindirect-branch-register) || \ - $(cc-option,-mretpoline-external-thunk) - -+config CC_HAS_RETURN_THUNK -+ # GCC >= 8 or Clang >= 15 -+ def_bool $(cc-option,-mfunction-return=thunk-extern) -+ - config HAS_AS_CET_SS - # binutils >= 2.29 or LLVM >= 6 - def_bool $(as-instr,wrssq %rax$(comma)0;setssbsy) -diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile -index a7e5a82689de..27806a81aca8 100644 ---- a/xen/arch/x86/Makefile -+++ b/xen/arch/x86/Makefile -@@ -43,6 +43,7 @@ obj-$(CONFIG_LIVEPATCH) += livepatch.o - obj-y += msi.o - obj-y += msr.o - obj-$(CONFIG_INDIRECT_THUNK) += indirect-thunk.o -+obj-$(CONFIG_RETURN_THUNK) += indirect-thunk.o - obj-$(CONFIG_PV) += ioport_emulate.o - obj-y += irq.o - obj-$(CONFIG_KEXEC) += machine_kexec.o -diff --git a/xen/arch/x86/acpi/wakeup_prot.S b/xen/arch/x86/acpi/wakeup_prot.S -index 66f799339913..97bd676aaee2 100644 ---- a/xen/arch/x86/acpi/wakeup_prot.S -+++ b/xen/arch/x86/acpi/wakeup_prot.S -@@ -133,7 +133,7 @@ ENTRY(s3_resume) - pop %r12 - pop %rbx - pop %rbp -- ret -+ RET - - .data - .align 16 -diff --git a/xen/arch/x86/alternative.c b/xen/arch/x86/alternative.c -index ec451d962c10..1b71ae959abe 100644 ---- a/xen/arch/x86/alternative.c -+++ b/xen/arch/x86/alternative.c -@@ -137,16 +137,45 @@ void init_or_livepatch add_nops(void *insns, unsigned int len) - } - } - -+void nocall __x86_return_thunk(void); -+ - /* - * Place a return at @ptr. @ptr must be in the writable alias of a stub. - * -+ * When CONFIG_RETURN_THUNK is active, this may be a JMP __x86_return_thunk -+ * instead, depending on the safety of @ptr with respect to Indirect Target -+ * Selection. -+ * - * Returns the next position to write into the stub. - */ - void *place_ret(void *ptr) - { -+ unsigned long addr = (unsigned long)ptr; - uint8_t *p = ptr; - -- *p++ = 0xc3; -+ /* -+ * When Return Thunks are used, if a RET would be unsafe at this location -+ * with respect to Indirect Target Selection (i.e. if addr is in the first -+ * half of a cacheline), insert a JMP __x86_return_thunk instead. -+ * -+ * The displacement needs to be relative to the executable alias of the -+ * stub, not to @ptr which is the writeable alias. -+ */ -+ if ( IS_ENABLED(CONFIG_RETURN_THUNK) && !(addr & 0x20) ) -+ { -+ long stub_va = (this_cpu(stubs.addr) & PAGE_MASK) + (addr & ~PAGE_MASK); -+ long disp = (long)__x86_return_thunk - (stub_va + 5); -+ -+ BUG_ON((int32_t)disp != disp); -+ -+ *p++ = 0xe9; -+ *(int32_t *)p = disp; -+ p += 4; -+ } -+ else -+ { -+ *p++ = 0xc3; -+ } - - return p; - } -diff --git a/xen/arch/x86/arch.mk b/xen/arch/x86/arch.mk -index b88d097a844b..85d3e7cbfeeb 100644 ---- a/xen/arch/x86/arch.mk -+++ b/xen/arch/x86/arch.mk -@@ -46,6 +46,9 @@ CFLAGS-$(CONFIG_CC_IS_GCC) += -fno-jump-tables - CFLAGS-$(CONFIG_CC_IS_CLANG) += -mretpoline-external-thunk - endif - -+# Compile with return thunk support if selected. -+CFLAGS-$(CONFIG_RETURN_THUNK) += -mfunction-return=thunk-extern -+ - # Disable the addition of a .note.gnu.property section to object files when - # livepatch support is enabled. The contents of that section can change - # depending on the instructions used, and livepatch-build-tools doesn't know -diff --git a/xen/arch/x86/bhb-thunk.S b/xen/arch/x86/bhb-thunk.S -index 52625f4e2c17..7f92201a3cbb 100644 ---- a/xen/arch/x86/bhb-thunk.S -+++ b/xen/arch/x86/bhb-thunk.S -@@ -23,7 +23,7 @@ FUNC(clear_bhb_tsx) - 0: .byte 0xc6, 0xf8, 0 /* xabort $0 */ - int3 - 1: -- ret -+ RET - END(clear_bhb_tsx) - - /* -diff --git a/xen/arch/x86/clear_page.S b/xen/arch/x86/clear_page.S -index d6c076f1d8bc..dc3c3c26bfb7 100644 ---- a/xen/arch/x86/clear_page.S -+++ b/xen/arch/x86/clear_page.S -@@ -1,6 +1,8 @@ - .file __FILE__ - - #include -+ -+#include - #include - - FUNC(clear_page_sse2) -@@ -16,5 +18,5 @@ FUNC(clear_page_sse2) - jnz 0b - - sfence -- ret -+ RET - END(clear_page_sse2) -diff --git a/xen/arch/x86/copy_page.S b/xen/arch/x86/copy_page.S -index c3c436545bac..e43e5370c815 100644 ---- a/xen/arch/x86/copy_page.S -+++ b/xen/arch/x86/copy_page.S -@@ -1,6 +1,8 @@ - .file __FILE__ - - #include -+ -+#include - #include - - #define src_reg %rsi -@@ -41,5 +43,5 @@ FUNC(copy_page_sse2) - movnti tmp4_reg, 3*WORD_SIZE(dst_reg) - - sfence -- ret -+ RET - END(copy_page_sse2) -diff --git a/xen/arch/x86/efi/check.c b/xen/arch/x86/efi/check.c -index 9e473faad3c9..23ba30abf330 100644 ---- a/xen/arch/x86/efi/check.c -+++ b/xen/arch/x86/efi/check.c -@@ -3,6 +3,9 @@ int __attribute__((__ms_abi__)) test(int i) - return i; - } - -+/* In case -mfunction-return is in use. */ -+void __x86_return_thunk(void) {}; -+ - /* - * Populate an array with "addresses" of relocatable and absolute values. - * This is to probe ld for (a) emitting base relocations at all and (b) not -diff --git a/xen/arch/x86/include/asm/asm-defns.h b/xen/arch/x86/include/asm/asm-defns.h -index 32d6b4491063..97ebe21298a2 100644 ---- a/xen/arch/x86/include/asm/asm-defns.h -+++ b/xen/arch/x86/include/asm/asm-defns.h -@@ -58,6 +58,12 @@ - .endif - .endm - -+#ifdef CONFIG_RETURN_THUNK -+# define RET jmp __x86_return_thunk -+#else -+# define RET ret -+#endif -+ - #ifdef CONFIG_XEN_IBT - # define ENDBR64 endbr64 - #else -diff --git a/xen/arch/x86/indirect-thunk.S b/xen/arch/x86/indirect-thunk.S -index c4b978d67b8e..26dad15f12c9 100644 ---- a/xen/arch/x86/indirect-thunk.S -+++ b/xen/arch/x86/indirect-thunk.S -@@ -15,6 +15,8 @@ - #undef SYM_ALIGN - #define SYM_ALIGN(align...) - -+#ifdef CONFIG_INDIRECT_THUNK -+ - .macro IND_THUNK_RETPOLINE reg:req - call 1f - int3 -@@ -62,3 +64,25 @@ END(__x86_indirect_thunk_\reg) - .irp reg, ax, cx, dx, bx, bp, si, di, 8, 9, 10, 11, 12, 13, 14, 15 - GEN_INDIRECT_THUNK reg=r\reg - .endr -+ -+#endif /* CONFIG_INDIRECT_THUNK */ -+ -+#ifdef CONFIG_RETURN_THUNK -+ .section .text.entry.__x86_return_thunk, "ax", @progbits -+ -+ /* -+ * The Indirect Target Selection speculative vulnerability means that -+ * indirect branches (including RETs) are unsafe when in the first -+ * half of a cacheline. Arrange for them to be in the second half. -+ * -+ * Align to 64, then skip 32. -+ */ -+ .balign 64 -+ .fill 32, 1, 0xcc -+ -+FUNC(__x86_return_thunk) -+ ret -+ int3 /* Halt straight-line speculation */ -+END(__x86_return_thunk) -+ -+#endif /* CONFIG_RETURN_THUNK */ -diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c -index ff5d1c9f8634..295d847ea24c 100644 ---- a/xen/arch/x86/pv/emul-priv-op.c -+++ b/xen/arch/x86/pv/emul-priv-op.c -@@ -131,7 +131,7 @@ static io_emul_stub_t *io_emul_stub_setup(struct priv_op_ctxt *ctxt, u8 opcode, - BUILD_BUG_ON(STUB_BUF_SIZE / 2 < - (sizeof(prologue) + sizeof(epilogue) + 10 /* 2x call */ + - MAX(3 /* default stub */, IOEMUL_QUIRK_STUB_BYTES) + -- 1 /* ret */)); -+ (IS_ENABLED(CONFIG_RETURN_THUNK) ? 5 : 1) /* ret */)); - /* Runtime confirmation that we haven't clobbered an adjacent stub. */ - BUG_ON(STUB_BUF_SIZE / 2 < (p - ctxt->io_emul_stub)); - -diff --git a/xen/arch/x86/pv/gpr_switch.S b/xen/arch/x86/pv/gpr_switch.S -index 5409ad3b1447..362b5d241623 100644 ---- a/xen/arch/x86/pv/gpr_switch.S -+++ b/xen/arch/x86/pv/gpr_switch.S -@@ -26,7 +26,7 @@ FUNC(load_guest_gprs) - movq UREGS_r15(%rdi), %r15 - movq UREGS_rcx(%rdi), %rcx - movq UREGS_rdi(%rdi), %rdi -- ret -+ RET - END(load_guest_gprs) - - /* Save guest GPRs. Parameter on the stack above the return address. */ -@@ -48,5 +48,5 @@ FUNC(save_guest_gprs) - movq %rbx, UREGS_rbx(%rdi) - movq %rdx, UREGS_rdx(%rdi) - movq %rcx, UREGS_rcx(%rdi) -- ret -+ RET - END(save_guest_gprs) -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 35351044f901..019a0a81f4a7 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -569,6 +569,9 @@ static void __init print_details(enum ind_thunk thunk) - #ifdef CONFIG_INDIRECT_THUNK - " INDIRECT_THUNK" - #endif -+#ifdef CONFIG_RETURN_THUNK -+ " RETURN_THUNK" -+#endif - #ifdef CONFIG_SHADOW_PAGING - " SHADOW_PAGING" - #endif -diff --git a/xen/arch/x86/x86_64/compat/entry.S b/xen/arch/x86/x86_64/compat/entry.S -index a99646c0cd4e..18f46c78cfbe 100644 ---- a/xen/arch/x86/x86_64/compat/entry.S -+++ b/xen/arch/x86/x86_64/compat/entry.S -@@ -180,7 +180,7 @@ FUNC(cr4_pv32_restore) - or cr4_pv32_mask(%rip), %rax - mov %rax, %cr4 - mov %rax, (%rcx) -- ret -+ RET - 0: - #ifndef NDEBUG - /* Check that _all_ of the bits intended to be set actually are. */ -@@ -198,7 +198,7 @@ FUNC(cr4_pv32_restore) - 1: - #endif - xor %eax, %eax -- ret -+ RET - END(cr4_pv32_restore) - - FUNC(compat_syscall) -@@ -329,7 +329,7 @@ __UNLIKELY_END(compat_bounce_null_selector) - xor %eax, %eax - mov %ax, TRAPBOUNCE_cs(%rdx) - mov %al, TRAPBOUNCE_flags(%rdx) -- ret -+ RET - - .section .fixup,"ax" - .Lfx13: -diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S -index 9b0cdb76408b..eb62e7c329bd 100644 ---- a/xen/arch/x86/x86_64/entry.S -+++ b/xen/arch/x86/x86_64/entry.S -@@ -604,7 +604,7 @@ __UNLIKELY_END(create_bounce_frame_bad_bounce_ip) - xor %eax, %eax - mov %rax, TRAPBOUNCE_eip(%rdx) - mov %al, TRAPBOUNCE_flags(%rdx) -- ret -+ RET - - .pushsection .fixup, "ax", @progbits - # Numeric tags below represent the intended overall %rsi adjustment. -diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S -index 9a1dfe1b340a..506993867502 100644 ---- a/xen/arch/x86/xen.lds.S -+++ b/xen/arch/x86/xen.lds.S -@@ -82,6 +82,7 @@ SECTIONS - . = ALIGN(PAGE_SIZE); - _stextentry = .; - *(.text.entry) -+ *(.text.entry.*) - . = ALIGN(PAGE_SIZE); - _etextentry = .; - -diff --git a/xen/common/Kconfig b/xen/common/Kconfig -index 565ceda741b9..da0fa7527643 100644 ---- a/xen/common/Kconfig -+++ b/xen/common/Kconfig -@@ -130,6 +130,17 @@ config INDIRECT_THUNK - When enabled, indirect branches are implemented using a new construct - called "retpoline" that prevents speculation. - -+config RETURN_THUNK -+ bool "Out-of-line Returns" -+ depends on CC_HAS_RETURN_THUNK -+ default INDIRECT_THUNK -+ help -+ Compile Xen with out-of-line returns. -+ -+ This allows Xen to mitigate a variety of speculative vulnerabilities -+ by choosing a hardware-dependent instruction sequence to implement -+ function returns safely. -+ - config SPECULATIVE_HARDEN_ARRAY - bool "Speculative Array Hardening" - default y diff --git a/xsa469-4.19-07.patch b/xsa469-4.19-07.patch deleted file mode 100644 index 2ba9d48..0000000 --- a/xsa469-4.19-07.patch +++ /dev/null @@ -1,154 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Synthesise ITS_NO to guests on unaffected hardware - -It is easier to express feature word 17 in terms of word 16 + [32, 64) as -that's how the layout is given in documentation. - -This is part of XSA-469 / CVE-2024-28956 - -Signed-off-by: Andrew Cooper -Reviewed-by: Roger Pau Monné - -diff --git a/xen/arch/x86/include/asm/cpufeature.h b/xen/arch/x86/include/asm/cpufeature.h -index 9bc553681f4a..1729ba0c3097 100644 ---- a/xen/arch/x86/include/asm/cpufeature.h -+++ b/xen/arch/x86/include/asm/cpufeature.h -@@ -216,6 +216,7 @@ static inline bool boot_cpu_has(unsigned int feat) - #define cpu_has_gds_no boot_cpu_has(X86_FEATURE_GDS_NO) - #define cpu_has_rfds_no boot_cpu_has(X86_FEATURE_RFDS_NO) - #define cpu_has_rfds_clear boot_cpu_has(X86_FEATURE_RFDS_CLEAR) -+#define cpu_has_its_no boot_cpu_has(X86_FEATURE_ITS_NO) - - /* Synthesized. */ - #define cpu_has_arch_perfmon boot_cpu_has(X86_FEATURE_ARCH_PERFMON) -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 019a0a81f4a7..94cdbd521c4d 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -1781,6 +1781,90 @@ static void __init bhi_calculations(void) - } - } - -+/* -+ * https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/advisory-guidance/indirect-target-selection.html -+ */ -+static void __init its_calculations(void) -+{ -+ /* -+ * Indirect Target Selection is a Branch Prediction bug whereby certain -+ * indirect branches (including RETs) get predicted using a direct branch -+ * target, rather than a suitable indirect target, bypassing hardware -+ * isolation protections. -+ * -+ * ITS affects Core (but not Atom) processors starting from the -+ * introduction of eIBRS, up to but not including Golden Cove cores -+ * (checked here with BHI_CTRL). -+ * -+ * The ITS_NO feature is not expected to be enumerated by hardware, and is -+ * only for VMMs to synthesise for guests. -+ * -+ * ITS comes in 3 flavours: -+ * -+ * 1) Across-IBPB. Indirect branches after the IBPB can be controlled -+ * by direct targets which existed prior to the IBPB. This is -+ * addressed in the IPU 2025.1 microcode drop, and has no other -+ * software interaction. -+ * -+ * 2) Guest/Host. Indirect branches in the VMM can be controlled by -+ * direct targets from the guest. This applies equally to PV guests -+ * (Ring3) and HVM guests (VMX), and applies to all Skylake-uarch -+ * cores with eIBRS. -+ * -+ * 3) Intra-mode. Indirect branches in the VMM can be controlled by -+ * other execution in the same mode. -+ */ -+ -+ /* -+ * If we can see ITS_NO, or we're virtualised, do nothing. We are or may -+ * migrate somewhere unsafe. -+ */ -+ if ( cpu_has_its_no || cpu_has_hypervisor ) -+ return; -+ -+ /* ITS is only known to affect Intel processors at this time. */ -+ if ( boot_cpu_data.x86_vendor != X86_VENDOR_INTEL ) -+ return; -+ -+ /* -+ * ITS does not exist on: -+ * - non-Family 6 CPUs -+ * - those without eIBRS -+ * - those with BHI_CTRL -+ * but we still need to synthesise ITS_NO. -+ */ -+ if ( boot_cpu_data.x86 != 6 || !cpu_has_eibrs || -+ boot_cpu_has(X86_FEATURE_BHI_CTRL) ) -+ goto synthesise; -+ -+ switch ( boot_cpu_data.x86_model ) -+ { -+ /* These Skylake-uarch cores suffer cases #2 and #3. */ -+ case INTEL_FAM6_SKYLAKE_X: -+ case INTEL_FAM6_KABYLAKE_L: -+ case INTEL_FAM6_KABYLAKE: -+ case INTEL_FAM6_COMETLAKE: -+ case INTEL_FAM6_COMETLAKE_L: -+ return; -+ -+ /* These Sunny/Willow/Cypress Cove cores suffer case #3. */ -+ case INTEL_FAM6_ICELAKE_X: -+ case INTEL_FAM6_ICELAKE_D: -+ case INTEL_FAM6_ICELAKE_L: -+ case INTEL_FAM6_TIGERLAKE_L: -+ case INTEL_FAM6_TIGERLAKE: -+ case INTEL_FAM6_ROCKETLAKE: -+ return; -+ -+ default: -+ break; -+ } -+ -+ /* Platforms remaining are not believed to be vulnerable to ITS. */ -+ synthesise: -+ setup_force_cpu_cap(X86_FEATURE_ITS_NO); -+} -+ - void spec_ctrl_init_domain(struct domain *d) - { - bool pv = is_pv_domain(d); -@@ -2331,6 +2415,8 @@ void __init init_speculation_mitigations(void) - - bhi_calculations(); - -+ its_calculations(); -+ - print_details(thunk); - - /* -diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h -index 9c98e4992861..4d9e468af653 100644 ---- a/xen/include/public/arch-x86/cpufeatureset.h -+++ b/xen/include/public/arch-x86/cpufeatureset.h -@@ -365,7 +365,8 @@ XEN_CPUFEATURE(GDS_NO, 16*32+26) /*A No Gather Data Sampling */ - XEN_CPUFEATURE(RFDS_NO, 16*32+27) /*A No Register File Data Sampling */ - XEN_CPUFEATURE(RFDS_CLEAR, 16*32+28) /*!A| Register File(s) cleared by VERW */ - --/* Intel-defined CPU features, MSR_ARCH_CAPS 0x10a.edx, word 17 */ -+/* Intel-defined CPU features, MSR_ARCH_CAPS 0x10a.edx, word 17 (express in terms of word 16) */ -+XEN_CPUFEATURE(ITS_NO, 16*32+62) /*!A No Indirect Target Selection */ - - #endif /* XEN_CPUFEATURE */ - -diff --git a/xen/tools/gen-cpuid.py b/xen/tools/gen-cpuid.py -index 601eec608983..dc33ca3181b1 100755 ---- a/xen/tools/gen-cpuid.py -+++ b/xen/tools/gen-cpuid.py -@@ -51,7 +51,7 @@ def parse_definitions(state): - r"\s+/\*([\w!|]*) .*$") - - word_regex = re.compile( -- r"^/\* .* word (\d*) \*/$") -+ r"^/\* .* word (\d*) .*\*/$") - last_word = -1 - - this = sys.modules[__name__] From e2d4d104a0d113f5b2c84986710e77e43f94caf8 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Fri, 25 Jul 2025 20:44:23 +0000 Subject: [PATCH 112/137] Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 8868407..8f2f0a5 100644 --- a/xen.spec +++ b/xen.spec @@ -51,7 +51,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.20.1 -Release: 1%{?dist} +Release: 2%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -806,6 +806,9 @@ fi %endif %changelog +* Fri Jul 25 2025 Fedora Release Engineering - 4.20.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild + * Sun Jul 13 2025 Michael Young - 4.20.1-1 - update to xen 4.20.1 remove old qemu code for spac file From 39101b511c87391cf9049115b44c7878093ac083 Mon Sep 17 00:00:00 2001 From: Python Maint Date: Fri, 15 Aug 2025 15:23:19 +0200 Subject: [PATCH 113/137] Rebuilt for Python 3.14.0rc2 bytecode --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 8f2f0a5..1490c91 100644 --- a/xen.spec +++ b/xen.spec @@ -51,7 +51,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.20.1 -Release: 2%{?dist} +Release: 3%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -806,6 +806,9 @@ fi %endif %changelog +* Fri Aug 15 2025 Python Maint - 4.20.1-3 +- Rebuilt for Python 3.14.0rc2 bytecode + * Fri Jul 25 2025 Fedora Release Engineering - 4.20.1-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild From ca8c5d51355e3e71281e73938c57d8821ac9fde0 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 2 Sep 2025 20:20:57 +0100 Subject: [PATCH 114/137] tools/xl: don't crash on NULL command line --- ...6b65098f790e6573f555c5ef170d3f373c6e.patch | 40 +++++++++++++++++++ xen.spec | 7 +++- 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 xen.git-7cda6b65098f790e6573f555c5ef170d3f373c6e.patch diff --git a/xen.git-7cda6b65098f790e6573f555c5ef170d3f373c6e.patch b/xen.git-7cda6b65098f790e6573f555c5ef170d3f373c6e.patch new file mode 100644 index 0000000..c072120 --- /dev/null +++ b/xen.git-7cda6b65098f790e6573f555c5ef170d3f373c6e.patch @@ -0,0 +1,40 @@ +From 7cda6b65098f790e6573f555c5ef170d3f373c6e Mon Sep 17 00:00:00 2001 +From: =?utf8?q?Marek=20Marczykowski-G=C3=B3recki?= + +Date: Mon, 4 Aug 2025 15:22:13 +0200 +Subject: [PATCH] tools/xl: don't crash on NULL command line +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf8 +Content-Transfer-Encoding: 8bit + +When running xl in a domU, it doesn't have access to the Xen command +line. Before the non-truncating xc_xenver_cmdline(), it was always set +with strdup, possibly of an empty string. Now it's NULL. Treat it the +same as empty cmdline, as it was before. Autoballoon isn't relevant for +xl devd in a domU anyway. + +Fixes: 75f91607621c ("tools: Introduce a non-truncating xc_xenver_cmdline()") +Signed-off-by: Marek Marczykowski-Górecki +Acked-by: Anthony PERARD +master commit: a4988c4177be81f225af1516e1bbb9ec14f76388 +master date: 2025-07-31 14:44:02 +0200 +--- + tools/xl/xl.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/xl/xl.c b/tools/xl/xl.c +index ec72ca60c3..b3abc91b60 100644 +--- a/tools/xl/xl.c ++++ b/tools/xl/xl.c +@@ -79,7 +79,7 @@ static int auto_autoballoon(void) + int ret; + + info = libxl_get_version_info(ctx); +- if (!info) ++ if (!info || !info->commandline) + return 1; /* default to on */ + + #define SIZE_PATTERN "-?[0-9]+[bBkKmMgGtT]?" +-- +2.39.5 + diff --git a/xen.spec b/xen.spec index 1490c91..7c23230 100644 --- a/xen.spec +++ b/xen.spec @@ -51,7 +51,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.20.1 -Release: 3%{?dist} +Release: 4%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -78,6 +78,7 @@ Patch43: xen.gcc11.fixes.patch Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch Patch49: xen.python3.12.patch +Patch50: xen.git-7cda6b65098f790e6573f555c5ef170d3f373c6e.patch # build using Fedora seabios and ipxe packages for roms @@ -257,6 +258,7 @@ manage Xen virtual machines. %patch 45 -p1 %patch 46 -p1 %patch 49 -p1 +%patch 50 -p1 # stubdom sources cp -v %{SOURCE10} %{SOURCE11} %{SOURCE12} %{SOURCE13} %{SOURCE14} %{SOURCE15} stubdom @@ -806,6 +808,9 @@ fi %endif %changelog +* Tue Sep 02 2025 - 4.20.1-4 +- tools/xl: don't crash on NULL command line + * Fri Aug 15 2025 Python Maint - 4.20.1-3 - Rebuilt for Python 3.14.0rc2 bytecode From 8bc3f5aa328322597668e9325147eb7bfcdaae79 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 10 Sep 2025 23:41:07 +0100 Subject: [PATCH 115/137] 2 security updates Mutiple vulnerabilities in the Viridian interface [XSA-472, CVE-2025-27466, CVE-2025-58142, CVE-2025-58143] Arm issues with page refcounting [XSA-473, CVE-2025-58144, CVE-2025-58145] --- xen.spec | 20 +++++++++-- xsa472-1.patch | 43 +++++++++++++++++++++++ xsa472-2.patch | 41 ++++++++++++++++++++++ xsa472-3.patch | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++ xsa473-1.patch | 43 +++++++++++++++++++++++ xsa473-2.patch | 62 +++++++++++++++++++++++++++++++++ 6 files changed, 301 insertions(+), 2 deletions(-) create mode 100644 xsa472-1.patch create mode 100644 xsa472-2.patch create mode 100644 xsa472-3.patch create mode 100644 xsa473-1.patch create mode 100644 xsa473-2.patch diff --git a/xen.spec b/xen.spec index 7c23230..fc230e5 100644 --- a/xen.spec +++ b/xen.spec @@ -51,7 +51,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.20.1 -Release: 4%{?dist} +Release: 5%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -79,6 +79,11 @@ Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch Patch49: xen.python3.12.patch Patch50: xen.git-7cda6b65098f790e6573f555c5ef170d3f373c6e.patch +Patch51: xsa472-1.patch +Patch52: xsa472-2.patch +Patch53: xsa472-3.patch +Patch54: xsa473-1.patch +Patch55: xsa473-2.patch # build using Fedora seabios and ipxe packages for roms @@ -259,6 +264,11 @@ manage Xen virtual machines. %patch 46 -p1 %patch 49 -p1 %patch 50 -p1 +%patch 51 -p1 +%patch 52 -p1 +%patch 53 -p1 +%patch 54 -p1 +%patch 55 -p1 # stubdom sources cp -v %{SOURCE10} %{SOURCE11} %{SOURCE12} %{SOURCE13} %{SOURCE14} %{SOURCE15} stubdom @@ -808,7 +818,13 @@ fi %endif %changelog -* Tue Sep 02 2025 - 4.20.1-4 +* Wed Sep 10 2025 Michael Young - 4.20.1-5 +- Mutiple vulnerabilities in the Viridian interface [XSA-472, + CVE-2025-27466, CVE-2025-58142, CVE-2025-58143] +- Arm issues with page refcounting [XSA-473, CVE-2025-58144, + CVE-2025-58145] + +* Tue Sep 02 2025 Michael Young - 4.20.1-4 - tools/xl: don't crash on NULL command line * Fri Aug 15 2025 Python Maint - 4.20.1-3 diff --git a/xsa472-1.patch b/xsa472-1.patch new file mode 100644 index 0000000..e69f3e7 --- /dev/null +++ b/xsa472-1.patch @@ -0,0 +1,43 @@ +From 262114a440bf7c32fd6d215e243b3eaebdd6d7cd Mon Sep 17 00:00:00 2001 +From: Roger Pau Monne +Date: Thu, 10 Jul 2025 15:51:40 +0200 +Subject: [PATCH 1/3] x86/viridian: avoid NULL pointer dereference in + update_reference_tsc() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The function is only called when the MSR has the enabled bit set, but even +then the page might not be mapped because the guest provided gfn is not +suitable. + +Prevent a NULL pointer dereference in update_reference_tsc() by checking +whether the page is mapped. + +This is CVE-2025-27466 / part of XSA-472. + +Fixes: 386b3365221d ('viridian: use viridian_map/unmap_guest_page() for reference tsc page') +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +--- + xen/arch/x86/hvm/viridian/time.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/xen/arch/x86/hvm/viridian/time.c b/xen/arch/x86/hvm/viridian/time.c +index 137577384f1e..ca6d526f46b7 100644 +--- a/xen/arch/x86/hvm/viridian/time.c ++++ b/xen/arch/x86/hvm/viridian/time.c +@@ -26,6 +26,10 @@ static void update_reference_tsc(const struct domain *d, bool initialize) + HV_REFERENCE_TSC_PAGE *p = rt->ptr; + uint32_t seq; + ++ /* Reference TSC page might not be mapped even if the MSR is enabled. */ ++ if ( !p ) ++ return; ++ + if ( initialize ) + clear_page(p); + +-- +2.49.0 + diff --git a/xsa472-2.patch b/xsa472-2.patch new file mode 100644 index 0000000..5b23b6d --- /dev/null +++ b/xsa472-2.patch @@ -0,0 +1,41 @@ +From 71c9568e290b51dfd7ab091ac98b272fd0aa0b90 Mon Sep 17 00:00:00 2001 +From: Roger Pau Monne +Date: Thu, 10 Jul 2025 15:58:51 +0200 +Subject: [PATCH 2/3] x86/viridian: avoid NULL pointer dereference in + viridian_synic_deliver_timer_msg() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The function is called unconditionally, regardless of whether the SIM page +is mapped. Avoid a NULL pointer dereference in +viridian_synic_deliver_timer_msg() by checking whether the SIM page is +mapped. + +This is CVE-2025-58142 / part of XSA-472. + +Fixes: 26fba3c85571 ('viridian: add implementation of synthetic timers') +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +--- + xen/arch/x86/hvm/viridian/synic.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/xen/arch/x86/hvm/viridian/synic.c b/xen/arch/x86/hvm/viridian/synic.c +index c3dc573b003d..e6cba7548f1b 100644 +--- a/xen/arch/x86/hvm/viridian/synic.c ++++ b/xen/arch/x86/hvm/viridian/synic.c +@@ -338,6 +338,10 @@ bool viridian_synic_deliver_timer_msg(struct vcpu *v, unsigned int sintx, + .DeliveryTime = delivery, + }; + ++ /* Don't assume SIM page to be mapped. */ ++ if ( !msg ) ++ return false; ++ + /* + * To avoid using an atomic test-and-set, and barrier before calling + * vlapic_set_irq(), this function must be called in context of the +-- +2.49.0 + diff --git a/xsa472-3.patch b/xsa472-3.patch new file mode 100644 index 0000000..5dfdf48 --- /dev/null +++ b/xsa472-3.patch @@ -0,0 +1,94 @@ +From aed4cfd64d178aee677a8790440addda03678cd6 Mon Sep 17 00:00:00 2001 +From: Roger Pau Monne +Date: Thu, 3 Jul 2025 13:09:03 +0200 +Subject: [PATCH 3/3] x86/viridian: protect concurrent modification of the + reference TSC page +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The reference TSC page is shared between all vCPUs, and the data stored in +the domain struct. However the handlers to set and clear it are not safe +against concurrent accesses. It's possible for two (or more) vCPUs to call +HV_X64_MSR_REFERENCE_TSC at the same time and cause the in-use reference +TSC page to be freed, while still being on the p2m. This creates an +information leak, where the page can end up mapped in another domain while +still being part of the original domain p2m. + +It's also possible to underflow the reference counter, as multiple +concurrent writes to HV_X64_MSR_REFERENCE_TSC can create an imbalance on +the number of put_page_and_type() calls. + +Introduce a lock to protect the reference TSC domain field, thus +serializing concurrent vCPU accesses. + +This is CVE-2025-58143 / part of XSA-472. + +Fixes: 386b3365221d ('viridian: use viridian_map/unmap_guest_page() for reference tsc page') +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +--- + xen/arch/x86/hvm/viridian/time.c | 4 ++++ + xen/arch/x86/hvm/viridian/viridian.c | 2 ++ + xen/arch/x86/include/asm/hvm/viridian.h | 1 + + 3 files changed, 7 insertions(+) + +diff --git a/xen/arch/x86/hvm/viridian/time.c b/xen/arch/x86/hvm/viridian/time.c +index ca6d526f46b7..9311858d63c0 100644 +--- a/xen/arch/x86/hvm/viridian/time.c ++++ b/xen/arch/x86/hvm/viridian/time.c +@@ -108,8 +108,10 @@ static void time_ref_count_thaw(const struct domain *d) + + trc->off = (int64_t)trc->val - trc_val(d, 0); + ++ spin_lock(&vd->lock); + if ( vd->reference_tsc.msr.enabled ) + update_reference_tsc(d, false); ++ spin_unlock(&vd->lock); + } + + static uint64_t time_ref_count(const struct domain *d) +@@ -331,6 +333,7 @@ int viridian_time_wrmsr(struct vcpu *v, uint32_t idx, uint64_t val) + if ( !(viridian_feature_mask(d) & HVMPV_reference_tsc) ) + return X86EMUL_EXCEPTION; + ++ spin_lock(&vd->lock); + viridian_unmap_guest_page(&vd->reference_tsc); + vd->reference_tsc.msr.raw = val; + viridian_dump_guest_page(v, "REFERENCE_TSC", &vd->reference_tsc); +@@ -339,6 +342,7 @@ int viridian_time_wrmsr(struct vcpu *v, uint32_t idx, uint64_t val) + viridian_map_guest_page(d, &vd->reference_tsc); + update_reference_tsc(d, true); + } ++ spin_unlock(&vd->lock); + break; + + case HV_X64_MSR_TIME_REF_COUNT: +diff --git a/xen/arch/x86/hvm/viridian/viridian.c b/xen/arch/x86/hvm/viridian/viridian.c +index 7ea6c9016894..c0be24bd2210 100644 +--- a/xen/arch/x86/hvm/viridian/viridian.c ++++ b/xen/arch/x86/hvm/viridian/viridian.c +@@ -494,6 +494,8 @@ int viridian_domain_init(struct domain *d) + if ( !d->arch.hvm.viridian ) + return -ENOMEM; + ++ spin_lock_init(&d->arch.hvm.viridian->lock); ++ + rc = viridian_synic_domain_init(d); + if ( rc ) + goto fail; +diff --git a/xen/arch/x86/include/asm/hvm/viridian.h b/xen/arch/x86/include/asm/hvm/viridian.h +index 4c8ff6e80b6f..47c9d13841ac 100644 +--- a/xen/arch/x86/include/asm/hvm/viridian.h ++++ b/xen/arch/x86/include/asm/hvm/viridian.h +@@ -71,6 +71,7 @@ struct viridian_domain + DECLARE_BITMAP(hypercall_flags, _HCALL_nr); + struct viridian_time_ref_count time_ref_count; + struct viridian_page reference_tsc; ++ spinlock_t lock; + }; + + void cpuid_viridian_leaves(const struct vcpu *v, uint32_t leaf, +-- +2.49.0 + diff --git a/xsa473-1.patch b/xsa473-1.patch new file mode 100644 index 0000000..e8689b6 --- /dev/null +++ b/xsa473-1.patch @@ -0,0 +1,43 @@ +From: Jan Beulich +Subject: Arm: foreign page handling in p2m_get_page_from_gfn() + +I can't see what would make the 1st of the assertions safe: For example, +the P2M lock not being held, the foreign page may disappear before we +get to call page_get_owner_and_reference(), which hence may return NULL. + +Even the 2nd, which appears to be safe safe, is lacking proper release +build fallbacks. + +Drop the former in favor of an if(), and convert the latter to the +equivalent of what x86 uses: ASSERT_UNREACHABLE() plus putting of the +obtained page. + +This is CVE-2025-58144 / part of XSA-473. + +Fixes: 9486a8d07ba8 ("xen/arm: Handle remove foreign mapping") +Signed-off-by: Jan Beulich +Reviewed-by: Julien Grall + +--- a/xen/arch/arm/p2m.c ++++ b/xen/arch/arm/p2m.c +@@ -74,10 +74,16 @@ struct page_info *p2m_get_page_from_gfn( + */ + if ( p2m_is_foreign(p2mt) ) + { +- struct domain *fdom = page_get_owner_and_reference(page); +- ASSERT(fdom != NULL); +- ASSERT(fdom != d); +- return page; ++ const struct domain *fdom = page_get_owner_and_reference(page); ++ ++ if ( fdom ) ++ { ++ if ( fdom != d ) ++ return page; ++ ASSERT_UNREACHABLE(); ++ put_page(page); ++ } ++ return NULL; + } + + return get_page(page, d) ? page : NULL; diff --git a/xsa473-2.patch b/xsa473-2.patch new file mode 100644 index 0000000..7d66f79 --- /dev/null +++ b/xsa473-2.patch @@ -0,0 +1,62 @@ +From: Jan Beulich +Subject: Arm: adjust locking in p2m_get_page_from_gfn() + +In order to safely acquire a reference for a foreign page mapping, the +P2M lock needs to be held until we have the reference in hand (or +getting one failed). Otherwise the page can change P2M type and +ownership in between. + +This is CVE-2025-58145 / part of XSA-473. + +Fixes: 9486a8d07ba8 ("xen/arm: Handle remove foreign mapping") +Signed-off-by: Jan Beulich +Reviewed-by: Julien Grall + +--- a/xen/arch/arm/p2m.c ++++ b/xen/arch/arm/p2m.c +@@ -53,18 +53,22 @@ mfn_t p2m_lookup(struct domain *d, gfn_t + struct page_info *p2m_get_page_from_gfn(struct domain *d, gfn_t gfn, + p2m_type_t *t) + { ++ struct p2m_domain *p2m = p2m_get_hostp2m(d); + struct page_info *page; + p2m_type_t p2mt; +- mfn_t mfn = p2m_lookup(d, gfn, &p2mt); ++ mfn_t mfn; ++ ++ p2m_read_lock(p2m); ++ mfn = p2m_get_entry(p2m, gfn, &p2mt, NULL, NULL, NULL); + + if ( t ) + *t = p2mt; + +- if ( !p2m_is_any_ram(p2mt) ) +- return NULL; +- +- if ( !mfn_valid(mfn) ) ++ if ( !p2m_is_any_ram(p2mt) || !mfn_valid(mfn) ) ++ { ++ p2m_read_unlock(p2m); + return NULL; ++ } + + page = mfn_to_page(mfn); + +@@ -76,6 +80,8 @@ struct page_info *p2m_get_page_from_gfn( + { + const struct domain *fdom = page_get_owner_and_reference(page); + ++ p2m_read_unlock(p2m); ++ + if ( fdom ) + { + if ( fdom != d ) +@@ -86,6 +92,8 @@ struct page_info *p2m_get_page_from_gfn( + return NULL; + } + ++ p2m_read_unlock(p2m); ++ + return get_page(page, d) ? page : NULL; + } + From d001d8f7cf1dda9f1809cd4ec2a6fd55587c7429 Mon Sep 17 00:00:00 2001 From: Python Maint Date: Fri, 19 Sep 2025 15:03:51 +0200 Subject: [PATCH 116/137] Rebuilt for Python 3.14.0rc3 bytecode --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index fc230e5..a16cd27 100644 --- a/xen.spec +++ b/xen.spec @@ -51,7 +51,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.20.1 -Release: 5%{?dist} +Release: 6%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -818,6 +818,9 @@ fi %endif %changelog +* Fri Sep 19 2025 Python Maint - 4.20.1-6 +- Rebuilt for Python 3.14.0rc3 bytecode + * Wed Sep 10 2025 Michael Young - 4.20.1-5 - Mutiple vulnerabilities in the Viridian interface [XSA-472, CVE-2025-27466, CVE-2025-58142, CVE-2025-58143] From 932125ac66f860a3fb1af7aacc744aa2113de00b Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Wed, 15 Oct 2025 17:21:56 +0100 Subject: [PATCH 117/137] OCaml 5.4.0 rebuild --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index a16cd27..01bf24c 100644 --- a/xen.spec +++ b/xen.spec @@ -51,7 +51,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.20.1 -Release: 6%{?dist} +Release: 7%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -818,6 +818,9 @@ fi %endif %changelog +* Wed Oct 15 2025 Richard W.M. Jones - 4.20.1-7 +- OCaml 5.4.0 rebuild + * Fri Sep 19 2025 Python Maint - 4.20.1-6 - Rebuilt for Python 3.14.0rc3 bytecode From 310309c8d59d280057f9e12df729a6d288e024dd Mon Sep 17 00:00:00 2001 From: Michael Young Date: Sun, 26 Oct 2025 12:39:49 +0000 Subject: [PATCH 118/137] teecr32_el1 and teehbr32_el1 support dropped in binutils 2.45.50-5.fc44 Incorrect removal of permissions on PCI device unplug [XSA-476, CVE-2025-58149] x86: Incorrect input sanitisation in Viridian hypercalls [XSA-475, CVE-2025-58147, CVE-2025-58148] --- dropped.regs.patch | 28 +++++++++++++++++++++++ xen.spec | 21 ++++++++++++++++- xsa475-1.patch | 26 +++++++++++++++++++++ xsa475-2.patch | 52 ++++++++++++++++++++++++++++++++++++++++++ xsa476-4.20.patch | 57 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 dropped.regs.patch create mode 100644 xsa475-1.patch create mode 100644 xsa475-2.patch create mode 100644 xsa476-4.20.patch diff --git a/dropped.regs.patch b/dropped.regs.patch new file mode 100644 index 0000000..995498e --- /dev/null +++ b/dropped.regs.patch @@ -0,0 +1,28 @@ +--- xen-4.21.0-rc2/xen/arch/arm/domain.c.orig 2025-10-22 12:01:02.000000000 +0100 ++++ xen-4.21.0-rc2/xen/arch/arm/domain.c 2025-10-26 00:02:03.017300510 +0100 +@@ -111,12 +111,6 @@ + p->arch.cntkctl = READ_SYSREG(CNTKCTL_EL1); + virt_timer_save(p); + +- if ( is_32bit_domain(p->domain) && cpu_has_thumbee ) +- { +- p->arch.teecr = READ_SYSREG(TEECR32_EL1); +- p->arch.teehbr = READ_SYSREG(TEEHBR32_EL1); +- } +- + #ifdef CONFIG_ARM_32 + p->arch.joscr = READ_CP32(JOSCR); + p->arch.jmcr = READ_CP32(JMCR); +@@ -244,12 +238,6 @@ + WRITE_SYSREG(n->arch.tpidrro_el0, TPIDRRO_EL0); + WRITE_SYSREG(n->arch.tpidr_el1, TPIDR_EL1); + +- if ( is_32bit_domain(n->domain) && cpu_has_thumbee ) +- { +- WRITE_SYSREG(n->arch.teecr, TEECR32_EL1); +- WRITE_SYSREG(n->arch.teehbr, TEEHBR32_EL1); +- } +- + #ifdef CONFIG_ARM_32 + WRITE_CP32(n->arch.joscr, JOSCR); + WRITE_CP32(n->arch.jmcr, JMCR); diff --git a/xen.spec b/xen.spec index 01bf24c..b817d86 100644 --- a/xen.spec +++ b/xen.spec @@ -51,7 +51,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.20.1 -Release: 7%{?dist} +Release: 9%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -84,6 +84,10 @@ Patch52: xsa472-2.patch Patch53: xsa472-3.patch Patch54: xsa473-1.patch Patch55: xsa473-2.patch +Patch56: xsa475-1.patch +Patch57: xsa475-2.patch +Patch58: xsa476-4.20.patch +Patch59: dropped.regs.patch # build using Fedora seabios and ipxe packages for roms @@ -269,6 +273,10 @@ manage Xen virtual machines. %patch 53 -p1 %patch 54 -p1 %patch 55 -p1 +%patch 56 -p1 +%patch 57 -p1 +%patch 58 -p1 +%patch 59 -p1 # stubdom sources cp -v %{SOURCE10} %{SOURCE11} %{SOURCE12} %{SOURCE13} %{SOURCE14} %{SOURCE15} stubdom @@ -818,6 +826,17 @@ fi %endif %changelog +* Sun Oct 26 2025 Michael Young - 4.20.1-9 +- teecr32_el1 and teehbr32_el1 support dropped in binutils 2.45.50-5.fc44 + +* Fri Oct 24 2025 Michael Young +- Incorrect removal of permissions on PCI device unplug [XSA-476, + CVE-2025-58149] + +* Tue Oct 21 2025 Michael Young +- x86: Incorrect input sanitisation in Viridian hypercalls [XSA-475, + CVE-2025-58147, CVE-2025-58148] + * Wed Oct 15 2025 Richard W.M. Jones - 4.20.1-7 - OCaml 5.4.0 rebuild diff --git a/xsa475-1.patch b/xsa475-1.patch new file mode 100644 index 0000000..6ebc3e0 --- /dev/null +++ b/xsa475-1.patch @@ -0,0 +1,26 @@ +From: Teddy Astie +Subject: x86/viridian: Enforce bounds check in vpmask_set() + +Callers can pass vp/mask values which exceed the size of vpmask->mask. Ensure +we only set bits which are within bounds. + +This is XSA-475 / CVE-2025-58147. + +Fixes: b4124682db6e ("viridian: add ExProcessorMasks variants of the flush hypercalls") +Signed-off-by: Teddy Astie +Reviewed-by: Andrew Cooper + +diff --git a/xen/arch/x86/hvm/viridian/viridian.c b/xen/arch/x86/hvm/viridian/viridian.c +index c0be24bd2210..703f9ac8bcc1 100644 +--- a/xen/arch/x86/hvm/viridian/viridian.c ++++ b/xen/arch/x86/hvm/viridian/viridian.c +@@ -562,7 +562,8 @@ static void vpmask_set(struct hypercall_vpmask *vpmask, unsigned int vp, + + if ( mask & 1 ) + { +- ASSERT(vp < HVM_MAX_VCPUS); ++ if ( vp >= HVM_MAX_VCPUS ) ++ break; + __set_bit(vp, vpmask->mask); + } + diff --git a/xsa475-2.patch b/xsa475-2.patch new file mode 100644 index 0000000..213d7ce --- /dev/null +++ b/xsa475-2.patch @@ -0,0 +1,52 @@ +From: Teddy Astie +Subject: x86/viridian: Enforce bounds check in send_ipi() + +Callers can pass in a vpmask which exceeds d->max_vcpus. Prevent out-of-bound +reads of d->vcpu[]. + +This is XSA-475 / CVE-2025-58148. + +Fixes: 728acba1ba4a ("viridian: use hypercall_vpmask in hvcall_ipi()") +Signed-off-by: Teddy Astie +Reviewed-by: Andrew Cooper + +diff --git a/xen/arch/x86/hvm/viridian/viridian.c b/xen/arch/x86/hvm/viridian/viridian.c +index 703f9ac8bcc1..f79cffcb3767 100644 +--- a/xen/arch/x86/hvm/viridian/viridian.c ++++ b/xen/arch/x86/hvm/viridian/viridian.c +@@ -577,26 +577,6 @@ static void vpmask_fill(struct hypercall_vpmask *vpmask) + bitmap_fill(vpmask->mask, HVM_MAX_VCPUS); + } + +-static unsigned int vpmask_first(const struct hypercall_vpmask *vpmask) +-{ +- return find_first_bit(vpmask->mask, HVM_MAX_VCPUS); +-} +- +-static unsigned int vpmask_next(const struct hypercall_vpmask *vpmask, +- unsigned int vp) +-{ +- /* +- * If vp + 1 > HVM_MAX_VCPUS then find_next_bit() will return +- * HVM_MAX_VCPUS, ensuring the for_each_vp ( ... ) loop terminates. +- */ +- return find_next_bit(vpmask->mask, HVM_MAX_VCPUS, vp + 1); +-} +- +-#define for_each_vp(vpmask, vp) \ +- for ( (vp) = vpmask_first(vpmask); \ +- (vp) < HVM_MAX_VCPUS; \ +- (vp) = vpmask_next(vpmask, vp) ) +- + static unsigned int vpmask_nr(const struct hypercall_vpmask *vpmask) + { + return bitmap_weight(vpmask->mask, HVM_MAX_VCPUS); +@@ -813,7 +793,7 @@ static void send_ipi(struct hypercall_vpmask *vpmask, uint8_t vector) + if ( nr > 1 ) + cpu_raise_softirq_batch_begin(); + +- for_each_vp ( vpmask, vp ) ++ bitmap_for_each ( vp, vpmask->mask, currd->max_vcpus ) + { + struct vlapic *vlapic = vcpu_vlapic(currd->vcpu[vp]); + diff --git a/xsa476-4.20.patch b/xsa476-4.20.patch new file mode 100644 index 0000000..2d3cc04 --- /dev/null +++ b/xsa476-4.20.patch @@ -0,0 +1,57 @@ +From: Jiqian Chen +Subject: tools/libs/light: fix BAR memory address truncation + +64-bit BAR memory address is truncated when removing a passthrough +pci device from guest since it uses "unsigned int". + +So, change to use 64-bit type to fix this problem. + +This is XSA-476 / CVE-2025-58149. + +Fixes: b0a1af61678b ("libxenlight: implement pci passthrough") +Signed-off-by: Jiqian Chen +Release-Acked-by: Oleksii Kurochko +Reviewed-by: Juergen Gross +Acked-by: Anthony PERARD + +diff --git a/tools/libs/light/libxl_pci.c b/tools/libs/light/libxl_pci.c +index 1647fd6f4756..7af602224aba 100644 +--- a/tools/libs/light/libxl_pci.c ++++ b/tools/libs/light/libxl_pci.c +@@ -2179,7 +2179,7 @@ static void pci_remove_detached(libxl__egc *egc, + { + STATE_AO_GC(prs->aodev->ao); + libxl_ctx *ctx = libxl__gc_owner(gc); +- unsigned int start = 0, end = 0, flags = 0, size = 0; ++ uint64_t start = 0, end = 0, flags = 0, size = 0; + int irq = 0, i, stubdomid = 0; + const char *sysfs_path; + FILE *f; +@@ -2209,7 +2209,8 @@ static void pci_remove_detached(libxl__egc *egc, + } + + for (i = 0; i < PROC_PCI_NUM_RESOURCES; i++) { +- if (fscanf(f, "0x%x 0x%x 0x%x\n", &start, &end, &flags) != 3) ++ if (fscanf(f, "0x%"SCNx64" 0x%"SCNx64" 0x%"SCNx64"\n", ++ &start, &end, &flags) != 3) + continue; + size = end - start + 1; + if (start) { +@@ -2218,7 +2219,7 @@ static void pci_remove_detached(libxl__egc *egc, + size, 0); + if (rc < 0) + LOGED(ERROR, domid, +- "xc_domain_ioport_permission error 0x%x/0x%x", ++ "xc_domain_ioport_permission error %#"PRIx64"/%#"PRIx64, + start, + size); + } else { +@@ -2228,7 +2229,7 @@ static void pci_remove_detached(libxl__egc *egc, + 0); + if (rc < 0) + LOGED(ERROR, domid, +- "xc_domain_iomem_permission error 0x%x/0x%x", ++ "xc_domain_iomem_permission error %#"PRIx64"/%#"PRIx64, + start, + size); + } From b27c4b799dbddd05aad6d2c62a05d9fb6461316f Mon Sep 17 00:00:00 2001 From: Michael Young Date: Fri, 14 Nov 2025 18:46:13 +0000 Subject: [PATCH 119/137] update to xen 4.20.2 --- .gitignore | 2 +- sources | 2 +- ...6b65098f790e6573f555c5ef170d3f373c6e.patch | 40 -------- xen.spec | 26 ++--- xsa472-1.patch | 43 --------- xsa472-2.patch | 41 -------- xsa472-3.patch | 94 ------------------- xsa473-1.patch | 43 --------- xsa473-2.patch | 62 ------------ xsa475-1.patch | 26 ----- xsa475-2.patch | 52 ---------- xsa476-4.20.patch | 57 ----------- 12 files changed, 8 insertions(+), 480 deletions(-) delete mode 100644 xen.git-7cda6b65098f790e6573f555c5ef170d3f373c6e.patch delete mode 100644 xsa472-1.patch delete mode 100644 xsa472-2.patch delete mode 100644 xsa472-3.patch delete mode 100644 xsa473-1.patch delete mode 100644 xsa473-2.patch delete mode 100644 xsa475-1.patch delete mode 100644 xsa475-2.patch delete mode 100644 xsa476-4.20.patch diff --git a/.gitignore b/.gitignore index 5d9b74e..2f6f692 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,5 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.20.1.tar.gz /mini-os-4.20.0.tar.xz +/xen-4.20.2.tar.gz diff --git a/sources b/sources index 0aa5da3..0032636 100644 --- a/sources +++ b/sources @@ -4,5 +4,5 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.20.1.tar.gz) = e4502ddaeb66de4c30d59e90af198586683c28b56bf0eb03ca5508f663fef5e6582c009861ff5b66b7f0df6e5f6bc12337eff0ec775d72e7cb189fd2079dbd9d SHA512 (mini-os-4.20.0.tar.xz) = 52d5d0985e1b4c26b761c94a3f7ca543bc388a96f69710524ebf34dd5b01d8f774fd5a3f6a50739c8b7c7edfdf07b594f5d44de8f389ea4138a62f9c5884c4e6 +SHA512 (xen-4.20.2.tar.gz) = 4a40668020cb142de0e182d6c7f846a58391828fb1db28c9325cd55d78ef0549c1187886cfe0c366c02803aa1b46c09334ab06748e30a2b509a9bbbb7886a8f1 diff --git a/xen.git-7cda6b65098f790e6573f555c5ef170d3f373c6e.patch b/xen.git-7cda6b65098f790e6573f555c5ef170d3f373c6e.patch deleted file mode 100644 index c072120..0000000 --- a/xen.git-7cda6b65098f790e6573f555c5ef170d3f373c6e.patch +++ /dev/null @@ -1,40 +0,0 @@ -From 7cda6b65098f790e6573f555c5ef170d3f373c6e Mon Sep 17 00:00:00 2001 -From: =?utf8?q?Marek=20Marczykowski-G=C3=B3recki?= - -Date: Mon, 4 Aug 2025 15:22:13 +0200 -Subject: [PATCH] tools/xl: don't crash on NULL command line -MIME-Version: 1.0 -Content-Type: text/plain; charset=utf8 -Content-Transfer-Encoding: 8bit - -When running xl in a domU, it doesn't have access to the Xen command -line. Before the non-truncating xc_xenver_cmdline(), it was always set -with strdup, possibly of an empty string. Now it's NULL. Treat it the -same as empty cmdline, as it was before. Autoballoon isn't relevant for -xl devd in a domU anyway. - -Fixes: 75f91607621c ("tools: Introduce a non-truncating xc_xenver_cmdline()") -Signed-off-by: Marek Marczykowski-Górecki -Acked-by: Anthony PERARD -master commit: a4988c4177be81f225af1516e1bbb9ec14f76388 -master date: 2025-07-31 14:44:02 +0200 ---- - tools/xl/xl.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tools/xl/xl.c b/tools/xl/xl.c -index ec72ca60c3..b3abc91b60 100644 ---- a/tools/xl/xl.c -+++ b/tools/xl/xl.c -@@ -79,7 +79,7 @@ static int auto_autoballoon(void) - int ret; - - info = libxl_get_version_info(ctx); -- if (!info) -+ if (!info || !info->commandline) - return 1; /* default to on */ - - #define SIZE_PATTERN "-?[0-9]+[bBkKmMgGtT]?" --- -2.39.5 - diff --git a/xen.spec b/xen.spec index b817d86..b6fe4e8 100644 --- a/xen.spec +++ b/xen.spec @@ -50,8 +50,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.20.1 -Release: 9%{?dist} +Version: 4.20.2 +Release: 2%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -78,15 +78,6 @@ Patch43: xen.gcc11.fixes.patch Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch Patch49: xen.python3.12.patch -Patch50: xen.git-7cda6b65098f790e6573f555c5ef170d3f373c6e.patch -Patch51: xsa472-1.patch -Patch52: xsa472-2.patch -Patch53: xsa472-3.patch -Patch54: xsa473-1.patch -Patch55: xsa473-2.patch -Patch56: xsa475-1.patch -Patch57: xsa475-2.patch -Patch58: xsa476-4.20.patch Patch59: dropped.regs.patch @@ -267,15 +258,6 @@ manage Xen virtual machines. %patch 45 -p1 %patch 46 -p1 %patch 49 -p1 -%patch 50 -p1 -%patch 51 -p1 -%patch 52 -p1 -%patch 53 -p1 -%patch 54 -p1 -%patch 55 -p1 -%patch 56 -p1 -%patch 57 -p1 -%patch 58 -p1 %patch 59 -p1 # stubdom sources @@ -826,6 +808,10 @@ fi %endif %changelog +* Thu Nov 13 2025 Michael Young - 4.20.2-2.fc44 +- update to xen 4.20.2 + remove patches now included or superceded upstream + * Sun Oct 26 2025 Michael Young - 4.20.1-9 - teecr32_el1 and teehbr32_el1 support dropped in binutils 2.45.50-5.fc44 diff --git a/xsa472-1.patch b/xsa472-1.patch deleted file mode 100644 index e69f3e7..0000000 --- a/xsa472-1.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 262114a440bf7c32fd6d215e243b3eaebdd6d7cd Mon Sep 17 00:00:00 2001 -From: Roger Pau Monne -Date: Thu, 10 Jul 2025 15:51:40 +0200 -Subject: [PATCH 1/3] x86/viridian: avoid NULL pointer dereference in - update_reference_tsc() -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The function is only called when the MSR has the enabled bit set, but even -then the page might not be mapped because the guest provided gfn is not -suitable. - -Prevent a NULL pointer dereference in update_reference_tsc() by checking -whether the page is mapped. - -This is CVE-2025-27466 / part of XSA-472. - -Fixes: 386b3365221d ('viridian: use viridian_map/unmap_guest_page() for reference tsc page') -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich ---- - xen/arch/x86/hvm/viridian/time.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/xen/arch/x86/hvm/viridian/time.c b/xen/arch/x86/hvm/viridian/time.c -index 137577384f1e..ca6d526f46b7 100644 ---- a/xen/arch/x86/hvm/viridian/time.c -+++ b/xen/arch/x86/hvm/viridian/time.c -@@ -26,6 +26,10 @@ static void update_reference_tsc(const struct domain *d, bool initialize) - HV_REFERENCE_TSC_PAGE *p = rt->ptr; - uint32_t seq; - -+ /* Reference TSC page might not be mapped even if the MSR is enabled. */ -+ if ( !p ) -+ return; -+ - if ( initialize ) - clear_page(p); - --- -2.49.0 - diff --git a/xsa472-2.patch b/xsa472-2.patch deleted file mode 100644 index 5b23b6d..0000000 --- a/xsa472-2.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 71c9568e290b51dfd7ab091ac98b272fd0aa0b90 Mon Sep 17 00:00:00 2001 -From: Roger Pau Monne -Date: Thu, 10 Jul 2025 15:58:51 +0200 -Subject: [PATCH 2/3] x86/viridian: avoid NULL pointer dereference in - viridian_synic_deliver_timer_msg() -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The function is called unconditionally, regardless of whether the SIM page -is mapped. Avoid a NULL pointer dereference in -viridian_synic_deliver_timer_msg() by checking whether the SIM page is -mapped. - -This is CVE-2025-58142 / part of XSA-472. - -Fixes: 26fba3c85571 ('viridian: add implementation of synthetic timers') -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich ---- - xen/arch/x86/hvm/viridian/synic.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/xen/arch/x86/hvm/viridian/synic.c b/xen/arch/x86/hvm/viridian/synic.c -index c3dc573b003d..e6cba7548f1b 100644 ---- a/xen/arch/x86/hvm/viridian/synic.c -+++ b/xen/arch/x86/hvm/viridian/synic.c -@@ -338,6 +338,10 @@ bool viridian_synic_deliver_timer_msg(struct vcpu *v, unsigned int sintx, - .DeliveryTime = delivery, - }; - -+ /* Don't assume SIM page to be mapped. */ -+ if ( !msg ) -+ return false; -+ - /* - * To avoid using an atomic test-and-set, and barrier before calling - * vlapic_set_irq(), this function must be called in context of the --- -2.49.0 - diff --git a/xsa472-3.patch b/xsa472-3.patch deleted file mode 100644 index 5dfdf48..0000000 --- a/xsa472-3.patch +++ /dev/null @@ -1,94 +0,0 @@ -From aed4cfd64d178aee677a8790440addda03678cd6 Mon Sep 17 00:00:00 2001 -From: Roger Pau Monne -Date: Thu, 3 Jul 2025 13:09:03 +0200 -Subject: [PATCH 3/3] x86/viridian: protect concurrent modification of the - reference TSC page -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The reference TSC page is shared between all vCPUs, and the data stored in -the domain struct. However the handlers to set and clear it are not safe -against concurrent accesses. It's possible for two (or more) vCPUs to call -HV_X64_MSR_REFERENCE_TSC at the same time and cause the in-use reference -TSC page to be freed, while still being on the p2m. This creates an -information leak, where the page can end up mapped in another domain while -still being part of the original domain p2m. - -It's also possible to underflow the reference counter, as multiple -concurrent writes to HV_X64_MSR_REFERENCE_TSC can create an imbalance on -the number of put_page_and_type() calls. - -Introduce a lock to protect the reference TSC domain field, thus -serializing concurrent vCPU accesses. - -This is CVE-2025-58143 / part of XSA-472. - -Fixes: 386b3365221d ('viridian: use viridian_map/unmap_guest_page() for reference tsc page') -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich ---- - xen/arch/x86/hvm/viridian/time.c | 4 ++++ - xen/arch/x86/hvm/viridian/viridian.c | 2 ++ - xen/arch/x86/include/asm/hvm/viridian.h | 1 + - 3 files changed, 7 insertions(+) - -diff --git a/xen/arch/x86/hvm/viridian/time.c b/xen/arch/x86/hvm/viridian/time.c -index ca6d526f46b7..9311858d63c0 100644 ---- a/xen/arch/x86/hvm/viridian/time.c -+++ b/xen/arch/x86/hvm/viridian/time.c -@@ -108,8 +108,10 @@ static void time_ref_count_thaw(const struct domain *d) - - trc->off = (int64_t)trc->val - trc_val(d, 0); - -+ spin_lock(&vd->lock); - if ( vd->reference_tsc.msr.enabled ) - update_reference_tsc(d, false); -+ spin_unlock(&vd->lock); - } - - static uint64_t time_ref_count(const struct domain *d) -@@ -331,6 +333,7 @@ int viridian_time_wrmsr(struct vcpu *v, uint32_t idx, uint64_t val) - if ( !(viridian_feature_mask(d) & HVMPV_reference_tsc) ) - return X86EMUL_EXCEPTION; - -+ spin_lock(&vd->lock); - viridian_unmap_guest_page(&vd->reference_tsc); - vd->reference_tsc.msr.raw = val; - viridian_dump_guest_page(v, "REFERENCE_TSC", &vd->reference_tsc); -@@ -339,6 +342,7 @@ int viridian_time_wrmsr(struct vcpu *v, uint32_t idx, uint64_t val) - viridian_map_guest_page(d, &vd->reference_tsc); - update_reference_tsc(d, true); - } -+ spin_unlock(&vd->lock); - break; - - case HV_X64_MSR_TIME_REF_COUNT: -diff --git a/xen/arch/x86/hvm/viridian/viridian.c b/xen/arch/x86/hvm/viridian/viridian.c -index 7ea6c9016894..c0be24bd2210 100644 ---- a/xen/arch/x86/hvm/viridian/viridian.c -+++ b/xen/arch/x86/hvm/viridian/viridian.c -@@ -494,6 +494,8 @@ int viridian_domain_init(struct domain *d) - if ( !d->arch.hvm.viridian ) - return -ENOMEM; - -+ spin_lock_init(&d->arch.hvm.viridian->lock); -+ - rc = viridian_synic_domain_init(d); - if ( rc ) - goto fail; -diff --git a/xen/arch/x86/include/asm/hvm/viridian.h b/xen/arch/x86/include/asm/hvm/viridian.h -index 4c8ff6e80b6f..47c9d13841ac 100644 ---- a/xen/arch/x86/include/asm/hvm/viridian.h -+++ b/xen/arch/x86/include/asm/hvm/viridian.h -@@ -71,6 +71,7 @@ struct viridian_domain - DECLARE_BITMAP(hypercall_flags, _HCALL_nr); - struct viridian_time_ref_count time_ref_count; - struct viridian_page reference_tsc; -+ spinlock_t lock; - }; - - void cpuid_viridian_leaves(const struct vcpu *v, uint32_t leaf, --- -2.49.0 - diff --git a/xsa473-1.patch b/xsa473-1.patch deleted file mode 100644 index e8689b6..0000000 --- a/xsa473-1.patch +++ /dev/null @@ -1,43 +0,0 @@ -From: Jan Beulich -Subject: Arm: foreign page handling in p2m_get_page_from_gfn() - -I can't see what would make the 1st of the assertions safe: For example, -the P2M lock not being held, the foreign page may disappear before we -get to call page_get_owner_and_reference(), which hence may return NULL. - -Even the 2nd, which appears to be safe safe, is lacking proper release -build fallbacks. - -Drop the former in favor of an if(), and convert the latter to the -equivalent of what x86 uses: ASSERT_UNREACHABLE() plus putting of the -obtained page. - -This is CVE-2025-58144 / part of XSA-473. - -Fixes: 9486a8d07ba8 ("xen/arm: Handle remove foreign mapping") -Signed-off-by: Jan Beulich -Reviewed-by: Julien Grall - ---- a/xen/arch/arm/p2m.c -+++ b/xen/arch/arm/p2m.c -@@ -74,10 +74,16 @@ struct page_info *p2m_get_page_from_gfn( - */ - if ( p2m_is_foreign(p2mt) ) - { -- struct domain *fdom = page_get_owner_and_reference(page); -- ASSERT(fdom != NULL); -- ASSERT(fdom != d); -- return page; -+ const struct domain *fdom = page_get_owner_and_reference(page); -+ -+ if ( fdom ) -+ { -+ if ( fdom != d ) -+ return page; -+ ASSERT_UNREACHABLE(); -+ put_page(page); -+ } -+ return NULL; - } - - return get_page(page, d) ? page : NULL; diff --git a/xsa473-2.patch b/xsa473-2.patch deleted file mode 100644 index 7d66f79..0000000 --- a/xsa473-2.patch +++ /dev/null @@ -1,62 +0,0 @@ -From: Jan Beulich -Subject: Arm: adjust locking in p2m_get_page_from_gfn() - -In order to safely acquire a reference for a foreign page mapping, the -P2M lock needs to be held until we have the reference in hand (or -getting one failed). Otherwise the page can change P2M type and -ownership in between. - -This is CVE-2025-58145 / part of XSA-473. - -Fixes: 9486a8d07ba8 ("xen/arm: Handle remove foreign mapping") -Signed-off-by: Jan Beulich -Reviewed-by: Julien Grall - ---- a/xen/arch/arm/p2m.c -+++ b/xen/arch/arm/p2m.c -@@ -53,18 +53,22 @@ mfn_t p2m_lookup(struct domain *d, gfn_t - struct page_info *p2m_get_page_from_gfn(struct domain *d, gfn_t gfn, - p2m_type_t *t) - { -+ struct p2m_domain *p2m = p2m_get_hostp2m(d); - struct page_info *page; - p2m_type_t p2mt; -- mfn_t mfn = p2m_lookup(d, gfn, &p2mt); -+ mfn_t mfn; -+ -+ p2m_read_lock(p2m); -+ mfn = p2m_get_entry(p2m, gfn, &p2mt, NULL, NULL, NULL); - - if ( t ) - *t = p2mt; - -- if ( !p2m_is_any_ram(p2mt) ) -- return NULL; -- -- if ( !mfn_valid(mfn) ) -+ if ( !p2m_is_any_ram(p2mt) || !mfn_valid(mfn) ) -+ { -+ p2m_read_unlock(p2m); - return NULL; -+ } - - page = mfn_to_page(mfn); - -@@ -76,6 +80,8 @@ struct page_info *p2m_get_page_from_gfn( - { - const struct domain *fdom = page_get_owner_and_reference(page); - -+ p2m_read_unlock(p2m); -+ - if ( fdom ) - { - if ( fdom != d ) -@@ -86,6 +92,8 @@ struct page_info *p2m_get_page_from_gfn( - return NULL; - } - -+ p2m_read_unlock(p2m); -+ - return get_page(page, d) ? page : NULL; - } - diff --git a/xsa475-1.patch b/xsa475-1.patch deleted file mode 100644 index 6ebc3e0..0000000 --- a/xsa475-1.patch +++ /dev/null @@ -1,26 +0,0 @@ -From: Teddy Astie -Subject: x86/viridian: Enforce bounds check in vpmask_set() - -Callers can pass vp/mask values which exceed the size of vpmask->mask. Ensure -we only set bits which are within bounds. - -This is XSA-475 / CVE-2025-58147. - -Fixes: b4124682db6e ("viridian: add ExProcessorMasks variants of the flush hypercalls") -Signed-off-by: Teddy Astie -Reviewed-by: Andrew Cooper - -diff --git a/xen/arch/x86/hvm/viridian/viridian.c b/xen/arch/x86/hvm/viridian/viridian.c -index c0be24bd2210..703f9ac8bcc1 100644 ---- a/xen/arch/x86/hvm/viridian/viridian.c -+++ b/xen/arch/x86/hvm/viridian/viridian.c -@@ -562,7 +562,8 @@ static void vpmask_set(struct hypercall_vpmask *vpmask, unsigned int vp, - - if ( mask & 1 ) - { -- ASSERT(vp < HVM_MAX_VCPUS); -+ if ( vp >= HVM_MAX_VCPUS ) -+ break; - __set_bit(vp, vpmask->mask); - } - diff --git a/xsa475-2.patch b/xsa475-2.patch deleted file mode 100644 index 213d7ce..0000000 --- a/xsa475-2.patch +++ /dev/null @@ -1,52 +0,0 @@ -From: Teddy Astie -Subject: x86/viridian: Enforce bounds check in send_ipi() - -Callers can pass in a vpmask which exceeds d->max_vcpus. Prevent out-of-bound -reads of d->vcpu[]. - -This is XSA-475 / CVE-2025-58148. - -Fixes: 728acba1ba4a ("viridian: use hypercall_vpmask in hvcall_ipi()") -Signed-off-by: Teddy Astie -Reviewed-by: Andrew Cooper - -diff --git a/xen/arch/x86/hvm/viridian/viridian.c b/xen/arch/x86/hvm/viridian/viridian.c -index 703f9ac8bcc1..f79cffcb3767 100644 ---- a/xen/arch/x86/hvm/viridian/viridian.c -+++ b/xen/arch/x86/hvm/viridian/viridian.c -@@ -577,26 +577,6 @@ static void vpmask_fill(struct hypercall_vpmask *vpmask) - bitmap_fill(vpmask->mask, HVM_MAX_VCPUS); - } - --static unsigned int vpmask_first(const struct hypercall_vpmask *vpmask) --{ -- return find_first_bit(vpmask->mask, HVM_MAX_VCPUS); --} -- --static unsigned int vpmask_next(const struct hypercall_vpmask *vpmask, -- unsigned int vp) --{ -- /* -- * If vp + 1 > HVM_MAX_VCPUS then find_next_bit() will return -- * HVM_MAX_VCPUS, ensuring the for_each_vp ( ... ) loop terminates. -- */ -- return find_next_bit(vpmask->mask, HVM_MAX_VCPUS, vp + 1); --} -- --#define for_each_vp(vpmask, vp) \ -- for ( (vp) = vpmask_first(vpmask); \ -- (vp) < HVM_MAX_VCPUS; \ -- (vp) = vpmask_next(vpmask, vp) ) -- - static unsigned int vpmask_nr(const struct hypercall_vpmask *vpmask) - { - return bitmap_weight(vpmask->mask, HVM_MAX_VCPUS); -@@ -813,7 +793,7 @@ static void send_ipi(struct hypercall_vpmask *vpmask, uint8_t vector) - if ( nr > 1 ) - cpu_raise_softirq_batch_begin(); - -- for_each_vp ( vpmask, vp ) -+ bitmap_for_each ( vp, vpmask->mask, currd->max_vcpus ) - { - struct vlapic *vlapic = vcpu_vlapic(currd->vcpu[vp]); - diff --git a/xsa476-4.20.patch b/xsa476-4.20.patch deleted file mode 100644 index 2d3cc04..0000000 --- a/xsa476-4.20.patch +++ /dev/null @@ -1,57 +0,0 @@ -From: Jiqian Chen -Subject: tools/libs/light: fix BAR memory address truncation - -64-bit BAR memory address is truncated when removing a passthrough -pci device from guest since it uses "unsigned int". - -So, change to use 64-bit type to fix this problem. - -This is XSA-476 / CVE-2025-58149. - -Fixes: b0a1af61678b ("libxenlight: implement pci passthrough") -Signed-off-by: Jiqian Chen -Release-Acked-by: Oleksii Kurochko -Reviewed-by: Juergen Gross -Acked-by: Anthony PERARD - -diff --git a/tools/libs/light/libxl_pci.c b/tools/libs/light/libxl_pci.c -index 1647fd6f4756..7af602224aba 100644 ---- a/tools/libs/light/libxl_pci.c -+++ b/tools/libs/light/libxl_pci.c -@@ -2179,7 +2179,7 @@ static void pci_remove_detached(libxl__egc *egc, - { - STATE_AO_GC(prs->aodev->ao); - libxl_ctx *ctx = libxl__gc_owner(gc); -- unsigned int start = 0, end = 0, flags = 0, size = 0; -+ uint64_t start = 0, end = 0, flags = 0, size = 0; - int irq = 0, i, stubdomid = 0; - const char *sysfs_path; - FILE *f; -@@ -2209,7 +2209,8 @@ static void pci_remove_detached(libxl__egc *egc, - } - - for (i = 0; i < PROC_PCI_NUM_RESOURCES; i++) { -- if (fscanf(f, "0x%x 0x%x 0x%x\n", &start, &end, &flags) != 3) -+ if (fscanf(f, "0x%"SCNx64" 0x%"SCNx64" 0x%"SCNx64"\n", -+ &start, &end, &flags) != 3) - continue; - size = end - start + 1; - if (start) { -@@ -2218,7 +2219,7 @@ static void pci_remove_detached(libxl__egc *egc, - size, 0); - if (rc < 0) - LOGED(ERROR, domid, -- "xc_domain_ioport_permission error 0x%x/0x%x", -+ "xc_domain_ioport_permission error %#"PRIx64"/%#"PRIx64, - start, - size); - } else { -@@ -2228,7 +2229,7 @@ static void pci_remove_detached(libxl__egc *egc, - 0); - if (rc < 0) - LOGED(ERROR, domid, -- "xc_domain_iomem_permission error 0x%x/0x%x", -+ "xc_domain_iomem_permission error %#"PRIx64"/%#"PRIx64, - start, - size); - } From aa9dce01d1aa8c08c2de629d31028b9c4f166ad3 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 7 Jan 2026 20:11:57 +0000 Subject: [PATCH 120/137] update to xen 4.21.0 --- .gitignore | 4 +- sources | 4 +- xen.gcc16.fixes.patch | 31 +++++++++++++++ xen.json.nocpuid.patch | 27 +++++++++++++ xen.spec | 87 ++++++++++++++++++++++++++++-------------- 5 files changed, 120 insertions(+), 33 deletions(-) create mode 100644 xen.gcc16.fixes.patch create mode 100644 xen.json.nocpuid.patch diff --git a/.gitignore b/.gitignore index 2f6f692..4d2b84e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,5 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/mini-os-4.20.0.tar.xz -/xen-4.20.2.tar.gz +/mini-os-4.21.0.tar.xz +/xen-4.21.0.tar.xz diff --git a/sources b/sources index 0032636..48e84e7 100644 --- a/sources +++ b/sources @@ -4,5 +4,5 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (mini-os-4.20.0.tar.xz) = 52d5d0985e1b4c26b761c94a3f7ca543bc388a96f69710524ebf34dd5b01d8f774fd5a3f6a50739c8b7c7edfdf07b594f5d44de8f389ea4138a62f9c5884c4e6 -SHA512 (xen-4.20.2.tar.gz) = 4a40668020cb142de0e182d6c7f846a58391828fb1db28c9325cd55d78ef0549c1187886cfe0c366c02803aa1b46c09334ab06748e30a2b509a9bbbb7886a8f1 +SHA512 (mini-os-4.21.0.tar.xz) = 7543774d15da84476d93d04154990923c82209cb3fa125574c0383652c5a310957200f54b63b34502161f9c3afce4907e0060d9036f3eaf3a7cb6b1b3119b546 +SHA512 (xen-4.21.0.tar.xz) = f8bd08e190b1f5013474718ecad687eddb97c57f7ae6b8262ffb7b91d70f0a5d30bce69c0301e7a97237d43ae2d75179e12be28f8c464d8cf5318a40acbf77ca diff --git a/xen.gcc16.fixes.patch b/xen.gcc16.fixes.patch new file mode 100644 index 0000000..5bad057 --- /dev/null +++ b/xen.gcc16.fixes.patch @@ -0,0 +1,31 @@ +--- xen-4.21.0/tools/libs/light/libxl_internal.c.orig 2025-11-18 18:02:13.000000000 +0000 ++++ xen-4.21.0/tools/libs/light/libxl_internal.c 2026-01-05 20:55:36.678719203 +0000 +@@ -204,7 +204,7 @@ + + char *libxl__dirname(libxl__gc *gc, const char *s) + { +- char *c = strrchr(s, '/'); ++ char *c = strrchr((char *) s, '/'); + + if (!c) + return NULL; +--- xen-4.21.0/tools/libs/light/libxl_cpuid.c.orig 2025-11-18 18:02:13.000000000 +0000 ++++ xen-4.21.0/tools/libs/light/libxl_cpuid.c 2026-01-05 23:46:40.203837602 +0000 +@@ -444,14 +444,14 @@ + return 4; + } + value = str[1] - 'a'; +- endptr = strchr(str, '='); ++ endptr = strchr((char *) str, '='); + if (value > 3 || endptr == NULL) { + return 4; + } + str = endptr + 1; +- endptr = strchr(str, ','); ++ endptr = strchr((char *) str, ','); + if (endptr == NULL) { +- endptr = strchr(str, 0); ++ endptr = strchr((char *) str, 0); + } + if (endptr - str != 32) { + return 5; diff --git a/xen.json.nocpuid.patch b/xen.json.nocpuid.patch new file mode 100644 index 0000000..f701f0d --- /dev/null +++ b/xen.json.nocpuid.patch @@ -0,0 +1,27 @@ +--- xen-4.21.0/tools/libs/light/libxl_nocpuid.c.orig 2025-11-18 18:02:13.000000000 +0000 ++++ xen-4.21.0/tools/libs/light/libxl_nocpuid.c 2025-11-20 09:03:56.517804514 +0000 +@@ -40,11 +40,24 @@ + return 0; + } + ++#ifdef HAVE_LIBJSONC ++#ifndef _hidden ++#define _hidden ++#endif ++_hidden int libxl_cpuid_policy_list_gen_jso(json_object **jso_r, ++ libxl_cpuid_policy_list *pcpuid) ++{ ++ return 0; ++} ++#endif ++ ++#if defined(HAVE_LIBYAJL) + yajl_gen_status libxl_cpuid_policy_list_gen_json(yajl_gen hand, + libxl_cpuid_policy_list *pcpuid) + { + return 0; + } ++#endif + + int libxl__cpuid_policy_list_parse_json(libxl__gc *gc, + const libxl__json_object *o, diff --git a/xen.spec b/xen.spec index b6fe4e8..925f4ff 100644 --- a/xen.spec +++ b/xen.spec @@ -46,16 +46,16 @@ %endif # Hypervisor ABI -%define hv_abi 4.20 +%define hv_abi 4.21 Summary: Xen is a virtual machine monitor Name: xen -Version: 4.20.2 -Release: 2%{?dist} +Version: 4.21.0 +Release: 1%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ -Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz +Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.xz Source2: %{name}.logrotate # used by stubdoms Source10: lwip-1.3.0.tar.gz @@ -66,19 +66,21 @@ Source14: grub-0.97.tar.gz Source15: polarssl-1.1.4-gpl.tgz # .config file for xen hypervisor Source21: xen.hypervisor.config -# mini-os xen-RELEASE-4.20.0 with .git and .gitignore stripped -Source22: mini-os-4.20.0.tar.xz +# mini-os xen-RELEASE-4.21.0 with .git and .gitignore stripped +Source22: mini-os-4.21.0.tar.xz -Patch5: xen.fedora.systemd.patch -Patch6: xen.ocaml.selinux.fix.patch -Patch34: xen.canonicalize.patch -Patch37: droplibvirtconflict.patch -Patch41: xen.gcc9.fixes.patch -Patch43: xen.gcc11.fixes.patch -Patch45: xen.gcc12.fixes.patch -Patch46: xen.efi.build.patch -Patch49: xen.python3.12.patch -Patch59: dropped.regs.patch +Patch1: xen.fedora.systemd.patch +Patch2: xen.ocaml.selinux.fix.patch +Patch3: xen.canonicalize.patch +Patch4: droplibvirtconflict.patch +Patch5: xen.gcc9.fixes.patch +Patch6: xen.gcc11.fixes.patch +Patch7: xen.gcc12.fixes.patch +Patch8: xen.efi.build.patch +Patch9: xen.python3.12.patch +Patch10: dropped.regs.patch +Patch11: xen.json.nocpuid.patch +Patch12: xen.gcc16.fixes.patch # build using Fedora seabios and ipxe packages for roms @@ -102,8 +104,10 @@ BuildRequires: acpica-tools BuildRequires: bzip2-devel xz-devel libzstd-devel # libfsimage BuildRequires: e2fsprogs-devel -# tools now require yajl and wget -BuildRequires: yajl-devel wget +# tools now require wget +BuildRequires: wget +# use json-c instead of yajl +BuildRequires: json-c-devel # remus support now needs libnl3 BuildRequires: libnl3-devel %if %with_xsm @@ -246,19 +250,25 @@ This package contains libraries for developing ocaml tools to manage Xen virtual machines. %endif +%package test +Summary: internal xen tests +%description test +This package contains files used in testing the xen builds %prep %setup -q +%patch 1 -p1 +%patch 2 -p1 +%patch 3 -p1 +%patch 4 -p1 %patch 5 -p1 %patch 6 -p1 -%patch 34 -p1 -%patch 37 -p1 -%patch 41 -p1 -%patch 43 -p1 -%patch 45 -p1 -%patch 46 -p1 -%patch 49 -p1 -%patch 59 -p1 +%patch 7 -p1 +%patch 8 -p1 +%patch 9 -p1 +%patch 10 -p1 +%patch 11 -p1 +%patch 12 -p1 # stubdom sources cp -v %{SOURCE10} %{SOURCE11} %{SOURCE12} %{SOURCE13} %{SOURCE14} %{SOURCE15} stubdom @@ -305,7 +315,7 @@ CONFIG_EXTRA="$CONFIG_EXTRA --enable-systemd" ./configure --prefix=%{_prefix} --libdir=%{_libdir} --libexecdir=%{_libexecdir} --with-system-qemu=/usr/bin/qemu-system-i386 --with-linux-backend-modules="xen-evtchn xen-gntdev xen-gntalloc xen-blkback xen-netback xen-pciback xen-scsiback xen-acpi-processor" $CONFIG_EXTRA unset CFLAGS CXXFLAGS FFLAGS LDFLAGS export LDFLAGS="$LDFLAGS_SAVE" -export CFLAGS=`echo "$CFLAGS_SAVE -Wno-error=address" | sed -e s/-specs=\/usr\/lib\/rpm\/redhat/redhat-annobin-cc1//g` +export CFLAGS=`echo "$CFLAGS_SAVE -Wno-error=address" | sed -e 's/-specs=\/usr\/lib\/rpm\/redhat/redhat-annobin-cc1//g'` %if %build_hyp %make_build prefix=/usr xen @@ -598,7 +608,7 @@ fi %{_libdir}/libxenlight.so.4.* %{_libdir}/libxenstat.so.4.* %{_libdir}/libxenstore.so.4 -%{_libdir}/libxenstore.so.4.0 +%{_libdir}/libxenstore.so.4.1 %{_libdir}/libxentoolcore.so.1 %{_libdir}/libxentoolcore.so.1.0 %{_libdir}/libxentoollog.so.1 @@ -608,6 +618,8 @@ fi %{_libdir}/xenfsimage %{_libdir}/libxenhypfs.so.1 %{_libdir}/libxenhypfs.so.1.0 +%{_libdir}/libxenmanage.so.1 +%{_libdir}/libxenmanage.so.1.0 # All runtime stuff except for XenD/xm python stuff %files runtime @@ -625,7 +637,8 @@ fi %{_unitdir}/xen-watchdog.service %{_unitdir}/xen-qemu-dom0-disk-backend.service %{_unitdir}/xendriverdomain.service -/usr/lib/modules-load.d/xen.conf +%{_modulesloaddir}/xen.conf +%{_systemd_util_dir}/system-sleep/xen-watchdog-sleep.sh %config(noreplace) %{_sysconfdir}/sysconfig/xencommons %config(noreplace) %{_sysconfdir}/xen/xl.conf @@ -807,7 +820,23 @@ fi %{_libexecdir}/xen/ocaml/xsd_glue/xenctrl_plugin/domain_getinfo_v1.cmxs %endif +%files test +%{_libexecdir}/xen/tests/* + %changelog +* Wed Jan 07 2026 Michael Young - 4.21.0-1 +- update to xen 4.21.0 + rebase mini-os + use .xz xen tarball instead of .gz + fix quotes around sed command + update libxenstore version + package libxenmanage and xen-watchdog-sleep.sh files + add a new package for test files + renumber patches + use json-c instead of yajl +- fix bug in xen code when using json-c +- fix code issues detected by gcc16 + * Thu Nov 13 2025 Michael Young - 4.20.2-2.fc44 - update to xen 4.20.2 remove patches now included or superceded upstream From ca062906823d74116cd6a04a9f346a77af2bfbd8 Mon Sep 17 00:00:00 2001 From: Jerry James Date: Tue, 13 Jan 2026 20:56:05 -0700 Subject: [PATCH 121/137] Fix a changelog entry --- xen.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 925f4ff..1c4f773 100644 --- a/xen.spec +++ b/xen.spec @@ -888,7 +888,7 @@ fi x86: Transitive Scheduler Attacks [XSA-471, CVE-2024-36350, CVE-2024-36357] -* Fri Jul 11 2025 Jerry James - 4.19.2-6 +* Fri Jul 11 2025 Jerry James - 4.19.2-6 - Rebuild to fix OCaml dependencies * Mon Jun 02 2025 Python Maint - 4.19.2-5 From 461c19a5ca798a589685fb403b57643af1eb6f83 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Sat, 17 Jan 2026 20:34:16 +0000 Subject: [PATCH 122/137] Rebuilt for https://fedoraproject.org/wiki/Fedora_44_Mass_Rebuild --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 1c4f773..467681d 100644 --- a/xen.spec +++ b/xen.spec @@ -51,7 +51,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.21.0 -Release: 1%{?dist} +Release: 2%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -824,6 +824,9 @@ fi %{_libexecdir}/xen/tests/* %changelog +* Sat Jan 17 2026 Fedora Release Engineering - 4.21.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_44_Mass_Rebuild + * Wed Jan 07 2026 Michael Young - 4.21.0-1 - update to xen 4.21.0 rebase mini-os From d409186bf51f4ef8a617a67e6708aa0fc9b36060 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 28 Jan 2026 22:25:55 +0000 Subject: [PATCH 123/137] 2 security updates x86: buffer overrun with shadow paging + tracing [XSA-477, CVE-2025-58150] x86: incomplete IBPB for vCPU isolation [XSA-479, CVE-2026-23553] --- xen.spec | 10 ++++- xsa477.patch | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++ xsa479.patch | 81 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 xsa477.patch create mode 100644 xsa479.patch diff --git a/xen.spec b/xen.spec index 467681d..8c9d9a9 100644 --- a/xen.spec +++ b/xen.spec @@ -51,7 +51,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.21.0 -Release: 2%{?dist} +Release: 3%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -81,6 +81,8 @@ Patch9: xen.python3.12.patch Patch10: dropped.regs.patch Patch11: xen.json.nocpuid.patch Patch12: xen.gcc16.fixes.patch +Patch13: xsa477.patch +Patch14: xsa479.patch # build using Fedora seabios and ipxe packages for roms @@ -269,6 +271,8 @@ This package contains files used in testing the xen builds %patch 10 -p1 %patch 11 -p1 %patch 12 -p1 +%patch 13 -p1 +%patch 14 -p1 # stubdom sources cp -v %{SOURCE10} %{SOURCE11} %{SOURCE12} %{SOURCE13} %{SOURCE14} %{SOURCE15} stubdom @@ -824,6 +828,10 @@ fi %{_libexecdir}/xen/tests/* %changelog +* Wed Jan 28 2026 Michael Young - 4.21.0-3 + x86: buffer overrun with shadow paging + tracing [XSA-477, CVE-2025-58150] + x86: incomplete IBPB for vCPU isolation [XSA-479, CVE-2026-23553] + * Sat Jan 17 2026 Fedora Release Engineering - 4.21.0-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_44_Mass_Rebuild diff --git a/xsa477.patch b/xsa477.patch new file mode 100644 index 0000000..48a9484 --- /dev/null +++ b/xsa477.patch @@ -0,0 +1,105 @@ +From: Jan Beulich +Subject: x86/shadow: don't overrun trace_emul_write_val + +Guests can do wider-than-PTE-size writes on page tables. The tracing +helper variable, however, only offers space for a single PTE (and it is +being switched to the more correct type right here). Therefore bound +incoming write sizes to the amount of space available. + +To not leave dead code (which is a Misra concern), drop the now unused +guest_pa_t as well. + +Also move and adjust GUEST_PTE_SIZE: Derive it rather than using hard- +coded numbers, and put it in the sole source file where it's actually +needed. This then also addresses a Misra rule 20.9 ("All identifiers +used in the controlling expression of #if or #elif preprocessing +directives shall be #define'd before evaluation") violation: +GUEST_PAGING_LEVELS is #define'd only in multi.c. + +This is XSA-477 / CVE-2025-58150. + +Fixes: 9a86ac1aa3d2 ("xentrace 5/7: Additional tracing for the shadow code") +Signed-off-by: Jan Beulich +Reviewed-by: Andrew Cooper + +--- a/xen/arch/x86/mm/shadow/multi.c ++++ b/xen/arch/x86/mm/shadow/multi.c +@@ -1970,15 +1970,15 @@ static void sh_prefetch(struct vcpu *v, + + #if GUEST_PAGING_LEVELS == 4 + typedef u64 guest_va_t; +-typedef u64 guest_pa_t; + #elif GUEST_PAGING_LEVELS == 3 + typedef u32 guest_va_t; +-typedef u64 guest_pa_t; + #else + typedef u32 guest_va_t; +-typedef u32 guest_pa_t; + #endif + ++/* Size (in bytes) of a guest PTE */ ++#define GUEST_PTE_SIZE sizeof(guest_l1e_t) ++ + /* Shadow trace event with GUEST_PAGING_LEVELS folded into the event field. */ + static void sh_trace(uint32_t event, unsigned int extra, const void *extra_data) + { +@@ -2048,11 +2048,14 @@ static void __maybe_unused sh_trace_gfn_ + static DEFINE_PER_CPU(guest_va_t,trace_emulate_initial_va); + static DEFINE_PER_CPU(int,trace_extra_emulation_count); + #endif +-static DEFINE_PER_CPU(guest_pa_t,trace_emulate_write_val); ++static DEFINE_PER_CPU(guest_l1e_t, trace_emulate_write_val); + + static void cf_check trace_emulate_write_val( + const void *ptr, unsigned long vaddr, const void *src, unsigned int bytes) + { ++ if ( bytes > sizeof(this_cpu(trace_emulate_write_val)) ) ++ bytes = sizeof(this_cpu(trace_emulate_write_val)); ++ + #if GUEST_PAGING_LEVELS == 3 + if ( vaddr == this_cpu(trace_emulate_initial_va) ) + memcpy(&this_cpu(trace_emulate_write_val), src, bytes); +@@ -2077,13 +2080,16 @@ static inline void sh_trace_emulate(gues + /* + * For GUEST_PAGING_LEVELS=3 (PAE paging), guest_l1e is 64 while + * guest_va is 32. Put it first to avoid padding. ++ * ++ * Note: .write_val is an arbitrary set of written bytes, possibly ++ * misaligned and possibly spanning the next gl1e. + */ + guest_l1e_t gl1e, write_val; + guest_va_t va; + uint32_t flags:29, emulation_count:3; + } d = { + .gl1e = gl1e, +- .write_val.l1 = this_cpu(trace_emulate_write_val), ++ .write_val = this_cpu(trace_emulate_write_val), + .va = va, + #if GUEST_PAGING_LEVELS == 3 + .emulation_count = this_cpu(trace_extra_emulation_count), +@@ -2672,7 +2677,7 @@ static int cf_check sh_page_fault( + paging_unlock(d); + put_gfn(d, gfn_x(gfn)); + +- this_cpu(trace_emulate_write_val) = 0; ++ this_cpu(trace_emulate_write_val) = (guest_l1e_t){}; + + #if SHADOW_OPTIMIZATIONS & SHOPT_FAST_EMULATION + early_emulation: +--- a/xen/arch/x86/mm/shadow/private.h ++++ b/xen/arch/x86/mm/shadow/private.h +@@ -120,14 +120,6 @@ enum { + TRCE_SFLAG_OOS_FIXUP_EVICT, + }; + +- +-/* Size (in bytes) of a guest PTE */ +-#if GUEST_PAGING_LEVELS >= 3 +-# define GUEST_PTE_SIZE 8 +-#else +-# define GUEST_PTE_SIZE 4 +-#endif +- + /****************************************************************************** + * Auditing routines + */ diff --git a/xsa479.patch b/xsa479.patch new file mode 100644 index 0000000..448d46b --- /dev/null +++ b/xsa479.patch @@ -0,0 +1,81 @@ +From: Roger Pau Monné +Subject: x86/spec-ctrl: Fix incomplete IBPB flushing during context switch + +The previous logic attempted to skip an IBPB in the case of vCPU returning to +a CPU on which it was the previous vCPU to run. While safe for Xen's +isolation between vCPUs, this prevents the guest kernel correctly isolation +between tasks. Consider: + + 1) vCPU runs on CPU A, running task 1. + 2) vCPU moves to CPU B, idle gets scheduled on A. Xen skips IBPB. + 3) On CPU B, guest kernel switches from task 1 to 2, issuing IBPB. + 4) vCPU moves back to CPU A. Xen skips IBPB again. + +Now, task 2 is running on CPU A with task 1's training still in the BTB. + +Do the flush unconditionally when switching to a vCPU different than the +idle one. Note there's no need to explicitly gate the IBPB to next domain +!= idle, as the context where the IBPB is issued is subject to that +condition already unless the pCPU is going offline, at which point we don't +really care to issue an extra IBPB. + +Also add a comment with the reasoning why the IBPB needs to be in +context_switch() rather than __context_switch(). + +This is XSA-479 / CVE-2026-23553. + +Fixes: a2ed643ed783 ("x86/ctxt: Issue a speculation barrier between vcpu contexts") +Reported-by: David Kaplan +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +--- + xen/arch/x86/domain.c | 36 +++++++++--------------------------- + 1 file changed, 9 insertions(+), 27 deletions(-) + +diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c +index c29a6b0decee..c1eded3eb604 100644 +--- a/xen/arch/x86/domain.c ++++ b/xen/arch/x86/domain.c +@@ -2174,33 +2174,15 @@ void context_switch(struct vcpu *prev, struct vcpu *next) + + ctxt_switch_levelling(next); + +- if ( opt_ibpb_ctxt_switch && !is_idle_domain(nextd) ) +- { +- static DEFINE_PER_CPU(unsigned int, last); +- unsigned int *last_id = &this_cpu(last); +- +- /* +- * Squash the domid and vcpu id together for comparison +- * efficiency. We could in principle stash and compare the struct +- * vcpu pointer, but this risks a false alias if a domain has died +- * and the same 4k page gets reused for a new vcpu. +- */ +- unsigned int next_id = (((unsigned int)nextd->domain_id << 16) | +- (uint16_t)next->vcpu_id); +- BUILD_BUG_ON(MAX_VIRT_CPUS > 0xffff); +- +- /* +- * When scheduling from a vcpu, to idle, and back to the same vcpu +- * (which might be common in a lightly loaded system, or when +- * using vcpu pinning), there is no need to issue IBPB, as we are +- * returning to the same security context. +- */ +- if ( *last_id != next_id ) +- { +- spec_ctrl_new_guest_context(); +- *last_id = next_id; +- } +- } ++ /* ++ * Issue an IBPB when scheduling a different vCPU if required. ++ * ++ * IBPB clears the RSB/RAS/RAP, but that's fine as we leave this ++ * function via reset_stack_and_call_ind() rather than via a RET ++ * instruction. ++ */ ++ if ( opt_ibpb_ctxt_switch ) ++ spec_ctrl_new_guest_context(); + + /* Update the top-of-stack block with the new speculation settings. */ + info->scf = From 2b36f2e8ca182677c4b05915912b8126bc0b6ad0 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Fri, 20 Feb 2026 16:06:05 +0000 Subject: [PATCH 124/137] OCaml 5.4.1 rebuild --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 8c9d9a9..ee1a627 100644 --- a/xen.spec +++ b/xen.spec @@ -51,7 +51,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.21.0 -Release: 3%{?dist} +Release: 4%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -828,6 +828,9 @@ fi %{_libexecdir}/xen/tests/* %changelog +* Fri Feb 20 2026 Richard W.M. Jones - 4.21.0-4 +- OCaml 5.4.1 rebuild + * Wed Jan 28 2026 Michael Young - 4.21.0-3 x86: buffer overrun with shadow paging + tracing [XSA-477, CVE-2025-58150] x86: incomplete IBPB for vCPU isolation [XSA-479, CVE-2026-23553] From fbec0bc65a05818f3c41391f36c828e7acfeb999 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 17 Mar 2026 19:57:45 +0000 Subject: [PATCH 125/137] 2 security updates Use after free of paging structures in EPT [XSA-480, CVE-2026-23554] Xenstored DoS by unprivileged domain [XSA-481, CVE-2026-23555] --- xen.spec | 10 +++++++++- xsa480.patch | 46 ++++++++++++++++++++++++++++++++++++++++++ xsa481.patch | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 xsa480.patch create mode 100644 xsa481.patch diff --git a/xen.spec b/xen.spec index ee1a627..2a74fb6 100644 --- a/xen.spec +++ b/xen.spec @@ -51,7 +51,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.21.0 -Release: 4%{?dist} +Release: 5%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -83,6 +83,8 @@ Patch11: xen.json.nocpuid.patch Patch12: xen.gcc16.fixes.patch Patch13: xsa477.patch Patch14: xsa479.patch +Patch15: xsa480.patch +Patch16: xsa481.patch # build using Fedora seabios and ipxe packages for roms @@ -273,6 +275,8 @@ This package contains files used in testing the xen builds %patch 12 -p1 %patch 13 -p1 %patch 14 -p1 +%patch 15 -p1 +%patch 16 -p1 # stubdom sources cp -v %{SOURCE10} %{SOURCE11} %{SOURCE12} %{SOURCE13} %{SOURCE14} %{SOURCE15} stubdom @@ -828,6 +832,10 @@ fi %{_libexecdir}/xen/tests/* %changelog +* Tue Mar 17 2026 Michael Young - 4.21.0-5 +- Use after free of paging structures in EPT [XSA-480, CVE-2026-23554] +- Xenstored DoS by unprivileged domain [XSA-481, CVE-2026-23555] + * Fri Feb 20 2026 Richard W.M. Jones - 4.21.0-4 - OCaml 5.4.1 rebuild diff --git a/xsa480.patch b/xsa480.patch new file mode 100644 index 0000000..d01ea35 --- /dev/null +++ b/xsa480.patch @@ -0,0 +1,46 @@ +From 45f6866e34b7e9ee8b6ac16d646a2e954c97e48e Mon Sep 17 00:00:00 2001 +From: Roger Pau Monne +Date: Tue, 17 Feb 2026 09:33:43 +0100 +Subject: [PATCH] x86/p2m: issue a sync flush before freeing paging pages +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +In the EPT implementation, the defer flushing logic is used +unconditionally, and that would lead to paging memory being returned to the +paging pool before its references had been flushed. + +Issue any pending flushes before freeing the paging memory back to the +pool. + +Note AMD (NPT) and Shadow paging are not affected, as they don't implement +the deferred flushing logic. + +This is XSA-480 / CVE-2026-23554 + +Fixes: 4a59e6bb3a96 ("x86/EPT: squash meaningless TLB flush") +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +--- + xen/arch/x86/mm/p2m.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c +index e915da26a832..fddecdf978ec 100644 +--- a/xen/arch/x86/mm/p2m.c ++++ b/xen/arch/x86/mm/p2m.c +@@ -479,6 +479,11 @@ void p2m_free_ptp(struct p2m_domain *p2m, struct page_info *pg) + ASSERT(p2m->domain); + ASSERT(p2m->domain->arch.paging.free_page); + ++ /* ++ * Issue any pending flush here, in case it was deferred before. The page ++ * will be returned to the paging pool now. ++ */ ++ p2m_tlb_flush_sync(p2m); + page_list_del(pg, &p2m->pages); + p2m->domain->arch.paging.free_page(p2m->domain, pg); + +-- +2.51.0 + diff --git a/xsa481.patch b/xsa481.patch new file mode 100644 index 0000000..c21f910 --- /dev/null +++ b/xsa481.patch @@ -0,0 +1,56 @@ +From 0cff16f0a997f1b0871b621a1d6050652530e5d9 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Thu, 12 Feb 2026 08:29:38 +0100 +Subject: [PATCH] tools/xenstored: fix canonicalize() error testing +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The setting of errno in canonicalize() is rather fragile and seems to +be even wrong in one corner case: when the invalid path "/local/domain/" +is passed, sscanf() will set errno to 0, resulting in canonicalize() to +return NULL with errno being 0. This can result in triggering the +assert(conn->in == NULL) in consider_message(). + +Don't assume the initial setting of errno to "EINVAL" will stay valid +in all cases and set it to EINVAL only when returning NULL due to an +invalid path. + +This is XSA-481/CVE-2026-23555 + +Reported-by: Marek Marczykowski-Górecki +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall +--- + tools/xenstored/core.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/tools/xenstored/core.c b/tools/xenstored/core.c +index 64c478a801..2e826f99eb 100644 +--- a/tools/xenstored/core.c ++++ b/tools/xenstored/core.c +@@ -1240,11 +1240,10 @@ const char *canonicalize(struct connection *conn, const void *ctx, + * - illegal character in node + * - starts with '@' but no special node allowed + */ +- errno = EINVAL; + if (!node || + !valid_chars(node) || + (node[0] == '@' && !allow_special)) +- return NULL; ++ goto inval; + + if (node[0] != '/' && node[0] != '@') { + name = talloc_asprintf(ctx, "%s/%s", get_implicit_path(conn), +@@ -1272,6 +1271,8 @@ const char *canonicalize(struct connection *conn, const void *ctx, + if (name != node) + talloc_free(name); + ++ inval: ++ errno = EINVAL; + return NULL; + } + +-- +2.53.0 + From 30cb19d1ba60c594520bb1bfa2ec62018a569fa0 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 26 Mar 2026 20:52:44 +0000 Subject: [PATCH 126/137] update to xen 4.21.1 --- .gitignore | 2 +- dropped.regs.patch | 28 ----------- sources | 2 +- xen.gcc16.fixes.patch | 31 ------------- xen.spec | 20 +++----- xsa477.patch | 105 ------------------------------------------ xsa479.patch | 81 -------------------------------- xsa480.patch | 46 ------------------ xsa481.patch | 56 ---------------------- 9 files changed, 8 insertions(+), 363 deletions(-) delete mode 100644 dropped.regs.patch delete mode 100644 xen.gcc16.fixes.patch delete mode 100644 xsa477.patch delete mode 100644 xsa479.patch delete mode 100644 xsa480.patch delete mode 100644 xsa481.patch diff --git a/.gitignore b/.gitignore index 4d2b84e..6b74118 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,4 @@ pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz /mini-os-4.21.0.tar.xz -/xen-4.21.0.tar.xz +/xen-4.21.1.tar.xz diff --git a/dropped.regs.patch b/dropped.regs.patch deleted file mode 100644 index 995498e..0000000 --- a/dropped.regs.patch +++ /dev/null @@ -1,28 +0,0 @@ ---- xen-4.21.0-rc2/xen/arch/arm/domain.c.orig 2025-10-22 12:01:02.000000000 +0100 -+++ xen-4.21.0-rc2/xen/arch/arm/domain.c 2025-10-26 00:02:03.017300510 +0100 -@@ -111,12 +111,6 @@ - p->arch.cntkctl = READ_SYSREG(CNTKCTL_EL1); - virt_timer_save(p); - -- if ( is_32bit_domain(p->domain) && cpu_has_thumbee ) -- { -- p->arch.teecr = READ_SYSREG(TEECR32_EL1); -- p->arch.teehbr = READ_SYSREG(TEEHBR32_EL1); -- } -- - #ifdef CONFIG_ARM_32 - p->arch.joscr = READ_CP32(JOSCR); - p->arch.jmcr = READ_CP32(JMCR); -@@ -244,12 +238,6 @@ - WRITE_SYSREG(n->arch.tpidrro_el0, TPIDRRO_EL0); - WRITE_SYSREG(n->arch.tpidr_el1, TPIDR_EL1); - -- if ( is_32bit_domain(n->domain) && cpu_has_thumbee ) -- { -- WRITE_SYSREG(n->arch.teecr, TEECR32_EL1); -- WRITE_SYSREG(n->arch.teehbr, TEEHBR32_EL1); -- } -- - #ifdef CONFIG_ARM_32 - WRITE_CP32(n->arch.joscr, JOSCR); - WRITE_CP32(n->arch.jmcr, JMCR); diff --git a/sources b/sources index 48e84e7..4b30fea 100644 --- a/sources +++ b/sources @@ -5,4 +5,4 @@ SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea3 SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 SHA512 (mini-os-4.21.0.tar.xz) = 7543774d15da84476d93d04154990923c82209cb3fa125574c0383652c5a310957200f54b63b34502161f9c3afce4907e0060d9036f3eaf3a7cb6b1b3119b546 -SHA512 (xen-4.21.0.tar.xz) = f8bd08e190b1f5013474718ecad687eddb97c57f7ae6b8262ffb7b91d70f0a5d30bce69c0301e7a97237d43ae2d75179e12be28f8c464d8cf5318a40acbf77ca +SHA512 (xen-4.21.1.tar.xz) = 8dfe65255e202b3dacf9d0d7265636bc1f97627c11b08babc13a5b8e74c7c65e7e2c6a1513e28b3c713fe512edb6702a73b2bf667e2a8f2ce825b196a2cd5aab diff --git a/xen.gcc16.fixes.patch b/xen.gcc16.fixes.patch deleted file mode 100644 index 5bad057..0000000 --- a/xen.gcc16.fixes.patch +++ /dev/null @@ -1,31 +0,0 @@ ---- xen-4.21.0/tools/libs/light/libxl_internal.c.orig 2025-11-18 18:02:13.000000000 +0000 -+++ xen-4.21.0/tools/libs/light/libxl_internal.c 2026-01-05 20:55:36.678719203 +0000 -@@ -204,7 +204,7 @@ - - char *libxl__dirname(libxl__gc *gc, const char *s) - { -- char *c = strrchr(s, '/'); -+ char *c = strrchr((char *) s, '/'); - - if (!c) - return NULL; ---- xen-4.21.0/tools/libs/light/libxl_cpuid.c.orig 2025-11-18 18:02:13.000000000 +0000 -+++ xen-4.21.0/tools/libs/light/libxl_cpuid.c 2026-01-05 23:46:40.203837602 +0000 -@@ -444,14 +444,14 @@ - return 4; - } - value = str[1] - 'a'; -- endptr = strchr(str, '='); -+ endptr = strchr((char *) str, '='); - if (value > 3 || endptr == NULL) { - return 4; - } - str = endptr + 1; -- endptr = strchr(str, ','); -+ endptr = strchr((char *) str, ','); - if (endptr == NULL) { -- endptr = strchr(str, 0); -+ endptr = strchr((char *) str, 0); - } - if (endptr - str != 32) { - return 5; diff --git a/xen.spec b/xen.spec index 2a74fb6..7c73619 100644 --- a/xen.spec +++ b/xen.spec @@ -50,8 +50,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.21.0 -Release: 5%{?dist} +Version: 4.21.1 +Release: 1%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -78,13 +78,7 @@ Patch6: xen.gcc11.fixes.patch Patch7: xen.gcc12.fixes.patch Patch8: xen.efi.build.patch Patch9: xen.python3.12.patch -Patch10: dropped.regs.patch Patch11: xen.json.nocpuid.patch -Patch12: xen.gcc16.fixes.patch -Patch13: xsa477.patch -Patch14: xsa479.patch -Patch15: xsa480.patch -Patch16: xsa481.patch # build using Fedora seabios and ipxe packages for roms @@ -270,13 +264,7 @@ This package contains files used in testing the xen builds %patch 7 -p1 %patch 8 -p1 %patch 9 -p1 -%patch 10 -p1 %patch 11 -p1 -%patch 12 -p1 -%patch 13 -p1 -%patch 14 -p1 -%patch 15 -p1 -%patch 16 -p1 # stubdom sources cp -v %{SOURCE10} %{SOURCE11} %{SOURCE12} %{SOURCE13} %{SOURCE14} %{SOURCE15} stubdom @@ -832,6 +820,10 @@ fi %{_libexecdir}/xen/tests/* %changelog +* Thu Mar 26 2026 Michael Young - 4.21.1-1 +- update to xen 4.21.0 + remove patches now included or superceded upstream + * Tue Mar 17 2026 Michael Young - 4.21.0-5 - Use after free of paging structures in EPT [XSA-480, CVE-2026-23554] - Xenstored DoS by unprivileged domain [XSA-481, CVE-2026-23555] diff --git a/xsa477.patch b/xsa477.patch deleted file mode 100644 index 48a9484..0000000 --- a/xsa477.patch +++ /dev/null @@ -1,105 +0,0 @@ -From: Jan Beulich -Subject: x86/shadow: don't overrun trace_emul_write_val - -Guests can do wider-than-PTE-size writes on page tables. The tracing -helper variable, however, only offers space for a single PTE (and it is -being switched to the more correct type right here). Therefore bound -incoming write sizes to the amount of space available. - -To not leave dead code (which is a Misra concern), drop the now unused -guest_pa_t as well. - -Also move and adjust GUEST_PTE_SIZE: Derive it rather than using hard- -coded numbers, and put it in the sole source file where it's actually -needed. This then also addresses a Misra rule 20.9 ("All identifiers -used in the controlling expression of #if or #elif preprocessing -directives shall be #define'd before evaluation") violation: -GUEST_PAGING_LEVELS is #define'd only in multi.c. - -This is XSA-477 / CVE-2025-58150. - -Fixes: 9a86ac1aa3d2 ("xentrace 5/7: Additional tracing for the shadow code") -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper - ---- a/xen/arch/x86/mm/shadow/multi.c -+++ b/xen/arch/x86/mm/shadow/multi.c -@@ -1970,15 +1970,15 @@ static void sh_prefetch(struct vcpu *v, - - #if GUEST_PAGING_LEVELS == 4 - typedef u64 guest_va_t; --typedef u64 guest_pa_t; - #elif GUEST_PAGING_LEVELS == 3 - typedef u32 guest_va_t; --typedef u64 guest_pa_t; - #else - typedef u32 guest_va_t; --typedef u32 guest_pa_t; - #endif - -+/* Size (in bytes) of a guest PTE */ -+#define GUEST_PTE_SIZE sizeof(guest_l1e_t) -+ - /* Shadow trace event with GUEST_PAGING_LEVELS folded into the event field. */ - static void sh_trace(uint32_t event, unsigned int extra, const void *extra_data) - { -@@ -2048,11 +2048,14 @@ static void __maybe_unused sh_trace_gfn_ - static DEFINE_PER_CPU(guest_va_t,trace_emulate_initial_va); - static DEFINE_PER_CPU(int,trace_extra_emulation_count); - #endif --static DEFINE_PER_CPU(guest_pa_t,trace_emulate_write_val); -+static DEFINE_PER_CPU(guest_l1e_t, trace_emulate_write_val); - - static void cf_check trace_emulate_write_val( - const void *ptr, unsigned long vaddr, const void *src, unsigned int bytes) - { -+ if ( bytes > sizeof(this_cpu(trace_emulate_write_val)) ) -+ bytes = sizeof(this_cpu(trace_emulate_write_val)); -+ - #if GUEST_PAGING_LEVELS == 3 - if ( vaddr == this_cpu(trace_emulate_initial_va) ) - memcpy(&this_cpu(trace_emulate_write_val), src, bytes); -@@ -2077,13 +2080,16 @@ static inline void sh_trace_emulate(gues - /* - * For GUEST_PAGING_LEVELS=3 (PAE paging), guest_l1e is 64 while - * guest_va is 32. Put it first to avoid padding. -+ * -+ * Note: .write_val is an arbitrary set of written bytes, possibly -+ * misaligned and possibly spanning the next gl1e. - */ - guest_l1e_t gl1e, write_val; - guest_va_t va; - uint32_t flags:29, emulation_count:3; - } d = { - .gl1e = gl1e, -- .write_val.l1 = this_cpu(trace_emulate_write_val), -+ .write_val = this_cpu(trace_emulate_write_val), - .va = va, - #if GUEST_PAGING_LEVELS == 3 - .emulation_count = this_cpu(trace_extra_emulation_count), -@@ -2672,7 +2677,7 @@ static int cf_check sh_page_fault( - paging_unlock(d); - put_gfn(d, gfn_x(gfn)); - -- this_cpu(trace_emulate_write_val) = 0; -+ this_cpu(trace_emulate_write_val) = (guest_l1e_t){}; - - #if SHADOW_OPTIMIZATIONS & SHOPT_FAST_EMULATION - early_emulation: ---- a/xen/arch/x86/mm/shadow/private.h -+++ b/xen/arch/x86/mm/shadow/private.h -@@ -120,14 +120,6 @@ enum { - TRCE_SFLAG_OOS_FIXUP_EVICT, - }; - -- --/* Size (in bytes) of a guest PTE */ --#if GUEST_PAGING_LEVELS >= 3 --# define GUEST_PTE_SIZE 8 --#else --# define GUEST_PTE_SIZE 4 --#endif -- - /****************************************************************************** - * Auditing routines - */ diff --git a/xsa479.patch b/xsa479.patch deleted file mode 100644 index 448d46b..0000000 --- a/xsa479.patch +++ /dev/null @@ -1,81 +0,0 @@ -From: Roger Pau Monné -Subject: x86/spec-ctrl: Fix incomplete IBPB flushing during context switch - -The previous logic attempted to skip an IBPB in the case of vCPU returning to -a CPU on which it was the previous vCPU to run. While safe for Xen's -isolation between vCPUs, this prevents the guest kernel correctly isolation -between tasks. Consider: - - 1) vCPU runs on CPU A, running task 1. - 2) vCPU moves to CPU B, idle gets scheduled on A. Xen skips IBPB. - 3) On CPU B, guest kernel switches from task 1 to 2, issuing IBPB. - 4) vCPU moves back to CPU A. Xen skips IBPB again. - -Now, task 2 is running on CPU A with task 1's training still in the BTB. - -Do the flush unconditionally when switching to a vCPU different than the -idle one. Note there's no need to explicitly gate the IBPB to next domain -!= idle, as the context where the IBPB is issued is subject to that -condition already unless the pCPU is going offline, at which point we don't -really care to issue an extra IBPB. - -Also add a comment with the reasoning why the IBPB needs to be in -context_switch() rather than __context_switch(). - -This is XSA-479 / CVE-2026-23553. - -Fixes: a2ed643ed783 ("x86/ctxt: Issue a speculation barrier between vcpu contexts") -Reported-by: David Kaplan -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich ---- - xen/arch/x86/domain.c | 36 +++++++++--------------------------- - 1 file changed, 9 insertions(+), 27 deletions(-) - -diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c -index c29a6b0decee..c1eded3eb604 100644 ---- a/xen/arch/x86/domain.c -+++ b/xen/arch/x86/domain.c -@@ -2174,33 +2174,15 @@ void context_switch(struct vcpu *prev, struct vcpu *next) - - ctxt_switch_levelling(next); - -- if ( opt_ibpb_ctxt_switch && !is_idle_domain(nextd) ) -- { -- static DEFINE_PER_CPU(unsigned int, last); -- unsigned int *last_id = &this_cpu(last); -- -- /* -- * Squash the domid and vcpu id together for comparison -- * efficiency. We could in principle stash and compare the struct -- * vcpu pointer, but this risks a false alias if a domain has died -- * and the same 4k page gets reused for a new vcpu. -- */ -- unsigned int next_id = (((unsigned int)nextd->domain_id << 16) | -- (uint16_t)next->vcpu_id); -- BUILD_BUG_ON(MAX_VIRT_CPUS > 0xffff); -- -- /* -- * When scheduling from a vcpu, to idle, and back to the same vcpu -- * (which might be common in a lightly loaded system, or when -- * using vcpu pinning), there is no need to issue IBPB, as we are -- * returning to the same security context. -- */ -- if ( *last_id != next_id ) -- { -- spec_ctrl_new_guest_context(); -- *last_id = next_id; -- } -- } -+ /* -+ * Issue an IBPB when scheduling a different vCPU if required. -+ * -+ * IBPB clears the RSB/RAS/RAP, but that's fine as we leave this -+ * function via reset_stack_and_call_ind() rather than via a RET -+ * instruction. -+ */ -+ if ( opt_ibpb_ctxt_switch ) -+ spec_ctrl_new_guest_context(); - - /* Update the top-of-stack block with the new speculation settings. */ - info->scf = diff --git a/xsa480.patch b/xsa480.patch deleted file mode 100644 index d01ea35..0000000 --- a/xsa480.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 45f6866e34b7e9ee8b6ac16d646a2e954c97e48e Mon Sep 17 00:00:00 2001 -From: Roger Pau Monne -Date: Tue, 17 Feb 2026 09:33:43 +0100 -Subject: [PATCH] x86/p2m: issue a sync flush before freeing paging pages -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -In the EPT implementation, the defer flushing logic is used -unconditionally, and that would lead to paging memory being returned to the -paging pool before its references had been flushed. - -Issue any pending flushes before freeing the paging memory back to the -pool. - -Note AMD (NPT) and Shadow paging are not affected, as they don't implement -the deferred flushing logic. - -This is XSA-480 / CVE-2026-23554 - -Fixes: 4a59e6bb3a96 ("x86/EPT: squash meaningless TLB flush") -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich ---- - xen/arch/x86/mm/p2m.c | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c -index e915da26a832..fddecdf978ec 100644 ---- a/xen/arch/x86/mm/p2m.c -+++ b/xen/arch/x86/mm/p2m.c -@@ -479,6 +479,11 @@ void p2m_free_ptp(struct p2m_domain *p2m, struct page_info *pg) - ASSERT(p2m->domain); - ASSERT(p2m->domain->arch.paging.free_page); - -+ /* -+ * Issue any pending flush here, in case it was deferred before. The page -+ * will be returned to the paging pool now. -+ */ -+ p2m_tlb_flush_sync(p2m); - page_list_del(pg, &p2m->pages); - p2m->domain->arch.paging.free_page(p2m->domain, pg); - --- -2.51.0 - diff --git a/xsa481.patch b/xsa481.patch deleted file mode 100644 index c21f910..0000000 --- a/xsa481.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 0cff16f0a997f1b0871b621a1d6050652530e5d9 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Thu, 12 Feb 2026 08:29:38 +0100 -Subject: [PATCH] tools/xenstored: fix canonicalize() error testing -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The setting of errno in canonicalize() is rather fragile and seems to -be even wrong in one corner case: when the invalid path "/local/domain/" -is passed, sscanf() will set errno to 0, resulting in canonicalize() to -return NULL with errno being 0. This can result in triggering the -assert(conn->in == NULL) in consider_message(). - -Don't assume the initial setting of errno to "EINVAL" will stay valid -in all cases and set it to EINVAL only when returning NULL due to an -invalid path. - -This is XSA-481/CVE-2026-23555 - -Reported-by: Marek Marczykowski-Górecki -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall ---- - tools/xenstored/core.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/tools/xenstored/core.c b/tools/xenstored/core.c -index 64c478a801..2e826f99eb 100644 ---- a/tools/xenstored/core.c -+++ b/tools/xenstored/core.c -@@ -1240,11 +1240,10 @@ const char *canonicalize(struct connection *conn, const void *ctx, - * - illegal character in node - * - starts with '@' but no special node allowed - */ -- errno = EINVAL; - if (!node || - !valid_chars(node) || - (node[0] == '@' && !allow_special)) -- return NULL; -+ goto inval; - - if (node[0] != '/' && node[0] != '@') { - name = talloc_asprintf(ctx, "%s/%s", get_implicit_path(conn), -@@ -1272,6 +1271,8 @@ const char *canonicalize(struct connection *conn, const void *ctx, - if (name != node) - talloc_free(name); - -+ inval: -+ errno = EINVAL; - return NULL; - } - --- -2.53.0 - From 545511922a2051d9845ed087831dd16da9c99818 Mon Sep 17 00:00:00 2001 From: Adam Williamson Date: Fri, 27 Mar 2026 14:13:04 -0700 Subject: [PATCH 127/137] Make licenses package noarch It only has licenses in it, after all. Signed-off-by: Adam Williamson --- xen.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/xen.spec b/xen.spec index 7c73619..b72f389 100644 --- a/xen.spec +++ b/xen.spec @@ -223,6 +223,7 @@ which manage Xen virtual machines. %package licenses Summary: License files from Xen source +BuildArch: noarch %description licenses This package contains the license files from the source used From 18cf277715db3e31199921761aecd370b56f1bb2 Mon Sep 17 00:00:00 2001 From: Adam Williamson Date: Fri, 27 Mar 2026 15:59:02 -0700 Subject: [PATCH 128/137] Revert "Make licenses package noarch" This reverts commit 545511922a2051d9845ed087831dd16da9c99818. Turns out we can't do this because different license files show up on x86_64 and aarch64 (there are more on x86_64 as we build more stuff there). --- xen.spec | 1 - 1 file changed, 1 deletion(-) diff --git a/xen.spec b/xen.spec index b72f389..7c73619 100644 --- a/xen.spec +++ b/xen.spec @@ -223,7 +223,6 @@ which manage Xen virtual machines. %package licenses Summary: License files from Xen source -BuildArch: noarch %description licenses This package contains the license files from the source used From cf70d8c84ec2e313566bd6de122a25476f281e6f Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 28 Apr 2026 21:20:56 +0100 Subject: [PATCH 129/137] 4 security patches +- oxenstored keeps quota related use counts across domain destruction + [XSA-483, CVE-2026-23556] +- Xenstored DoS via XS_RESET_WATCHES command [XSA-484, CVE-2026-23557] +- grant table v2 race in status page mapping [XSA-486, CVE-2026-23558] +- x86: Floating Point Divider State Sampling [XSA-488, CVE-2025-54505] --- ...0547b756a5cf9b0fec9fb0de5b361e8bf4c3.patch | 88 +++++++++ xen.spec | 19 +- xsa483.patch | 30 +++ xsa484.patch | 89 +++++++++ xsa486.patch | 181 ++++++++++++++++++ 5 files changed, 405 insertions(+), 2 deletions(-) create mode 100644 xen.git-90b20547b756a5cf9b0fec9fb0de5b361e8bf4c3.patch create mode 100644 xsa483.patch create mode 100644 xsa484.patch create mode 100644 xsa486.patch diff --git a/xen.git-90b20547b756a5cf9b0fec9fb0de5b361e8bf4c3.patch b/xen.git-90b20547b756a5cf9b0fec9fb0de5b361e8bf4c3.patch new file mode 100644 index 0000000..a5e65ba --- /dev/null +++ b/xen.git-90b20547b756a5cf9b0fec9fb0de5b361e8bf4c3.patch @@ -0,0 +1,88 @@ +From 90b20547b756a5cf9b0fec9fb0de5b361e8bf4c3 Mon Sep 17 00:00:00 2001 +From: Andrew Cooper +Date: Fri, 10 Apr 2026 21:55:46 +0100 +Subject: [PATCH] x86/amd: Mitigate AMD-SN-7053 / FP-DSS +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf8 +Content-Transfer-Encoding: 8bit + +This is XSA-488 / CVE-2025-54505 + +Signed-off-by: Andrew Cooper +Reviewed-by: Roger Pau Monné +(cherry picked from commit 99912d346009fda1e7fb1510c9501fbab17e92a0) +--- + xen/arch/x86/cpu/amd.c | 37 ++++++++++++++++++++++++++++ + xen/arch/x86/include/asm/msr-index.h | 1 + + 2 files changed, 38 insertions(+) + +diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c +index 8c55d233f3..1bb0766ebf 100644 +--- a/xen/arch/x86/cpu/amd.c ++++ b/xen/arch/x86/cpu/amd.c +@@ -1048,6 +1048,42 @@ void amd_init_de_cfg(const struct cpuinfo_x86 *c) + wrmsrl(MSR_AMD64_DE_CFG, val | new); + } + ++static void amd_init_fp_cfg(const struct cpuinfo_x86 *c) ++{ ++ uint64_t val, new = 0; ++ ++ /* If virtualised, we won't have mutable access even if we can read it. */ ++ if ( cpu_has_hypervisor ) ++ return; ++ ++ /* ++ * On Zen1, mitigate SB-7053 / FP-DSS Floating Point Divider State ++ * Sampling by setting bit 9 as instructed. ++ */ ++ if ( c->family == 0x17 && is_zen1_uarch() ) ++ new |= 1 << 9; ++ ++ /* ++ * Avoid reading FP_CFG if we don't intend to change anything. The ++ * register doesn't exist on all families. ++ */ ++ if ( !new ) ++ return; ++ ++ val = rdmsr(MSR_AMD64_FP_CFG); ++ ++ if ( (val & new) == new ) ++ return; ++ ++ /* ++ * FP_CFG is a Core-scoped MSR, and this write is racy. However, both ++ * threads calculate the new value from state which expected to be ++ * consistent across CPUs and unrelated to the old value, so the result ++ * should be consistent. ++ */ ++ wrmsr(MSR_AMD64_FP_CFG, val | new); ++} ++ + void __init amd_init_lfence_dispatch(void) + { + struct cpuinfo_x86 *c = &boot_cpu_data; +@@ -1120,6 +1156,7 @@ static void cf_check init_amd(struct cpuinfo_x86 *c) + uint64_t value; + + amd_init_de_cfg(c); ++ amd_init_fp_cfg(c); + + if (c == &boot_cpu_data) + amd_init_lfence_dispatch(); /* Needs amd_init_de_cfg() */ +diff --git a/xen/arch/x86/include/asm/msr-index.h b/xen/arch/x86/include/asm/msr-index.h +index df52587c85..6c5b2569e1 100644 +--- a/xen/arch/x86/include/asm/msr-index.h ++++ b/xen/arch/x86/include/asm/msr-index.h +@@ -428,6 +428,7 @@ + #define MSR_AMD64_LS_CFG 0xc0011020U + #define MSR_AMD64_IC_CFG 0xc0011021U + #define MSR_AMD64_DC_CFG 0xc0011022U ++#define MSR_AMD64_FP_CFG 0xc0011028U + #define MSR_AMD64_DE_CFG 0xc0011029U + #define AMD64_DE_CFG_LFENCE_SERIALISE (_AC(1, ULL) << 1) + #define MSR_AMD64_EX_CFG 0xc001102cU +-- +2.39.5 + diff --git a/xen.spec b/xen.spec index 7c73619..ba3662c 100644 --- a/xen.spec +++ b/xen.spec @@ -51,7 +51,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.21.1 -Release: 1%{?dist} +Release: 2%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -79,6 +79,10 @@ Patch7: xen.gcc12.fixes.patch Patch8: xen.efi.build.patch Patch9: xen.python3.12.patch Patch11: xen.json.nocpuid.patch +Patch12: xsa483.patch +Patch13: xsa484.patch +Patch14: xsa486.patch +Patch15: xen.git-90b20547b756a5cf9b0fec9fb0de5b361e8bf4c3.patch # build using Fedora seabios and ipxe packages for roms @@ -265,6 +269,10 @@ This package contains files used in testing the xen builds %patch 8 -p1 %patch 9 -p1 %patch 11 -p1 +%patch 12 -p1 +%patch 13 -p1 +%patch 14 -p1 +%patch 15 -p1 # stubdom sources cp -v %{SOURCE10} %{SOURCE11} %{SOURCE12} %{SOURCE13} %{SOURCE14} %{SOURCE15} stubdom @@ -820,8 +828,15 @@ fi %{_libexecdir}/xen/tests/* %changelog +* Tue Apr 28 2026 Michael Young - 4.21.1-2 +- oxenstored keeps quota related use counts across domain destruction + [XSA-483, CVE-2026-23556] +- Xenstored DoS via XS_RESET_WATCHES command [XSA-484, CVE-2026-23557] +- grant table v2 race in status page mapping [XSA-486, CVE-2026-23558] +- x86: Floating Point Divider State Sampling [XSA-488, CVE-2025-54505] + * Thu Mar 26 2026 Michael Young - 4.21.1-1 -- update to xen 4.21.0 +- update to xen 4.21.1 remove patches now included or superceded upstream * Tue Mar 17 2026 Michael Young - 4.21.0-5 diff --git a/xsa483.patch b/xsa483.patch new file mode 100644 index 0000000..8ecb2e9 --- /dev/null +++ b/xsa483.patch @@ -0,0 +1,30 @@ +From: Andrii Sultanov +Subject: tools/oxenstored: Reset quota when resetting permissions + +The quota object contains both limits and the current node usage counts. + +When a domain is torn down, the node data itself is cleaned up but the node +usage counts are not. A later domain reusing the same domid can create fewer +nodes before being deemed to be over quota. + +Reset the count when the node permissions are cleaned up. + +This is XSA-483 / CVE-2026-23556. + +Signed-off-by: Andrii Sultanov +Signed-off-by: Andrew Cooper + +diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml +index 9b8dd2812df0..aa9204ead3ec 100644 +--- a/tools/ocaml/xenstored/store.ml ++++ b/tools/ocaml/xenstored/store.ml +@@ -465,7 +465,8 @@ let reset_permissions store domid = + if perms <> node.perms then + Logging.debug "store|node" "Changed permissions for node %s" (Node.get_name node); + Some { node with Node.perms } +- ) store.root ++ ) store.root; ++ store.quota <- Quota.del store.quota domid + + type ops = { + store: t; diff --git a/xsa484.patch b/xsa484.patch new file mode 100644 index 0000000..522549e --- /dev/null +++ b/xsa484.patch @@ -0,0 +1,89 @@ +From 3d0d19ad17f29c64dde4a7baf392da4fd58f3654 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Mon, 16 Mar 2026 15:06:11 +0100 +Subject: [PATCH] tools/xenstored: make conn_delete_all_transactions() + idempotent + +conn_delete_all_transactions() should be callable in any context, +resetting ALL transaction related data. + +This includes number of active transactions and the transaction +pointer in struct connection. + +So reset conn->trans to NULL in conn_delete_all_transactions() and +do the cleanup for each transaction in destroy_transaction(). + +This avoids triggering the assert() in conn_delete_all_transactions() +in case e.g. ignore_connection() was called while an operation inside +a transaction was performed, or XS_RESET_WATCHES was called in a +transaction. + +This is XSA-484 / CVE-2026-23557. + +Reported-by: Andrii Sultanov +Fixes: 1f9d04fb021c ("xenstored: allow guest to shutdown all its watches/transactions") +Signed-off-by: Juergen Gross +--- + tools/xenstored/transaction.c | 20 +++++++++----------- + 1 file changed, 9 insertions(+), 11 deletions(-) + +diff --git a/tools/xenstored/transaction.c b/tools/xenstored/transaction.c +index 167cd597fd..0825c48859 100644 +--- a/tools/xenstored/transaction.c ++++ b/tools/xenstored/transaction.c +@@ -432,17 +432,23 @@ static int finalize_transaction(struct connection *conn, + static int destroy_transaction(void *_transaction) + { + struct transaction *trans = _transaction; ++ struct connection *conn = trans->conn; + struct accessed_node *i; + + wrl_ntransactions--; + trace_destroy(trans, "transaction"); + while ((i = list_top(&trans->accessed, struct accessed_node, list))) { + if (i->ta_node) +- db_delete(trans->conn, i->trans_name, NULL); ++ db_delete(conn, i->trans_name, NULL); + list_del(&i->list); + talloc_free(i); + } + ++ list_del(&trans->list); ++ domain_transaction_dec(conn); ++ if (list_empty(&conn->transaction_list)) ++ conn->ta_start_time = 0; ++ + return 0; + } + +@@ -523,10 +529,6 @@ int do_transaction_end(const void *ctx, struct connection *conn, + return ENOENT; + + conn->transaction = NULL; +- list_del(&trans->list); +- domain_transaction_dec(conn); +- if (list_empty(&conn->transaction_list)) +- conn->ta_start_time = 0; + + chk_quota = trans->node_created && domain_is_unprivileged(conn); + +@@ -572,14 +574,10 @@ void conn_delete_all_transactions(struct connection *conn) + struct transaction *trans; + + while ((trans = list_top(&conn->transaction_list, +- struct transaction, list))) { +- list_del(&trans->list); ++ struct transaction, list))) + talloc_free(trans); +- } +- +- assert(conn->transaction == NULL); + +- conn->ta_start_time = 0; ++ conn->transaction = NULL; + } + + int check_transactions(struct hashtable *hash) +-- +2.53.0 + diff --git a/xsa486.patch b/xsa486.patch new file mode 100644 index 0000000..654e957 --- /dev/null +++ b/xsa486.patch @@ -0,0 +1,181 @@ +From: Jan Beulich +Subject: gnttab: split gnttab_map_frame() + +If a domain tries to map status frames in parallel to switching grant +table version from 2 to 1, the mapping operation may put in place P2M +entries referencing MFNs which gnttab_unpopulate_status_frames() is in the +process of freeing. + +Ideally we would refcount pages when entered into P2M tables, but that's a +significant change. Extend the grant-table-locked region instead in +xenmem_add_to_physmap_one() (being the sole caller of gnttab_map_frame()), +such that a race with gnttab_unpopulate_status_frames() is no longer +possible. + +This is XSA-486 / CVE-2026-23558. + +Fixes: 5ce8fafa947c ("Dynamic grant-table sizing") +Fixes: a98dc13703e0 ("Introduce a grant_entry_v2 structure") +Reported-by: Rafal Wojtczuk +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné + +--- a/xen/arch/arm/mm.c ++++ b/xen/arch/arm/mm.c +@@ -174,12 +174,10 @@ int xenmem_add_to_physmap_one( + switch ( space ) + { + case XENMAPSPACE_grant_table: +- rc = gnttab_map_frame(d, idx, gfn, &mfn); ++ rc = gnttab_map_frame_begin(d, idx, gfn, &mfn); + if ( rc ) + return rc; + +- /* Need to take care of the reference obtained in gnttab_map_frame(). */ +- page = mfn_to_page(mfn); + t = p2m_ram_rw; + + break; +@@ -281,10 +279,23 @@ int xenmem_add_to_physmap_one( + * to drop the reference we took earlier. In all other cases we need to + * drop any reference we took earlier (perhaps indirectly). + */ +- if ( space == XENMAPSPACE_gmfn_foreign ? rc : page != NULL ) ++ switch ( space ) + { ++ default: ++ if ( page ) ++ put_page(page); ++ break; ++ ++ case XENMAPSPACE_grant_table: ++ gnttab_map_frame_end(d, mfn); ++ break; ++ ++ case XENMAPSPACE_gmfn_foreign: ++ if ( !rc ) ++ break; + ASSERT(page != NULL); + put_page(page); ++ break; + } + + return rc; +--- a/xen/arch/x86/mm/p2m.c ++++ b/xen/arch/x86/mm/p2m.c +@@ -2009,11 +2009,9 @@ int xenmem_add_to_physmap_one( + break; + + case XENMAPSPACE_grant_table: +- rc = gnttab_map_frame(d, idx, gfn, &mfn); ++ rc = gnttab_map_frame_begin(d, idx, gfn, &mfn); + if ( rc ) + return rc; +- /* Need to take care of the reference obtained in gnttab_map_frame(). */ +- page = mfn_to_page(mfn); + break; + + case XENMAPSPACE_gmfn: +@@ -2095,19 +2093,28 @@ int xenmem_add_to_physmap_one( + put_gfn(d, gfn_x(gfn)); + + put_both: +- /* +- * In the XENMAPSPACE_gmfn case, we took a ref of the gfn at the top. +- * We also may need to transfer ownership of the page reference to our +- * caller. +- */ +- if ( space == XENMAPSPACE_gmfn ) ++ switch ( space ) + { ++ case XENMAPSPACE_gmfn: ++ /* ++ * We took a ref of the gfn at the top. We also may need to transfer ++ * ownership of the page reference to our caller. ++ */ + put_gfn(d, gmfn); + if ( !rc && extra.ppage ) + { + *extra.ppage = page; + page = NULL; + } ++ break; ++ ++ case XENMAPSPACE_grant_table: ++ /* ++ * We (gnttab_map_frame_begin()) acquired a lock and took a ref of the ++ * page underlying the MFN at the top. ++ */ ++ gnttab_map_frame_end(d, mfn); ++ break; + } + + if ( page ) +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -4250,7 +4250,8 @@ int gnttab_acquire_resource( + return rc; + } + +-int gnttab_map_frame(struct domain *d, unsigned long idx, gfn_t gfn, mfn_t *mfn) ++int gnttab_map_frame_begin( ++ struct domain *d, unsigned long idx, gfn_t gfn, mfn_t *mfn) + { + int rc = 0; + struct grant_table *gt = d->grant_table; +@@ -4288,11 +4289,19 @@ int gnttab_map_frame(struct domain *d, u + put_page(pg); + } + +- grant_write_unlock(gt); ++ if ( rc ) ++ grant_write_unlock(d->grant_table); + + return rc; + } + ++void gnttab_map_frame_end(struct domain *d, mfn_t mfn) ++{ ++ put_page(mfn_to_page(mfn)); ++ ++ grant_write_unlock(d->grant_table); ++} ++ + static void gnttab_usage_print(struct domain *rd) + { + int first = 1; +--- a/xen/include/xen/grant_table.h ++++ b/xen/include/xen/grant_table.h +@@ -60,8 +60,13 @@ int gnttab_release_mappings(struct domai + int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref, + gfn_t *gfn, uint16_t *status); + +-int gnttab_map_frame(struct domain *d, unsigned long idx, gfn_t gfn, +- mfn_t *mfn); ++/* ++ * These need to be used as a pair, as the first (in the success case) returns ++ * with a lock and page reference held which the second needs to drop. ++ */ ++int gnttab_map_frame_begin(struct domain *d, unsigned long idx, gfn_t gfn, ++ mfn_t *mfn); ++void gnttab_map_frame_end(struct domain *d, mfn_t mfn); + + unsigned int gnttab_resource_max_frames(const struct domain *d, unsigned int id); + +@@ -100,12 +105,14 @@ static inline int mem_sharing_gref_to_gf + return -EINVAL; + } + +-static inline int gnttab_map_frame(struct domain *d, unsigned long idx, +- gfn_t gfn, mfn_t *mfn) ++static inline int gnttab_map_frame_begin(struct domain *d, unsigned long idx, ++ gfn_t gfn, mfn_t *mfn) + { + return -EINVAL; + } + ++static inline void gnttab_map_frame_end(struct domain *d, mfn_t mfn) {} ++ + static inline unsigned int gnttab_resource_max_frames( + const struct domain *d, unsigned int id) + { From b448fd6e2fcf9fe6fd1ef10ae18f820a63c6d218 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 12 May 2026 20:57:06 +0100 Subject: [PATCH 130/137] x86: CPU Opcode Cache corruption [XSA-490,CVE-2025-54518] --- xen.spec | 7 ++++++- xsa490-4.21.patch | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 xsa490-4.21.patch diff --git a/xen.spec b/xen.spec index ba3662c..fdb9fba 100644 --- a/xen.spec +++ b/xen.spec @@ -51,7 +51,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.21.1 -Release: 2%{?dist} +Release: 3%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -83,6 +83,7 @@ Patch12: xsa483.patch Patch13: xsa484.patch Patch14: xsa486.patch Patch15: xen.git-90b20547b756a5cf9b0fec9fb0de5b361e8bf4c3.patch +Patch16: xsa490-4.21.patch # build using Fedora seabios and ipxe packages for roms @@ -273,6 +274,7 @@ This package contains files used in testing the xen builds %patch 13 -p1 %patch 14 -p1 %patch 15 -p1 +%patch 16 -p1 # stubdom sources cp -v %{SOURCE10} %{SOURCE11} %{SOURCE12} %{SOURCE13} %{SOURCE14} %{SOURCE15} stubdom @@ -828,6 +830,9 @@ fi %{_libexecdir}/xen/tests/* %changelog +* Tue May 12 2026 Michael Young - 4.21.1-3 +- x86: CPU Opcode Cache corruption [XSA-490,CVE-2025-54518] + * Tue Apr 28 2026 Michael Young - 4.21.1-2 - oxenstored keeps quota related use counts across domain destruction [XSA-483, CVE-2026-23556] diff --git a/xsa490-4.21.patch b/xsa490-4.21.patch new file mode 100644 index 0000000..5a560cb --- /dev/null +++ b/xsa490-4.21.patch @@ -0,0 +1,43 @@ +From: Andrew Cooper +Subject: x86/amd: Mitigate AMD-SN-7052 + +This is XSA-490 / CVE-2025-54518. + +Signed-off-by: Andrew Cooper +Reviewed-by: Roger Pau Monné + +diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c +index 1bb0766ebf13..b5bf2b732e8f 100644 +--- a/xen/arch/x86/cpu/amd.c ++++ b/xen/arch/x86/cpu/amd.c +@@ -1116,11 +1116,25 @@ static void amd_check_bp_cfg(void) + { + uint64_t val, new = 0; + +- /* +- * AMD Erratum #1485. Set bit 5, as instructed. +- */ +- if (!cpu_has_hypervisor && boot_cpu_data.x86 == 0x19 && is_zen4_uarch()) +- new |= (1 << 5); ++ if (!cpu_has_hypervisor) { ++ /* ++ * AMD Erratum #1485. If SMT is enabled and STIBP disabled, ++ * the CPU may fetch incorrect instruction bytes. ++ * ++ * Set bit 5, as instructed. ++ */ ++ if (boot_cpu_data.x86 == 0x19 && is_zen4_uarch()) ++ new |= (1 << 5); ++ ++ /* ++ * AMD SB-7052. CPU OP Cache corruption, causing instructions ++ * to be executed at a higher privilege. ++ * ++ * Set bit 33, as instructed. ++ */ ++ if (boot_cpu_data.x86 == 0x17 && is_zen2_uarch()) ++ new |= (1UL << 33); ++ } + + /* + * On hardware supporting SRSO_MSR_FIX, activate BP_SPEC_REDUCE by From f67d5718e2ead23721883b7f90bdb20148b5a453 Mon Sep 17 00:00:00 2001 From: Python Maint Date: Wed, 3 Jun 2026 19:38:16 +0200 Subject: [PATCH 131/137] Rebuilt for Python 3.15 --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index fdb9fba..bdcc012 100644 --- a/xen.spec +++ b/xen.spec @@ -51,7 +51,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.21.1 -Release: 3%{?dist} +Release: 4%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -830,6 +830,9 @@ fi %{_libexecdir}/xen/tests/* %changelog +* Wed Jun 03 2026 Python Maint - 4.21.1-4 +- Rebuilt for Python 3.15 + * Tue May 12 2026 Michael Young - 4.21.1-3 - x86: CPU Opcode Cache corruption [XSA-490,CVE-2025-54518] From 124455e6320df45354cd73d2d5fb7a16dd755f90 Mon Sep 17 00:00:00 2001 From: Yaakov Selkowitz Date: Fri, 12 Jun 2026 20:07:37 -0400 Subject: [PATCH 132/137] Rebuilt for openssl 4.0 --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index bdcc012..68100b4 100644 --- a/xen.spec +++ b/xen.spec @@ -51,7 +51,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.21.1 -Release: 4%{?dist} +Release: 5%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -830,6 +830,9 @@ fi %{_libexecdir}/xen/tests/* %changelog +* Sat Jun 13 2026 Yaakov Selkowitz - 4.21.1-5 +- Rebuilt for openssl 4.0 + * Wed Jun 03 2026 Python Maint - 4.21.1-4 - Rebuilt for Python 3.15 From 0c18c23c4c540b04358f824f0130e82c7734c2ab Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 18 Jun 2026 21:14:50 +0100 Subject: [PATCH 133/137] 4 security updates x86 HVM I/O port list traversal [XSA-491, CVE-2026-42487] domctl lock open to abuse [XSA-492, CVE-2026-42489, CVE-2026-42490] Arm: Completion of memory accesses not guaranteed by completion of a TLBI [XSA-493, CVE-2025-10263] x86: mismatched mapcache metadata [XSA-494, CVE-2026-42488] --- xen.spec | 61 ++++++- xsa491-4.21.patch | 211 ++++++++++++++++++++++ xsa492-4.21-01.patch | 264 ++++++++++++++++++++++++++++ xsa492-4.21-02.patch | 104 +++++++++++ xsa492-4.21-03.patch | 87 ++++++++++ xsa492-4.21-04.patch | 81 +++++++++ xsa492-4.21-05.patch | 156 +++++++++++++++++ xsa492-4.21-06.patch | 84 +++++++++ xsa492-4.21-07.patch | 202 ++++++++++++++++++++++ xsa492-4.21-08.patch | 85 +++++++++ xsa492-4.21-09.patch | 194 +++++++++++++++++++++ xsa492-4.21-10.patch | 97 +++++++++++ xsa492-4.21-11.patch | 128 ++++++++++++++ xsa492-4.21-12.patch | 172 ++++++++++++++++++ xsa492-4.21-13.patch | 163 +++++++++++++++++ xsa492-4.21-14.patch | 179 +++++++++++++++++++ xsa492-4.21-15.patch | 108 ++++++++++++ xsa492-4.21-16.patch | 112 ++++++++++++ xsa492-4.21-17.patch | 124 +++++++++++++ xsa492-4.21-18.patch | 94 ++++++++++ xsa492-4.21-19.patch | 378 ++++++++++++++++++++++++++++++++++++++++ xsa492-4.21-20.patch | 123 +++++++++++++ xsa493-4.21-01.patch | 311 +++++++++++++++++++++++++++++++++ xsa493-4.21-02.patch | 71 ++++++++ xsa493-4.21-03.patch | 37 ++++ xsa493-4.21-04.patch | 37 ++++ xsa494-4.21.patch | 404 +++++++++++++++++++++++++++++++++++++++++++ 27 files changed, 4066 insertions(+), 1 deletion(-) create mode 100644 xsa491-4.21.patch create mode 100644 xsa492-4.21-01.patch create mode 100644 xsa492-4.21-02.patch create mode 100644 xsa492-4.21-03.patch create mode 100644 xsa492-4.21-04.patch create mode 100644 xsa492-4.21-05.patch create mode 100644 xsa492-4.21-06.patch create mode 100644 xsa492-4.21-07.patch create mode 100644 xsa492-4.21-08.patch create mode 100644 xsa492-4.21-09.patch create mode 100644 xsa492-4.21-10.patch create mode 100644 xsa492-4.21-11.patch create mode 100644 xsa492-4.21-12.patch create mode 100644 xsa492-4.21-13.patch create mode 100644 xsa492-4.21-14.patch create mode 100644 xsa492-4.21-15.patch create mode 100644 xsa492-4.21-16.patch create mode 100644 xsa492-4.21-17.patch create mode 100644 xsa492-4.21-18.patch create mode 100644 xsa492-4.21-19.patch create mode 100644 xsa492-4.21-20.patch create mode 100644 xsa493-4.21-01.patch create mode 100644 xsa493-4.21-02.patch create mode 100644 xsa493-4.21-03.patch create mode 100644 xsa493-4.21-04.patch create mode 100644 xsa494-4.21.patch diff --git a/xen.spec b/xen.spec index 68100b4..a9bf097 100644 --- a/xen.spec +++ b/xen.spec @@ -51,7 +51,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.21.1 -Release: 5%{?dist} +Release: 6%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -84,6 +84,32 @@ Patch13: xsa484.patch Patch14: xsa486.patch Patch15: xen.git-90b20547b756a5cf9b0fec9fb0de5b361e8bf4c3.patch Patch16: xsa490-4.21.patch +Patch17: xsa491-4.21.patch +Patch18: xsa492-4.21-01.patch +Patch19: xsa492-4.21-02.patch +Patch20: xsa492-4.21-03.patch +Patch21: xsa492-4.21-04.patch +Patch22: xsa492-4.21-05.patch +Patch23: xsa492-4.21-06.patch +Patch24: xsa492-4.21-07.patch +Patch25: xsa492-4.21-08.patch +Patch26: xsa492-4.21-09.patch +Patch27: xsa492-4.21-10.patch +Patch28: xsa492-4.21-11.patch +Patch29: xsa492-4.21-12.patch +Patch30: xsa492-4.21-13.patch +Patch31: xsa492-4.21-14.patch +Patch32: xsa492-4.21-15.patch +Patch33: xsa492-4.21-16.patch +Patch34: xsa492-4.21-17.patch +Patch35: xsa492-4.21-18.patch +Patch36: xsa492-4.21-19.patch +Patch37: xsa492-4.21-20.patch +Patch38: xsa493-4.21-01.patch +Patch39: xsa493-4.21-02.patch +Patch40: xsa493-4.21-03.patch +Patch41: xsa493-4.21-04.patch +Patch42: xsa494-4.21.patch # build using Fedora seabios and ipxe packages for roms @@ -275,6 +301,32 @@ This package contains files used in testing the xen builds %patch 14 -p1 %patch 15 -p1 %patch 16 -p1 +%patch 17 -p1 +%patch 18 -p1 +%patch 19 -p1 +%patch 20 -p1 +%patch 21 -p1 +%patch 22 -p1 +%patch 23 -p1 +%patch 24 -p1 +%patch 25 -p1 +%patch 26 -p1 +%patch 27 -p1 +%patch 28 -p1 +%patch 29 -p1 +%patch 30 -p1 +%patch 31 -p1 +%patch 32 -p1 +%patch 33 -p1 +%patch 34 -p1 +%patch 35 -p1 +%patch 36 -p1 +%patch 37 -p1 +%patch 38 -p1 +%patch 39 -p1 +%patch 40 -p1 +%patch 41 -p1 +%patch 42 -p1 # stubdom sources cp -v %{SOURCE10} %{SOURCE11} %{SOURCE12} %{SOURCE13} %{SOURCE14} %{SOURCE15} stubdom @@ -830,6 +882,13 @@ fi %{_libexecdir}/xen/tests/* %changelog +* Thu Jun 18 2026 Michael Young - 4.21.1-6 +- x86 HVM I/O port list traversal [XSA-491, CVE-2026-42487] +- domctl lock open to abuse [XSA-492, CVE-2026-42489, CVE-2026-42490] +- Arm: Completion of memory accesses not guaranteed by completion of a TLBI + [XSA-493, CVE-2025-10263] +- x86: mismatched mapcache metadata [XSA-494, CVE-2026-42488] + * Sat Jun 13 2026 Yaakov Selkowitz - 4.21.1-5 - Rebuilt for openssl 4.0 diff --git a/xsa491-4.21.patch b/xsa491-4.21.patch new file mode 100644 index 0000000..d1ebc1a --- /dev/null +++ b/xsa491-4.21.patch @@ -0,0 +1,211 @@ +From: Jan Beulich +Subject: x86/HVM: add locking to I/O port translation list traversal + +XEN_DOMCTL_ioport_mapping is usable by DM stubdoms, and hence we can't +assume the list to be left unaltered while the guest (really: the +hypervisor on behalf of the guest) is accessing it. + +This is XSA-491 / CVE-2026-42487. + +Fixes: 192c4dabc344 ("domctl and p2m changes for PCI passthru") +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné + +--- a/xen/arch/x86/domctl.c ++++ b/xen/arch/x86/domctl.c +@@ -663,6 +663,7 @@ long arch_do_domctl( + "ioport_map:add: dom%d gport=%x mport=%x nr=%x\n", + d->domain_id, fgp, fmp, np); + ++ write_lock(&hvm->g2m_ioport_lock); + list_for_each_entry(g2m_ioport, &hvm->g2m_ioport_list, list) + if (g2m_ioport->mport == fmp ) + { +@@ -684,11 +685,14 @@ long arch_do_domctl( + g2m_ioport->np = np; + list_add_tail(&g2m_ioport->list, &hvm->g2m_ioport_list); + } ++ write_unlock(&hvm->g2m_ioport_lock); + if ( !ret ) + ret = ioports_permit_access(d, fmp, fmp + np - 1); + if ( ret && !found && g2m_ioport ) + { ++ write_lock(&hvm->g2m_ioport_lock); + list_del(&g2m_ioport->list); ++ write_unlock(&hvm->g2m_ioport_lock); + xfree(g2m_ioport); + } + } +@@ -697,6 +701,8 @@ long arch_do_domctl( + printk(XENLOG_G_INFO + "ioport_map:remove: dom%d gport=%x mport=%x nr=%x\n", + d->domain_id, fgp, fmp, np); ++ ++ write_lock(&hvm->g2m_ioport_lock); + list_for_each_entry(g2m_ioport, &hvm->g2m_ioport_list, list) + if ( g2m_ioport->mport == fmp ) + { +@@ -704,6 +710,8 @@ long arch_do_domctl( + xfree(g2m_ioport); + break; + } ++ write_unlock(&hvm->g2m_ioport_lock); ++ + ret = ioports_deny_access(d, fmp, fmp + np - 1); + if ( ret && is_hardware_domain(currd) ) + printk(XENLOG_ERR +--- a/xen/arch/x86/hvm/emulate.c ++++ b/xen/arch/x86/hvm/emulate.c +@@ -160,7 +160,6 @@ void hvmemul_cancel(struct vcpu *v) + hvio->mmio_insn_bytes = 0; + hvio->mmio_access = (struct npfec){}; + hvio->mmio_retry = false; +- hvio->g2m_ioport = NULL; + + hvmemul_cache_disable(v); + } +--- a/xen/arch/x86/hvm/hvm.c ++++ b/xen/arch/x86/hvm/hvm.c +@@ -610,6 +610,7 @@ int hvm_domain_initialise(struct domain + spin_lock_init(&d->arch.hvm.irq_lock); + spin_lock_init(&d->arch.hvm.uc_lock); + spin_lock_init(&d->arch.hvm.write_map.lock); ++ rwlock_init(&d->arch.hvm.g2m_ioport_lock); + rwlock_init(&d->arch.hvm.mmcfg_lock); + INIT_LIST_HEAD(&d->arch.hvm.write_map.list); + INIT_LIST_HEAD(&d->arch.hvm.g2m_ioport_list); +--- a/xen/arch/x86/hvm/io.c ++++ b/xen/arch/x86/hvm/io.c +@@ -143,36 +143,56 @@ bool handle_pio(uint16_t port, unsigned + return true; + } + +-static bool cf_check g2m_portio_accept( +- const struct hvm_io_handler *handler, const ioreq_t *p) ++/* NB: Returns with the lock held in the success case. */ ++static const struct g2m_ioport *g2m_portio_find_and_lock(struct hvm_domain *hvm, ++ uint64_t addr, ++ uint32_t size) + { +- struct vcpu *curr = current; +- const struct hvm_domain *hvm = &curr->domain->arch.hvm; +- struct hvm_vcpu_io *hvio = &curr->arch.hvm.hvm_io; +- struct g2m_ioport *g2m_ioport; +- unsigned int start, end; ++ const struct g2m_ioport *g2m_ioport; ++ ++ read_lock(&hvm->g2m_ioport_lock); + + list_for_each_entry( g2m_ioport, &hvm->g2m_ioport_list, list ) + { +- start = g2m_ioport->gport; +- end = start + g2m_ioport->np; +- if ( (p->addr >= start) && (p->addr + p->size <= end) ) +- { +- hvio->g2m_ioport = g2m_ioport; +- return 1; +- } ++ unsigned int start = g2m_ioport->gport; ++ ++ if ( addr >= start && addr + size <= start + g2m_ioport->np ) ++ return g2m_ioport; + } + +- return 0; ++ read_unlock(&hvm->g2m_ioport_lock); ++ ++ return NULL; ++} ++ ++static bool cf_check g2m_portio_accept( ++ const struct hvm_io_handler *handler, const ioreq_t *p) ++{ ++ struct hvm_domain *hvm = ¤t->domain->arch.hvm; ++ const struct g2m_ioport *g2m_ioport = ++ g2m_portio_find_and_lock(hvm, p->addr, p->size); ++ ++ if ( !g2m_ioport ) ++ return false; ++ ++ read_unlock(&hvm->g2m_ioport_lock); ++ ++ return true; + } + + static int cf_check g2m_portio_read( + const struct hvm_io_handler *handler, uint64_t addr, uint32_t size, + uint64_t *data) + { +- struct hvm_vcpu_io *hvio = ¤t->arch.hvm.hvm_io; +- const struct g2m_ioport *g2m_ioport = hvio->g2m_ioport; +- unsigned int mport = (addr - g2m_ioport->gport) + g2m_ioport->mport; ++ struct hvm_domain *hvm = ¤t->domain->arch.hvm; ++ const struct g2m_ioport *g2m_ioport = ++ g2m_portio_find_and_lock(hvm, addr, size); ++ unsigned int mport; ++ ++ if ( !g2m_ioport ) ++ return X86EMUL_RETRY; ++ ++ mport = addr - g2m_ioport->gport + g2m_ioport->mport; + + switch ( size ) + { +@@ -189,6 +209,8 @@ static int cf_check g2m_portio_read( + BUG(); + } + ++ read_unlock(&hvm->g2m_ioport_lock); ++ + return X86EMUL_OKAY; + } + +@@ -196,9 +218,15 @@ static int cf_check g2m_portio_write( + const struct hvm_io_handler *handler, uint64_t addr, uint32_t size, + uint64_t data) + { +- struct hvm_vcpu_io *hvio = ¤t->arch.hvm.hvm_io; +- const struct g2m_ioport *g2m_ioport = hvio->g2m_ioport; +- unsigned int mport = (addr - g2m_ioport->gport) + g2m_ioport->mport; ++ struct hvm_domain *hvm = ¤t->domain->arch.hvm; ++ const struct g2m_ioport *g2m_ioport = ++ g2m_portio_find_and_lock(hvm, addr, size); ++ unsigned int mport; ++ ++ if ( !g2m_ioport ) ++ return X86EMUL_RETRY; ++ ++ mport = addr - g2m_ioport->gport + g2m_ioport->mport; + + switch ( size ) + { +@@ -215,6 +243,8 @@ static int cf_check g2m_portio_write( + BUG(); + } + ++ read_unlock(&hvm->g2m_ioport_lock); ++ + return X86EMUL_OKAY; + } + +--- a/xen/arch/x86/include/asm/hvm/domain.h ++++ b/xen/arch/x86/include/asm/hvm/domain.h +@@ -125,6 +125,7 @@ struct hvm_domain { + + /* List of guest to machine IO ports mapping. */ + struct list_head g2m_ioport_list; ++ rwlock_t g2m_ioport_lock; + + /* List of MMCFG regions trapped by Xen. */ + struct list_head mmcfg_regions; +--- a/xen/arch/x86/include/asm/hvm/vcpu.h ++++ b/xen/arch/x86/include/asm/hvm/vcpu.h +@@ -54,8 +54,6 @@ struct hvm_vcpu_io { + unsigned long msix_unmask_address; + unsigned long msix_snoop_address; + unsigned long msix_snoop_gpa; +- +- const struct g2m_ioport *g2m_ioport; + }; + + struct nestedvcpu { diff --git a/xsa492-4.21-01.patch b/xsa492-4.21-01.patch new file mode 100644 index 0000000..7244ebd --- /dev/null +++ b/xsa492-4.21-01.patch @@ -0,0 +1,264 @@ +From: Jan Beulich +Subject: sched: use sequence counter to enlighten vcpu_runstate_get() + +Subsequently XEN_DOMCTL_getdomaininfo will want to invoke the function +without holding a lock, thus allowing parallel execution of potentially +many instances. As was learned from 228ab9992ffb ("domctl: improve +locking during domain destruction"), reverted by d0887cc6b16e, such +parallelism can result in severe lock contention on any (previously) +inner lock. To avoid taking that risk replace the use of the scheduler +lock in vcpu_runstate_get() by a newly introduced sequence counter. +Convert the "no lock if current" property to "use a local counter +instance", thus guaranteeing the loop to exit after the first iteration. + +Skeleton and commentary of the seqcount implementation based on / +derived from Linux 6.11-rc. + +To have runstate_seq placed next to runstate in struct vcpu, without +introducing a new obvious padding hole, yet while keeping the latter +adjacent to runstate_guest{,_area} as well, move runstate down a little. + +This is part of XSA-492. + +Requested-by: Andrew Cooper +Signed-off-by: Jan Beulich +Signed-off-by: Andrew Cooper +Reviewed-by: Roger Pau Monné +Reviewed-by: Juergen Gross + +--- a/xen/common/sched/core.c ++++ b/xen/common/sched/core.c +@@ -281,13 +281,18 @@ static inline void vcpu_runstate_change( + } + + delta = new_entry_time - v->runstate.state_entry_time; +- if ( delta > 0 ) ++ ++ /* Serialization: ->schedule_lock (see ASSERT() above). */ ++ with_seq_write(&v->runstate_seq) + { +- v->runstate.time[v->runstate.state] += delta; +- v->runstate.state_entry_time = new_entry_time; +- } ++ if ( delta > 0 ) ++ { ++ v->runstate.time[v->runstate.state] += delta; ++ v->runstate.state_entry_time = new_entry_time; ++ } + +- v->runstate.state = new_state; ++ v->runstate.state = new_state; ++ } + } + + void sched_guest_idle(void (*idle) (void), unsigned int cpu) +@@ -307,30 +312,18 @@ void sched_guest_idle(void (*idle) (void + void vcpu_runstate_get(const struct vcpu *v, + struct vcpu_runstate_info *runstate) + { +- spinlock_t *lock; +- s_time_t delta; +- struct sched_unit *unit; ++ struct seqcount seq = SEQCNT_ZERO(); ++ const struct seqcount *s = likely(v == current) ? &seq : &v->runstate_seq; + +- rcu_read_lock(&sched_res_rculock); +- +- /* +- * Be careful in case of an idle vcpu: the assignment to a unit might +- * change even with the scheduling lock held, so be sure to use the +- * correct unit for locking in order to avoid triggering an ASSERT() in +- * the unlock function. +- */ +- unit = is_idle_vcpu(v) ? get_sched_res(v->processor)->sched_unit_idle +- : v->sched_unit; +- lock = likely(v == current) ? NULL : unit_schedule_lock_irq(unit); +- memcpy(runstate, &v->runstate, sizeof(*runstate)); +- delta = NOW() - runstate->state_entry_time; +- if ( delta > 0 ) +- runstate->time[runstate->state] += delta; +- +- if ( unlikely(lock != NULL) ) +- unit_schedule_unlock_irq(lock, unit); ++ until_seq_read(s) ++ { ++ s_time_t delta; + +- rcu_read_unlock(&sched_res_rculock); ++ *runstate = v->runstate; ++ delta = NOW() - runstate->state_entry_time; ++ if ( delta > 0 ) ++ runstate->time[runstate->state] += delta; ++ } + } + + uint64_t get_cpu_idle_time(unsigned int cpu) +--- a/xen/include/xen/sched.h ++++ b/xen/include/xen/sched.h +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -198,7 +199,6 @@ struct vcpu + + struct sched_unit *sched_unit; + +- struct vcpu_runstate_info runstate; + #ifndef CONFIG_COMPAT + # define runstate_guest(v) ((v)->runstate_guest) + XEN_GUEST_HANDLE(vcpu_runstate_info_t) runstate_guest; /* guest address */ +@@ -210,6 +210,8 @@ struct vcpu + } runstate_guest; /* guest address */ + #endif + struct guest_area runstate_guest_area; ++ struct vcpu_runstate_info runstate; ++ struct seqcount runstate_seq; + unsigned int new_state; + + /* Has the FPU been initialised? */ +--- /dev/null ++++ b/xen/include/xen/seqcount.h +@@ -0,0 +1,139 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++#ifndef XEN_SEQCOUNT_H ++#define XEN_SEQCOUNT_H ++ ++#include ++#include ++ ++#include ++#include ++ ++/* ++ * Sequence counters (seqcount_t) ++ * ++ * This is the raw counting mechanism, without any writer protection. ++ * ++ * Write side critical sections must be serialized (and non-preemptible). ++ * ++ * If readers can be invoked from interrupt contexts, interrupts must also ++ * be respectively disabled before entering the write section. ++ * ++ * This mechanism can't be used if the protected data contains pointers, ++ * as the writer can invalidate a pointer that a reader is following. ++ */ ++struct seqcount { ++ unsigned int sequence; ++}; ++ ++/* ++ * SEQCNT_ZERO() - initializer for seqcount_t ++ * @name: Name of the struct seqcount instance ++ */ ++#define SEQCNT_ZERO() { .sequence = 0 } ++ ++static inline unsigned int seqprop_sequence(const struct seqcount *s) ++{ ++ return ACCESS_ONCE(s->sequence); ++} ++ ++/* ++ * read_seqcount_begin() - begin a seqcount read critical section ++ * @s: Pointer to struct seqcount ++ * ++ * Return: count to be passed to read_seqcount_retry() ++ */ ++static inline unsigned int _read_seqcount_begin(const struct seqcount *s) ++{ ++ unsigned int seq; ++ ++ while ((seq = seqprop_sequence(s)) & 1) ++ cpu_relax(); ++ ++ smp_rmb(); ++ ++ return seq; ++} ++ ++static always_inline unsigned int read_seqcount_begin(const struct seqcount *s) ++{ ++ unsigned int seq = _read_seqcount_begin(s); ++ ++ block_lock_speculation(); ++ ++ return seq; ++} ++ ++/* ++ * read_seqcount_retry() - end a seqcount read critical section ++ * @s: Pointer to struct seqcount ++ * @start: count, from read_seqcount_begin() ++ * ++ * read_seqcount_retry closes the read critical section of given struct ++ * seqcount. If the critical section was invalid, it must be ignored ++ * (and typically retried). ++ * ++ * Return: true if a read section retry is required, else false ++ */ ++static inline bool _read_seqcount_retry(const struct seqcount *s, ++ unsigned int start) ++{ ++ smp_rmb(); ++ return unlikely(seqprop_sequence(s) != start); ++} ++ ++static always_inline bool read_seqcount_retry(const struct seqcount *s, ++ unsigned int start) ++{ ++ return lock_evaluate_nospec(_read_seqcount_retry(s, start)); ++} ++ ++/* Loops until a consistent count has been observed across the loop body. */ ++#define until_seq_read(seq) \ ++ for ( unsigned int retry_ = 1, count_; \ ++ retry_ && (count_ = read_seqcount_begin(seq), true); \ ++ retry_ = read_seqcount_retry(seq, count_) ) ++ ++/* ++ * write_seqcount_begin() - start a struct seqcount write side critical section ++ * @s: Pointer to struct seqcount ++ * ++ * Context: sequence counter write side sections must be serialized. ++ * If readers can be invoked from interrupt context, interrupts must be ++ * respectively disabled. ++ */ ++static inline void write_seqcount_begin(struct seqcount *s) ++{ ++ add_sized(&s->sequence, 1); ++ smp_wmb(); ++} ++ ++/* ++ * write_seqcount_end() - end a struct seqcount write side critical section ++ * @s: Pointer to seqcount ++ */ ++static inline void write_seqcount_end(struct seqcount *s) ++{ ++ smp_wmb(); ++ add_sized(&s->sequence, 1); ++} ++ ++/* ++ * Not really a loop, but we need write_seqcount_{begin,end}() in the correct ++ * position. ++ */ ++#define with_seq_write(seq) \ ++ for ( bool once_ = true; \ ++ once_ && (write_seqcount_begin(seq), true); \ ++ (write_seqcount_end(seq), once_ = false) ) ++ ++#endif /* XEN_SEQCOUNT_H */ ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-file-style: "BSD" ++ * c-basic-offset: 4 ++ * tab-width: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ diff --git a/xsa492-4.21-02.patch b/xsa492-4.21-02.patch new file mode 100644 index 0000000..75ca8ca --- /dev/null +++ b/xsa492-4.21-02.patch @@ -0,0 +1,104 @@ +From: Jan Beulich +Subject: domctl: handle XEN_DOMCTL_getdomaininfo without acquiring domctl lock + +getdomaininfo() is not called under consistently the same lock. Thus, +with caller side locking irrelevant, it can as well be called with the +domctl lock not held. (Callers not pausing the domain they want to +retrieve information for already need to be aware that not all of the +data returned can be relied on as being consistent; most data will also +be stale by the time the caller gets to look at it.) + +Move the handling not only ahead of acquiring the lock, but also ahead +of the XSM check, leveraging that the sub-op has its own hook. + +While moving, convert an assignment to an assertion: The domain in +question was determined from the field which previously was "updated". + +This is part of XSA-492. + +Fixes: 5513bd0b4675 ("add xenstore domain flag to hypervisor") +Reported-by: Andrew Cooper +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné +Acked-by: Daniel P. Smith + +--- a/xen/common/domctl.c ++++ b/xen/common/domctl.c +@@ -318,6 +318,26 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + break; + } + ++ /* Handle sub-ops not requiring the domctl lock. */ ++ switch ( op->cmd ) ++ { ++ case XEN_DOMCTL_getdomaininfo: ++ ret = xsm_getdomaininfo(XSM_XS_PRIV, d); ++ if ( !ret ) ++ { ++ getdomaininfo(d, &op->u.getdomaininfo); ++ ++ ASSERT(op->domain == op->u.getdomaininfo.domain); ++ copyback = true; ++ } ++ ++ goto domctl_out_unlock_domonly; ++ ++ default: ++ /* Everything else handled further down. */ ++ break; ++ } ++ + ret = xsm_domctl(XSM_OTHER, d, op->cmd, + /* SSIDRef only applicable for cmd == createdomain */ + op->u.createdomain.ssidref); +@@ -516,17 +536,6 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + copyback = 1; + break; + +- case XEN_DOMCTL_getdomaininfo: +- ret = xsm_getdomaininfo(XSM_XS_PRIV, d); +- if ( ret ) +- break; +- +- getdomaininfo(d, &op->u.getdomaininfo); +- +- op->domain = op->u.getdomaininfo.domain; +- copyback = 1; +- break; +- + case XEN_DOMCTL_getvcpucontext: + { + vcpu_guest_context_u c = { .nat = NULL }; +--- a/xen/include/xsm/dummy.h ++++ b/xen/include/xsm/dummy.h +@@ -172,9 +172,13 @@ static XSM_INLINE int cf_check xsm_domct + case XEN_DOMCTL_bind_pt_irq: + case XEN_DOMCTL_unbind_pt_irq: + return xsm_default_action(XSM_DM_PRIV, current->domain, d); +- case XEN_DOMCTL_getdomaininfo: + case XEN_DOMCTL_get_domain_state: + return xsm_default_action(XSM_XS_PRIV, current->domain, d); ++ ++ case XEN_DOMCTL_getdomaininfo: ++ ASSERT_UNREACHABLE(); ++ return -EILSEQ; ++ + default: + return xsm_default_action(XSM_PRIV, current->domain, d); + } +--- a/xen/xsm/flask/hooks.c ++++ b/xen/xsm/flask/hooks.c +@@ -682,8 +682,12 @@ static int cf_check flask_domctl(struct + */ + return avc_current_has_perm(ssidref, SECCLASS_DOMAIN, DOMAIN__CREATE, NULL); + +- /* These have individual XSM hooks (common/domctl.c) */ ++ /* These have individual XSM hooks and don't make it here. */ + case XEN_DOMCTL_getdomaininfo: ++ ASSERT_UNREACHABLE(); ++ return -EILSEQ; ++ ++ /* These have individual XSM hooks (common/domctl.c) */ + case XEN_DOMCTL_scheduler_op: + case XEN_DOMCTL_irq_permission: + case XEN_DOMCTL_iomem_permission: diff --git a/xsa492-4.21-03.patch b/xsa492-4.21-03.patch new file mode 100644 index 0000000..5a0db22 --- /dev/null +++ b/xsa492-4.21-03.patch @@ -0,0 +1,87 @@ +From: Daniel P. Smith +Subject: domctl: protect locking for get_domain_state + +When DOMID_INVALID is passed, the dom exec handler lock is being taken +without any check that the domain is even allowed to take the lock. This +allows for an unauthorized domain to DoS the get_domain_state domctl op. +Move to consider the op effectively being called against the hypervisor. +Thus it is the target of the call being invoked to identify the last +domain with a state change. The subsequent check of whether the source +domain is allowed the state of the last domain to change state is still +relevant. + +This is part of XSA-492. + +Signed-off-by: Daniel P. Smith +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné + +--- a/tools/flask/policy/modules/xenstore.te ++++ b/tools/flask/policy/modules/xenstore.te +@@ -14,6 +14,7 @@ allow xenstore_t xen_t:xen writeconsole; + # Xenstore queries domaininfo on all domains + allow xenstore_t domain_type:domain getdomaininfo; + allow xenstore_t domain_type:domain2 get_domain_state; ++allow xenstore_t domxen_t:domain2 get_domain_state; + + # As a shortcut, the following 3 rules are used instead of adding a domain_comms + # rule between xenstore_t and every domain type that talks to xenstore +--- a/xen/common/domain.c ++++ b/xen/common/domain.c +@@ -216,12 +216,8 @@ int get_domain_state(struct xen_domctl_g + if ( info->pad0 ) + return -EINVAL; + +- if ( d ) ++ if ( d != dom_xen ) + { +- rc = xsm_get_domain_state(XSM_XS_PRIV, d); +- if ( rc ) +- return rc; +- + set_domain_state_info(info, d); + + return 0; +--- a/xen/common/domctl.c ++++ b/xen/common/domctl.c +@@ -304,13 +304,19 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + fallthrough; + case XEN_DOMCTL_test_assign_device: + case XEN_DOMCTL_vm_event_op: +- case XEN_DOMCTL_get_domain_state: + if ( op->domain == DOMID_INVALID ) + { + d = NULL; + break; + } + fallthrough; ++ case XEN_DOMCTL_get_domain_state: ++ if ( op->domain == DOMID_INVALID ) ++ { ++ d = dom_xen; ++ break; ++ } ++ fallthrough; + default: + d = rcu_lock_domain_by_id(op->domain); + if ( !d ) +@@ -863,7 +869,9 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + break; + + case XEN_DOMCTL_get_domain_state: +- ret = get_domain_state(&op->u.get_domain_state, d, &op->domain); ++ ret = xsm_get_domain_state(XSM_XS_PRIV, d); ++ if ( !ret ) ++ ret = get_domain_state(&op->u.get_domain_state, d, &op->domain); + if ( !ret ) + copyback = true; + break; +@@ -876,7 +884,7 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + domctl_lock_release(); + + domctl_out_unlock_domonly: +- if ( d && d != dom_io ) ++ if ( d && !is_system_domain(d) ) + rcu_unlock_domain(d); + + if ( copyback && __copy_to_guest(u_domctl, op, 1) ) diff --git a/xsa492-4.21-04.patch b/xsa492-4.21-04.patch new file mode 100644 index 0000000..481ff5d --- /dev/null +++ b/xsa492-4.21-04.patch @@ -0,0 +1,81 @@ +From: Jan Beulich +Subject: domctl: handle XEN_DOMCTL_get_domain_state without acquiring domctl lock + +get_domain_state() uses its own locking. Thus, with caller side locking +irrelevant, it can as well be called with the domctl lock not held. + +Move the handling not only ahead of acquiring the lock, but also ahead +of the XSM check, leveraging that the sub-op has its own hook. + +This is part of XSA-492. + +Fixes: 3ad3df1bd0aa ("xen: add new domctl get_domain_state") +Reported-by: Andrew Cooper +Signed-off-by: Jan Beulich +Acked-by: Daniel P. Smith +Reviewed-by: Roger Pau Monné + +--- a/xen/common/domctl.c ++++ b/xen/common/domctl.c +@@ -339,6 +339,14 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + + goto domctl_out_unlock_domonly; + ++ case XEN_DOMCTL_get_domain_state: ++ ret = xsm_get_domain_state(XSM_XS_PRIV, d); ++ if ( !ret ) ++ ret = get_domain_state(&op->u.get_domain_state, d, &op->domain); ++ if ( !ret ) ++ copyback = true; ++ goto domctl_out_unlock_domonly; ++ + default: + /* Everything else handled further down. */ + break; +@@ -868,14 +876,6 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + ret = -EOPNOTSUPP; + break; + +- case XEN_DOMCTL_get_domain_state: +- ret = xsm_get_domain_state(XSM_XS_PRIV, d); +- if ( !ret ) +- ret = get_domain_state(&op->u.get_domain_state, d, &op->domain); +- if ( !ret ) +- copyback = true; +- break; +- + default: + ret = arch_do_domctl(op, d, u_domctl); + break; +--- a/xen/include/xsm/dummy.h ++++ b/xen/include/xsm/dummy.h +@@ -172,10 +172,9 @@ static XSM_INLINE int cf_check xsm_domct + case XEN_DOMCTL_bind_pt_irq: + case XEN_DOMCTL_unbind_pt_irq: + return xsm_default_action(XSM_DM_PRIV, current->domain, d); +- case XEN_DOMCTL_get_domain_state: +- return xsm_default_action(XSM_XS_PRIV, current->domain, d); + + case XEN_DOMCTL_getdomaininfo: ++ case XEN_DOMCTL_get_domain_state: + ASSERT_UNREACHABLE(); + return -EILSEQ; + +--- a/xen/xsm/flask/hooks.c ++++ b/xen/xsm/flask/hooks.c +@@ -684,6 +684,7 @@ static int cf_check flask_domctl(struct + + /* These have individual XSM hooks and don't make it here. */ + case XEN_DOMCTL_getdomaininfo: ++ case XEN_DOMCTL_get_domain_state: + ASSERT_UNREACHABLE(); + return -EILSEQ; + +@@ -694,7 +695,6 @@ static int cf_check flask_domctl(struct + case XEN_DOMCTL_memory_mapping: + case XEN_DOMCTL_set_target: + case XEN_DOMCTL_vm_event_op: +- case XEN_DOMCTL_get_domain_state: + + /* These have individual XSM hooks (arch/../domctl.c) */ + case XEN_DOMCTL_bind_pt_irq: diff --git a/xsa492-4.21-05.patch b/xsa492-4.21-05.patch new file mode 100644 index 0000000..cb7beaa --- /dev/null +++ b/xsa492-4.21-05.patch @@ -0,0 +1,156 @@ +From: Jan Beulich +Subject: domain: locking for iomem_caps accesses + +In order to be able to pull at least the XEN_DOMCTL_iomem_mapping handling +out of the domctl-locked region, a separate (per-domain) lock is needed to +synchronize in particular with XEN_DOMCTL_iomem_permission. + +Locking is added only as far as domctl-s are affected. Uses presently +outside of the domctl lock may want dealing with subsequently (perhaps +limited to non-__init code). + +This is part of XSA-492. + +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné + +--- a/xen/common/domain.c ++++ b/xen/common/domain.c +@@ -518,10 +518,15 @@ static int late_hwdom_init(struct domain + * may be modified after this hypercall returns if a more complex + * device model is desired. + */ ++ write_lock(&dom0->caps_lock); + rangeset_swap(d->irq_caps, dom0->irq_caps); + rangeset_swap(d->iomem_caps, dom0->iomem_caps); + #ifdef CONFIG_X86 + rangeset_swap(d->arch.ioport_caps, dom0->arch.ioport_caps); ++#endif ++ write_unlock(&dom0->caps_lock); ++ ++#ifdef CONFIG_X86 + setup_io_bitmap(d); + setup_io_bitmap(dom0); + #endif +@@ -873,6 +878,7 @@ struct domain *domain_create(domid_t dom + rspin_lock_init_prof(d, domain_lock); + rspin_lock_init_prof(d, page_alloc_lock); + spin_lock_init(&d->hypercall_deadlock_mutex); ++ rwlock_init(&d->caps_lock); + INIT_PAGE_LIST_HEAD(&d->page_list); + INIT_PAGE_LIST_HEAD(&d->extra_page_list); + INIT_PAGE_LIST_HEAD(&d->xenpage_list); +--- a/xen/common/domctl.c ++++ b/xen/common/domctl.c +@@ -267,6 +267,35 @@ static struct vnuma_info *vnuma_init(con + return ERR_PTR(ret); + } + ++void iocaps_double_lock(struct domain *d, bool write) ++{ ++ struct domain *currd = current->domain; ++ ++ if ( d->domain_id > currd->domain_id ) ++ read_lock(&currd->caps_lock); ++ ++ if ( write ) ++ write_lock(&d->caps_lock); ++ else ++ read_lock(&d->caps_lock); ++ ++ if ( d->domain_id < currd->domain_id ) ++ read_lock(&currd->caps_lock); ++} ++ ++void iocaps_double_unlock(struct domain *d, bool write) ++{ ++ struct domain *currd = current->domain; ++ ++ if ( d != currd ) ++ read_unlock(&currd->caps_lock); ++ ++ if ( write ) ++ write_unlock(&d->caps_lock); ++ else ++ read_unlock(&d->caps_lock); ++} ++ + static bool is_stable_domctl(uint32_t cmd) + { + return cmd == XEN_DOMCTL_get_domain_state; +@@ -687,6 +716,8 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + if ( (mfn + nr_mfns - 1) < mfn ) /* wrap? */ + break; + ++ iocaps_double_lock(d, true); ++ + if ( !iomem_access_permitted(current->domain, + mfn, mfn + nr_mfns - 1) || + xsm_iomem_permission(XSM_HOOK, d, mfn, mfn + nr_mfns - 1, allow) ) +@@ -695,6 +726,8 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + ret = iomem_permit_access(d, mfn, mfn + nr_mfns - 1); + else + ret = iomem_deny_access(d, mfn, mfn + nr_mfns - 1); ++ ++ iocaps_double_unlock(d, true); + break; + } + +@@ -719,19 +752,15 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + break; + #endif + ++ iocaps_double_lock(d, false); ++ + ret = -EPERM; + if ( !iomem_access_permitted(current->domain, mfn, mfn_end) || +- !iomem_access_permitted(d, mfn, mfn_end) ) +- break; +- +- ret = xsm_iomem_mapping(XSM_HOOK, d, mfn, mfn_end, add); +- if ( ret ) +- break; +- +- if ( !paging_mode_translate(d) ) +- break; +- +- if ( add ) ++ !iomem_access_permitted(d, mfn, mfn_end) || ++ (ret = xsm_iomem_mapping(XSM_HOOK, d, mfn, mfn_end, add)) || ++ !paging_mode_translate(d) ) ++ /* Nothing. */; ++ else if ( add ) + { + printk(XENLOG_G_DEBUG + "memory_map:add: dom%d gfn=%lx mfn=%lx nr=%lx\n", +@@ -755,6 +784,8 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + "memory_map: error %ld removing dom%d access to [%lx,%lx]\n", + ret, d->domain_id, mfn, mfn_end); + } ++ ++ iocaps_double_unlock(d, false); + break; + } + +--- a/xen/include/xen/iocap.h ++++ b/xen/include/xen/iocap.h +@@ -12,6 +12,9 @@ + #include + #include + ++void iocaps_double_lock(struct domain *d, bool write); ++void iocaps_double_unlock(struct domain *d, bool write); ++ + static inline int iomem_permit_access(struct domain *d, unsigned long s, + unsigned long e) + { +--- a/xen/include/xen/sched.h ++++ b/xen/include/xen/sched.h +@@ -536,6 +536,7 @@ struct domain + #endif + + /* I/O capabilities (access to IRQs and memory-mapped I/O). */ ++ rwlock_t caps_lock; + struct rangeset *iomem_caps; + struct rangeset *irq_caps; + diff --git a/xsa492-4.21-06.patch b/xsa492-4.21-06.patch new file mode 100644 index 0000000..c9e0061 --- /dev/null +++ b/xsa492-4.21-06.patch @@ -0,0 +1,84 @@ +From: Jan Beulich +Subject: x86/domain: locking for ioport_caps accesses + +In order to be able to pull at least the XEN_DOMCTL_ioport_mapping +handling out of the domctl-locked region, the new separate (per-domain) +lock is used to synchronize in particular with +XEN_DOMCTL_ioport_permission. + +Locking is added only as far as domctl-s are affected. Uses presently +outside of the domctl lock may want dealing with subsequently (perhaps +limited to non-__init code). + +This is part of XSA-492. + +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné + +--- a/xen/arch/x86/domctl.c ++++ b/xen/arch/x86/domctl.c +@@ -233,6 +233,8 @@ long arch_do_domctl( + unsigned int np = domctl->u.ioport_permission.nr_ports; + int allow = domctl->u.ioport_permission.allow_access; + ++ iocaps_double_lock(d, true); ++ + if ( (fp + np) <= fp || (fp + np) > MAX_IOPORTS ) + ret = -EINVAL; + else if ( !ioports_access_permitted(currd, fp, fp + np - 1) || +@@ -242,6 +244,8 @@ long arch_do_domctl( + ret = ioports_permit_access(d, fp, fp + np - 1); + else + ret = ioports_deny_access(d, fp, fp + np - 1); ++ ++ iocaps_double_unlock(d, true); + break; + } + +@@ -648,16 +652,13 @@ long arch_do_domctl( + break; + } + +- ret = -EPERM; +- if ( !ioports_access_permitted(currd, fmp, fmp + np - 1) ) +- break; +- +- ret = xsm_ioport_mapping(XSM_HOOK, d, fmp, fmp + np - 1, add); +- if ( ret ) +- break; +- + hvm = &d->arch.hvm; +- if ( add ) ++ iocaps_double_lock(d, true); ++ ++ if ( !ioports_access_permitted(currd, fmp, fmp + np - 1) || ++ (ret = xsm_ioport_mapping(XSM_HOOK, d, fmp, fmp + np - 1, add)) ) ++ ret = ret ?: -EPERM; ++ else if ( add ) + { + printk(XENLOG_G_INFO + "ioport_map:add: dom%d gport=%x mport=%x nr=%x\n", +@@ -718,6 +720,8 @@ long arch_do_domctl( + "ioport_map: error %ld denying dom%d access to [%x,%x]\n", + ret, d->domain_id, fmp, fmp + np - 1); + } ++ ++ iocaps_double_unlock(d, true); + break; + } + +--- a/xen/arch/x86/setup.c ++++ b/xen/arch/x86/setup.c +@@ -2339,9 +2339,12 @@ void __hwdom_init setup_io_bitmap(struct + return; + + bitmap_fill(d->arch.hvm.io_bitmap, 0x10000); ++ ++ read_lock(&d->caps_lock); + if ( rangeset_report_ranges(d->arch.ioport_caps, 0, 0x10000, + io_bitmap_cb, d) ) + BUG(); ++ read_unlock(&d->caps_lock); + + /* + * We need to trap 4-byte accesses to 0xcf8 (see admin_io_okay(), diff --git a/xsa492-4.21-07.patch b/xsa492-4.21-07.patch new file mode 100644 index 0000000..e343773 --- /dev/null +++ b/xsa492-4.21-07.patch @@ -0,0 +1,202 @@ +From: Jan Beulich +Subject: domain: locking for irq_caps accesses + +In order to be able to pull at least the XEN_DOMCTL_{,un}bind_pt_irq +handling out of the domctl-locked region, a separate (per-domain) lock is +needed to synchronize in particular with XEN_DOMCTL_{irq,gsi}_permission. + +Locking is added only as far as domctl-s are affected. Uses presently +outside of the domctl lock may want dealing with subsequently (perhaps +limited to non-__init code). + +This is part of XSA-492. + +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné +Reviewed-by: Julien Grall + +--- a/xen/arch/arm/domctl.c ++++ b/xen/arch/arm/domctl.c +@@ -76,6 +76,7 @@ long arch_do_domctl(struct xen_domctl *d + case XEN_DOMCTL_bind_pt_irq: + { + int rc; ++ struct domain *currd = current->domain; + struct xen_domctl_bind_pt_irq *bind = &domctl->u.bind_pt_irq; + uint32_t irq = bind->u.spi.spi; + uint32_t virq = bind->machine_irq; +@@ -107,21 +108,26 @@ long arch_do_domctl(struct xen_domctl *d + if ( rc ) + return rc; + +- if ( !irq_access_permitted(current->domain, irq) ) +- return -EPERM; ++ read_lock(&currd->caps_lock); + +- if ( !vgic_reserve_virq(d, virq) ) +- return -EBUSY; +- +- rc = route_irq_to_guest(d, virq, irq, "routed IRQ"); +- if ( rc ) +- vgic_free_virq(d, virq); ++ if ( !irq_access_permitted(currd, irq) ) ++ rc = -EPERM; ++ else if ( !vgic_reserve_virq(d, virq) ) ++ rc = -EBUSY; ++ else ++ { ++ rc = route_irq_to_guest(d, virq, irq, "routed IRQ"); ++ if ( rc ) ++ vgic_free_virq(d, virq); ++ } + ++ read_unlock(&currd->caps_lock); + return rc; + } + case XEN_DOMCTL_unbind_pt_irq: + { + int rc; ++ struct domain *currd = current->domain; + struct xen_domctl_bind_pt_irq *bind = &domctl->u.bind_pt_irq; + uint32_t irq = bind->u.spi.spi; + uint32_t virq = bind->machine_irq; +@@ -138,16 +144,15 @@ long arch_do_domctl(struct xen_domctl *d + if ( rc ) + return rc; + +- if ( !irq_access_permitted(current->domain, irq) ) +- return -EPERM; +- +- rc = release_guest_irq(d, virq); +- if ( rc ) +- return rc; ++ read_lock(&currd->caps_lock); + +- vgic_free_virq(d, virq); ++ if ( !irq_access_permitted(currd, irq) ) ++ rc = -EPERM; ++ else if ( !(rc = release_guest_irq(d, virq)) ) ++ vgic_free_virq(d, virq); + +- return 0; ++ read_unlock(&currd->caps_lock); ++ return rc; + } + + case XEN_DOMCTL_vuart_op: +--- a/xen/arch/x86/domctl.c ++++ b/xen/arch/x86/domctl.c +@@ -267,16 +267,17 @@ long arch_do_domctl( + break; + } + +- ret = -EPERM; ++ iocaps_double_lock(d, true); ++ + if ( !irq_access_permitted(currd, irq) || + xsm_irq_permission(XSM_HOOK, d, irq, flags) ) +- break; +- +- if ( flags ) ++ ret = -EPERM; ++ else if ( flags ) + ret = irq_permit_access(d, irq); + else + ret = irq_deny_access(d, irq); + ++ iocaps_double_unlock(d, true); + break; + } + +@@ -579,20 +580,27 @@ long arch_do_domctl( + break; + + irq = domain_pirq_to_irq(d, bind->machine_irq); +- ret = -EPERM; +- if ( irq <= 0 || !irq_access_permitted(currd, irq) ) +- break; ++ if ( irq <= 0 ) ++ ret = -EPERM; + +- ret = -ESRCH; +- if ( is_iommu_enabled(d) ) ++ read_lock(&currd->caps_lock); ++ ++ if ( !irq_access_permitted(currd, irq) ) ++ ret = -EPERM; ++ else if ( is_iommu_enabled(d) ) + { + pcidevs_lock(); + ret = pt_irq_create_bind(d, bind); + pcidevs_unlock(); ++ ++ if ( ret < 0 ) ++ printk(XENLOG_G_ERR "pt_irq_create_bind failed (%ld) for %pd\n", ++ ret, d); + } +- if ( ret < 0 ) +- printk(XENLOG_G_ERR "pt_irq_create_bind failed (%ld) for dom%d\n", +- ret, d->domain_id); ++ else ++ ret = -ESRCH; ++ ++ read_unlock(&currd->caps_lock); + break; + } + +@@ -605,23 +613,26 @@ long arch_do_domctl( + if ( !is_hvm_domain(d) ) + break; + +- ret = -EPERM; +- if ( irq <= 0 || !irq_access_permitted(currd, irq) ) +- break; +- + ret = xsm_unbind_pt_irq(XSM_HOOK, d, bind); + if ( ret ) + break; + +- if ( is_iommu_enabled(d) ) ++ read_lock(&currd->caps_lock); ++ ++ if ( !irq_access_permitted(currd, irq) ) ++ ret = -EPERM; ++ else if ( is_iommu_enabled(d) ) + { + pcidevs_lock(); + ret = pt_irq_destroy_bind(d, bind); + pcidevs_unlock(); ++ ++ if ( ret < 0 ) ++ printk(XENLOG_G_ERR "pt_irq_destroy_bind failed (%ld) for %pd\n", ++ ret, d); + } +- if ( ret < 0 ) +- printk(XENLOG_G_ERR "pt_irq_destroy_bind failed (%ld) for dom%d\n", +- ret, d->domain_id); ++ ++ read_unlock(&currd->caps_lock); + break; + } + +--- a/xen/common/domctl.c ++++ b/xen/common/domctl.c +@@ -695,6 +695,9 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + ret = -EINVAL; + break; + } ++ ++ iocaps_double_lock(d, true); ++ + irq = pirq_access_permitted(current->domain, pirq); + if ( !irq || xsm_irq_permission(XSM_HOOK, d, irq, allow) ) + ret = -EPERM; +@@ -702,6 +705,8 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + ret = irq_permit_access(d, irq); + else + ret = irq_deny_access(d, irq); ++ ++ iocaps_double_unlock(d, true); + break; + } + #endif diff --git a/xsa492-4.21-08.patch b/xsa492-4.21-08.patch new file mode 100644 index 0000000..4aefbf4 --- /dev/null +++ b/xsa492-4.21-08.patch @@ -0,0 +1,85 @@ +From: Jan Beulich +Subject: XSM/Flask: split the .iomem_mapping() hook + +It's used twice in entirely different situations. The use in do_domctl() +wants to become an ordinary XSM_DM_PRIV invocation, while the one in vPCI +code need to remain XSM_HOOK (it may plausibly become XSM_TARGET). For +Flask, the same backing function will continue to be used for the time +being. + +This is part of XSA-492. + +Signed-off-by: Jan Beulich +Acked-by: Daniel P. Smith + +--- a/xen/drivers/vpci/header.c ++++ b/xen/drivers/vpci/header.c +@@ -67,7 +67,7 @@ static int cf_check map_range( + return -EPERM; + } + +- rc = xsm_iomem_mapping(XSM_HOOK, map->d, map_mfn, m_end, map->map); ++ rc = xsm_iomem_mapping_vpci(XSM_HOOK, map->d, map_mfn, m_end, map->map); + if ( rc ) + { + printk(XENLOG_G_WARNING +--- a/xen/include/xsm/dummy.h ++++ b/xen/include/xsm/dummy.h +@@ -580,6 +580,13 @@ static XSM_INLINE int cf_check xsm_iomem + return xsm_default_action(action, current->domain, d); + } + ++static XSM_INLINE int cf_check xsm_iomem_mapping_vpci( ++ XSM_DEFAULT_ARG struct domain *d, uint64_t s, uint64_t e, uint8_t allow) ++{ ++ XSM_ASSERT_ACTION(XSM_HOOK); ++ return xsm_default_action(action, current->domain, d); ++} ++ + static XSM_INLINE int cf_check xsm_pci_config_permission( + XSM_DEFAULT_ARG struct domain *d, uint32_t machine_bdf, uint16_t start, + uint16_t end, uint8_t access) +--- a/xen/include/xsm/xsm.h ++++ b/xen/include/xsm/xsm.h +@@ -118,6 +118,8 @@ struct xsm_ops { + uint8_t allow); + int (*iomem_mapping)(struct domain *d, uint64_t s, uint64_t e, + uint8_t allow); ++ int (*iomem_mapping_vpci)(struct domain *d, uint64_t s, uint64_t e, ++ uint8_t allow); + int (*pci_config_permission)(struct domain *d, uint32_t machine_bdf, + uint16_t start, uint16_t end, uint8_t access); + +@@ -523,6 +525,12 @@ static inline int xsm_iomem_mapping( + return alternative_call(xsm_ops.iomem_mapping, d, s, e, allow); + } + ++static inline int xsm_iomem_mapping_vpci( ++ xsm_default_t def, struct domain *d, uint64_t s, uint64_t e, uint8_t allow) ++{ ++ return alternative_call(xsm_ops.iomem_mapping_vpci, d, s, e, allow); ++} ++ + static inline int xsm_pci_config_permission( + xsm_default_t def, struct domain *d, uint32_t machine_bdf, uint16_t start, + uint16_t end, uint8_t access) +--- a/xen/xsm/dummy.c ++++ b/xen/xsm/dummy.c +@@ -76,6 +76,7 @@ static const struct xsm_ops __initconst_ + .irq_permission = xsm_irq_permission, + .iomem_permission = xsm_iomem_permission, + .iomem_mapping = xsm_iomem_mapping, ++ .iomem_mapping_vpci = xsm_iomem_mapping_vpci, + .pci_config_permission = xsm_pci_config_permission, + .get_vnumainfo = xsm_get_vnumainfo, + +--- a/xen/xsm/flask/hooks.c ++++ b/xen/xsm/flask/hooks.c +@@ -1950,6 +1950,7 @@ static const struct xsm_ops __initconst_ + .irq_permission = flask_irq_permission, + .iomem_permission = flask_iomem_permission, + .iomem_mapping = flask_iomem_mapping, ++ .iomem_mapping_vpci = flask_iomem_mapping, + .pci_config_permission = flask_pci_config_permission, + + .resource_plug_core = flask_resource_plug_core, diff --git a/xsa492-4.21-09.patch b/xsa492-4.21-09.patch new file mode 100644 index 0000000..96e9403 --- /dev/null +++ b/xsa492-4.21-09.patch @@ -0,0 +1,194 @@ +From: Jan Beulich +Subject: domctl: handle XEN_DOMCTL_memory_mapping without acquiring domctl lock + +With dedicated locking added, the domctl lock isn't required here anymore. +Move the re-purposed dedicated XSM check as early as possible. + +Minimal "modernization": Switch "add" to bool and use %pd in log messages. + +This is part of XSA-492. + +Fixes: fda49f9b3fbb ("Add build option to allow more hypercalls from stubdoms") +Reported-by: Andrew Cooper +Signed-off-by: Jan Beulich +Acked-by: Daniel P. Smith +Reviewed-by: Roger Pau Monné + +--- a/xen/common/domctl.c ++++ b/xen/common/domctl.c +@@ -376,6 +376,66 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + copyback = true; + goto domctl_out_unlock_domonly; + ++ case XEN_DOMCTL_memory_mapping: ++ { ++ unsigned long gfn = op->u.memory_mapping.first_gfn; ++ unsigned long mfn = op->u.memory_mapping.first_mfn; ++ unsigned long nr_mfns = op->u.memory_mapping.nr_mfns; ++ unsigned long mfn_end = mfn + nr_mfns - 1; ++ bool add = op->u.memory_mapping.add_mapping; ++ ++ ret = -EINVAL; ++ if ( mfn_end < mfn || /* Wrap? */ ++ ((mfn | mfn_end) >> (paddr_bits - PAGE_SHIFT)) || ++ (gfn + nr_mfns - 1) < gfn ) /* Wrap? */ ++ goto domctl_out_unlock_domonly; ++ ++ ret = xsm_iomem_mapping(XSM_DM_PRIV, d, mfn, mfn_end, add); ++ if ( ret || !paging_mode_translate(d) ) ++ goto domctl_out_unlock_domonly; ++ ++#ifndef CONFIG_X86 /* XXX ARM!? */ ++ ret = -E2BIG; ++ /* Must break hypercall up as this could take a while. */ ++ if ( nr_mfns > 64 ) ++ goto domctl_out_unlock_domonly; ++#endif ++ ++ iocaps_double_lock(d, false); ++ ++ ret = -EPERM; ++ if ( !iomem_access_permitted(current->domain, mfn, mfn_end) || ++ !iomem_access_permitted(d, mfn, mfn_end) ) ++ /* Nothing. */; ++ else if ( add ) ++ { ++ printk(XENLOG_G_DEBUG ++ "memory_map:add: %pd gfn=%lx mfn=%lx nr=%lx\n", ++ d, gfn, mfn, nr_mfns); ++ ++ ret = map_mmio_regions(d, _gfn(gfn), nr_mfns, _mfn(mfn)); ++ if ( ret < 0 ) ++ printk(XENLOG_G_WARNING ++ "memory_map:fail: %pd gfn=%lx mfn=%lx nr=%lx ret:%ld\n", ++ d, gfn, mfn, nr_mfns, ret); ++ } ++ else ++ { ++ printk(XENLOG_G_DEBUG ++ "memory_map:remove: %pd gfn=%lx mfn=%lx nr=%lx\n", ++ d, gfn, mfn, nr_mfns); ++ ++ ret = unmap_mmio_regions(d, _gfn(gfn), nr_mfns, _mfn(mfn)); ++ if ( ret < 0 && is_hardware_domain(current->domain) ) ++ printk(XENLOG_ERR ++ "memory_map: error %ld removing %pd access to [%lx,%lx]\n", ++ ret, d, mfn, mfn_end); ++ } ++ ++ iocaps_double_unlock(d, false); ++ goto domctl_out_unlock_domonly; ++ } ++ + default: + /* Everything else handled further down. */ + break; +@@ -736,64 +796,6 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + break; + } + +- case XEN_DOMCTL_memory_mapping: +- { +- unsigned long gfn = op->u.memory_mapping.first_gfn; +- unsigned long mfn = op->u.memory_mapping.first_mfn; +- unsigned long nr_mfns = op->u.memory_mapping.nr_mfns; +- unsigned long mfn_end = mfn + nr_mfns - 1; +- int add = op->u.memory_mapping.add_mapping; +- +- ret = -EINVAL; +- if ( mfn_end < mfn || /* wrap? */ +- ((mfn | mfn_end) >> (paddr_bits - PAGE_SHIFT)) || +- (gfn + nr_mfns - 1) < gfn ) /* wrap? */ +- break; +- +-#ifndef CONFIG_X86 /* XXX ARM!? */ +- ret = -E2BIG; +- /* Must break hypercall up as this could take a while. */ +- if ( nr_mfns > 64 ) +- break; +-#endif +- +- iocaps_double_lock(d, false); +- +- ret = -EPERM; +- if ( !iomem_access_permitted(current->domain, mfn, mfn_end) || +- !iomem_access_permitted(d, mfn, mfn_end) || +- (ret = xsm_iomem_mapping(XSM_HOOK, d, mfn, mfn_end, add)) || +- !paging_mode_translate(d) ) +- /* Nothing. */; +- else if ( add ) +- { +- printk(XENLOG_G_DEBUG +- "memory_map:add: dom%d gfn=%lx mfn=%lx nr=%lx\n", +- d->domain_id, gfn, mfn, nr_mfns); +- +- ret = map_mmio_regions(d, _gfn(gfn), nr_mfns, _mfn(mfn)); +- if ( ret < 0 ) +- printk(XENLOG_G_WARNING +- "memory_map:fail: dom%d gfn=%lx mfn=%lx nr=%lx ret:%ld\n", +- d->domain_id, gfn, mfn, nr_mfns, ret); +- } +- else +- { +- printk(XENLOG_G_DEBUG +- "memory_map:remove: dom%d gfn=%lx mfn=%lx nr=%lx\n", +- d->domain_id, gfn, mfn, nr_mfns); +- +- ret = unmap_mmio_regions(d, _gfn(gfn), nr_mfns, _mfn(mfn)); +- if ( ret < 0 && is_hardware_domain(current->domain) ) +- printk(XENLOG_ERR +- "memory_map: error %ld removing dom%d access to [%lx,%lx]\n", +- ret, d->domain_id, mfn, mfn_end); +- } +- +- iocaps_double_unlock(d, false); +- break; +- } +- + case XEN_DOMCTL_settimeoffset: + domain_set_time_offset(d, op->u.settimeoffset.time_offset_seconds); + break; +--- a/xen/include/xsm/dummy.h ++++ b/xen/include/xsm/dummy.h +@@ -168,13 +168,13 @@ static XSM_INLINE int cf_check xsm_domct + switch ( cmd ) + { + case XEN_DOMCTL_ioport_mapping: +- case XEN_DOMCTL_memory_mapping: + case XEN_DOMCTL_bind_pt_irq: + case XEN_DOMCTL_unbind_pt_irq: + return xsm_default_action(XSM_DM_PRIV, current->domain, d); + + case XEN_DOMCTL_getdomaininfo: + case XEN_DOMCTL_get_domain_state: ++ case XEN_DOMCTL_memory_mapping: + ASSERT_UNREACHABLE(); + return -EILSEQ; + +@@ -576,7 +576,7 @@ static XSM_INLINE int cf_check xsm_iomem + static XSM_INLINE int cf_check xsm_iomem_mapping( + XSM_DEFAULT_ARG struct domain *d, uint64_t s, uint64_t e, uint8_t allow) + { +- XSM_ASSERT_ACTION(XSM_HOOK); ++ XSM_ASSERT_ACTION(XSM_DM_PRIV); + return xsm_default_action(action, current->domain, d); + } + +--- a/xen/xsm/flask/hooks.c ++++ b/xen/xsm/flask/hooks.c +@@ -685,6 +685,7 @@ static int cf_check flask_domctl(struct + /* These have individual XSM hooks and don't make it here. */ + case XEN_DOMCTL_getdomaininfo: + case XEN_DOMCTL_get_domain_state: ++ case XEN_DOMCTL_memory_mapping: + ASSERT_UNREACHABLE(); + return -EILSEQ; + +@@ -692,7 +693,6 @@ static int cf_check flask_domctl(struct + case XEN_DOMCTL_scheduler_op: + case XEN_DOMCTL_irq_permission: + case XEN_DOMCTL_iomem_permission: +- case XEN_DOMCTL_memory_mapping: + case XEN_DOMCTL_set_target: + case XEN_DOMCTL_vm_event_op: + diff --git a/xsa492-4.21-10.patch b/xsa492-4.21-10.patch new file mode 100644 index 0000000..6406a19 --- /dev/null +++ b/xsa492-4.21-10.patch @@ -0,0 +1,97 @@ +From: Jan Beulich +Subject: domctl: handle XEN_DOMCTL_ioport_mapping without acquiring domctl lock + +With dedicated locking added, the domctl lock isn't required here anymore. +As the handling is in arch-specific code (x86 only), almost no code is +being moved, but a 2nd (extensible to other sub-ops) invocation of +arch_do_domctl() is being added. Move just the re-purposed dedicated XSM +check as early as possible. + +In flask_domctl() don't put #ifdef around the moved case label. + +This is part of XSA-492. + +Fixes: fda49f9b3fbb ("Add build option to allow more hypercalls from stubdoms") +Reported-by: Andrew Cooper +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné +Acked-by: Daniel P. Smith + +--- a/xen/arch/x86/domctl.c ++++ b/xen/arch/x86/domctl.c +@@ -663,12 +663,15 @@ long arch_do_domctl( + break; + } + ++ ret = xsm_ioport_mapping(XSM_DM_PRIV, d, fmp, fmp + np - 1, add); ++ if ( ret ) ++ break; ++ + hvm = &d->arch.hvm; + iocaps_double_lock(d, true); + +- if ( !ioports_access_permitted(currd, fmp, fmp + np - 1) || +- (ret = xsm_ioport_mapping(XSM_HOOK, d, fmp, fmp + np - 1, add)) ) +- ret = ret ?: -EPERM; ++ if ( !ioports_access_permitted(currd, fmp, fmp + np - 1) ) ++ ret = -EPERM; + else if ( add ) + { + printk(XENLOG_G_INFO +--- a/xen/common/domctl.c ++++ b/xen/common/domctl.c +@@ -436,6 +436,10 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + goto domctl_out_unlock_domonly; + } + ++ case XEN_DOMCTL_ioport_mapping: ++ ret = arch_do_domctl(op, d, u_domctl); ++ goto domctl_out_unlock_domonly; ++ + default: + /* Everything else handled further down. */ + break; +--- a/xen/include/xsm/dummy.h ++++ b/xen/include/xsm/dummy.h +@@ -167,13 +167,13 @@ static XSM_INLINE int cf_check xsm_domct + XSM_ASSERT_ACTION(XSM_OTHER); + switch ( cmd ) + { +- case XEN_DOMCTL_ioport_mapping: + case XEN_DOMCTL_bind_pt_irq: + case XEN_DOMCTL_unbind_pt_irq: + return xsm_default_action(XSM_DM_PRIV, current->domain, d); + + case XEN_DOMCTL_getdomaininfo: + case XEN_DOMCTL_get_domain_state: ++ case XEN_DOMCTL_ioport_mapping: + case XEN_DOMCTL_memory_mapping: + ASSERT_UNREACHABLE(); + return -EILSEQ; +@@ -772,7 +772,7 @@ static XSM_INLINE int cf_check xsm_iopor + static XSM_INLINE int cf_check xsm_ioport_mapping( + XSM_DEFAULT_ARG struct domain *d, uint32_t s, uint32_t e, uint8_t allow) + { +- XSM_ASSERT_ACTION(XSM_HOOK); ++ XSM_ASSERT_ACTION(XSM_DM_PRIV); + return xsm_default_action(action, current->domain, d); + } + +--- a/xen/xsm/flask/hooks.c ++++ b/xen/xsm/flask/hooks.c +@@ -685,6 +685,7 @@ static int cf_check flask_domctl(struct + /* These have individual XSM hooks and don't make it here. */ + case XEN_DOMCTL_getdomaininfo: + case XEN_DOMCTL_get_domain_state: ++ case XEN_DOMCTL_ioport_mapping: + case XEN_DOMCTL_memory_mapping: + ASSERT_UNREACHABLE(); + return -EILSEQ; +@@ -703,7 +704,6 @@ static int cf_check flask_domctl(struct + /* These have individual XSM hooks (arch/x86/domctl.c) */ + case XEN_DOMCTL_shadow_op: + case XEN_DOMCTL_ioport_permission: +- case XEN_DOMCTL_ioport_mapping: + case XEN_DOMCTL_gsi_permission: + #endif + #ifdef CONFIG_HAS_PASSTHROUGH diff --git a/xsa492-4.21-11.patch b/xsa492-4.21-11.patch new file mode 100644 index 0000000..647fd5a --- /dev/null +++ b/xsa492-4.21-11.patch @@ -0,0 +1,128 @@ +From: Jan Beulich +Subject: domctl: handle XEN_DOMCTL_{,un}bind_pt_irq without acquiring domctl lock + +With dedicated locking added, the domctl lock isn't required here anymore. +(It also already isn't used when pt_irq_{create,destroy}_bind() are +invoked for PVH Dom0.) As the handling is in arch-specific code, no code +is being moved, but the 2nd (extensible to other sub-ops like the ones +here) invocation of arch_do_domctl() is being re-used. + +This is part of XSA-492. + +Fixes: fda49f9b3fbb ("Add build option to allow more hypercalls from stubdoms") +Reported-by: Andrew Cooper +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné +Acked-by: Daniel P. Smith +Acked-by: Julien Grall + +--- a/xen/arch/arm/domctl.c ++++ b/xen/arch/arm/domctl.c +@@ -104,7 +104,7 @@ long arch_do_domctl(struct xen_domctl *d + if ( rc ) + return rc; + +- rc = xsm_bind_pt_irq(XSM_HOOK, d, bind); ++ rc = xsm_bind_pt_irq(XSM_DM_PRIV, d, bind); + if ( rc ) + return rc; + +@@ -140,7 +140,7 @@ long arch_do_domctl(struct xen_domctl *d + if ( irq != virq ) + return -EINVAL; + +- rc = xsm_unbind_pt_irq(XSM_HOOK, d, bind); ++ rc = xsm_unbind_pt_irq(XSM_DM_PRIV, d, bind); + if ( rc ) + return rc; + +--- a/xen/arch/x86/domctl.c ++++ b/xen/arch/x86/domctl.c +@@ -575,7 +575,7 @@ long arch_do_domctl( + if ( !is_hvm_domain(d) ) + break; + +- ret = xsm_bind_pt_irq(XSM_HOOK, d, bind); ++ ret = xsm_bind_pt_irq(XSM_DM_PRIV, d, bind); + if ( ret ) + break; + +@@ -613,7 +613,7 @@ long arch_do_domctl( + if ( !is_hvm_domain(d) ) + break; + +- ret = xsm_unbind_pt_irq(XSM_HOOK, d, bind); ++ ret = xsm_unbind_pt_irq(XSM_DM_PRIV, d, bind); + if ( ret ) + break; + +--- a/xen/common/domctl.c ++++ b/xen/common/domctl.c +@@ -437,6 +437,8 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + } + + case XEN_DOMCTL_ioport_mapping: ++ case XEN_DOMCTL_bind_pt_irq: ++ case XEN_DOMCTL_unbind_pt_irq: + ret = arch_do_domctl(op, d, u_domctl); + goto domctl_out_unlock_domonly; + +--- a/xen/include/xsm/dummy.h ++++ b/xen/include/xsm/dummy.h +@@ -168,13 +168,11 @@ static XSM_INLINE int cf_check xsm_domct + switch ( cmd ) + { + case XEN_DOMCTL_bind_pt_irq: +- case XEN_DOMCTL_unbind_pt_irq: +- return xsm_default_action(XSM_DM_PRIV, current->domain, d); +- + case XEN_DOMCTL_getdomaininfo: + case XEN_DOMCTL_get_domain_state: + case XEN_DOMCTL_ioport_mapping: + case XEN_DOMCTL_memory_mapping: ++ case XEN_DOMCTL_unbind_pt_irq: + ASSERT_UNREACHABLE(); + return -EILSEQ; + +@@ -541,14 +539,14 @@ static XSM_INLINE int cf_check xsm_unmap + static XSM_INLINE int cf_check xsm_bind_pt_irq( + XSM_DEFAULT_ARG struct domain *d, struct xen_domctl_bind_pt_irq *bind) + { +- XSM_ASSERT_ACTION(XSM_HOOK); ++ XSM_ASSERT_ACTION(XSM_DM_PRIV); + return xsm_default_action(action, current->domain, d); + } + + static XSM_INLINE int cf_check xsm_unbind_pt_irq( + XSM_DEFAULT_ARG struct domain *d, struct xen_domctl_bind_pt_irq *bind) + { +- XSM_ASSERT_ACTION(XSM_HOOK); ++ XSM_ASSERT_ACTION(XSM_DM_PRIV); + return xsm_default_action(action, current->domain, d); + } + +--- a/xen/xsm/flask/hooks.c ++++ b/xen/xsm/flask/hooks.c +@@ -683,10 +683,12 @@ static int cf_check flask_domctl(struct + return avc_current_has_perm(ssidref, SECCLASS_DOMAIN, DOMAIN__CREATE, NULL); + + /* These have individual XSM hooks and don't make it here. */ ++ case XEN_DOMCTL_bind_pt_irq: + case XEN_DOMCTL_getdomaininfo: + case XEN_DOMCTL_get_domain_state: + case XEN_DOMCTL_ioport_mapping: + case XEN_DOMCTL_memory_mapping: ++ case XEN_DOMCTL_unbind_pt_irq: + ASSERT_UNREACHABLE(); + return -EILSEQ; + +@@ -697,9 +699,6 @@ static int cf_check flask_domctl(struct + case XEN_DOMCTL_set_target: + case XEN_DOMCTL_vm_event_op: + +- /* These have individual XSM hooks (arch/../domctl.c) */ +- case XEN_DOMCTL_bind_pt_irq: +- case XEN_DOMCTL_unbind_pt_irq: + #ifdef CONFIG_X86 + /* These have individual XSM hooks (arch/x86/domctl.c) */ + case XEN_DOMCTL_shadow_op: diff --git a/xsa492-4.21-12.patch b/xsa492-4.21-12.patch new file mode 100644 index 0000000..c19d1e1 --- /dev/null +++ b/xsa492-4.21-12.patch @@ -0,0 +1,172 @@ +From: Jan Beulich +Subject: domctl: handle XEN_DOMCTL_io{mem,port}_permission without acquiring domctl lock + +With dedicated locking added, the domctl lock isn't required here anymore. +As the I/O port handling is in arch-specific code (x86 only), no code is +being moved, but the 2nd invocation of arch_do_domctl() is re-used. Move +the re-purposed dedicated XSM checks as early as possible. + +This is part of XSA-492. + +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné +Acked-by: Daniel P. Smith + +--- a/xen/arch/x86/domctl.c ++++ b/xen/arch/x86/domctl.c +@@ -233,12 +233,17 @@ long arch_do_domctl( + unsigned int np = domctl->u.ioport_permission.nr_ports; + int allow = domctl->u.ioport_permission.allow_access; + ++ ret = -EINVAL; ++ if ( (fp + np) <= fp || (fp + np) > MAX_IOPORTS ) ++ break; ++ ++ ret = xsm_ioport_permission(XSM_PRIV, d, fp, fp + np - 1, allow); ++ if ( ret ) ++ break; ++ + iocaps_double_lock(d, true); + +- if ( (fp + np) <= fp || (fp + np) > MAX_IOPORTS ) +- ret = -EINVAL; +- else if ( !ioports_access_permitted(currd, fp, fp + np - 1) || +- xsm_ioport_permission(XSM_HOOK, d, fp, fp + np - 1, allow) ) ++ if ( !ioports_access_permitted(currd, fp, fp + np - 1) ) + ret = -EPERM; + else if ( allow ) + ret = ioports_permit_access(d, fp, fp + np - 1); +--- a/xen/common/domctl.c ++++ b/xen/common/domctl.c +@@ -376,6 +376,34 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + copyback = true; + goto domctl_out_unlock_domonly; + ++ case XEN_DOMCTL_iomem_permission: ++ { ++ unsigned long mfn = op->u.iomem_permission.first_mfn; ++ unsigned long nr_mfns = op->u.iomem_permission.nr_mfns; ++ bool allow = op->u.iomem_permission.allow_access; ++ ++ ret = -EINVAL; ++ if ( (mfn + nr_mfns - 1) < mfn ) /* Wrap? */ ++ goto domctl_out_unlock_domonly; ++ ++ ret = xsm_iomem_permission(XSM_PRIV, d, mfn, mfn + nr_mfns - 1, allow); ++ if ( ret ) ++ goto domctl_out_unlock_domonly; ++ ++ iocaps_double_lock(d, true); ++ ++ if ( !iomem_access_permitted(current->domain, ++ mfn, mfn + nr_mfns - 1) ) ++ ret = -EPERM; ++ else if ( allow ) ++ ret = iomem_permit_access(d, mfn, mfn + nr_mfns - 1); ++ else ++ ret = iomem_deny_access(d, mfn, mfn + nr_mfns - 1); ++ ++ iocaps_double_unlock(d, true); ++ goto domctl_out_unlock_domonly; ++ } ++ + case XEN_DOMCTL_memory_mapping: + { + unsigned long gfn = op->u.memory_mapping.first_gfn; +@@ -436,6 +464,7 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + goto domctl_out_unlock_domonly; + } + ++ case XEN_DOMCTL_ioport_permission: + case XEN_DOMCTL_ioport_mapping: + case XEN_DOMCTL_bind_pt_irq: + case XEN_DOMCTL_unbind_pt_irq: +@@ -777,31 +806,6 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + } + #endif + +- case XEN_DOMCTL_iomem_permission: +- { +- unsigned long mfn = op->u.iomem_permission.first_mfn; +- unsigned long nr_mfns = op->u.iomem_permission.nr_mfns; +- int allow = op->u.iomem_permission.allow_access; +- +- ret = -EINVAL; +- if ( (mfn + nr_mfns - 1) < mfn ) /* wrap? */ +- break; +- +- iocaps_double_lock(d, true); +- +- if ( !iomem_access_permitted(current->domain, +- mfn, mfn + nr_mfns - 1) || +- xsm_iomem_permission(XSM_HOOK, d, mfn, mfn + nr_mfns - 1, allow) ) +- ret = -EPERM; +- else if ( allow ) +- ret = iomem_permit_access(d, mfn, mfn + nr_mfns - 1); +- else +- ret = iomem_deny_access(d, mfn, mfn + nr_mfns - 1); +- +- iocaps_double_unlock(d, true); +- break; +- } +- + case XEN_DOMCTL_settimeoffset: + domain_set_time_offset(d, op->u.settimeoffset.time_offset_seconds); + break; +--- a/xen/include/xsm/dummy.h ++++ b/xen/include/xsm/dummy.h +@@ -170,7 +170,9 @@ static XSM_INLINE int cf_check xsm_domct + case XEN_DOMCTL_bind_pt_irq: + case XEN_DOMCTL_getdomaininfo: + case XEN_DOMCTL_get_domain_state: ++ case XEN_DOMCTL_iomem_permission: + case XEN_DOMCTL_ioport_mapping: ++ case XEN_DOMCTL_ioport_permission: + case XEN_DOMCTL_memory_mapping: + case XEN_DOMCTL_unbind_pt_irq: + ASSERT_UNREACHABLE(); +@@ -567,7 +569,7 @@ static XSM_INLINE int cf_check xsm_irq_p + static XSM_INLINE int cf_check xsm_iomem_permission( + XSM_DEFAULT_ARG struct domain *d, uint64_t s, uint64_t e, uint8_t allow) + { +- XSM_ASSERT_ACTION(XSM_HOOK); ++ XSM_ASSERT_ACTION(XSM_PRIV); + return xsm_default_action(action, current->domain, d); + } + +@@ -763,7 +765,7 @@ static XSM_INLINE int cf_check xsm_priv_ + static XSM_INLINE int cf_check xsm_ioport_permission( + XSM_DEFAULT_ARG struct domain *d, uint32_t s, uint32_t e, uint8_t allow) + { +- XSM_ASSERT_ACTION(XSM_HOOK); ++ XSM_ASSERT_ACTION(XSM_PRIV); + return xsm_default_action(action, current->domain, d); + } + +--- a/xen/xsm/flask/hooks.c ++++ b/xen/xsm/flask/hooks.c +@@ -686,7 +686,9 @@ static int cf_check flask_domctl(struct + case XEN_DOMCTL_bind_pt_irq: + case XEN_DOMCTL_getdomaininfo: + case XEN_DOMCTL_get_domain_state: ++ case XEN_DOMCTL_iomem_permission: + case XEN_DOMCTL_ioport_mapping: ++ case XEN_DOMCTL_ioport_permission: + case XEN_DOMCTL_memory_mapping: + case XEN_DOMCTL_unbind_pt_irq: + ASSERT_UNREACHABLE(); +@@ -695,14 +697,12 @@ static int cf_check flask_domctl(struct + /* These have individual XSM hooks (common/domctl.c) */ + case XEN_DOMCTL_scheduler_op: + case XEN_DOMCTL_irq_permission: +- case XEN_DOMCTL_iomem_permission: + case XEN_DOMCTL_set_target: + case XEN_DOMCTL_vm_event_op: + + #ifdef CONFIG_X86 + /* These have individual XSM hooks (arch/x86/domctl.c) */ + case XEN_DOMCTL_shadow_op: +- case XEN_DOMCTL_ioport_permission: + case XEN_DOMCTL_gsi_permission: + #endif + #ifdef CONFIG_HAS_PASSTHROUGH diff --git a/xsa492-4.21-13.patch b/xsa492-4.21-13.patch new file mode 100644 index 0000000..91ce1ae --- /dev/null +++ b/xsa492-4.21-13.patch @@ -0,0 +1,163 @@ +From: Jan Beulich +Subject: domctl: handle XEN_DOMCTL_{irq,gsi}_permission without acquiring domctl lock + +With dedicated locking added, the domctl lock isn't required here anymore. +As the GSI handling is in arch-specific code (x86 only), no code is being +moved there; the 2nd invocation of arch_do_domctl() is re-used. Move the +re-purposed (XSM_HOOK -> XSM_PRIV, as xsm_domctl() is now bypassed) +dedicated XSM checks as early as possible. + +This is part of XSA-492. + +Signed-off-by: Jan Beulich +Acked-by: Daniel P. Smith +Reviewed-by: Roger Pau Monné + +--- a/xen/arch/x86/domctl.c ++++ b/xen/arch/x86/domctl.c +@@ -272,10 +272,13 @@ long arch_do_domctl( + break; + } + ++ ret = xsm_irq_permission(XSM_PRIV, d, irq, flags); ++ if ( ret ) ++ break; ++ + iocaps_double_lock(d, true); + +- if ( !irq_access_permitted(currd, irq) || +- xsm_irq_permission(XSM_HOOK, d, irq, flags) ) ++ if ( !irq_access_permitted(currd, irq) ) + ret = -EPERM; + else if ( flags ) + ret = irq_permit_access(d, irq); +--- a/xen/common/domctl.c ++++ b/xen/common/domctl.c +@@ -464,8 +464,41 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + goto domctl_out_unlock_domonly; + } + ++#ifdef CONFIG_HAS_PIRQ ++ case XEN_DOMCTL_irq_permission: ++ { ++ unsigned int pirq = op->u.irq_permission.pirq, irq; ++ bool allow = op->u.irq_permission.allow_access; ++ ++ ret = -EINVAL; ++ if ( pirq >= current->domain->nr_pirqs ) ++ goto domctl_out_unlock_domonly; ++ ++ irq = domain_pirq_to_irq(current->domain, pirq); ++ ++ ret = -EPERM; ++ if ( irq ) ++ ret = xsm_irq_permission(XSM_PRIV, d, irq, allow); ++ if ( ret ) ++ goto domctl_out_unlock_domonly; ++ ++ iocaps_double_lock(d, true); ++ ++ if ( !irq_access_permitted(current->domain, irq) ) ++ ret = -EPERM; ++ else if ( allow ) ++ ret = irq_permit_access(d, irq); ++ else ++ ret = irq_deny_access(d, irq); ++ ++ iocaps_double_unlock(d, true); ++ goto domctl_out_unlock_domonly; ++ } ++#endif ++ + case XEN_DOMCTL_ioport_permission: + case XEN_DOMCTL_ioport_mapping: ++ case XEN_DOMCTL_gsi_permission: + case XEN_DOMCTL_bind_pt_irq: + case XEN_DOMCTL_unbind_pt_irq: + ret = arch_do_domctl(op, d, u_domctl); +@@ -779,33 +812,6 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + } + break; + +-#ifdef CONFIG_HAS_PIRQ +- case XEN_DOMCTL_irq_permission: +- { +- unsigned int pirq = op->u.irq_permission.pirq, irq; +- int allow = op->u.irq_permission.allow_access; +- +- if ( pirq >= current->domain->nr_pirqs ) +- { +- ret = -EINVAL; +- break; +- } +- +- iocaps_double_lock(d, true); +- +- irq = pirq_access_permitted(current->domain, pirq); +- if ( !irq || xsm_irq_permission(XSM_HOOK, d, irq, allow) ) +- ret = -EPERM; +- else if ( allow ) +- ret = irq_permit_access(d, irq); +- else +- ret = irq_deny_access(d, irq); +- +- iocaps_double_unlock(d, true); +- break; +- } +-#endif +- + case XEN_DOMCTL_settimeoffset: + domain_set_time_offset(d, op->u.settimeoffset.time_offset_seconds); + break; +--- a/xen/include/xsm/dummy.h ++++ b/xen/include/xsm/dummy.h +@@ -170,9 +170,11 @@ static XSM_INLINE int cf_check xsm_domct + case XEN_DOMCTL_bind_pt_irq: + case XEN_DOMCTL_getdomaininfo: + case XEN_DOMCTL_get_domain_state: ++ case XEN_DOMCTL_gsi_permission: + case XEN_DOMCTL_iomem_permission: + case XEN_DOMCTL_ioport_mapping: + case XEN_DOMCTL_ioport_permission: ++ case XEN_DOMCTL_irq_permission: + case XEN_DOMCTL_memory_mapping: + case XEN_DOMCTL_unbind_pt_irq: + ASSERT_UNREACHABLE(); +@@ -562,7 +564,7 @@ static XSM_INLINE int cf_check xsm_unmap + static XSM_INLINE int cf_check xsm_irq_permission( + XSM_DEFAULT_ARG struct domain *d, int pirq, uint8_t allow) + { +- XSM_ASSERT_ACTION(XSM_HOOK); ++ XSM_ASSERT_ACTION(XSM_PRIV); + return xsm_default_action(action, current->domain, d); + } + +--- a/xen/xsm/flask/hooks.c ++++ b/xen/xsm/flask/hooks.c +@@ -686,9 +686,11 @@ static int cf_check flask_domctl(struct + case XEN_DOMCTL_bind_pt_irq: + case XEN_DOMCTL_getdomaininfo: + case XEN_DOMCTL_get_domain_state: ++ case XEN_DOMCTL_gsi_permission: + case XEN_DOMCTL_iomem_permission: + case XEN_DOMCTL_ioport_mapping: + case XEN_DOMCTL_ioport_permission: ++ case XEN_DOMCTL_irq_permission: + case XEN_DOMCTL_memory_mapping: + case XEN_DOMCTL_unbind_pt_irq: + ASSERT_UNREACHABLE(); +@@ -696,14 +698,12 @@ static int cf_check flask_domctl(struct + + /* These have individual XSM hooks (common/domctl.c) */ + case XEN_DOMCTL_scheduler_op: +- case XEN_DOMCTL_irq_permission: + case XEN_DOMCTL_set_target: + case XEN_DOMCTL_vm_event_op: + + #ifdef CONFIG_X86 + /* These have individual XSM hooks (arch/x86/domctl.c) */ + case XEN_DOMCTL_shadow_op: +- case XEN_DOMCTL_gsi_permission: + #endif + #ifdef CONFIG_HAS_PASSTHROUGH + /* diff --git a/xsa492-4.21-14.patch b/xsa492-4.21-14.patch new file mode 100644 index 0000000..2b13377 --- /dev/null +++ b/xsa492-4.21-14.patch @@ -0,0 +1,179 @@ +From: Jan Beulich +Subject: domctl/XSM: drop vm_event_control hook + +Integrate the checking with xsm_domctl(). Care needs to be taken with the +GET_VERSION sub-op, which may be invoked with DOMID_INVALID, and which has +been (and continues to be) bypassing XSM checking. + +Since the latter two parameters were unused, monitor_domctl() invoking the +hook was actually redundant with the earlier xsm_domctl() (as can be seen +nicely from the hunks changing xsm/flask/hooks.c). + +As a positive side effect, permissions are then checked at the same early +point with and without Flask. + +While folding XEN_DOMCTL_monitor_op and XEN_DOMCTL_vm_event_op in +flask_domctl(), also fold in XEN_DOMCTL_set_access_required. + +This is part of XSA-492. + +Signed-off-by: Jan Beulich +Acked-by: Daniel P. Smith +Reviewed-by: Roger Pau Monné + +--- a/xen/common/domctl.c ++++ b/xen/common/domctl.c +@@ -496,6 +496,23 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + } + #endif + ++ case XEN_DOMCTL_vm_event_op: ++ if ( op->u.vm_event_op.op == XEN_VM_EVENT_GET_VERSION ) ++ { ++ /* No XSM check (and potentially d == NULL) here. */ ++ ret = vm_event_domctl(d, &op->u.vm_event_op); ++ if ( !ret ) ++ copyback = true; ++ goto domctl_out_unlock_domonly; ++ } ++ if ( !d ) ++ { ++ ret = -ESRCH; ++ goto domctl_out_unlock_domonly; ++ } ++ /* Other sub-ops handled further down. */ ++ break; ++ + case XEN_DOMCTL_ioport_permission: + case XEN_DOMCTL_ioport_mapping: + case XEN_DOMCTL_gsi_permission: +--- a/xen/common/monitor.c ++++ b/xen/common/monitor.c +@@ -30,16 +30,11 @@ + + int monitor_domctl(struct domain *d, struct xen_domctl_monitor_op *mop) + { +- int rc; + bool requested_status = false; + + if ( unlikely(current->domain == d) ) /* no domain_pause() */ + return -EPERM; + +- rc = xsm_vm_event_control(XSM_PRIV, d, mop->op, mop->event); +- if ( unlikely(rc) ) +- return rc; +- + switch ( mop->op ) + { + case XEN_DOMCTL_MONITOR_OP_ENABLE: +--- a/xen/common/vm_event.c ++++ b/xen/common/vm_event.c +@@ -603,11 +603,10 @@ int vm_event_domctl(struct domain *d, st + + /* All other subops need to target a real domain. */ + if ( unlikely(d == NULL) ) +- return -ESRCH; +- +- rc = xsm_vm_event_control(XSM_PRIV, d, vec->mode, vec->op); +- if ( rc ) +- return rc; ++ { ++ ASSERT_UNREACHABLE(); ++ return -EILSEQ; ++ } + + if ( unlikely(d == current->domain) ) /* no domain_pause() */ + { +--- a/xen/include/xsm/dummy.h ++++ b/xen/include/xsm/dummy.h +@@ -652,13 +652,6 @@ static XSM_INLINE int cf_check xsm_hvm_a + } + } + +-static XSM_INLINE int cf_check xsm_vm_event_control( +- XSM_DEFAULT_ARG struct domain *d, int mode, int op) +-{ +- XSM_ASSERT_ACTION(XSM_PRIV); +- return xsm_default_action(action, current->domain, d); +-} +- + #ifdef CONFIG_VM_EVENT + static XSM_INLINE int cf_check xsm_mem_access(XSM_DEFAULT_ARG struct domain *d) + { +--- a/xen/include/xsm/xsm.h ++++ b/xen/include/xsm/xsm.h +@@ -157,8 +157,6 @@ struct xsm_ops { + int (*hvm_altp2mhvm_op)(struct domain *d, uint64_t mode, uint32_t op); + int (*get_vnumainfo)(struct domain *d); + +- int (*vm_event_control)(struct domain *d, int mode, int op); +- + #ifdef CONFIG_VM_EVENT + int (*mem_access)(struct domain *d); + #endif +@@ -657,12 +655,6 @@ static inline int xsm_get_vnumainfo(xsm_ + return alternative_call(xsm_ops.get_vnumainfo, d); + } + +-static inline int xsm_vm_event_control( +- xsm_default_t def, struct domain *d, int mode, int op) +-{ +- return alternative_call(xsm_ops.vm_event_control, d, mode, op); +-} +- + #ifdef CONFIG_VM_EVENT + static inline int xsm_mem_access(xsm_default_t def, struct domain *d) + { +--- a/xen/xsm/dummy.c ++++ b/xen/xsm/dummy.c +@@ -116,8 +116,6 @@ static const struct xsm_ops __initconst_ + .remove_from_physmap = xsm_remove_from_physmap, + .map_gmfn_foreign = xsm_map_gmfn_foreign, + +- .vm_event_control = xsm_vm_event_control, +- + #ifdef CONFIG_VM_EVENT + .mem_access = xsm_mem_access, + #endif +--- a/xen/xsm/flask/hooks.c ++++ b/xen/xsm/flask/hooks.c +@@ -699,7 +699,6 @@ static int cf_check flask_domctl(struct + /* These have individual XSM hooks (common/domctl.c) */ + case XEN_DOMCTL_scheduler_op: + case XEN_DOMCTL_set_target: +- case XEN_DOMCTL_vm_event_op: + + #ifdef CONFIG_X86 + /* These have individual XSM hooks (arch/x86/domctl.c) */ +@@ -793,9 +792,8 @@ static int cf_check flask_domctl(struct + return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__TRIGGER); + + case XEN_DOMCTL_set_access_required: +- return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__VM_EVENT); +- + case XEN_DOMCTL_monitor_op: ++ case XEN_DOMCTL_vm_event_op: + return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__VM_EVENT); + + case XEN_DOMCTL_debug_op: +@@ -1368,11 +1366,6 @@ static int cf_check flask_hvm_altp2mhvm_ + return current_has_perm(d, SECCLASS_HVM, HVM__ALTP2MHVM_OP); + } + +-static int cf_check flask_vm_event_control(struct domain *d, int mode, int op) +-{ +- return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__VM_EVENT); +-} +- + #ifdef CONFIG_VM_EVENT + static int cf_check flask_mem_access(struct domain *d) + { +@@ -1971,8 +1964,6 @@ static const struct xsm_ops __initconst_ + .do_xsm_op = do_flask_op, + .get_vnumainfo = flask_get_vnumainfo, + +- .vm_event_control = flask_vm_event_control, +- + #ifdef CONFIG_VM_EVENT + .mem_access = flask_mem_access, + #endif diff --git a/xsa492-4.21-15.patch b/xsa492-4.21-15.patch new file mode 100644 index 0000000..ac87f3b --- /dev/null +++ b/xsa492-4.21-15.patch @@ -0,0 +1,108 @@ +From: Jan Beulich +Subject: domctl/XSM: pass full struct xen_domctl to xsm_domctl() + +Subsequently some sub-ops will want to inspect their sub-sub-ops. Plus +this way we don't need to pass SSIDref separately anymore for +domain_create. + +This is part of XSA-492. + +Signed-off-by: Jan Beulich +Acked-by: Daniel P. Smith + +--- a/xen/arch/x86/mm/paging.c ++++ b/xen/arch/x86/mm/paging.c +@@ -735,7 +735,7 @@ long do_paging_domctl_cont( + if ( d == NULL ) + return -ESRCH; + +- ret = xsm_domctl(XSM_OTHER, d, op.cmd, 0 /* SSIDref not applicable */); ++ ret = xsm_domctl(XSM_OTHER, d, &op); + if ( !ret ) + { + if ( domctl_lock_acquire() ) +--- a/xen/common/domctl.c ++++ b/xen/common/domctl.c +@@ -526,9 +526,7 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + break; + } + +- ret = xsm_domctl(XSM_OTHER, d, op->cmd, +- /* SSIDRef only applicable for cmd == createdomain */ +- op->u.createdomain.ssidref); ++ ret = xsm_domctl(XSM_OTHER, d, op); + if ( ret ) + goto domctl_out_unlock_domonly; + +--- a/xen/include/xsm/dummy.h ++++ b/xen/include/xsm/dummy.h +@@ -162,10 +162,10 @@ static XSM_INLINE int cf_check xsm_set_t + } + + static XSM_INLINE int cf_check xsm_domctl( +- XSM_DEFAULT_ARG struct domain *d, unsigned int cmd, uint32_t ssidref) ++ XSM_DEFAULT_ARG struct domain *d, struct xen_domctl *op) + { + XSM_ASSERT_ACTION(XSM_OTHER); +- switch ( cmd ) ++ switch ( op->cmd ) + { + case XEN_DOMCTL_bind_pt_irq: + case XEN_DOMCTL_getdomaininfo: +--- a/xen/include/xsm/xsm.h ++++ b/xen/include/xsm/xsm.h +@@ -61,7 +61,7 @@ struct xsm_ops { + int (*sysctl_scheduler_op)(int op); + #endif + int (*set_target)(struct domain *d, struct domain *e); +- int (*domctl)(struct domain *d, unsigned int cmd, uint32_t ssidref); ++ int (*domctl)(struct domain *d, struct xen_domctl *op); + int (*sysctl)(int cmd); + int (*readconsole)(uint32_t clear); + +@@ -260,9 +260,9 @@ static inline int xsm_set_target( + } + + static inline int xsm_domctl(xsm_default_t def, struct domain *d, +- unsigned int cmd, uint32_t ssidref) ++ struct xen_domctl *op) + { +- return alternative_call(xsm_ops.domctl, d, cmd, ssidref); ++ return alternative_call(xsm_ops.domctl, d, op); + } + + static inline int xsm_sysctl(xsm_default_t def, int cmd) +--- a/xen/xsm/flask/hooks.c ++++ b/xen/xsm/flask/hooks.c +@@ -667,10 +667,9 @@ static int cf_check flask_set_target(str + return rc; + } + +-static int cf_check flask_domctl(struct domain *d, unsigned int cmd, +- uint32_t ssidref) ++static int cf_check flask_domctl(struct domain *d, struct xen_domctl *op) + { +- switch ( cmd ) ++ switch ( op->cmd ) + { + case XEN_DOMCTL_createdomain: + /* +@@ -680,7 +679,8 @@ static int cf_check flask_domctl(struct + * Note that d is NULL because we haven't even allocated memory for it + * this early in XEN_DOMCTL_createdomain. + */ +- return avc_current_has_perm(ssidref, SECCLASS_DOMAIN, DOMAIN__CREATE, NULL); ++ return avc_current_has_perm(op->u.createdomain.ssidref, SECCLASS_DOMAIN, ++ DOMAIN__CREATE, NULL); + + /* These have individual XSM hooks and don't make it here. */ + case XEN_DOMCTL_bind_pt_irq: +@@ -855,7 +855,7 @@ static int cf_check flask_domctl(struct + return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__SET_LLC_COLORS); + + default: +- return avc_unknown_permission("domctl", cmd); ++ return avc_unknown_permission("domctl", op->cmd); + } + } + diff --git a/xsa492-4.21-16.patch b/xsa492-4.21-16.patch new file mode 100644 index 0000000..2cb8619 --- /dev/null +++ b/xsa492-4.21-16.patch @@ -0,0 +1,112 @@ +From: Jan Beulich +Subject: domctl/XSM: drop scheduler_op hook + +Integrate the checking with xsm_domctl(), now that it has the full op +struct passed. As a positive side effect, permissions are then checked at +the same early point with and without Flask. + +This is part of XSA-492. + +Signed-off-by: Jan Beulich +Acked-by: Daniel P. Smith +Reviewed-by: Juergen Gross + +--- a/xen/common/sched/core.c ++++ b/xen/common/sched/core.c +@@ -2074,10 +2074,6 @@ long sched_adjust(struct domain *d, stru + { + long ret; + +- ret = xsm_domctl_scheduler_op(XSM_HOOK, d, op->cmd); +- if ( ret ) +- return ret; +- + if ( op->sched_id != dom_scheduler(d)->sched_id ) + return -EINVAL; + +--- a/xen/include/xsm/dummy.h ++++ b/xen/include/xsm/dummy.h +@@ -141,13 +141,6 @@ static XSM_INLINE int cf_check xsm_getdo + return xsm_default_action(action, current->domain, d); + } + +-static XSM_INLINE int cf_check xsm_domctl_scheduler_op( +- XSM_DEFAULT_ARG struct domain *d, int cmd) +-{ +- XSM_ASSERT_ACTION(XSM_HOOK); +- return xsm_default_action(action, current->domain, d); +-} +- + static XSM_INLINE int cf_check xsm_sysctl_scheduler_op(XSM_DEFAULT_ARG int cmd) + { + XSM_ASSERT_ACTION(XSM_HOOK); +--- a/xen/include/xsm/xsm.h ++++ b/xen/include/xsm/xsm.h +@@ -56,7 +56,6 @@ struct xsm_ops { + struct xen_domctl_getdomaininfo *info); + int (*domain_create)(struct domain *d, uint32_t ssidref); + int (*getdomaininfo)(struct domain *d); +- int (*domctl_scheduler_op)(struct domain *d, int op); + #ifdef CONFIG_SYSCTL + int (*sysctl_scheduler_op)(int op); + #endif +@@ -240,12 +239,6 @@ static inline int xsm_get_domain_state(x + return alternative_call(xsm_ops.get_domain_state, d); + } + +-static inline int xsm_domctl_scheduler_op( +- xsm_default_t def, struct domain *d, int cmd) +-{ +- return alternative_call(xsm_ops.domctl_scheduler_op, d, cmd); +-} +- + #ifdef CONFIG_SYSCTL + static inline int xsm_sysctl_scheduler_op(xsm_default_t def, int cmd) + { +--- a/xen/xsm/dummy.c ++++ b/xen/xsm/dummy.c +@@ -18,7 +18,6 @@ static const struct xsm_ops __initconst_ + .security_domaininfo = xsm_security_domaininfo, + .domain_create = xsm_domain_create, + .getdomaininfo = xsm_getdomaininfo, +- .domctl_scheduler_op = xsm_domctl_scheduler_op, + #ifdef CONFIG_SYSCTL + .sysctl_scheduler_op = xsm_sysctl_scheduler_op, + #endif +--- a/xen/xsm/flask/hooks.c ++++ b/xen/xsm/flask/hooks.c +@@ -609,7 +609,7 @@ static int cf_check flask_getdomaininfo( + return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__GETDOMAININFO); + } + +-static int cf_check flask_domctl_scheduler_op(struct domain *d, int op) ++static int flask_domctl_scheduler_op(struct domain *d, int op) + { + switch ( op ) + { +@@ -697,7 +697,6 @@ static int cf_check flask_domctl(struct + return -EILSEQ; + + /* These have individual XSM hooks (common/domctl.c) */ +- case XEN_DOMCTL_scheduler_op: + case XEN_DOMCTL_set_target: + + #ifdef CONFIG_X86 +@@ -745,6 +744,9 @@ static int cf_check flask_domctl(struct + case XEN_DOMCTL_setdomainhandle: + return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__SETDOMAINHANDLE); + ++ case XEN_DOMCTL_scheduler_op: ++ return flask_domctl_scheduler_op(d, op->u.scheduler_op.cmd); ++ + case XEN_DOMCTL_set_ext_vcpucontext: + case XEN_DOMCTL_set_vcpu_msrs: + case XEN_DOMCTL_setvcpucontext: +@@ -1884,7 +1886,6 @@ static const struct xsm_ops __initconst_ + .security_domaininfo = flask_security_domaininfo, + .domain_create = flask_domain_create, + .getdomaininfo = flask_getdomaininfo, +- .domctl_scheduler_op = flask_domctl_scheduler_op, + #ifdef CONFIG_SYSCTL + .sysctl_scheduler_op = flask_sysctl_scheduler_op, + #endif diff --git a/xsa492-4.21-17.patch b/xsa492-4.21-17.patch new file mode 100644 index 0000000..99542df --- /dev/null +++ b/xsa492-4.21-17.patch @@ -0,0 +1,124 @@ +From: Jan Beulich +Subject: domctl/XSM: drop shadow_control_op hook + +Integrate the checking with xsm_domctl(), now that it has the full op +struct passed. As a positive side effect, permissions are then checked at +the same early point with and without Flask. + +This is part of XSA-492. + +Signed-off-by: Jan Beulich +Acked-by: Daniel P. Smith + +--- a/xen/arch/x86/mm/paging.c ++++ b/xen/arch/x86/mm/paging.c +@@ -677,10 +677,6 @@ int paging_domctl(struct domain *d, stru + return -EBUSY; + } + +- rc = xsm_shadow_control(XSM_HOOK, d, sc->op); +- if ( rc ) +- return rc; +- + /* Code to handle log-dirty. Note that some log dirty operations + * piggy-back on shadow operations. For example, when + * XEN_DOMCTL_SHADOW_OP_OFF is called, it first checks whether log dirty +--- a/xen/include/xsm/dummy.h ++++ b/xen/include/xsm/dummy.h +@@ -682,13 +682,6 @@ static XSM_INLINE int cf_check xsm_do_mc + return xsm_default_action(action, current->domain, NULL); + } + +-static XSM_INLINE int cf_check xsm_shadow_control( +- XSM_DEFAULT_ARG struct domain *d, uint32_t op) +-{ +- XSM_ASSERT_ACTION(XSM_HOOK); +- return xsm_default_action(action, current->domain, d); +-} +- + static XSM_INLINE int cf_check xsm_mem_sharing_op( + XSM_DEFAULT_ARG struct domain *d, struct domain *cd, int op) + { +--- a/xen/include/xsm/xsm.h ++++ b/xen/include/xsm/xsm.h +@@ -172,7 +172,6 @@ struct xsm_ops { + + #ifdef CONFIG_X86 + int (*do_mca)(void); +- int (*shadow_control)(struct domain *d, uint32_t op); + int (*mem_sharing_op)(struct domain *d, struct domain *cd, int op); + int (*apic)(struct domain *d, int cmd); + int (*machine_memory_map)(void); +@@ -680,12 +679,6 @@ static inline int xsm_do_mca(xsm_default + return alternative_call(xsm_ops.do_mca); + } + +-static inline int xsm_shadow_control( +- xsm_default_t def, struct domain *d, uint32_t op) +-{ +- return alternative_call(xsm_ops.shadow_control, d, op); +-} +- + static inline int xsm_mem_sharing_op( + xsm_default_t def, struct domain *d, struct domain *cd, int op) + { +--- a/xen/xsm/dummy.c ++++ b/xen/xsm/dummy.c +@@ -130,7 +130,6 @@ static const struct xsm_ops __initconst_ + .platform_op = xsm_platform_op, + #ifdef CONFIG_X86 + .do_mca = xsm_do_mca, +- .shadow_control = xsm_shadow_control, + .mem_sharing_op = xsm_mem_sharing_op, + .apic = xsm_apic, + .machine_memory_map = xsm_machine_memory_map, +--- a/xen/xsm/flask/hooks.c ++++ b/xen/xsm/flask/hooks.c +@@ -40,6 +40,7 @@ + + #ifdef CONFIG_X86 + #include ++static int flask_shadow_control(struct domain *d, unsigned int op); + #else + #define pv_shim false + #endif +@@ -699,10 +700,6 @@ static int cf_check flask_domctl(struct + /* These have individual XSM hooks (common/domctl.c) */ + case XEN_DOMCTL_set_target: + +-#ifdef CONFIG_X86 +- /* These have individual XSM hooks (arch/x86/domctl.c) */ +- case XEN_DOMCTL_shadow_op: +-#endif + #ifdef CONFIG_HAS_PASSTHROUGH + /* + * These have individual XSM hooks +@@ -787,6 +784,11 @@ static int cf_check flask_domctl(struct + case XEN_DOMCTL_get_address_size: + return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__GETADDRSIZE); + ++#ifdef CONFIG_X86 ++ case XEN_DOMCTL_shadow_op: ++ return flask_shadow_control(d, op->u.shadow_op.op); ++#endif ++ + case XEN_DOMCTL_mem_sharing_op: + return current_has_perm(d, SECCLASS_HVM, HVM__MEM_SHARING); + +@@ -1603,7 +1605,7 @@ static int cf_check flask_do_mca(void) + return domain_has_xen(current->domain, XEN__MCA_OP); + } + +-static int cf_check flask_shadow_control(struct domain *d, uint32_t op) ++static int flask_shadow_control(struct domain *d, unsigned int op) + { + uint32_t perm; + +@@ -1999,7 +2001,6 @@ static const struct xsm_ops __initconst_ + .platform_op = flask_platform_op, + #ifdef CONFIG_X86 + .do_mca = flask_do_mca, +- .shadow_control = flask_shadow_control, + .mem_sharing_op = flask_mem_sharing_op, + .apic = flask_apic, + .machine_memory_map = flask_machine_memory_map, diff --git a/xsa492-4.21-18.patch b/xsa492-4.21-18.patch new file mode 100644 index 0000000..1d82124 --- /dev/null +++ b/xsa492-4.21-18.patch @@ -0,0 +1,94 @@ +From: Jan Beulich +Subject: domctl: handle XEN_DOMCTL_get_device_group without acquiring domctl lock + +iommu_get_device_group() uses its own locking. Thus, with caller side +locking irrelevant, it can as well be called with the domctl lock not +held. + +Move the handling not only ahead of acquiring the lock, but also ahead +of the XSM check, leveraging that the sub-op has its own hook. + +This is part of XSA-492. + +Signed-off-by: Jan Beulich +Acked-by: Daniel P. Smith +Reviewed-by: Roger Pau Monné + +--- a/xen/common/domctl.c ++++ b/xen/common/domctl.c +@@ -513,6 +513,10 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + /* Other sub-ops handled further down. */ + break; + ++ case XEN_DOMCTL_get_device_group: ++ ret = iommu_do_domctl(op, d, u_domctl); ++ goto domctl_out_unlock_domonly; ++ + case XEN_DOMCTL_ioport_permission: + case XEN_DOMCTL_ioport_mapping: + case XEN_DOMCTL_gsi_permission: +@@ -918,7 +922,6 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + case XEN_DOMCTL_assign_device: + case XEN_DOMCTL_test_assign_device: + case XEN_DOMCTL_deassign_device: +- case XEN_DOMCTL_get_device_group: + ret = iommu_do_domctl(op, d, u_domctl); + break; + +--- a/xen/drivers/passthrough/pci.c ++++ b/xen/drivers/passthrough/pci.c +@@ -1620,7 +1620,7 @@ static int iommu_get_device_group( + if ( (pdev->seg != seg) || ((b == bus) && (df == devfn)) ) + continue; + +- if ( xsm_get_device_group(XSM_HOOK, (seg << 16) | (b << 8) | df) ) ++ if ( xsm_get_device_group(XSM_PRIV, (seg << 16) | (b << 8) | df) ) + continue; + + sdev_id = iommu_call(ops, get_device_group_id, seg, b, df); +@@ -1690,7 +1690,7 @@ int iommu_do_pci_domctl( + u32 max_sdevs; + XEN_GUEST_HANDLE_64(uint32) sdevs; + +- ret = xsm_get_device_group(XSM_HOOK, domctl->u.get_device_group.machine_sbdf); ++ ret = xsm_get_device_group(XSM_PRIV, domctl->u.get_device_group.machine_sbdf); + if ( ret ) + break; + +--- a/xen/include/xsm/dummy.h ++++ b/xen/include/xsm/dummy.h +@@ -162,6 +162,7 @@ static XSM_INLINE int cf_check xsm_domct + { + case XEN_DOMCTL_bind_pt_irq: + case XEN_DOMCTL_getdomaininfo: ++ case XEN_DOMCTL_get_device_group: + case XEN_DOMCTL_get_domain_state: + case XEN_DOMCTL_gsi_permission: + case XEN_DOMCTL_iomem_permission: +@@ -401,7 +402,7 @@ static XSM_INLINE int cf_check xsm_get_v + static XSM_INLINE int cf_check xsm_get_device_group( + XSM_DEFAULT_ARG uint32_t machine_bdf) + { +- XSM_ASSERT_ACTION(XSM_HOOK); ++ XSM_ASSERT_ACTION(XSM_PRIV); + return xsm_default_action(action, current->domain, NULL); + } + +--- a/xen/xsm/flask/hooks.c ++++ b/xen/xsm/flask/hooks.c +@@ -686,6 +686,7 @@ static int cf_check flask_domctl(struct + /* These have individual XSM hooks and don't make it here. */ + case XEN_DOMCTL_bind_pt_irq: + case XEN_DOMCTL_getdomaininfo: ++ case XEN_DOMCTL_get_device_group: + case XEN_DOMCTL_get_domain_state: + case XEN_DOMCTL_gsi_permission: + case XEN_DOMCTL_iomem_permission: +@@ -705,7 +706,6 @@ static int cf_check flask_domctl(struct + * These have individual XSM hooks + * (drivers/passthrough/{pci,device_tree.c) + */ +- case XEN_DOMCTL_get_device_group: + case XEN_DOMCTL_test_assign_device: + case XEN_DOMCTL_assign_device: + case XEN_DOMCTL_deassign_device: diff --git a/xsa492-4.21-19.patch b/xsa492-4.21-19.patch new file mode 100644 index 0000000..54a1117 --- /dev/null +++ b/xsa492-4.21-19.patch @@ -0,0 +1,378 @@ +From: Jan Beulich +Subject: domctl/XSM: drop {,de}assign_{,dt}device hooks + +Integrate the checking with xsm_domctl(). As a positive side effect, +permissions are then checked at the same early point with and without +Flask. As the DT device path needs fetching earlier (but must not be +double fetched), cache it in a private field of the public interface +struct. + +This is part of XSA-492. + +Signed-off-by: Jan Beulich +Acked-by: Daniel P. Smith +Reviewed-by: Roger Pau Monné + +--- a/xen/common/domctl.c ++++ b/xen/common/domctl.c +@@ -325,6 +325,10 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + case XEN_DOMCTL_deassign_device: + if ( op->domain == DOMID_IO ) + { ++#ifdef CONFIG_HAS_DEVICE_TREE_DISCOVERY ++ if ( op->u.assign_device.dev == XEN_DOMCTL_DEV_DT ) ++ op->u.assign_device.u.dt.dev = NULL; ++#endif + d = dom_io; + break; + } +@@ -332,6 +336,11 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + return -ESRCH; + fallthrough; + case XEN_DOMCTL_test_assign_device: ++#ifdef CONFIG_HAS_DEVICE_TREE_DISCOVERY ++ if ( op->u.assign_device.dev == XEN_DOMCTL_DEV_DT ) ++ op->u.assign_device.u.dt.dev = NULL; ++ fallthrough; ++#endif + case XEN_DOMCTL_vm_event_op: + if ( op->domain == DOMID_INVALID ) + { +--- a/xen/drivers/passthrough/device_tree.c ++++ b/xen/drivers/passthrough/device_tree.c +@@ -340,15 +340,15 @@ int iommu_do_dt_domctl(struct xen_domctl + if ( (d && d->is_dying) || domctl->u.assign_device.flags ) + break; + +- ret = dt_find_node_by_gpath(domctl->u.assign_device.u.dt.path, +- domctl->u.assign_device.u.dt.size, +- &dev); +- if ( ret ) +- break; +- +- ret = xsm_assign_dtdevice(XSM_HOOK, d, dt_node_full_name(dev)); +- if ( ret ) +- break; ++ dev = domctl->u.assign_device.u.dt.dev; ++ if ( !dev ) ++ { ++ ret = dt_find_node_by_gpath(domctl->u.assign_device.u.dt.path, ++ domctl->u.assign_device.u.dt.size, ++ &dev); ++ if ( ret ) ++ break; ++ } + + if ( domctl->cmd == XEN_DOMCTL_test_assign_device ) + { +@@ -396,15 +396,15 @@ int iommu_do_dt_domctl(struct xen_domctl + if ( domctl->u.assign_device.flags ) + break; + +- ret = dt_find_node_by_gpath(domctl->u.assign_device.u.dt.path, +- domctl->u.assign_device.u.dt.size, +- &dev); +- if ( ret ) +- break; +- +- ret = xsm_deassign_dtdevice(XSM_HOOK, d, dt_node_full_name(dev)); +- if ( ret ) +- break; ++ dev = domctl->u.assign_device.u.dt.dev; ++ if ( !dev ) ++ { ++ ret = dt_find_node_by_gpath(domctl->u.assign_device.u.dt.path, ++ domctl->u.assign_device.u.dt.size, ++ &dev); ++ if ( ret ) ++ break; ++ } + + if ( d == dom_io ) + { +--- a/xen/drivers/passthrough/pci.c ++++ b/xen/drivers/passthrough/pci.c +@@ -1740,10 +1740,6 @@ int iommu_do_pci_domctl( + + machine_sbdf = domctl->u.assign_device.u.pci.machine_sbdf; + +- ret = xsm_assign_device(XSM_HOOK, d, machine_sbdf); +- if ( ret ) +- break; +- + seg = machine_sbdf >> 16; + bus = PCI_BUS(machine_sbdf); + devfn = PCI_DEVFN(machine_sbdf); +@@ -1785,10 +1781,6 @@ int iommu_do_pci_domctl( + + machine_sbdf = domctl->u.assign_device.u.pci.machine_sbdf; + +- ret = xsm_deassign_device(XSM_HOOK, d, machine_sbdf); +- if ( ret ) +- break; +- + seg = machine_sbdf >> 16; + bus = PCI_BUS(machine_sbdf); + devfn = PCI_DEVFN(machine_sbdf); +--- a/xen/include/public/domctl.h ++++ b/xen/include/public/domctl.h +@@ -575,7 +575,10 @@ struct xen_domctl_assign_device { + } pci; + struct { + uint32_t size; /* Length of the path */ +- XEN_GUEST_HANDLE_64(char) path; /* path to the device tree node */ ++ XEN_GUEST_HANDLE_64(char) path; /* Path to the device tree node */ ++#ifdef __XEN__ ++ struct dt_device_node *dev; /* Resolved device node of the above */ ++#endif + } dt; + } u; + }; +--- a/xen/include/xsm/dummy.h ++++ b/xen/include/xsm/dummy.h +@@ -405,40 +405,8 @@ static XSM_INLINE int cf_check xsm_get_d + XSM_ASSERT_ACTION(XSM_PRIV); + return xsm_default_action(action, current->domain, NULL); + } +- +-static XSM_INLINE int cf_check xsm_assign_device( +- XSM_DEFAULT_ARG struct domain *d, uint32_t machine_bdf) +-{ +- XSM_ASSERT_ACTION(XSM_HOOK); +- return xsm_default_action(action, current->domain, d); +-} +- +-static XSM_INLINE int cf_check xsm_deassign_device( +- XSM_DEFAULT_ARG struct domain *d, uint32_t machine_bdf) +-{ +- XSM_ASSERT_ACTION(XSM_HOOK); +- return xsm_default_action(action, current->domain, d); +-} +- + #endif /* HAS_PASSTHROUGH && HAS_PCI */ + +-#if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_DEVICE_TREE_DISCOVERY) +-static XSM_INLINE int cf_check xsm_assign_dtdevice( +- XSM_DEFAULT_ARG struct domain *d, const char *dtpath) +-{ +- XSM_ASSERT_ACTION(XSM_HOOK); +- return xsm_default_action(action, current->domain, d); +-} +- +-static XSM_INLINE int cf_check xsm_deassign_dtdevice( +- XSM_DEFAULT_ARG struct domain *d, const char *dtpath) +-{ +- XSM_ASSERT_ACTION(XSM_HOOK); +- return xsm_default_action(action, current->domain, d); +-} +- +-#endif /* HAS_PASSTHROUGH && HAS_DEVICE_TREE_DISCOVERY */ +- + static XSM_INLINE int cf_check xsm_resource_plug_core(XSM_DEFAULT_VOID) + { + XSM_ASSERT_ACTION(XSM_HOOK); +--- a/xen/include/xsm/xsm.h ++++ b/xen/include/xsm/xsm.h +@@ -124,13 +124,6 @@ struct xsm_ops { + + #if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_PCI) + int (*get_device_group)(uint32_t machine_bdf); +- int (*assign_device)(struct domain *d, uint32_t machine_bdf); +- int (*deassign_device)(struct domain *d, uint32_t machine_bdf); +-#endif +- +-#if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_DEVICE_TREE_DISCOVERY) +- int (*assign_dtdevice)(struct domain *d, const char *dtpath); +- int (*deassign_dtdevice)(struct domain *d, const char *dtpath); + #endif + + int (*resource_plug_core)(void); +@@ -533,35 +526,8 @@ static inline int xsm_get_device_group(x + { + return alternative_call(xsm_ops.get_device_group, machine_bdf); + } +- +-static inline int xsm_assign_device( +- xsm_default_t def, struct domain *d, uint32_t machine_bdf) +-{ +- return alternative_call(xsm_ops.assign_device, d, machine_bdf); +-} +- +-static inline int xsm_deassign_device( +- xsm_default_t def, struct domain *d, uint32_t machine_bdf) +-{ +- return alternative_call(xsm_ops.deassign_device, d, machine_bdf); +-} + #endif /* HAS_PASSTHROUGH && HAS_PCI) */ + +-#if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_DEVICE_TREE_DISCOVERY) +-static inline int xsm_assign_dtdevice( +- xsm_default_t def, struct domain *d, const char *dtpath) +-{ +- return alternative_call(xsm_ops.assign_dtdevice, d, dtpath); +-} +- +-static inline int xsm_deassign_dtdevice( +- xsm_default_t def, struct domain *d, const char *dtpath) +-{ +- return alternative_call(xsm_ops.deassign_dtdevice, d, dtpath); +-} +- +-#endif /* HAS_PASSTHROUGH && HAS_DEVICE_TREE_DISCOVERY */ +- + static inline int xsm_resource_plug_pci(xsm_default_t def, uint32_t machine_bdf) + { + return alternative_call(xsm_ops.resource_plug_pci, machine_bdf); +--- a/xen/xsm/dummy.c ++++ b/xen/xsm/dummy.c +@@ -81,13 +81,6 @@ static const struct xsm_ops __initconst_ + + #if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_PCI) + .get_device_group = xsm_get_device_group, +- .assign_device = xsm_assign_device, +- .deassign_device = xsm_deassign_device, +-#endif +- +-#if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_DEVICE_TREE_DISCOVERY) +- .assign_dtdevice = xsm_assign_dtdevice, +- .deassign_dtdevice = xsm_deassign_dtdevice, + #endif + + .resource_plug_core = xsm_resource_plug_core, +--- a/xen/xsm/flask/hooks.c ++++ b/xen/xsm/flask/hooks.c +@@ -45,6 +45,17 @@ static int flask_shadow_control(struct d + #define pv_shim false + #endif + ++#ifdef CONFIG_HAS_PASSTHROUGH ++#ifdef CONFIG_HAS_PCI ++static int flask_assign_device(struct domain *d, unsigned int machine_bdf); ++static int flask_deassign_device(struct domain *d, unsigned int machine_bdf); ++#endif ++#ifdef CONFIG_HAS_DEVICE_TREE_DISCOVERY ++static int flask_assign_dtdevice(struct domain *d, const char *dtpath); ++static int flask_deassign_dtdevice(struct domain *d, const char *dtpath); ++#endif ++#endif /* CONFIG_HAS_PASSTHROUGH */ ++ + static uint32_t domain_sid(const struct domain *dom) + { + struct domain_security_struct *dsec = dom->ssid; +@@ -700,16 +711,6 @@ static int cf_check flask_domctl(struct + + /* These have individual XSM hooks (common/domctl.c) */ + case XEN_DOMCTL_set_target: +- +-#ifdef CONFIG_HAS_PASSTHROUGH +- /* +- * These have individual XSM hooks +- * (drivers/passthrough/{pci,device_tree.c) +- */ +- case XEN_DOMCTL_test_assign_device: +- case XEN_DOMCTL_assign_device: +- case XEN_DOMCTL_deassign_device: +-#endif + return 0; + + case XEN_DOMCTL_destroydomain: +@@ -789,6 +790,49 @@ static int cf_check flask_domctl(struct + return flask_shadow_control(d, op->u.shadow_op.op); + #endif + ++#ifdef CONFIG_HAS_PASSTHROUGH ++ ++ case XEN_DOMCTL_test_assign_device: ++ case XEN_DOMCTL_assign_device: ++ case XEN_DOMCTL_deassign_device: ++ switch ( op->u.assign_device.dev ) ++ { ++#ifdef CONFIG_HAS_PCI ++ case XEN_DOMCTL_DEV_PCI: ++ return op->cmd != XEN_DOMCTL_deassign_device ++ ? flask_assign_device( ++ d, op->u.assign_device.u.pci.machine_sbdf) ++ : flask_deassign_device( ++ d, op->u.assign_device.u.pci.machine_sbdf); ++#endif ++ ++#ifdef CONFIG_HAS_DEVICE_TREE_DISCOVERY ++ case XEN_DOMCTL_DEV_DT: ++ { ++ struct dt_device_node *dev; ++ int ret = dt_find_node_by_gpath(op->u.assign_device.u.dt.path, ++ op->u.assign_device.u.dt.size, ++ &dev); ++ ++ if ( ret ) ++ return ret; ++ ++ op->u.assign_device.u.dt.dev = dev; ++ ++ return op->cmd != XEN_DOMCTL_deassign_device ++ ? flask_assign_dtdevice(d, dt_node_full_name(dev)) ++ : flask_deassign_dtdevice(d, dt_node_full_name(dev)); ++ } ++#endif ++ ++ default: ++ /* Unknown type. */ ++ break; ++ } ++ return avc_unknown_permission("assign_device", op->cmd); ++ ++#endif /* CONFIG_HAS_PASSTHROUGH */ ++ + case XEN_DOMCTL_mem_sharing_op: + return current_has_perm(d, SECCLASS_HVM, HVM__MEM_SHARING); + +@@ -1416,7 +1460,7 @@ static int flask_test_assign_device(uint + return avc_current_has_perm(rsid, SECCLASS_RESOURCE, RESOURCE__STAT_DEVICE, NULL); + } + +-static int cf_check flask_assign_device(struct domain *d, uint32_t machine_bdf) ++static int flask_assign_device(struct domain *d, uint32_t machine_bdf) + { + uint32_t dsid, rsid; + int rc = -EPERM; +@@ -1446,7 +1490,7 @@ static int cf_check flask_assign_device( + return avc_has_perm(dsid, rsid, SECCLASS_RESOURCE, dperm, &ad); + } + +-static int cf_check flask_deassign_device( ++static int flask_deassign_device( + struct domain *d, uint32_t machine_bdf) + { + uint32_t rsid; +@@ -1478,7 +1522,7 @@ static int flask_test_assign_dtdevice(co + NULL); + } + +-static int cf_check flask_assign_dtdevice(struct domain *d, const char *dtpath) ++static int flask_assign_dtdevice(struct domain *d, const char *dtpath) + { + uint32_t dsid, rsid; + int rc = -EPERM; +@@ -1508,7 +1552,7 @@ static int cf_check flask_assign_dtdevic + return avc_has_perm(dsid, rsid, SECCLASS_RESOURCE, dperm, &ad); + } + +-static int cf_check flask_deassign_dtdevice( ++static int flask_deassign_dtdevice( + struct domain *d, const char *dtpath) + { + uint32_t rsid; +@@ -1989,13 +2033,6 @@ static const struct xsm_ops __initconst_ + + #if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_PCI) + .get_device_group = flask_get_device_group, +- .assign_device = flask_assign_device, +- .deassign_device = flask_deassign_device, +-#endif +- +-#if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_DEVICE_TREE_DISCOVERY) +- .assign_dtdevice = flask_assign_dtdevice, +- .deassign_dtdevice = flask_deassign_dtdevice, + #endif + + .platform_op = flask_platform_op, diff --git a/xsa492-4.21-20.patch b/xsa492-4.21-20.patch new file mode 100644 index 0000000..bfd10a9 --- /dev/null +++ b/xsa492-4.21-20.patch @@ -0,0 +1,123 @@ +From: Jan Beulich +Subject: domctl: handle XEN_DOMCTL_set_target without acquiring domctl lock + +The only locking required here is that between checking d->target and +setting it. To avoid the need for an explicit lock, use cmpxchgptr() to +update d->target. + +Move the handling not only ahead of acquiring the lock, but also ahead +of the XSM check, leveraging that the sub-op has its own hook. + +This is part of XSA-492. + +Signed-off-by: Jan Beulich +Acked-by: Daniel P. Smith +Reviewed-by: Roger Pau Monné + +--- a/xen/common/domctl.c ++++ b/xen/common/domctl.c +@@ -505,6 +505,30 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + } + #endif + ++ case XEN_DOMCTL_set_target: ++ { ++ struct domain *e = get_domain_by_id(op->u.set_target.target); ++ ++ ret = -ESRCH; ++ if ( !e ) ++ goto domctl_out_unlock_domonly; ++ ++ if ( d == e ) ++ ret = -EINVAL; ++ else if ( !is_hvm_domain(e) ) ++ ret = -EOPNOTSUPP; ++ else ++ ret = xsm_set_target(XSM_PRIV, d, e); ++ ++ /* Hold reference on @e until we destroy @d. */ ++ if ( !ret && cmpxchgptr(&d->target, NULL, e) ) ++ ret = -EINVAL; ++ ++ if ( ret ) ++ put_domain(e); ++ goto domctl_out_unlock_domonly; ++ } ++ + case XEN_DOMCTL_vm_event_op: + if ( op->u.vm_event_op.op == XEN_VM_EVENT_GET_VERSION ) + { +@@ -844,36 +868,6 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + domain_set_time_offset(d, op->u.settimeoffset.time_offset_seconds); + break; + +- case XEN_DOMCTL_set_target: +- { +- struct domain *e; +- +- ret = -ESRCH; +- e = get_domain_by_id(op->u.set_target.target); +- if ( e == NULL ) +- break; +- +- ret = -EINVAL; +- if ( (d == e) || (d->target != NULL) ) +- { +- put_domain(e); +- break; +- } +- +- ret = -EOPNOTSUPP; +- if ( is_hvm_domain(e) ) +- ret = xsm_set_target(XSM_HOOK, d, e); +- if ( ret ) +- { +- put_domain(e); +- break; +- } +- +- /* Hold reference on @e until we destroy @d. */ +- d->target = e; +- break; +- } +- + case XEN_DOMCTL_subscribe: + d->suspend_evtchn = op->u.subscribe.port; + break; +--- a/xen/include/xsm/dummy.h ++++ b/xen/include/xsm/dummy.h +@@ -150,7 +150,7 @@ static XSM_INLINE int cf_check xsm_sysct + static XSM_INLINE int cf_check xsm_set_target( + XSM_DEFAULT_ARG struct domain *d, struct domain *e) + { +- XSM_ASSERT_ACTION(XSM_HOOK); ++ XSM_ASSERT_ACTION(XSM_PRIV); + return xsm_default_action(action, current->domain, NULL); + } + +@@ -170,6 +170,7 @@ static XSM_INLINE int cf_check xsm_domct + case XEN_DOMCTL_ioport_permission: + case XEN_DOMCTL_irq_permission: + case XEN_DOMCTL_memory_mapping: ++ case XEN_DOMCTL_set_target: + case XEN_DOMCTL_unbind_pt_irq: + ASSERT_UNREACHABLE(); + return -EILSEQ; +--- a/xen/xsm/flask/hooks.c ++++ b/xen/xsm/flask/hooks.c +@@ -705,14 +705,11 @@ static int cf_check flask_domctl(struct + case XEN_DOMCTL_ioport_permission: + case XEN_DOMCTL_irq_permission: + case XEN_DOMCTL_memory_mapping: ++ case XEN_DOMCTL_set_target: + case XEN_DOMCTL_unbind_pt_irq: + ASSERT_UNREACHABLE(); + return -EILSEQ; + +- /* These have individual XSM hooks (common/domctl.c) */ +- case XEN_DOMCTL_set_target: +- return 0; +- + case XEN_DOMCTL_destroydomain: + return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__DESTROY); + diff --git a/xsa493-4.21-01.patch b/xsa493-4.21-01.patch new file mode 100644 index 0000000..c06b9a5 --- /dev/null +++ b/xsa493-4.21-01.patch @@ -0,0 +1,311 @@ +From 2e21b5301765de353c06081eee953255bf327176 Mon Sep 17 00:00:00 2001 +From: Michal Orzel +Date: Tue, 14 Apr 2026 10:11:24 +0200 +Subject: xen/arm64: flushtlb: Optimize ARM64_WORKAROUND_REPEAT_TLBI + +The ARM64_WORKAROUND_REPEAT_TLBI workaround is used to mitigate several +errata where broadcast TLBI;DSB sequences don't provide all the +architecturally required synchronization. The workaround performs more +work than necessary, and can have significant overhead. This patch +optimizes the workaround, as explained below. + +1. All relevant errata only affect the ordering and/or completion of + memory accesses which have been translated by an invalidated TLB + entry. The actual invalidation of TLB entries is unaffected. + +2. The existing workaround is applied to both broadcast and local TLB + invalidation, whereas for all relevant errata it is only necessary to + apply a workaround for broadcast invalidation. + +3. The existing workaround replaces every TLBI with a TLBI;DSB;TLBI + sequence, whereas for all relevant errata it is only necessary to + execute a single additional TLBI;DSB sequence after any number of + TLBIs are completed by a DSB. + + For example, for a sequence of batched TLBIs: + + TLBI [, ] + TLBI [, ] + TLBI [, ] + DSB ISH + + ... the existing workaround will expand this to: + + TLBI [, ] + DSB ISH // additional + TLBI [, ] // additional + TLBI [, ] + DSB ISH // additional + TLBI [, ] // additional + TLBI [, ] + DSB ISH // additional + TLBI [, ] // additional + DSB ISH + + ... whereas it is sufficient to have: + + TLBI [, ] + TLBI [, ] + TLBI [, ] + DSB ISH + TLBI [, ] // additional + DSB ISH // additional + + Using a single additional TLBI and DSB at the end of the sequence can + have significantly lower overhead as each DSB which completes a TLBI + must synchronize with other PEs in the system, with potential + performance effects both locally and system-wide. + +4. The existing workaround repeats each specific TLBI operation, whereas + for all relevant errata it is sufficient for the additional TLBI to + use *any* operation which will be broadcast, regardless of which + translation regime or stage of translation the operation applies to. + + For example, for a single TLBI: + + TLBI ALLE2IS + DSB ISH + + ... the existing workaround will expand this to: + + TLBI ALLE2IS + DSB ISH + TLBI ALLE2IS // additional + DSB ISH // additional + + ... whereas it is sufficient to have: + + TLBI ALLE2IS + DSB ISH + TLBI VALE1IS, XZR // additional + DSB ISH // additional + + As the additional TLBI doesn't have to match a specific earlier TLBI, + the additional TLBI can be implemented in separate code, with no + memory of the earlier TLBIs. The additional TLBI can also use a + cheaper TLBI operation. + +5. The existing workaround is applied to both Stage-1 and Stage-2 TLB + invalidation, whereas for all relevant errata it is only necessary to + apply a workaround for Stage-1 invalidation. + + Architecturally, TLBI operations which invalidate only Stage-2 + information (e.g. IPAS2E1IS) are not required to invalidate TLB + entries which combine information from Stage-1 and Stage-2 + translation table entries, and consequently may not complete memory + accesses translated by those combined entries. In these cases, + completion of memory accesses is only guaranteed after subsequent + invalidation of Stage-1 information (e.g. VMALLE1IS). + +Rework the workaround logic as follows: + - add TLB_HELPER_LOCAL() to be used for local TLB ops without a + workaround, + - modify TLB_HELPER() workaround to use tlbi vale2is, xzr as a second + TLBI, + - drop TLB_HELPER_VA(). It's used only by __flush_xen_tlb_one_local + which is local and does not need workaround and by + __flush_xen_tlb_one. In the latter case, since it's used in a loop, + we don't need a workaround in the middle. Add __tlb_repeat_sync with + a workaround to be used at the end after DSB and before final ISB, + - TLBI VALE2IS passing XZR is used as an additional TLBI. While there is + an identity mapping there, it's used very rarely. The performance + impact is therefore negligible. If things change in the future, we + can revisit the decision. + +Signed-off-by: Michal Orzel +Reviewed-by: Luca Fancellu +Reviewed-by: Julien Grall +(cherry picked from commit 7c502d7591519135765b8041cbd1c70e56e5a0b9) + +diff --git a/xen/arch/arm/include/asm/arm32/flushtlb.h b/xen/arch/arm/include/asm/arm32/flushtlb.h +index 61c25a318998..5483be08fbbe 100644 +--- a/xen/arch/arm/include/asm/arm32/flushtlb.h ++++ b/xen/arch/arm/include/asm/arm32/flushtlb.h +@@ -57,6 +57,9 @@ static inline void __flush_xen_tlb_one(vaddr_t va) + asm volatile(STORE_CP32(0, TLBIMVAHIS) : : "r" (va) : "memory"); + } + ++/* Only for ARM64_WORKAROUND_REPEAT_TLBI */ ++static inline void __tlb_repeat_sync(void) {} ++ + #endif /* __ASM_ARM_ARM32_FLUSHTLB_H__ */ + /* + * Local variables: +diff --git a/xen/arch/arm/include/asm/arm64/flushtlb.h b/xen/arch/arm/include/asm/arm64/flushtlb.h +index 3b99c11b50d1..1606b26bf28a 100644 +--- a/xen/arch/arm/include/asm/arm64/flushtlb.h ++++ b/xen/arch/arm/include/asm/arm64/flushtlb.h +@@ -12,9 +12,14 @@ + * ARM64_WORKAROUND_REPEAT_TLBI: + * Modification of the translation table for a virtual address might lead to + * read-after-read ordering violation. +- * The workaround repeats TLBI+DSB ISH operation for all the TLB flush +- * operations. While this is strictly not necessary, we don't want to +- * take any risk. ++ * The workaround repeats TLBI+DSB ISH operation for broadcast TLB flush ++ * operations. The workaround is not needed for local operations. ++ * ++ * It is sufficient for the additional TLBI to use *any* operation which will ++ * be broadcast, regardless of which translation regime or stage of translation ++ * the operation applies to. TLBI VALE2IS is used passing XZR. While there is ++ * an identity mapping there, it's only used during suspend/resume, CPU on/off, ++ * so the impact (performance if any) is negligible. + * + * For Xen page-tables the ISB will discard any instructions fetched + * from the old mappings. +@@ -26,69 +31,90 @@ + * Note that for local TLB flush, using non-shareable (nsh) is sufficient + * (see D5-4929 in ARM DDI 0487H.a). Although, the memory barrier in + * for the workaround is left as inner-shareable to match with Linux +- * v6.1-rc8. ++ * v6.19. + */ +-#define TLB_HELPER(name, tlbop, sh) \ ++#define TLB_HELPER_LOCAL(name, tlbop) \ + static inline void name(void) \ + { \ + asm_inline volatile ( \ +- "dsb " # sh "st;" \ ++ "dsb nshst;" \ + "tlbi " # tlbop ";" \ +- ALTERNATIVE( \ +- "nop; nop;", \ +- "dsb ish;" \ +- "tlbi " # tlbop ";", \ +- ARM64_WORKAROUND_REPEAT_TLBI, \ +- CONFIG_ARM64_WORKAROUND_REPEAT_TLBI) \ +- "dsb " # sh ";" \ ++ "dsb nsh;" \ + "isb;" \ + : : : "memory"); \ + } + +-/* +- * FLush TLB by VA. This will likely be used in a loop, so the caller +- * is responsible to use the appropriate memory barriers before/after +- * the sequence. +- * +- * See above about the ARM64_WORKAROUND_REPEAT_TLBI sequence. +- */ +-#define TLB_HELPER_VA(name, tlbop) \ +-static inline void name(vaddr_t va) \ +-{ \ +- asm_inline volatile ( \ +- "tlbi " # tlbop ", %0;" \ +- ALTERNATIVE( \ +- "nop; nop;", \ +- "dsb ish;" \ +- "tlbi " # tlbop ", %0;", \ +- ARM64_WORKAROUND_REPEAT_TLBI, \ +- CONFIG_ARM64_WORKAROUND_REPEAT_TLBI) \ +- : : "r" (va >> PAGE_SHIFT) : "memory"); \ ++#define TLB_HELPER(name, tlbop) \ ++static inline void name(void) \ ++{ \ ++ asm_inline volatile ( \ ++ "dsb ishst;" \ ++ "tlbi " # tlbop ";" \ ++ ALTERNATIVE( \ ++ "nop; nop;", \ ++ "dsb ish;" \ ++ "tlbi vale2is, xzr;", \ ++ ARM64_WORKAROUND_REPEAT_TLBI, \ ++ CONFIG_ARM64_WORKAROUND_REPEAT_TLBI) \ ++ "dsb ish;" \ ++ "isb;" \ ++ : : : "memory"); \ + } + + /* Flush local TLBs, current VMID only. */ +-TLB_HELPER(flush_guest_tlb_local, vmalls12e1, nsh) ++TLB_HELPER_LOCAL(flush_guest_tlb_local, vmalls12e1) + + /* Flush innershareable TLBs, current VMID only */ +-TLB_HELPER(flush_guest_tlb, vmalls12e1is, ish) ++TLB_HELPER(flush_guest_tlb, vmalls12e1is) + + /* Flush local TLBs, all VMIDs, non-hypervisor mode */ +-TLB_HELPER(flush_all_guests_tlb_local, alle1, nsh) ++TLB_HELPER_LOCAL(flush_all_guests_tlb_local, alle1) + + /* Flush innershareable TLBs, all VMIDs, non-hypervisor mode */ +-TLB_HELPER(flush_all_guests_tlb, alle1is, ish) ++TLB_HELPER(flush_all_guests_tlb, alle1is) + + /* Flush all hypervisor mappings from the TLB of the local processor. */ +-TLB_HELPER(flush_xen_tlb_local, alle2, nsh) ++TLB_HELPER_LOCAL(flush_xen_tlb_local, alle2) ++ ++#undef TLB_HELPER_LOCAL ++#undef TLB_HELPER ++ ++/* ++ * FLush TLB by VA. This will likely be used in a loop, so the caller ++ * is responsible to use the appropriate memory barriers before/after ++ * the sequence. ++ */ + + /* Flush TLB of local processor for address va. */ +-TLB_HELPER_VA(__flush_xen_tlb_one_local, vae2) ++static inline void __flush_xen_tlb_one_local(vaddr_t va) ++{ ++ asm_inline volatile ( ++ "tlbi vae2, %0" : : "r" (va >> PAGE_SHIFT) : "memory"); ++} + + /* Flush TLB of all processors in the inner-shareable domain for address va. */ +-TLB_HELPER_VA(__flush_xen_tlb_one, vae2is) ++static inline void __flush_xen_tlb_one(vaddr_t va) ++{ ++ asm_inline volatile ( ++ "tlbi vae2is, %0" : : "r" (va >> PAGE_SHIFT) : "memory"); ++} + +-#undef TLB_HELPER +-#undef TLB_HELPER_VA ++/* ++ * ARM64_WORKAROUND_REPEAT_TLBI: ++ * For all relevant erratas it is only necessary to execute a single ++ * additional TLBI;DSB sequence after any number of TLBIs are completed by DSB. ++ */ ++static inline void __tlb_repeat_sync(void) ++{ ++ asm_inline volatile ( ++ ALTERNATIVE( ++ "nop; nop;", ++ "tlbi vale2is, xzr;" ++ "dsb ish;", ++ ARM64_WORKAROUND_REPEAT_TLBI, ++ CONFIG_ARM64_WORKAROUND_REPEAT_TLBI) ++ : : : "memory"); ++} + + #endif /* __ASM_ARM_ARM64_FLUSHTLB_H__ */ + /* +diff --git a/xen/arch/arm/include/asm/flushtlb.h b/xen/arch/arm/include/asm/flushtlb.h +index e45fb6d97b02..c292c3c00d29 100644 +--- a/xen/arch/arm/include/asm/flushtlb.h ++++ b/xen/arch/arm/include/asm/flushtlb.h +@@ -65,6 +65,7 @@ static inline void flush_xen_tlb_range_va(vaddr_t va, + va += PAGE_SIZE; + } + dsb(ish); /* Ensure the TLB invalidation has completed */ ++ __tlb_repeat_sync(); + isb(); + } + +diff --git a/xen/arch/arm/include/asm/mmu/layout.h b/xen/arch/arm/include/asm/mmu/layout.h +index 19c0ec63a59a..feafc14ebfda 100644 +--- a/xen/arch/arm/include/asm/mmu/layout.h ++++ b/xen/arch/arm/include/asm/mmu/layout.h +@@ -23,6 +23,10 @@ + * + * Reserved to identity map Xen + * ++ * Note: As part of ARM64_WORKAROUND_REPEAT_TLBI, VA 0 is used for an extra ++ * TLBI operation given its rare use (only identity mapping) and thus ++ * negligible performance impact. ++ * + * 0x00000a0000000000 - 0x00000a7fffffffff (512GB, L0 slot [20]) + * (Relative offsets) + * 0 - 2M Unmapped diff --git a/xsa493-4.21-02.patch b/xsa493-4.21-02.patch new file mode 100644 index 0000000..f80119c --- /dev/null +++ b/xsa493-4.21-02.patch @@ -0,0 +1,71 @@ +From 7e70b87512c966248b1e8453d9ac54c643c06f44 Mon Sep 17 00:00:00 2001 +From: Michal Orzel +Date: Fri, 22 May 2026 09:35:55 +0200 +Subject: xen/arm: Sync missing definitions for Arm CPUs with Linux + +Synchronize with Linux kernel 7.0 definitions for the following CPUs: + - Cortex-A76AE, + - Cortex-A78AE, + - Cortex-X1C, + - Cortex-X3, + - Neoverse-V2, + - Cortex-X4, + - Neoverse-V3AE, + - Neoverse-V3, + - Cortex-X925. + +These will be used for errata detection in subsequent patches. + +Signed-off-by: Michal Orzel +Reviewed-by: Julien Grall + +diff --git a/xen/arch/arm/include/asm/processor.h b/xen/arch/arm/include/asm/processor.h +index ec23fd098b63..907778683b08 100644 +--- a/xen/arch/arm/include/asm/processor.h ++++ b/xen/arch/arm/include/asm/processor.h +@@ -89,13 +89,22 @@ + #define ARM_CPU_PART_CORTEX_A76 0xD0B + #define ARM_CPU_PART_NEOVERSE_N1 0xD0C + #define ARM_CPU_PART_CORTEX_A77 0xD0D ++#define ARM_CPU_PART_CORTEX_A76AE 0xD0E + #define ARM_CPU_PART_NEOVERSE_V1 0xD40 + #define ARM_CPU_PART_CORTEX_A78 0xD41 ++#define ARM_CPU_PART_CORTEX_A78AE 0xD42 + #define ARM_CPU_PART_CORTEX_X1 0xD44 + #define ARM_CPU_PART_CORTEX_A710 0xD47 + #define ARM_CPU_PART_CORTEX_X2 0xD48 + #define ARM_CPU_PART_NEOVERSE_N2 0xD49 + #define ARM_CPU_PART_CORTEX_A78C 0xD4B ++#define ARM_CPU_PART_CORTEX_X1C 0xD4C ++#define ARM_CPU_PART_CORTEX_X3 0xD4E ++#define ARM_CPU_PART_NEOVERSE_V2 0xD4F ++#define ARM_CPU_PART_CORTEX_X4 0xD82 ++#define ARM_CPU_PART_NEOVERSE_V3AE 0xD83 ++#define ARM_CPU_PART_NEOVERSE_V3 0xD84 ++#define ARM_CPU_PART_CORTEX_X925 0xD85 + + #define MIDR_CORTEX_A12 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A12) + #define MIDR_CORTEX_A17 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A17) +@@ -110,13 +119,22 @@ + #define MIDR_CORTEX_A76 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A76) + #define MIDR_NEOVERSE_N1 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_N1) + #define MIDR_CORTEX_A77 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A77) ++#define MIDR_CORTEX_A76AE MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A76AE) + #define MIDR_NEOVERSE_V1 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V1) + #define MIDR_CORTEX_A78 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A78) ++#define MIDR_CORTEX_A78AE MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A78AE) + #define MIDR_CORTEX_X1 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X1) + #define MIDR_CORTEX_A710 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A710) + #define MIDR_CORTEX_X2 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X2) + #define MIDR_NEOVERSE_N2 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_N2) + #define MIDR_CORTEX_A78C MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A78C) ++#define MIDR_CORTEX_X1C MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X1C) ++#define MIDR_CORTEX_X3 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X3) ++#define MIDR_NEOVERSE_V2 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V2) ++#define MIDR_CORTEX_X4 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X4) ++#define MIDR_NEOVERSE_V3AE MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V3AE) ++#define MIDR_NEOVERSE_V3 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V3) ++#define MIDR_CORTEX_X925 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X925) + + /* MPIDR Multiprocessor Affinity Register */ + #define _MPIDR_UP (30) diff --git a/xsa493-4.21-03.patch b/xsa493-4.21-03.patch new file mode 100644 index 0000000..86bae68 --- /dev/null +++ b/xsa493-4.21-03.patch @@ -0,0 +1,37 @@ +From c0f7b40fdbb986b3cf470ed51f3878261e33f9cb Mon Sep 17 00:00:00 2001 +From: Michal Orzel +Date: Fri, 22 May 2026 09:35:56 +0200 +Subject: xen/arm: Add C1-Ultra definitions + +Add processor definitions for C1-Ultra. These will be used for errata +detection in subsequent patches. + +These values can be found in the C1-Ultra TRM: + + https://developer.arm.com/documentation/108014/0100/ + +... in section A.5.1 ("MIDR_EL1, Main ID Register"). + +Signed-off-by: Michal Orzel +Reviewed-by: Julien Grall + +diff --git a/xen/arch/arm/include/asm/processor.h b/xen/arch/arm/include/asm/processor.h +index 907778683b08..72745cca62bc 100644 +--- a/xen/arch/arm/include/asm/processor.h ++++ b/xen/arch/arm/include/asm/processor.h +@@ -105,6 +105,7 @@ + #define ARM_CPU_PART_NEOVERSE_V3AE 0xD83 + #define ARM_CPU_PART_NEOVERSE_V3 0xD84 + #define ARM_CPU_PART_CORTEX_X925 0xD85 ++#define ARM_CPU_PART_C1_ULTRA 0xD8C + + #define MIDR_CORTEX_A12 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A12) + #define MIDR_CORTEX_A17 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A17) +@@ -135,6 +136,7 @@ + #define MIDR_NEOVERSE_V3AE MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V3AE) + #define MIDR_NEOVERSE_V3 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V3) + #define MIDR_CORTEX_X925 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X925) ++#define MIDR_C1_ULTRA MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_C1_ULTRA) + + /* MPIDR Multiprocessor Affinity Register */ + #define _MPIDR_UP (30) diff --git a/xsa493-4.21-04.patch b/xsa493-4.21-04.patch new file mode 100644 index 0000000..b59ee74 --- /dev/null +++ b/xsa493-4.21-04.patch @@ -0,0 +1,37 @@ +From 6af67aeca418bffb807424eb3415fab59e581733 Mon Sep 17 00:00:00 2001 +From: Michal Orzel +Date: Fri, 22 May 2026 09:35:57 +0200 +Subject: xen/arm: Add C1-Premium definitions + +Add processor definitions for C1-Premium. These will be used for errata +detection in subsequent patches. + +These values can be found in the C1-Premium TRM: + + https://developer.arm.com/documentation/109416/0100/ + +... in section A.5.1 ("MIDR_EL1, Main ID Register"). + +Signed-off-by: Michal Orzel +Reviewed-by: Julien Grall + +diff --git a/xen/arch/arm/include/asm/processor.h b/xen/arch/arm/include/asm/processor.h +index 72745cca62bc..25c5762c6706 100644 +--- a/xen/arch/arm/include/asm/processor.h ++++ b/xen/arch/arm/include/asm/processor.h +@@ -106,6 +106,7 @@ + #define ARM_CPU_PART_NEOVERSE_V3 0xD84 + #define ARM_CPU_PART_CORTEX_X925 0xD85 + #define ARM_CPU_PART_C1_ULTRA 0xD8C ++#define ARM_CPU_PART_C1_PREMIUM 0xD90 + + #define MIDR_CORTEX_A12 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A12) + #define MIDR_CORTEX_A17 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A17) +@@ -137,6 +138,7 @@ + #define MIDR_NEOVERSE_V3 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V3) + #define MIDR_CORTEX_X925 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X925) + #define MIDR_C1_ULTRA MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_C1_ULTRA) ++#define MIDR_C1_PREMIUM MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_C1_PREMIUM) + + /* MPIDR Multiprocessor Affinity Register */ + #define _MPIDR_UP (30) diff --git a/xsa494-4.21.patch b/xsa494-4.21.patch new file mode 100644 index 0000000..d52a0f1 --- /dev/null +++ b/xsa494-4.21.patch @@ -0,0 +1,404 @@ +From 579016a359741044c9076bf0884e1dbab00ab080 Mon Sep 17 00:00:00 2001 +From: Roger Pau Monne +Date: Mon, 16 Mar 2026 11:03:22 +0100 +Subject: [PATCH] x86/mm: accurately track which vCPU page-tables are loaded +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Neither current nor curr_vcpu per-CPU fields accurately track which +page-tables are loaded. There are corner cases when dealing with shadow +paging failures that switch to the idle vCPU page-tables without changing +current or curr_vcpu per-CPU fields. + +Introduce a new per-CPU field that attempts to track which vCPU page-tables +are loaded. Update such tracking when cr3 is changed, and do so in a +region with interrupts disabled, as to avoid handling interrupts with a +mismatch between the vCPU tracking field and the loaded page-tables. + +As a result of this newly more accurate tracking the mapcache override +functionality can be removed: the dom0 PV builder was the only user of it, +and it's updated here to properly signal which vCPU page-tables are loaded +in the calls to switch_cr3_cr4(). + +Note the EFI page-tables have the Xen owned L4 slots copied from the idle +page-tables, so for the effects of the mapcache the EFI page-tables could +use the idle mapcache if it had one. Pass the idle vCPU in the +switch_cr3_cr4() call that switches to the runtime EFI page-tables. + +There are known issues with the use of mapcache in NMI context.  This patch +does not alter the behaviour. + +This is CVE-2026-42488 / XSA-494. + +Fixes: fb0ff49fe9f7 ("x86/shadow: defer releasing of PV's top-level shadow reference") +Signed-off-by: Roger Pau Monné +Acked-by: Andrew Cooper +--- + xen/arch/x86/domain_page.c | 48 ++++++++++++---------------- + xen/arch/x86/flushtlb.c | 5 ++- + xen/arch/x86/include/asm/domain.h | 1 - + xen/arch/x86/include/asm/flushtlb.h | 2 +- + xen/arch/x86/include/asm/processor.h | 3 ++ + xen/arch/x86/mm.c | 4 +-- + xen/arch/x86/pv/dom0_build.c | 12 +++---- + xen/arch/x86/pv/domain.c | 13 ++++++-- + xen/arch/x86/smpboot.c | 1 + + xen/common/efi/common-stub.c | 5 --- + xen/common/efi/runtime.c | 21 +++++------- + xen/include/xen/efi.h | 1 - + 12 files changed, 54 insertions(+), 62 deletions(-) + +diff --git a/xen/arch/x86/domain_page.c b/xen/arch/x86/domain_page.c +index eac5e3304fb8..72c00194f315 100644 +--- a/xen/arch/x86/domain_page.c ++++ b/xen/arch/x86/domain_page.c +@@ -18,48 +18,40 @@ + #include + #include + +-static DEFINE_PER_CPU(struct vcpu *, override); +- + static inline struct vcpu *mapcache_current_vcpu(void) + { +- /* In the common case we use the mapcache of the running VCPU. */ +- struct vcpu *v = this_cpu(override) ?: current; +- +- /* +- * When current isn't properly set up yet, this is equivalent to +- * running in an idle vCPU (callers must check for NULL). +- */ +- if ( !v ) +- return NULL; ++ struct vcpu *v = this_cpu(pgtable_vcpu); ++ struct vcpu *curr = current; + + /* +- * When using efi runtime page tables, we have the equivalent of the idle +- * domain's page tables but current may point at another domain's VCPU. +- * Return NULL as though current is not properly set up yet. ++ * During early boot pgtable_vcpu is not set, callers must handle NULL. ++ * Non-PV domains don't have a mapcache, the directmap covers all physical ++ * address space. + */ +- if ( efi_rs_using_pgtables() ) ++ if ( !v || !is_pv_vcpu(v) ) + return NULL; + + /* +- * If guest_table is NULL, and we are running a paravirtualised guest, +- * then it means we are running on the idle domain's page table and must +- * therefore use its mapcache. ++ * If we are in a lazy context-switch state from a PV vCPU do a full switch ++ * to the idle vCPU now, otherwise an incoming FLUSH_VCPU_STATE IPI would ++ * change the page tables under our feet an invalidate any in-use mapcache ++ * entries. + */ +- if ( unlikely(pagetable_is_null(v->arch.guest_table)) && is_pv_vcpu(v) ) ++ if ( unlikely(this_cpu(curr_vcpu) != curr) ) + { +- /* If we really are idling, perform lazy context switch now. */ +- if ( (v = idle_vcpu[smp_processor_id()]) == current ) +- sync_local_execstate(); ++ ASSERT(curr == idle_vcpu[smp_processor_id()]); ++ sync_local_execstate(); + /* We must now be running on the idle page table. */ + ASSERT(cr3_pa(read_cr3()) == __pa(idle_pg_table)); + } + +- return v; +-} +- +-void __init mapcache_override_current(struct vcpu *v) +-{ +- this_cpu(override) = v; ++ /* ++ * At this point we can guarantee Xen is not in lazy context switch: either ++ * the code above will have synced the state, or an incoming ++ * FLUSH_VCPU_STATE IPI has done so behind our back. Use ACCESS_ONCE to ++ * ensure the compiler never returns the locally cached pgtable_vcpu value. ++ */ ++ return ACCESS_ONCE(this_cpu(pgtable_vcpu)); + } + + #define mapcache_l2_entry(e) ((e) >> PAGETABLE_ORDER) +diff --git a/xen/arch/x86/flushtlb.c b/xen/arch/x86/flushtlb.c +index 09e676c151fa..928bca66b433 100644 +--- a/xen/arch/x86/flushtlb.c ++++ b/xen/arch/x86/flushtlb.c +@@ -111,7 +111,9 @@ static void do_tlb_flush(void) + local_irq_restore(flags); + } + +-void switch_cr3_cr4(unsigned long cr3, unsigned long cr4) ++DEFINE_PER_CPU(struct vcpu *, pgtable_vcpu); ++ ++void switch_cr3_cr4(struct vcpu *v, unsigned long cr3, unsigned long cr4) + { + unsigned long flags, old_cr4; + u32 t = 0; +@@ -155,6 +157,7 @@ void switch_cr3_cr4(unsigned long cr3, unsigned long cr4) + if ( (old_cr4 & X86_CR4_PCIDE) > (cr4 & X86_CR4_PCIDE) ) + cr3 |= X86_CR3_NOFLUSH; + write_cr3(cr3); ++ this_cpu(pgtable_vcpu) = v; + + if ( old_cr4 != cr4 ) + write_cr4(cr4); +diff --git a/xen/arch/x86/include/asm/domain.h b/xen/arch/x86/include/asm/domain.h +index 828f42c3e448..10d2b9fe2546 100644 +--- a/xen/arch/x86/include/asm/domain.h ++++ b/xen/arch/x86/include/asm/domain.h +@@ -75,7 +75,6 @@ struct mapcache_domain { + + int mapcache_domain_init(struct domain *d); + int mapcache_vcpu_init(struct vcpu *v); +-void mapcache_override_current(struct vcpu *v); + + /* x86/64: toggle guest between kernel and user modes. */ + void toggle_guest_mode(struct vcpu *v); +diff --git a/xen/arch/x86/include/asm/flushtlb.h b/xen/arch/x86/include/asm/flushtlb.h +index 7bcbca2b7f31..345677eb72ae 100644 +--- a/xen/arch/x86/include/asm/flushtlb.h ++++ b/xen/arch/x86/include/asm/flushtlb.h +@@ -104,7 +104,7 @@ static inline void invlpg(const void *p) + } + + /* Write pagetable base and implicitly tick the tlbflush clock. */ +-void switch_cr3_cr4(unsigned long cr3, unsigned long cr4); ++void switch_cr3_cr4(struct vcpu *v, unsigned long cr3, unsigned long cr4); + + /* flush_* flag fields: */ + /* +diff --git a/xen/arch/x86/include/asm/processor.h b/xen/arch/x86/include/asm/processor.h +index 2e087c625770..d2cacdfedb74 100644 +--- a/xen/arch/x86/include/asm/processor.h ++++ b/xen/arch/x86/include/asm/processor.h +@@ -328,6 +328,9 @@ DECLARE_PER_CPU(struct tss_page, tss_page); + + DECLARE_PER_CPU(root_pgentry_t *, root_pgt); + ++/* vCPU of the currently loaded page-tables. */ ++DECLARE_PER_CPU(struct vcpu *, pgtable_vcpu); ++ + extern void write_ptbase(struct vcpu *v); + + /* PAUSE (encoding: REP NOP) is a good thing to insert into busy-wait loops. */ +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 2b23bf2e7a75..d02c9862d387 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -535,7 +535,7 @@ void write_ptbase(struct vcpu *v) + cpu_info->pv_cr3 = __pa(this_cpu(root_pgt)); + if ( new_cr4 & X86_CR4_PCIDE ) + cpu_info->pv_cr3 |= get_pcid_bits(v, true); +- switch_cr3_cr4(v->arch.cr3, new_cr4); ++ switch_cr3_cr4(v, v->arch.cr3, new_cr4); + } + else + { +@@ -543,7 +543,7 @@ void write_ptbase(struct vcpu *v) + cpu_info->use_pv_cr3 = false; + cpu_info->xen_cr3 = 0; + /* switch_cr3_cr4() serializes. */ +- switch_cr3_cr4(v->arch.cr3, new_cr4); ++ switch_cr3_cr4(v, v->arch.cr3, new_cr4); + cpu_info->pv_cr3 = 0; + } + } +diff --git a/xen/arch/x86/pv/dom0_build.c b/xen/arch/x86/pv/dom0_build.c +index 37729091dfaa..42bc530c0f0d 100644 +--- a/xen/arch/x86/pv/dom0_build.c ++++ b/xen/arch/x86/pv/dom0_build.c +@@ -828,8 +828,7 @@ static int __init dom0_construct(const struct boot_domain *bd) + update_cr3(v); + + /* We run on dom0's page tables for the final part of the build process. */ +- switch_cr3_cr4(cr3_pa(v->arch.cr3), read_cr4()); +- mapcache_override_current(v); ++ switch_cr3_cr4(v, cr3_pa(v->arch.cr3), read_cr4()); + + /* Copy the OS image and free temporary buffer. */ + elf.dest_base = (void*)vkern_start; +@@ -838,8 +837,7 @@ static int __init dom0_construct(const struct boot_domain *bd) + rc = elf_load_binary(&elf); + if ( rc < 0 ) + { +- mapcache_override_current(NULL); +- switch_cr3_cr4(current->arch.cr3, read_cr4()); ++ switch_cr3_cr4(current, current->arch.cr3, read_cr4()); + printk("Failed to load the kernel binary\n"); + goto out; + } +@@ -850,8 +848,7 @@ static int __init dom0_construct(const struct boot_domain *bd) + if ( (parms.virt_hypercall < v_start) || + (parms.virt_hypercall >= v_end) ) + { +- mapcache_override_current(NULL); +- switch_cr3_cr4(current->arch.cr3, read_cr4()); ++ switch_cr3_cr4(current, current->arch.cr3, read_cr4()); + printk("Invalid HYPERCALL_PAGE field in ELF notes.\n"); + return -EINVAL; + } +@@ -992,8 +989,7 @@ static int __init dom0_construct(const struct boot_domain *bd) + #endif + + /* Return to idle domain's page tables. */ +- mapcache_override_current(NULL); +- switch_cr3_cr4(current->arch.cr3, read_cr4()); ++ switch_cr3_cr4(current, current->arch.cr3, read_cr4()); + + update_domain_wallclock_time(d); + +diff --git a/xen/arch/x86/pv/domain.c b/xen/arch/x86/pv/domain.c +index ef4f442e7332..d9e52f5f88f3 100644 +--- a/xen/arch/x86/pv/domain.c ++++ b/xen/arch/x86/pv/domain.c +@@ -451,6 +451,8 @@ static void _toggle_guest_pt(struct vcpu *v) + pagetable_t old_shadow; + unsigned long cr3; + ++ ASSERT(local_irq_is_enabled()); ++ + v->arch.flags ^= TF_kernel_mode; + guest_update = v->arch.flags & TF_kernel_mode; + old_shadow = update_cr3(v); +@@ -473,15 +475,22 @@ static void _toggle_guest_pt(struct vcpu *v) + { + cr3 &= ~X86_CR3_NOFLUSH; + ++ local_irq_disable(); + if ( unlikely(mfn_eq(pagetable_get_mfn(old_shadow), + maddr_to_mfn(cr3))) ) + { +- cr3 = idle_vcpu[v->processor]->arch.cr3; + /* Also suppress runstate/time area updates below. */ + guest_update = false; ++ ++ cr3 = idle_vcpu[v->processor]->arch.cr3; ++ this_cpu(pgtable_vcpu) = idle_vcpu[v->processor]; + } ++ ++ write_cr3(cr3); ++ local_irq_enable(); + } +- write_cr3(cr3); ++ else ++ write_cr3(cr3); + + if ( !pagetable_is_null(old_shadow) ) + shadow_put_top_level(v->domain, old_shadow); +diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c +index 27628800a821..b37feab3bef4 100644 +--- a/xen/arch/x86/smpboot.c ++++ b/xen/arch/x86/smpboot.c +@@ -1063,6 +1063,7 @@ static int cpu_smpboot_alloc(unsigned int cpu) + + info->current_vcpu = idle_vcpu[cpu]; /* set_current() */ + per_cpu(curr_vcpu, cpu) = idle_vcpu[cpu]; ++ per_cpu(pgtable_vcpu, cpu) = idle_vcpu[cpu]; + + gdt = per_cpu(gdt, cpu) ?: alloc_xenheap_pages(0, memflags); + if ( gdt == NULL ) +diff --git a/xen/common/efi/common-stub.c b/xen/common/efi/common-stub.c +index 77f138a6c574..7b12005bea3f 100644 +--- a/xen/common/efi/common-stub.c ++++ b/xen/common/efi/common-stub.c +@@ -7,11 +7,6 @@ bool efi_enabled(unsigned int feature) + return false; + } + +-bool efi_rs_using_pgtables(void) +-{ +- return false; +-} +- + unsigned long efi_get_time(void) + { + BUG(); +diff --git a/xen/common/efi/runtime.c b/xen/common/efi/runtime.c +index 30d649ca5c1b..feb09acf754c 100644 +--- a/xen/common/efi/runtime.c ++++ b/xen/common/efi/runtime.c +@@ -49,7 +49,6 @@ const CHAR16 *__read_mostly efi_fw_vendor; + const EFI_RUNTIME_SERVICES *__read_mostly efi_rs; + #ifndef CONFIG_ARM /* TODO - disabled until implemented on ARM */ + static DEFINE_SPINLOCK(efi_rs_lock); +-static unsigned int efi_rs_on_cpu = NR_CPUS; + #endif + + UINTN __read_mostly efi_memmap_size; +@@ -92,6 +91,11 @@ struct efi_rs_state efi_rs_enter(void) + if ( mfn_eq(efi_l4_mfn, INVALID_MFN) ) + return state; + ++ /* ++ * If in lazy idle context switch state sync now to avoid an incoming ++ * FLUSH_VCPU_STATE IPI changing the loaded page-tables. ++ */ ++ sync_local_execstate(); + state.cr3 = read_cr3(); + save_fpu_enable(); + asm volatile ( "fnclex; fldcw %0" :: "m" (fcw) ); +@@ -99,8 +103,6 @@ struct efi_rs_state efi_rs_enter(void) + + spin_lock(&efi_rs_lock); + +- efi_rs_on_cpu = smp_processor_id(); +- + /* prevent fixup_page_fault() from doing anything */ + irq_enter(); + +@@ -115,7 +117,8 @@ struct efi_rs_state efi_rs_enter(void) + lgdt(&gdt_desc); + } + +- switch_cr3_cr4(mfn_to_maddr(efi_l4_mfn), read_cr4()); ++ switch_cr3_cr4(idle_vcpu[smp_processor_id()], mfn_to_maddr(efi_l4_mfn), ++ read_cr4()); + + /* + * At the time of writing (2022), no UEFI firwmare is CET-IBT compatible. +@@ -143,7 +146,7 @@ void efi_rs_leave(struct efi_rs_state *state) + if ( state->msr_s_cet ) + wrmsrl(MSR_S_CET, state->msr_s_cet); + +- switch_cr3_cr4(state->cr3, read_cr4()); ++ switch_cr3_cr4(curr, state->cr3, read_cr4()); + if ( is_pv_vcpu(curr) && !is_idle_vcpu(curr) ) + { + struct desc_ptr gdt_desc = { +@@ -154,18 +157,10 @@ void efi_rs_leave(struct efi_rs_state *state) + lgdt(&gdt_desc); + } + irq_exit(); +- efi_rs_on_cpu = NR_CPUS; + spin_unlock(&efi_rs_lock); + vcpu_restore_fpu_nonlazy(curr, true); + } + +-bool efi_rs_using_pgtables(void) +-{ +- return !mfn_eq(efi_l4_mfn, INVALID_MFN) && +- (smp_processor_id() == efi_rs_on_cpu) && +- (read_cr3() == mfn_to_maddr(efi_l4_mfn)); +-} +- + unsigned long efi_get_time(void) + { + EFI_TIME time; +diff --git a/xen/include/xen/efi.h b/xen/include/xen/efi.h +index 723cb8085270..9953197ee553 100644 +--- a/xen/include/xen/efi.h ++++ b/xen/include/xen/efi.h +@@ -40,7 +40,6 @@ extern bool efi_secure_boot; + + void efi_init_memory(void); + bool efi_boot_mem_unused(unsigned long *start, unsigned long *end); +-bool efi_rs_using_pgtables(void); + unsigned long efi_get_time(void); + void efi_halt_system(void); + void efi_reset_system(bool warm); +-- +2.53.0 + From 1e49e52adbc73af767fcb7be16d77f985daabbb6 Mon Sep 17 00:00:00 2001 From: Yaakov Selkowitz Date: Thu, 18 Jun 2026 16:36:54 -0400 Subject: [PATCH 134/137] Rebuilt for openssl 4.0 --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index a9bf097..9c859aa 100644 --- a/xen.spec +++ b/xen.spec @@ -51,7 +51,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.21.1 -Release: 6%{?dist} +Release: 7%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -882,6 +882,9 @@ fi %{_libexecdir}/xen/tests/* %changelog +* Thu Jun 18 2026 Yaakov Selkowitz - 4.21.1-7 +- Rebuilt for openssl 4.0 + * Thu Jun 18 2026 Michael Young - 4.21.1-6 - x86 HVM I/O port list traversal [XSA-491, CVE-2026-42487] - domctl lock open to abuse [XSA-492, CVE-2026-42489, CVE-2026-42490] From 87e40ff1baa988d7407049254ba9504a5c327569 Mon Sep 17 00:00:00 2001 From: Jerry James Date: Thu, 9 Jul 2026 11:46:55 -0600 Subject: [PATCH 135/137] OCaml 5.5.0 rebuild --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 9c859aa..ece45a0 100644 --- a/xen.spec +++ b/xen.spec @@ -51,7 +51,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.21.1 -Release: 7%{?dist} +Release: 8%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -882,6 +882,9 @@ fi %{_libexecdir}/xen/tests/* %changelog +* Thu Jul 09 2026 Jerry James - 4.21.1-8 +- OCaml 5.5.0 rebuild + * Thu Jun 18 2026 Yaakov Selkowitz - 4.21.1-7 - Rebuilt for openssl 4.0 From 5bd254f0062a21e3a9bb302a4dd674a3b2f6bb0d Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Fri, 17 Jul 2026 09:07:32 +0000 Subject: [PATCH 136/137] Rebuilt for https://fedoraproject.org/wiki/Fedora_45_Mass_Rebuild --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index ece45a0..3f9106e 100644 --- a/xen.spec +++ b/xen.spec @@ -51,7 +51,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.21.1 -Release: 8%{?dist} +Release: 9%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -882,6 +882,9 @@ fi %{_libexecdir}/xen/tests/* %changelog +* Fri Jul 17 2026 Fedora Release Engineering - 4.21.1-9 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_45_Mass_Rebuild + * Thu Jul 09 2026 Jerry James - 4.21.1-8 - OCaml 5.5.0 rebuild From c8af4d85b66f3c03fe405e620cda2e12a255d77a Mon Sep 17 00:00:00 2001 From: Python Maint Date: Wed, 22 Jul 2026 10:47:19 +0200 Subject: [PATCH 137/137] Rebuilt for Python 3.15.0b4 ABI change --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 3f9106e..1bea8ef 100644 --- a/xen.spec +++ b/xen.spec @@ -51,7 +51,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.21.1 -Release: 9%{?dist} +Release: 10%{?dist} # Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD URL: http://xen.org/ @@ -882,6 +882,9 @@ fi %{_libexecdir}/xen/tests/* %changelog +* Wed Jul 22 2026 Python Maint - 4.21.1-10 +- Rebuilt for Python 3.15.0b4 ABI change + * Fri Jul 17 2026 Fedora Release Engineering - 4.21.1-9 - Rebuilt for https://fedoraproject.org/wiki/Fedora_45_Mass_Rebuild