From 937578a14e8f0f063d58632bf018917c0af0bbf4 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 4 Apr 2019 20:48:28 +0100 Subject: [PATCH 001/194] update to xen-4.12.0 --- xen.gcc8.temp.fix.patch | 70 -------- xen.gcc9.fixes.patch | 13 +- xen.glibcfix.patch | 20 --- xen.hypervisor.config | 55 ++++++- xen.python.env.patch | 12 +- xen.spec | 65 +++----- xen.stubdom.build.patch | 11 -- xen.use.fedora.ipxe.patch | 33 ---- xen.vwprintw.fix.patch | 11 -- xsa284.patch | 31 ---- xsa285-4.11.patch | 43 ----- xsa287-4.11.patch | 328 -------------------------------------- xsa288-4.11.patch | 308 ----------------------------------- xsa290-4.11-1.patch | 237 --------------------------- xsa290-4.11-2.patch | 71 --------- xsa291-4.11.patch | 53 ------ xsa292.patch | 95 ----------- xsa293-4.11-1.patch | 317 ------------------------------------ xsa293-4.11-2.patch | 260 ------------------------------ xsa294-4.11.patch | 71 --------- 20 files changed, 72 insertions(+), 2032 deletions(-) delete mode 100644 xen.gcc8.temp.fix.patch delete mode 100644 xen.glibcfix.patch delete mode 100644 xen.stubdom.build.patch delete mode 100644 xen.use.fedora.ipxe.patch delete mode 100644 xen.vwprintw.fix.patch delete mode 100644 xsa284.patch delete mode 100644 xsa285-4.11.patch delete mode 100644 xsa287-4.11.patch delete mode 100644 xsa288-4.11.patch delete mode 100644 xsa290-4.11-1.patch delete mode 100644 xsa290-4.11-2.patch delete mode 100644 xsa291-4.11.patch delete mode 100644 xsa292.patch delete mode 100644 xsa293-4.11-1.patch delete mode 100644 xsa293-4.11-2.patch delete mode 100644 xsa294-4.11.patch diff --git a/xen.gcc8.temp.fix.patch b/xen.gcc8.temp.fix.patch deleted file mode 100644 index 74321aa..0000000 --- a/xen.gcc8.temp.fix.patch +++ /dev/null @@ -1,70 +0,0 @@ ---- xen-4.10.0/tools/Makefile.orig 2017-12-13 11:37:59.000000000 +0000 -+++ xen-4.10.0/tools/Makefile 2018-02-27 12:04:44.376192357 +0000 -@@ -8,7 +8,7 @@ - SUBDIRS-y += libs - SUBDIRS-y += libxc - SUBDIRS-y += flask --SUBDIRS-y += fuzz -+#SUBDIRS-y += fuzz - SUBDIRS-y += xenstore - SUBDIRS-y += misc - SUBDIRS-y += examples ---- xen-4.10.0/tools/debugger/kdd/kdd.c.orig 2018-02-22 12:31:57.007039159 +0000 -+++ xen-4.10.0/tools/debugger/kdd/kdd.c 2018-02-22 18:27:37.213653422 +0000 -@@ -687,7 +687,7 @@ - } - } else { - /* 32-bit control-register space starts at 0x[2]cc, for 84 bytes */ -- uint32_t offset = addr; -+/* uint32_t offset = addr; - if (offset > 0x200) - offset -= 0x200; - offset -= 0xcc; -@@ -696,7 +696,9 @@ - len = 0; - } else { - memcpy(buf, ((uint8_t *)&ctrl.c32) + offset, len); -- } -+ } */ -+ /* disable above code due to compile issue for now */ -+ len = 0; - } - - s->txp.cmd.mem.addr = addr; ---- xen-4.10.0/tools/libxl/libxl_arm_acpi.c.orig 2017-12-13 11:37:59.000000000 +0000 -+++ xen-4.10.0/tools/libxl/libxl_arm_acpi.c 2018-02-28 12:37:08.887221211 +0000 -@@ -190,7 +190,7 @@ - struct acpi_table_rsdp *rsdp = (void *)dom->acpi_modules[0].data + offset; - - memcpy(rsdp->signature, "RSD PTR ", sizeof(rsdp->signature)); -- memcpy(rsdp->oem_id, ACPI_OEM_ID, sizeof(rsdp->oem_id)); -+ memcpy(rsdp->oem_id, ACPI_OEM_ID, sizeof(ACPI_OEM_ID)); - rsdp->length = acpitables[RSDP].size; - rsdp->revision = 0x02; - rsdp->xsdt_physical_address = acpitables[XSDT].addr; -@@ -205,11 +205,11 @@ - memcpy(h->signature, sig, 4); - h->length = len; - h->revision = rev; -- memcpy(h->oem_id, ACPI_OEM_ID, sizeof(h->oem_id)); -- memcpy(h->oem_table_id, ACPI_OEM_TABLE_ID, sizeof(h->oem_table_id)); -+ memcpy(h->oem_id, ACPI_OEM_ID, sizeof(ACPI_OEM_ID)); -+ memcpy(h->oem_table_id, ACPI_OEM_TABLE_ID, sizeof(ACPI_OEM_TABLE_ID)); - h->oem_revision = 0; - memcpy(h->asl_compiler_id, ACPI_ASL_COMPILER_ID, -- sizeof(h->asl_compiler_id)); -+ sizeof(ACPI_ASL_COMPILER_ID)); - h->asl_compiler_revision = 0; - h->checksum = 0; - } ---- xen-4.10.0/tools/xenpmd/xenpmd.c.orig 2018-02-28 16:18:50.377726049 +0000 -+++ xen-4.10.0/tools/xenpmd/xenpmd.c 2018-02-28 16:20:31.502426829 +0000 -@@ -352,7 +352,7 @@ - strlen(info->model_number) + - strlen(info->serial_number) + - strlen(info->battery_type) + -- strlen(info->oem_info) + 4)); -+ strlen(info->oem_info) + 4) & 0xff); - write_ulong_lsb_first(val+2, info->present); - write_ulong_lsb_first(val+10, info->design_capacity); - write_ulong_lsb_first(val+18, info->last_full_capacity); diff --git a/xen.gcc9.fixes.patch b/xen.gcc9.fixes.patch index 7c8244b..111b063 100644 --- a/xen.gcc9.fixes.patch +++ b/xen.gcc9.fixes.patch @@ -24,7 +24,7 @@ +++ xen-4.11.1/xen/arch/x86/cpu/mtrr/generic.c 2019-02-10 19:24:09.378805103 +0000 @@ -171,6 +171,9 @@ printk("%sMTRR variable ranges %sabled:\n", level, - mtrr_state.enabled & 2 ? "en" : "dis"); + mtrr_state.enabled ? "en" : "dis"); width = (paddr_bits - PAGE_SHIFT + 3) / 4; + if ( width > 64 ) { + width=64; @@ -32,14 +32,3 @@ for (i = 0; i < num_var_ranges; ++i) { if (mtrr_state.var_ranges[i].mask & MTRR_PHYSMASK_VALID) ---- xen-4.11.1/tools/firmware/rombios/32bit/rombios_compat.h.orig 2018-11-29 14:04:11.000000000 +0000 -+++ xen-4.11.1/tools/firmware/rombios/32bit/rombios_compat.h 2019-02-14 21:16:28.660456669 +0000 -@@ -52,7 +52,7 @@ - Bit16u filler4; - } r8; - } u; --} __attribute__((packed)) pushad_regs_t; -+} pushad_regs_t; - - - diff --git a/xen.glibcfix.patch b/xen.glibcfix.patch deleted file mode 100644 index 7b264d5..0000000 --- a/xen.glibcfix.patch +++ /dev/null @@ -1,20 +0,0 @@ ---- xen-4.7.0/tools/blktap2/control/tap-ctl-allocate.c.orig 2016-06-20 11:38:15.000000000 +0100 -+++ xen-4.7.0/tools/blktap2/control/tap-ctl-allocate.c 2016-09-02 10:07:55.964084808 +0100 -@@ -36,6 +36,7 @@ - #include - #include - #include -+#include - #include - - #include "tap-ctl.h" ---- xen-4.7.0/tools/libxl/libxl_internal.h.orig 2016-06-20 11:38:15.000000000 +0100 -+++ xen-4.7.0/tools/libxl/libxl_internal.h 2016-09-02 17:35:24.853783711 +0100 -@@ -47,6 +47,7 @@ - #include - #include - #include -+#include - - #include - #include diff --git a/xen.hypervisor.config b/xen.hypervisor.config index 50b237c..e3d9369 100644 --- a/xen.hypervisor.config +++ b/xen.hypervisor.config @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Xen/x86 4.10.0 Configuration +# Xen/x86 4.12.0 Configuration # CONFIG_X86_64=y CONFIG_X86=y @@ -17,6 +17,7 @@ CONFIG_SHADOW_PAGING=y # CONFIG_BIGMEM is not set # CONFIG_HVM_FEP is not set CONFIG_TBOOT=y +# CONFIG_XEN_GUEST is not set # # Common Features @@ -25,27 +26,30 @@ CONFIG_COMPAT=y CONFIG_CORE_PARKING=y CONFIG_HAS_ALTERNATIVE=y CONFIG_HAS_EX_TABLE=y -CONFIG_HAS_MEM_ACCESS=y +CONFIG_MEM_ACCESS_ALWAYS_ON=y +CONFIG_MEM_ACCESS=y CONFIG_HAS_MEM_PAGING=y CONFIG_HAS_MEM_SHARING=y CONFIG_HAS_PDX=y +CONFIG_HAS_UBSAN=y CONFIG_HAS_KEXEC=y CONFIG_HAS_GDBSX=y CONFIG_HAS_IOPORTS=y +CONFIG_NEEDS_LIBELF=y CONFIG_KEXEC=y -CONFIG_TMEM=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_ARINC653 is not set CONFIG_SCHED_NULL=y -CONFIG_SCHED_DEFAULT="credit" +CONFIG_SCHED_DEFAULT="credit2" CONFIG_CRYPTO=y CONFIG_LIVEPATCH=y CONFIG_FAST_SYMBOL_LOOKUP=y CONFIG_CMDLINE="" +CONFIG_DOM0_MEM="" # # Device Drivers @@ -60,14 +64,31 @@ CONFIG_HAS_PASSTHROUGH=y CONFIG_HAS_PCI=y CONFIG_VIDEO=y CONFIG_VGA=y +CONFIG_HAS_VPCI=y + +# +# Deprecated Functionality +# +# CONFIG_PV_LDT_PAGING is not set CONFIG_DEFCONFIG_LIST="$ARCH_DEFCONFIG" -CONFIG_XEN_GUEST=n +CONFIG_ARCH_SUPPORTS_INT128=y # # Debugging Options # # CONFIG_DEBUG is not set +# ARM64 settings +CONFIG_64BIT=y +CONFIG_ARM_64=y +CONFIG_ARM=y +CONFIG_GICV3=y +# CONFIG_NEW_VGIC is not set +CONFIG_SBSA_VUART_CONSOLE=y +CONFIG_ARM_SSBD=y +CONFIG_HARDEN_BRANCH_PREDICTOR=y + +# # ARM errata workaround via the alternative framework # CONFIG_ARM64_ERRATUM_827319=y @@ -75,6 +96,24 @@ CONFIG_ARM64_ERRATUM_824069=y CONFIG_ARM64_ERRATUM_819472=y CONFIG_ARM64_ERRATUM_832075=y CONFIG_ARM64_ERRATUM_834220=y +CONFIG_ARM64_HARDEN_BRANCH_PREDICTOR=y +CONFIG_ALL_PLAT=y +# CONFIG_QEMU is not set +# CONFIG_RCAR3 is not set +# CONFIG_MPSOC is not set +# CONFIG_NO_PLAT is not set +CONFIG_ALL64_PLAT=y +CONFIG_MPSOC_PLATFORM=y +CONFIG_HAS_DEVICE_TREE=y +CONFIG_HAS_CADENCE_UART=y +CONFIG_HAS_MVEBU=y +CONFIG_HAS_PL011=y +CONFIG_HAS_SCIF=y +CONFIG_ARM_SMMU=y -CONFIG_SBSA_VUART_CONSOLE=y -# CONFIG_NEW_VGIC is not set +# ARM32 settings +CONFIG_ALL32_PLAT=y +CONFIG_ARM32_HARDEN_BRANCH_PREDICTOR=y +CONFIG_ARM_32=y +CONFIG_HAS_EXYNOS4210=y +CONFIG_HAS_OMAP=y diff --git a/xen.python.env.patch b/xen.python.env.patch index e33d370..8844b18 100644 --- a/xen.python.env.patch +++ b/xen.python.env.patch @@ -4,19 +4,11 @@ $(INSTALL_DIR) $(DESTDIR)$(sbindir) $(INSTALL_PROG) xenbaked $(DESTDIR)$(sbindir)/xenbaked $(INSTALL_PROG) xentrace_setmask $(DESTDIR)$(sbindir)/xentrace_setmask -- $(INSTALL_PROG) xenmon.py $(DESTDIR)$(sbindir)/xenmon.py -+ $(INSTALL_PYTHON_PROG) xenmon.py $(DESTDIR)$(sbindir)/xenmon.py +- $(INSTALL_PROG) xenmon.py $(DESTDIR)$(sbindir)/xenmon ++ $(INSTALL_PYTHON_PROG) xenmon.py $(DESTDIR)$(sbindir)/xenmon .PHONY: uninstall uninstall: ---- xen-4.11.0/tools/misc/xen-ringwatch.orig 2018-07-09 14:47:19.000000000 +0100 -+++ xen-4.11.0/tools/misc/xen-ringwatch 2018-09-10 21:18:53.191063128 +0100 -@@ -1,4 +1,4 @@ --#!/usr/bin/python -+#!/usr/bin/python2 - # - # Copyright (C) 2011 Citrix Systems, Inc. - # --- xen-4.11.0/tools/python/Makefile.orig 2018-07-09 14:47:19.000000000 +0100 +++ xen-4.11.0/tools/python/Makefile 2018-09-10 21:21:07.097979007 +0100 @@ -20,8 +20,8 @@ diff --git a/xen.spec b/xen.spec index b11a535..7835edc 100644 --- a/xen.spec +++ b/xen.spec @@ -62,12 +62,12 @@ %endif # Hypervisor ABI -%define hv_abi 4.11 +%define hv_abi 4.12 Summary: Xen is a virtual machine monitor Name: xen -Version: 4.11.1 -Release: 4%{?dist} +Version: 4.12.0 +Release: 0%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -83,7 +83,6 @@ Source15: polarssl-1.1.4-gpl.tgz Source21: xen.hypervisor.config Patch1: xen-net-disable-iptables-on-bridge.patch -Patch2: xen.use.fedora.ipxe.patch Patch3: xen.fedora.efi.build.patch Patch4: CVE-2014-0150.patch Patch5: xen.fedora.systemd.patch @@ -108,7 +107,6 @@ 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 -Patch28: xen.glibcfix.patch Patch29: qemu.trad.CVE-2016-8669.patch Patch30: qemu.trad.CVE-2016-8910.patch Patch31: qemu.trad.bug1399055.patch @@ -120,23 +118,9 @@ 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.gcc8.temp.fix.patch Patch41: xen.drop.brctl.patch -Patch42: xen.stubdom.build.patch -Patch44: xen.vwprintw.fix.patch Patch45: xen.python.env.patch Patch46: xen.gcc9.fixes.patch -Patch47: xsa284.patch -Patch48: xsa285-4.11.patch -Patch49: xsa287-4.11.patch -Patch50: xsa288-4.11.patch -Patch51: xsa290-4.11-1.patch -Patch52: xsa290-4.11-2.patch -Patch53: xsa291-4.11.patch -Patch54: xsa292.patch -Patch55: xsa293-4.11-1.patch -Patch56: xsa293-4.11-2.patch -Patch57: xsa294-4.11.patch %if %build_qemutrad @@ -331,29 +315,13 @@ manage Xen virtual machines. %patch23 -p1 %patch24 -p1 %patch25 -p1 -%patch28 -p1 %patch33 -p1 %patch34 -p1 %patch37 -p1 -%patch2 -p1 %patch3 -p1 -%patch40 -p1 %patch41 -p1 -%patch42 -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 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -425,6 +393,9 @@ CONFIG_EXTRA="" %if %build_ovmf CONFIG_EXTRA="$CONFIG_EXTRA --with-system-ovmf=%{_libexecdir}/%{name}/boot/ovmf.bin" %endif +%ifnarch armv7hl aarch64 +CONFIG_EXTRA="$CONFIG_EXTRA --with-system-ipxe=/usr/share/ipxe" +%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 make %{?_smp_mflags} %{?ocaml_flags} prefix=/usr tools %if %build_docs @@ -692,7 +663,7 @@ fi %files libs %{_libdir}/*.so.* -%{_libdir}/fs +%{_libdir}/xenfsimage # All runtime stuff except for XenD/xm python stuff %files runtime @@ -754,7 +725,7 @@ fi %{_mandir}/man7/xl-numa-placement.7.gz %endif -%{python2_sitearch}/fsimage.so +%{python2_sitearch}/xenfsimage.so %{python2_sitearch}/grub %{python2_sitearch}/pygrub-*.egg-info @@ -804,9 +775,8 @@ fi %{_bindir}/xencov_split %ifnarch armv7hl aarch64 %{_sbindir}/gdbsx -%{_sbindir}/kdd +%{_sbindir}/xen-kdd %endif -%{_sbindir}/xen-bugtool %ifnarch armv7hl aarch64 %{_sbindir}/xen-hptool %{_sbindir}/xen-hvmcrash @@ -815,7 +785,7 @@ fi %{_sbindir}/xen-tmem-list-parse %{_sbindir}/xenconsoled %{_sbindir}/xenlockprof -%{_sbindir}/xenmon.py* +%{_sbindir}/xenmon %{_sbindir}/xentop %{_sbindir}/xentrace_setmask %{_sbindir}/xenbaked @@ -828,14 +798,11 @@ fi %ifnarch armv7hl aarch64 %{_sbindir}/xen-lowmemd %endif -%{_sbindir}/xen-ringwatch %{_sbindir}/xencov %ifnarch armv7hl aarch64 %{_sbindir}/xen-mfndump %endif -%ifnarch armv7hl aarch64 %{_bindir}/xenalyze -%endif %{_sbindir}/xentrace %{_sbindir}/xentrace_setsize %ifnarch armv7hl aarch64 @@ -843,6 +810,7 @@ fi %endif %{_sbindir}/xen-livepatch %{_sbindir}/xen-diag +/usr/lib/debug/usr/bin/xenstore* # Xen logfiles %dir %attr(0700,root,root) %{_localstatedir}/log/xen @@ -905,6 +873,17 @@ fi %endif %changelog +* Thu Apr 04 2019 Michael Young - 4.12.0-0 +- update to 4.12.0 (#1694695) + remove patches for issues now fixed upstream + replace xen.use.fedora.ipxe.patch with --with-system-ipxe + drop xen.glibcfix.patch xen.gcc8.temp.fix.patch which are no longer needed + adjust xen.python.env.patch xen.gcc9.fixes.patch + xen.hypervisor.config refresh + kdd is now xen-kdd, xenmon.py is now xenmon, fsimage.so is now xenfsimage.so + fs libdir is now xenfsimage libdir + xen-ringwatch xen-bugtool have been dropped + * Tue Mar 05 2019 Michael Young - 4.11.1-4 - xen: various flaws (#1685577) grant table transfer issues on large hosts [XSA-284] diff --git a/xen.stubdom.build.patch b/xen.stubdom.build.patch deleted file mode 100644 index 05d1b7c..0000000 --- a/xen.stubdom.build.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- xen-4.11.0/stubdom/Makefile.orig 2018-07-09 14:47:19.000000000 +0100 -+++ xen-4.11.0/stubdom/Makefile 2018-07-11 22:33:00.764078143 +0100 -@@ -87,7 +87,7 @@ - patch -d $@ -p0 < newlib.patch - patch -d $@ -p0 < newlib-chk.patch - patch -d $@ -p1 < newlib-stdint-size_max-fix-from-1.17.0.patch -- find $@ -type f | xargs perl -i.bak \ -+ find $@ -type f | xargs egrep -l "tzname|daylight|timezone" | xargs perl -i.bak \ - -pe 's/\b_(tzname|daylight|timezone)\b/$$1/g' - touch $@ - diff --git a/xen.use.fedora.ipxe.patch b/xen.use.fedora.ipxe.patch deleted file mode 100644 index 49871c6..0000000 --- a/xen.use.fedora.ipxe.patch +++ /dev/null @@ -1,33 +0,0 @@ ---- xen-4.2.0/tools/firmware/hvmloader/Makefile.orig 2012-05-27 21:57:04.481812859 +0100 -+++ xen-4.2.0/tools/firmware/hvmloader/Makefile 2012-06-02 18:52:44.935034128 +0100 -@@ -48,7 +48,7 @@ - else - CIRRUSVGA_ROM := ../vgabios/VGABIOS-lgpl-latest.cirrus.bin - endif --ETHERBOOT_ROMS := $(addprefix ../etherboot/ipxe/src/bin/, $(addsuffix .rom, $(ETHERBOOT_NICS))) -+ETHERBOOT_ROMS := $(addprefix /usr/share/ipxe/, $(addsuffix .rom, $(ETHERBOOT_NICS))) - endif - - ROMS := ---- xen-4.2.0/Config.mk.orig 2012-05-27 21:57:04.479812884 +0100 -+++ xen-4.2.0/Config.mk 2012-06-02 18:55:14.087169469 +0100 -@@ -206,7 +206,7 @@ - - SEABIOS_UPSTREAM_REVISION ?= rel-1.11.1 - --ETHERBOOT_NICS ?= rtl8139 8086100e -+ETHERBOOT_NICS ?= 10ec8139 8086100e - - - QEMU_TRADITIONAL_REVISION ?= xen-4.11.1 ---- xen-4.2.0/tools/firmware/Makefile.orig 2012-05-27 21:57:04.480812871 +0100 -+++ xen-4.2.0/tools/firmware/Makefile 2012-06-02 19:03:52.254691484 +0100 -@@ -10,7 +10,7 @@ - SUBDIRS-$(CONFIG_SEABIOS) += seabios-dir - SUBDIRS-$(CONFIG_ROMBIOS) += rombios - SUBDIRS-$(CONFIG_ROMBIOS) += vgabios --SUBDIRS-$(CONFIG_ROMBIOS) += etherboot -+#SUBDIRS-$(CONFIG_ROMBIOS) += etherboot - SUBDIRS-$(CONFIG_PV_SHIM) += xen-dir - SUBDIRS-y += hvmloader - diff --git a/xen.vwprintw.fix.patch b/xen.vwprintw.fix.patch deleted file mode 100644 index 5223f67..0000000 --- a/xen.vwprintw.fix.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- xen-4.11.0/tools/xenstat/xentop/xentop.c.orig 2018-07-09 14:47:19.000000000 +0100 -+++ xen-4.11.0/tools/xenstat/xentop/xentop.c 2018-08-14 22:41:08.035898962 +0100 -@@ -301,7 +301,7 @@ - if (!batch) { - if((current_row() < lines()-1)) { - va_start(args, fmt); -- vwprintw(stdscr, (curses_str_t)fmt, args); -+ vw_printw(stdscr, (curses_str_t)fmt, args); - va_end(args); - } - } else { diff --git a/xsa284.patch b/xsa284.patch deleted file mode 100644 index 0b5dcd0..0000000 --- a/xsa284.patch +++ /dev/null @@ -1,31 +0,0 @@ -From: Jan Beulich -Subject: gnttab: set page refcount for copy-on-grant-transfer - -Commit 5cc77f9098 ("32-on-64: Fix domain address-size clamping, -implement"), which introduced this functionality, took care of clearing -the old page's PGC_allocated, but failed to set the bit (and install the -associated reference) on the newly allocated one. Furthermore the "mfn" -local variable was never updated, and hence the wrong MFN was passed to -guest_physmap_add_page() (and back to the destination domain) in this -case, leading to an IOMMU mapping into an unowned page. - -Ideally the code would use assign_pages(), but the call to -gnttab_prepare_for_transfer() sits in the middle of the actions -mirroring that function. - -This is XSA-284. - -Signed-off-by: Jan Beulich -Acked-by: George Dunlap - ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -2183,6 +2183,8 @@ gnttab_transfer( - page->count_info &= ~(PGC_count_mask|PGC_allocated); - free_domheap_page(page); - page = new_page; -+ page->count_info = PGC_allocated | 1; -+ mfn = page_to_mfn(page); - } - - spin_lock(&e->page_alloc_lock); diff --git a/xsa285-4.11.patch b/xsa285-4.11.patch deleted file mode 100644 index 4d1cde4..0000000 --- a/xsa285-4.11.patch +++ /dev/null @@ -1,43 +0,0 @@ -From: Jan Beulich -Subject: IOMMU/x86: fix type ref-counting race upon IOMMU page table construction - -When arch_iommu_populate_page_table() gets invoked for an already -running guest, simply looking at page types once isn't enough, as they -may change at any time. Add logic to re-check the type after having -mapped the page, unmapping it again if needed. - -This is XSA-285. - -Signed-off-by: Jan Beulich -Tentatively-Acked-by: Andrew Cooper - ---- a/xen/drivers/passthrough/x86/iommu.c -+++ b/xen/drivers/passthrough/x86/iommu.c -@@ -68,6 +68,27 @@ int arch_iommu_populate_page_table(struct domain *d) - rc = hd->platform_ops->map_page(d, gfn, mfn, - IOMMUF_readable | - IOMMUF_writable); -+ -+ /* -+ * We may be working behind the back of a running guest, which -+ * may change the type of a page at any time. We can't prevent -+ * this (for instance, by bumping the type count while mapping -+ * the page) without causing legitimate guest type-change -+ * operations to fail. So after adding the page to the IOMMU, -+ * check again to make sure this is still valid. NB that the -+ * writable entry in the iommu is harmless until later, when -+ * the actual device gets assigned. -+ */ -+ if ( !rc && !is_hvm_domain(d) && -+ ((page->u.inuse.type_info & PGT_type_mask) != -+ PGT_writable_page) ) -+ { -+ rc = hd->platform_ops->unmap_page(d, gfn); -+ /* If the type changed yet again, simply force a retry. */ -+ if ( !rc && ((page->u.inuse.type_info & PGT_type_mask) == -+ PGT_writable_page) ) -+ rc = -ERESTART; -+ } - } - if ( rc ) - { diff --git a/xsa287-4.11.patch b/xsa287-4.11.patch deleted file mode 100644 index 8563560..0000000 --- a/xsa287-4.11.patch +++ /dev/null @@ -1,328 +0,0 @@ -From 67620c1ccb13f7b58645f48248ba1f408b021fdc Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Fri, 18 Jan 2019 15:00:34 +0000 -Subject: [PATCH] steal_page: Get rid of bogus struct page states - -The original rules for `struct page` required the following invariants -at all times: - -- refcount > 0 implies owner != NULL -- PGC_allocated implies refcount > 0 - -steal_page, in a misguided attempt to protect against unknown races, -violates both of these rules, thus introducing other races: - -- Temporarily, the count_info has the refcount go to 0 while - PGC_allocated is set - -- It explicitly returns the page PGC_allocated set, but owner == NULL - and page not on the page_list. - -The second one meant that page_get_owner_and_reference() could return -NULL even after having successfully grabbed a reference on the page, -leading the caller to leak the reference (since "couldn't get ref" and -"got ref but no owner" look the same). - -Furthermore, rather than grabbing a page reference to ensure that the -owner doesn't change under its feet, it appears to rely on holding -d->page_alloc lock to prevent this. - -Unfortunately, this is ineffective: page->owner remains non-NULL for -some time after the count has been set to 0; meaning that it would be -entirely possible for the page to be freed and re-allocated to a -different domain between the page_get_owner() check and the count_info -check. - -Modify steal_page to instead follow the appropriate access discipline, -taking the page through series of states similar to being freed and -then re-allocated with MEMF_no_owner: - -- Grab an extra reference to make sure we don't race with anyone else - freeing the page - -- Drop both references and PGC_allocated atomically, so that (if -successful), anyone else trying to grab a reference will fail - -- Attempt to reset Xen's mappings - -- Reset the rest of the state. - -Then, modify the two callers appropriately: - -- Leave count_info alone (it's already been cleared) -- Call free_domheap_page() directly if appropriate -- Call assign_pages() rather than open-coding a partial assign - -With all callers to assign_pages() now passing in pages with the -type_info field clear, tighten the respective assertion there. - -This is XSA-287. - -Signed-off-by: George Dunlap -Signed-off-by: Jan Beulich ---- - xen/arch/x86/mm.c | 84 ++++++++++++++++++++++++++++------------ - xen/common/grant_table.c | 20 +++++----- - xen/common/memory.c | 19 +++++---- - xen/common/page_alloc.c | 2 +- - 4 files changed, 83 insertions(+), 42 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 6509035a5c..d8ff58c901 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -3966,70 +3966,106 @@ int donate_page( - return -EINVAL; - } - -+/* -+ * Steal page will attempt to remove `page` from domain `d`. Upon -+ * return, `page` will be in a state similar to the state of a page -+ * returned from alloc_domheap_page() with MEMF_no_owner set: -+ * - refcount 0 -+ * - type count cleared -+ * - owner NULL -+ * - page caching attributes cleaned up -+ * - removed from the domain's page_list -+ * -+ * If MEMF_no_refcount is not set, the domain's tot_pages will be -+ * adjusted. If this results in the page count falling to 0, -+ * put_domain() will be called. -+ * -+ * The caller should either call free_domheap_page() to free the -+ * page, or assign_pages() to put it back on some domain's page list. -+ */ - int steal_page( - struct domain *d, struct page_info *page, unsigned int memflags) - { - unsigned long x, y; - bool drop_dom_ref = false; -- const struct domain *owner = dom_xen; -+ const struct domain *owner; -+ int rc; - - if ( paging_mode_external(d) ) - return -EOPNOTSUPP; - -- spin_lock(&d->page_alloc_lock); -- -- if ( is_xen_heap_page(page) || ((owner = page_get_owner(page)) != d) ) -+ /* Grab a reference to make sure the page doesn't change under our feet */ -+ rc = -EINVAL; -+ if ( !(owner = page_get_owner_and_reference(page)) ) - goto fail; - -+ if ( owner != d || is_xen_heap_page(page) ) -+ goto fail_put; -+ - /* -- * We require there is just one reference (PGC_allocated). We temporarily -- * drop this reference now so that we can safely swizzle the owner. -+ * We require there are exactly two references -- the one we just -+ * took, and PGC_allocated. We temporarily drop both these -+ * references so that the page becomes effectively non-"live" for -+ * the domain. - */ - y = page->count_info; - do { - x = y; -- if ( (x & (PGC_count_mask|PGC_allocated)) != (1 | PGC_allocated) ) -- goto fail; -- y = cmpxchg(&page->count_info, x, x & ~PGC_count_mask); -+ if ( (x & (PGC_count_mask|PGC_allocated)) != (2 | PGC_allocated) ) -+ goto fail_put; -+ y = cmpxchg(&page->count_info, x, x & ~(PGC_count_mask|PGC_allocated)); - } while ( y != x ); - - /* -- * With the sole reference dropped temporarily, no-one can update type -- * information. Type count also needs to be zero in this case, but e.g. -- * PGT_seg_desc_page may still have PGT_validated set, which we need to -- * clear before transferring ownership (as validation criteria vary -- * depending on domain type). -+ * NB this is safe even if the page ends up being given back to -+ * the domain, because the count is zero: subsequent mappings will -+ * cause the cache attributes to be re-instated inside -+ * get_page_from_l1e(). -+ */ -+ if ( (rc = cleanup_page_cacheattr(page)) ) -+ { -+ /* -+ * Couldn't fixup Xen's mappings; put things the way we found -+ * it and return an error -+ */ -+ page->count_info |= PGC_allocated | 1; -+ goto fail; -+ } -+ -+ /* -+ * With the reference count now zero, nobody can grab references -+ * to do anything else with the page. Return the page to a state -+ * that it might be upon return from alloc_domheap_pages with -+ * MEMF_no_owner set. - */ -+ spin_lock(&d->page_alloc_lock); -+ - BUG_ON(page->u.inuse.type_info & (PGT_count_mask | PGT_locked | - PGT_pinned)); - page->u.inuse.type_info = 0; -- -- /* Swizzle the owner then reinstate the PGC_allocated reference. */ - page_set_owner(page, NULL); -- y = page->count_info; -- do { -- x = y; -- BUG_ON((x & (PGC_count_mask|PGC_allocated)) != PGC_allocated); -- } while ( (y = cmpxchg(&page->count_info, x, x | 1)) != x ); -+ page_list_del(page, &d->page_list); - - /* Unlink from original owner. */ - if ( !(memflags & MEMF_no_refcount) && !domain_adjust_tot_pages(d, -1) ) - drop_dom_ref = true; -- page_list_del(page, &d->page_list); - - spin_unlock(&d->page_alloc_lock); -+ - if ( unlikely(drop_dom_ref) ) - put_domain(d); -+ - return 0; - -+ fail_put: -+ put_page(page); - fail: -- spin_unlock(&d->page_alloc_lock); - gdprintk(XENLOG_WARNING, "Bad steal mfn %" PRI_mfn - " from d%d (owner d%d) caf=%08lx taf=%" PRtype_info "\n", - mfn_x(page_to_mfn(page)), d->domain_id, - owner ? owner->domain_id : DOMID_INVALID, - page->count_info, page->u.inuse.type_info); -- return -EINVAL; -+ return rc; - } - - static int __do_update_va_mapping( -diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c -index c0585d33f4..656fad1b42 100644 ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -2179,7 +2179,7 @@ gnttab_transfer( - rcu_unlock_domain(e); - put_gfn_and_copyback: - put_gfn(d, gop.mfn); -- page->count_info &= ~(PGC_count_mask|PGC_allocated); -+ /* The count_info has already been cleaned */ - free_domheap_page(page); - goto copyback; - } -@@ -2202,10 +2202,9 @@ gnttab_transfer( - - copy_domain_page(page_to_mfn(new_page), mfn); - -- page->count_info &= ~(PGC_count_mask|PGC_allocated); -+ /* The count_info has already been cleared */ - free_domheap_page(page); - page = new_page; -- page->count_info = PGC_allocated | 1; - mfn = page_to_mfn(page); - } - -@@ -2245,12 +2244,17 @@ gnttab_transfer( - */ - spin_unlock(&e->page_alloc_lock); - okay = gnttab_prepare_for_transfer(e, d, gop.ref); -- spin_lock(&e->page_alloc_lock); - -- if ( unlikely(!okay) || unlikely(e->is_dying) ) -+ if ( unlikely(!okay || assign_pages(e, page, 0, MEMF_no_refcount)) ) - { -- bool_t drop_dom_ref = !domain_adjust_tot_pages(e, -1); -+ bool drop_dom_ref; - -+ /* -+ * Need to grab this again to safely free our "reserved" -+ * page in the page total -+ */ -+ spin_lock(&e->page_alloc_lock); -+ drop_dom_ref = !domain_adjust_tot_pages(e, -1); - spin_unlock(&e->page_alloc_lock); - - if ( okay /* i.e. e->is_dying due to the surrounding if() */ ) -@@ -2263,10 +2267,6 @@ gnttab_transfer( - goto unlock_and_copyback; - } - -- page_list_add_tail(page, &e->page_list); -- page_set_owner(page, e); -- -- spin_unlock(&e->page_alloc_lock); - put_gfn(d, gop.mfn); - - TRACE_1D(TRC_MEM_PAGE_GRANT_TRANSFER, e->domain_id); -diff --git a/xen/common/memory.c b/xen/common/memory.c -index 4fb7962c79..f71163221f 100644 ---- a/xen/common/memory.c -+++ b/xen/common/memory.c -@@ -675,20 +675,22 @@ static long memory_exchange(XEN_GUEST_HANDLE_PARAM(xen_memory_exchange_t) arg) - * Success! Beyond this point we cannot fail for this chunk. - */ - -- /* Destroy final reference to each input page. */ -+ /* -+ * These pages have already had owner and reference cleared. -+ * Do the final two steps: Remove from the physmap, and free -+ * them. -+ */ - while ( (page = page_list_remove_head(&in_chunk_list)) ) - { - unsigned long gfn; - -- if ( !test_and_clear_bit(_PGC_allocated, &page->count_info) ) -- BUG(); - mfn = page_to_mfn(page); - gfn = mfn_to_gmfn(d, mfn_x(mfn)); - /* Pages were unshared above */ - BUG_ON(SHARED_M2P(gfn)); - if ( guest_physmap_remove_page(d, _gfn(gfn), mfn, 0) ) - domain_crash(d); -- put_page(page); -+ free_domheap_page(page); - } - - /* Assign each output page to the domain. */ -@@ -761,13 +763,16 @@ static long memory_exchange(XEN_GUEST_HANDLE_PARAM(xen_memory_exchange_t) arg) - * chunks succeeded. - */ - fail: -- /* Reassign any input pages we managed to steal. */ -+ /* -+ * Reassign any input pages we managed to steal. NB that if the assign -+ * fails again, we're on the hook for freeing the page, since we've already -+ * cleared PGC_allocated. -+ */ - while ( (page = page_list_remove_head(&in_chunk_list)) ) - if ( assign_pages(d, page, 0, MEMF_no_refcount) ) - { - BUG_ON(!d->is_dying); -- if ( test_and_clear_bit(_PGC_allocated, &page->count_info) ) -- put_page(page); -+ free_domheap_page(page); - } - - dying: -diff --git a/xen/common/page_alloc.c b/xen/common/page_alloc.c -index 482f0988f7..52da7762e3 100644 ---- a/xen/common/page_alloc.c -+++ b/xen/common/page_alloc.c -@@ -2221,7 +2221,7 @@ int assign_pages( - for ( i = 0; i < (1 << order); i++ ) - { - ASSERT(page_get_owner(&pg[i]) == NULL); -- ASSERT((pg[i].count_info & ~(PGC_allocated | 1)) == 0); -+ ASSERT(!pg[i].count_info); - page_set_owner(&pg[i], d); - smp_wmb(); /* Domain pointer must be visible before updating refcnt. */ - pg[i].count_info = PGC_allocated | 1; --- -2.20.1 - diff --git a/xsa288-4.11.patch b/xsa288-4.11.patch deleted file mode 100644 index 9dc4e37..0000000 --- a/xsa288-4.11.patch +++ /dev/null @@ -1,308 +0,0 @@ -From 5d3a02e320f88747b75e3794c2e694284ae64c3e Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Wed, 23 Jan 2019 11:57:46 +0000 -Subject: [PATCH] xen: Make coherent PV IOMMU discipline - -In order for a PV domain to set up DMA from a passed-through device to -one of its pages, the page must be mapped in the IOMMU. On the other -hand, before a PV page may be used as a "special" page type (such as a -pagetable or descriptor table), it _must not_ be writable in the IOMMU -(otherwise a malicious guest could DMA arbitrary page tables into the -memory, bypassing Xen's safety checks); and Xen's current rule is to -have such pages not in the IOMMU at all. - -At the moment, in order to accomplish this, the code borrows HVM -domain's "physmap" concept: When a page is assigned to a guest, -guess_physmap_add_entry() is called, which for PV guests, will create -a writable IOMMU mapping; and when a page is removed, -guest_physmap_remove_entry() is called, which will remove the mapping. - -Additionally, when a page gains the PGT_writable page type, the page -will be added into the IOMMU; and when the page changes away from a -PGT_writable type, the page will be removed from the IOMMU. - -Unfortunately, borrowing the "physmap" concept from HVM domains is -problematic. HVM domains have a lock on their p2m tables, ensuring -synchronization between modifications to the p2m; and all hypercall -parameters must first be translated through the p2m before being used. - -Trying to mix this locked-and-gated approach with PV's lock-free -approach leads to several races and inconsistencies: - -* A race between a page being assigned and it being put into the - physmap; for example: - - P1: call populate_physmap() { A = allocate_domheap_pages() } - - P2: Guess page A's mfn, and call decrease_reservation(A). A is owned by the domain, - and so Xen will clear the PGC_allocated bit and free the page - - P1: finishes populate_physmap() { guest_physmap_add_entry() } - - Now the domain has a writable IOMMU mapping to a page it no longer owns. - -* Pages start out as type PGT_none, but with a writable IOMMU mapping. - If a guest uses a page as a page table without ever having created a - writable mapping, the IOMMU mapping will not be removed; the guest - will have a writable IOMMU mapping to a page it is currently using - as a page table. - -* A newly-allocated page can be DMA'd into with no special actions on - the part of the guest; However, if a page is promoted to a - non-writable type, the page must be mapped with a writable type before - DMA'ing to it again, or the transaction will fail. - -To fix this, do away with the "PV physmap" concept entirely, and -replace it with the following IOMMU discipline for PV guests: - - (type == PGT_writable) <=> in iommu (even if type_count == 0) - - Upon a final put_page(), check to see if type is PGT_writable; if so, - iommu_unmap. - -In order to achieve that: - -- Remove PV IOMMU related code from guest_physmap_* - -- Repurpose cleanup_page_cacheattr() into a general - cleanup_page_mappings() function, which will both fix up Xen - mappings for pages with special cache attributes, and also check for - a PGT_writable type and remove pages if appropriate. - -- For compatibility with current guests, grab-and-release a - PGT_writable_page type for PV guests in guest_physmap_add_entry(). - This will cause most "normal" guest pages to start out life with - PGT_writable_page type (and thus an IOMMU mapping), but no type - count (so that they can be used as special cases at will). - -Also, note that there is one exception to to the "PGT_writable => in -iommu" rule: xenheap pages shared with guests may be given a -PGT_writable type with one type reference. This reference prevents -the type from changing, which in turn prevents page from gaining an -IOMMU mapping in get_page_type(). It's not clear whether this was -intentional or not, but it's not something to change in a security -update. - -This is XSA-288. - -Reported-by: Paul Durrant -Signed-off-by: George Dunlap -Signed-off-by: Jan Beulich ---- - xen/arch/x86/mm.c | 95 +++++++++++++++++++++++++++++++++++++++---- - xen/arch/x86/mm/p2m.c | 57 ++++++++++++-------------- - 2 files changed, 111 insertions(+), 41 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index d8ff58c901..ad8aacad68 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -81,6 +81,22 @@ - * OS's, which will generally use the WP bit to simplify copy-on-write - * implementation (in that case, OS wants a fault when it writes to - * an application-supplied buffer). -+ * -+ * PV domUs and IOMMUs: -+ * -------------------- -+ * For a guest to be able to DMA into a page, that page must be in the -+ * domain's IOMMU. However, we *must not* allow DMA into 'special' -+ * pages (such as page table pages, descriptor tables, &c); and we -+ * must also ensure that mappings are removed from the IOMMU when the -+ * page is freed. Finally, it is inherently racy to make any changes -+ * based on a page with a non-zero type count. -+ * -+ * To that end, we put the page in the IOMMU only when a page gains -+ * the PGT_writeable type; and we remove the page when it loses the -+ * PGT_writeable type (not when the type count goes to zero). This -+ * effectively protects the IOMMU status update with the type count we -+ * have just acquired. We must also check for PGT_writable type when -+ * doing the final put_page(), and remove it from the iommu if so. - */ - - #include -@@ -2275,19 +2291,79 @@ static int mod_l4_entry(l4_pgentry_t *pl4e, - return rc; - } - --static int cleanup_page_cacheattr(struct page_info *page) -+/* -+ * In the course of a page's use, it may have caused other secondary -+ * mappings to have changed: -+ * - Xen's mappings may have been changed to accomodate the requested -+ * cache attibutes -+ * - A page may have been put into the IOMMU of a PV guest when it -+ * gained a writable mapping. -+ * -+ * Now that the page is being freed, clean up these mappings if -+ * appropriate. NB that at this point the page is still "allocated", -+ * but not "live" (i.e., its refcount is 0), so it's safe to read the -+ * count_info, owner, and type_info without synchronization. -+ */ -+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 ( likely(cacheattr == 0) ) -- return 0; -+ /* -+ * 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; - -- page->count_info &= ~PGC_cacheattr_mask; -+ BUG_ON(is_xen_heap_page(page)); - -- BUG_ON(is_xen_heap_page(page)); -+ rc = update_xen_mappings(mfn, 0); -+ } - -- return update_xen_mappings(mfn_x(page_to_mfn(page)), 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 -+ * implementation-specific code, rather than by get_page_type(). As such: -+ * - They aren't expected to have an IOMMU mapping, and -+ * - We don't necessarily expect the type count to be zero when the final -+ * put_page happens. -+ * -+ * Go ahead and attemp to call iommu_unmap() on xenheap pages anyway, just -+ * in case; but only ASSERT() that the type count is zero and remove the -+ * PGT_writable type for non-xenheap pages. -+ */ -+ if ( (page->u.inuse.type_info & PGT_type_mask) == PGT_writable_page ) -+ { -+ struct domain *d = page_get_owner(page); -+ -+ if ( d && is_pv_domain(d) && unlikely(need_iommu(d)) ) -+ { -+ int rc2 = iommu_unmap_page(d, mfn); -+ -+ if ( !rc ) -+ rc = rc2; -+ } -+ -+ if ( likely(!is_xen_heap_page(page)) ) -+ { -+ ASSERT((page->u.inuse.type_info & -+ (PGT_type_mask | PGT_count_mask)) == PGT_writable_page); -+ /* -+ * Clear the type to record the fact that all writable mappings -+ * have been removed. But if either operation failed, leave -+ * type_info alone. -+ */ -+ if ( likely(!rc) ) -+ page->u.inuse.type_info &= ~(PGT_type_mask | PGT_count_mask); -+ } -+ } -+ -+ return rc; - } - - void put_page(struct page_info *page) -@@ -2303,7 +2379,7 @@ void put_page(struct page_info *page) - - if ( unlikely((nx & PGC_count_mask) == 0) ) - { -- if ( cleanup_page_cacheattr(page) == 0 ) -+ if ( !cleanup_page_mappings(page) ) - free_domheap_page(page); - else - gdprintk(XENLOG_WARNING, -@@ -4020,9 +4096,10 @@ int steal_page( - * NB this is safe even if the page ends up being given back to - * the domain, because the count is zero: subsequent mappings will - * cause the cache attributes to be re-instated inside -- * get_page_from_l1e(). -+ * get_page_from_l1e(), or the page to be added back to the IOMMU -+ * upon the type changing to PGT_writeable, as appropriate. - */ -- if ( (rc = cleanup_page_cacheattr(page)) ) -+ if ( (rc = cleanup_page_mappings(page)) ) - { - /* - * Couldn't fixup Xen's mappings; put things the way we found -diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c -index c53cab44d9..2b62bc61dd 100644 ---- a/xen/arch/x86/mm/p2m.c -+++ b/xen/arch/x86/mm/p2m.c -@@ -708,23 +708,9 @@ p2m_remove_page(struct p2m_domain *p2m, unsigned long gfn_l, unsigned long mfn, - p2m_type_t t; - p2m_access_t a; - -+ /* IOMMU for PV guests is handled in get_page_type() and put_page(). */ - if ( !paging_mode_translate(p2m->domain) ) -- { -- int rc = 0; -- -- if ( need_iommu(p2m->domain) ) -- { -- for ( i = 0; i < (1 << page_order); i++ ) -- { -- int ret = iommu_unmap_page(p2m->domain, mfn + i); -- -- if ( !rc ) -- rc = ret; -- } -- } -- -- return rc; -- } -+ return 0; - - ASSERT(gfn_locked_by_me(p2m, gfn)); - P2M_DEBUG("removing gfn=%#lx mfn=%#lx\n", gfn_l, mfn); -@@ -769,26 +755,33 @@ guest_physmap_add_entry(struct domain *d, gfn_t gfn, mfn_t mfn, - int pod_count = 0; - int rc = 0; - -+ /* IOMMU for PV guests is handled in get_page_type() and put_page(). */ - if ( !paging_mode_translate(d) ) - { -- if ( need_iommu(d) && t == p2m_ram_rw ) -- { -- for ( i = 0; i < (1 << page_order); i++ ) -- { -- rc = iommu_map_page(d, mfn_x(mfn_add(mfn, i)), -- mfn_x(mfn_add(mfn, i)), -- IOMMUF_readable|IOMMUF_writable); -- if ( rc != 0 ) -- { -- while ( i-- > 0 ) -- /* If statement to satisfy __must_check. */ -- if ( iommu_unmap_page(d, mfn_x(mfn_add(mfn, i))) ) -- continue; -+ struct page_info *page = mfn_to_page(mfn); - -- return rc; -- } -- } -+ /* -+ * Our interface for PV guests wrt IOMMU entries hasn't been very -+ * clear; but historically, pages have started out with IOMMU mappings, -+ * and only lose them when changed to a different page type. -+ * -+ * Retain this property by grabbing a writable type ref and then -+ * dropping it immediately. The result will be pages that have a -+ * writable type (and an IOMMU entry), but a count of 0 (such that -+ * any guest-requested type changes succeed and remove the IOMMU -+ * entry). -+ */ -+ if ( !need_iommu(d) || t != p2m_ram_rw ) -+ return 0; -+ -+ for ( i = 0; i < (1UL << page_order); ++i, ++page ) -+ { -+ if ( get_page_and_type(page, d, PGT_writable_page) ) -+ put_page_and_type(page); -+ else -+ return -EINVAL; - } -+ - return 0; - } - --- -2.20.1 - diff --git a/xsa290-4.11-1.patch b/xsa290-4.11-1.patch deleted file mode 100644 index 38ddb56..0000000 --- a/xsa290-4.11-1.patch +++ /dev/null @@ -1,237 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: also allow L2 (un)validation to be preemptible - -Commit c612481d1c ("x86/mm: Plumbing to allow any PTE update to fail -with -ERESTART") added assertions next to the {alloc,free}_l2_table() -invocations to document (and validate in debug builds) that L2 -(un)validations are always preemptible. - -The assertion in free_page_type() was now observed to trigger when -recursive L2 page tables get cleaned up. - -In particular put_page_from_l2e()'s assumption that _put_page_type() -would always succeed is now wrong, resulting in a partially un-validated -page left in a domain, which has no other means of getting cleaned up -later on. If not causing any problems earlier, this would ultimately -trigger the check for ->u.inuse.type_info having a zero count when -freeing the page during cleanup after the domain has died. - -As a result it should be considered a mistake to not have extended -preemption fully to L2 when it was added to L3/L4 table handling, which -this change aims to correct. - -The validation side additions are done just for symmetry. - -This is part of XSA-290. - -Reported-by: Manuel Bouyer -Tested-by: Manuel Bouyer -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper - ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1126,7 +1126,7 @@ get_page_from_l1e( - define_get_linear_pagetable(l2); - static int - get_page_from_l2e( -- l2_pgentry_t l2e, unsigned long pfn, struct domain *d) -+ l2_pgentry_t l2e, unsigned long pfn, struct domain *d, int partial) - { - unsigned long mfn = l2e_get_pfn(l2e); - int rc; -@@ -1141,7 +1141,8 @@ get_page_from_l2e( - return -EINVAL; - } - -- rc = get_page_and_type_from_mfn(_mfn(mfn), PGT_l1_page_table, d, 0, 0); -+ rc = get_page_and_type_from_mfn(_mfn(mfn), PGT_l1_page_table, d, -+ partial, false); - if ( unlikely(rc == -EINVAL) && get_l2_linear_pagetable(l2e, pfn, d) ) - rc = 0; - -@@ -1295,8 +1296,11 @@ void put_page_from_l1e(l1_pgentry_t l1e, - * NB. Virtual address 'l2e' maps to a machine address within frame 'pfn'. - * Note also that this automatically deals correctly with linear p.t.'s. - */ --static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn) -+static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, -+ int partial, bool defer) - { -+ int rc = 0; -+ - if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) || (l2e_get_pfn(l2e) == pfn) ) - return 1; - -@@ -1311,13 +1315,27 @@ static int put_page_from_l2e(l2_pgentry_ - else - { - struct page_info *pg = l2e_get_page(l2e); -- int rc = _put_page_type(pg, false, mfn_to_page(_mfn(pfn))); -+ struct page_info *ptpg = mfn_to_page(_mfn(pfn)); - -- ASSERT(!rc); -- put_page(pg); -+ if ( unlikely(partial > 0) ) -+ { -+ ASSERT(!defer); -+ rc = _put_page_type(pg, true, ptpg); -+ } -+ else if ( defer ) -+ { -+ current->arch.old_guest_ptpg = ptpg; -+ current->arch.old_guest_table = pg; -+ } -+ else -+ { -+ rc = _put_page_type(pg, true, ptpg); -+ if ( likely(!rc) ) -+ put_page(pg); -+ } - } - -- return 0; -+ return rc; - } - - static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, -@@ -1487,11 +1505,12 @@ static int alloc_l2_table(struct page_in - unsigned long pfn = mfn_x(page_to_mfn(page)); - l2_pgentry_t *pl2e; - unsigned int i; -- int rc = 0; -+ int rc = 0, partial = page->partial_pte; - - pl2e = map_domain_page(_mfn(pfn)); - -- for ( i = page->nr_validated_ptes; i < L2_PAGETABLE_ENTRIES; i++ ) -+ for ( i = page->nr_validated_ptes; i < L2_PAGETABLE_ENTRIES; -+ i++, partial = 0 ) - { - if ( i > page->nr_validated_ptes && hypercall_preempt_check() ) - { -@@ -1501,23 +1520,33 @@ static int alloc_l2_table(struct page_in - } - - if ( !is_guest_l2_slot(d, type, i) || -- (rc = get_page_from_l2e(pl2e[i], pfn, d)) > 0 ) -+ (rc = get_page_from_l2e(pl2e[i], pfn, d, partial)) > 0 ) - continue; - -- if ( unlikely(rc == -ERESTART) ) -+ if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- break; -+ page->partial_pte = partial ?: 1; - } -- -- if ( rc < 0 ) -+ else if ( rc == -EINTR && i ) -+ { -+ page->nr_validated_ptes = i; -+ page->partial_pte = 0; -+ rc = -ERESTART; -+ } -+ else if ( rc < 0 && rc != -EINTR ) - { - gdprintk(XENLOG_WARNING, "Failure in alloc_l2_table: slot %#x\n", i); -- while ( i-- > 0 ) -- if ( is_guest_l2_slot(d, type, i) ) -- put_page_from_l2e(pl2e[i], pfn); -- break; -+ if ( i ) -+ { -+ page->nr_validated_ptes = i; -+ page->partial_pte = 0; -+ current->arch.old_guest_ptpg = NULL; -+ current->arch.old_guest_table = page; -+ } - } -+ if ( rc < 0 ) -+ break; - - pl2e[i] = adjust_guest_l2e(pl2e[i], d); - } -@@ -1797,28 +1826,50 @@ static int free_l2_table(struct page_inf - struct domain *d = page_get_owner(page); - unsigned long pfn = mfn_x(page_to_mfn(page)); - l2_pgentry_t *pl2e; -- unsigned int i = page->nr_validated_ptes - 1; -- int err = 0; -+ int rc = 0, partial = page->partial_pte; -+ unsigned int i = page->nr_validated_ptes - !partial; - - pl2e = map_domain_page(_mfn(pfn)); - -- ASSERT(page->nr_validated_ptes); -- do { -- if ( is_guest_l2_slot(d, page->u.inuse.type_info, i) && -- put_page_from_l2e(pl2e[i], pfn) == 0 && -- i && hypercall_preempt_check() ) -+ for ( ; ; ) -+ { -+ if ( is_guest_l2_slot(d, page->u.inuse.type_info, i) ) -+ rc = put_page_from_l2e(pl2e[i], pfn, partial, false); -+ if ( rc < 0 ) -+ break; -+ -+ partial = 0; -+ -+ if ( !i-- ) -+ break; -+ -+ if ( hypercall_preempt_check() ) - { -- page->nr_validated_ptes = i; -- err = -ERESTART; -+ rc = -EINTR; -+ break; - } -- } while ( !err && i-- ); -+ } - - unmap_domain_page(pl2e); - -- if ( !err ) -+ if ( rc >= 0 ) -+ { - page->u.inuse.type_info &= ~PGT_pae_xen_l2; -+ rc = 0; -+ } -+ else if ( rc == -ERESTART ) -+ { -+ page->nr_validated_ptes = i; -+ page->partial_pte = partial ?: -1; -+ } -+ else if ( rc == -EINTR && i < L2_PAGETABLE_ENTRIES - 1 ) -+ { -+ page->nr_validated_ptes = i + 1; -+ page->partial_pte = 0; -+ rc = -ERESTART; -+ } - -- return err; -+ return rc; - } - - static int free_l3_table(struct page_info *page) -@@ -2138,7 +2189,7 @@ static int mod_l2_entry(l2_pgentry_t *pl - return -EBUSY; - } - -- if ( unlikely((rc = get_page_from_l2e(nl2e, pfn, d)) < 0) ) -+ if ( unlikely((rc = get_page_from_l2e(nl2e, pfn, d, 0)) < 0) ) - return rc; - - nl2e = adjust_guest_l2e(nl2e, d); -@@ -2157,7 +2208,8 @@ static int mod_l2_entry(l2_pgentry_t *pl - return -EBUSY; - } - -- put_page_from_l2e(ol2e, pfn); -+ put_page_from_l2e(ol2e, pfn, 0, true); -+ - return rc; - } - diff --git a/xsa290-4.11-2.patch b/xsa290-4.11-2.patch deleted file mode 100644 index 9355e02..0000000 --- a/xsa290-4.11-2.patch +++ /dev/null @@ -1,71 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: add explicit preemption checks to L3 (un)validation - -When recursive page tables are used at the L3 level, unvalidation of a -single L4 table may incur unvalidation of two levels of L3 tables, i.e. -a maximum iteration count of 512^3 for unvalidating an L4 table. The -preemption check in free_l2_table() as well as the one in -_put_page_type() may never be reached, so explicit checking is needed in -free_l3_table(). - -When recursive page tables are used at the L4 level, the iteration count -at L4 alone is capped at 512^2. As soon as a present L3 entry is hit -which itself needs unvalidation (and hence requiring another nested loop -with 512 iterations), the preemption checks added here kick in, so no -further preemption checking is needed at L4 (until we decide to permit -5-level paging for PV guests). - -The validation side additions are done just for symmetry. - -This is part of XSA-290. - -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper - ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1581,6 +1581,13 @@ static int alloc_l3_table(struct page_in - for ( i = page->nr_validated_ptes; i < L3_PAGETABLE_ENTRIES; - i++, partial = 0 ) - { -+ if ( i > page->nr_validated_ptes && hypercall_preempt_check() ) -+ { -+ page->nr_validated_ptes = i; -+ rc = -ERESTART; -+ break; -+ } -+ - if ( is_pv_32bit_domain(d) && (i == 3) ) - { - if ( !(l3e_get_flags(pl3e[i]) & _PAGE_PRESENT) || -@@ -1882,15 +1889,25 @@ static int free_l3_table(struct page_inf - - pl3e = map_domain_page(_mfn(pfn)); - -- do { -+ for ( ; ; ) -+ { - rc = put_page_from_l3e(pl3e[i], pfn, partial, 0); - if ( rc < 0 ) - break; -+ - partial = 0; -- if ( rc > 0 ) -- continue; -- pl3e[i] = unadjust_guest_l3e(pl3e[i], d); -- } while ( i-- ); -+ if ( rc == 0 ) -+ pl3e[i] = unadjust_guest_l3e(pl3e[i], d); -+ -+ if ( !i-- ) -+ break; -+ -+ if ( hypercall_preempt_check() ) -+ { -+ rc = -EINTR; -+ break; -+ } -+ } - - unmap_domain_page(pl3e); - diff --git a/xsa291-4.11.patch b/xsa291-4.11.patch deleted file mode 100644 index 392e8e7..0000000 --- a/xsa291-4.11.patch +++ /dev/null @@ -1,53 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: don't retain page type reference when IOMMU operation fails - -The IOMMU update in _get_page_type() happens between recording of the -new reference and validation of the page for its new type (if -necessary). If the IOMMU operation fails, there's no point in actually -carrying out validation. Furthermore, with this resulting in failure -getting indicated to the caller, the recorded type reference also needs -to be dropped again. - -Note that in case of failure of alloc_page_type() there's no need to -undo the IOMMU operation: Only special types get handed to the function. -The function, upon failure, clears ->u.inuse.type_info, effectively -converting the page to PGT_none. The IOMMU mapping, however, solely -depends on whether the type is PGT_writable_page. - -This is XSA-291. - -Reported-by: Igor Druzhinin -Reported-by: Andrew Cooper -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper - ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -2751,6 +2751,13 @@ static int _get_page_type(struct page_in - iommu_ret = iommu_map_page(d, gfn_x(gfn), - mfn_x(page_to_mfn(page)), - IOMMUF_readable|IOMMUF_writable); -+ -+ if ( unlikely(iommu_ret) ) -+ { -+ _put_page_type(page, false, NULL); -+ rc = iommu_ret; -+ goto out; -+ } - } - } - -@@ -2765,12 +2772,10 @@ static int _get_page_type(struct page_in - rc = alloc_page_type(page, type, preemptible); - } - -+ out: - if ( (x & PGT_partial) && !(nx & PGT_partial) ) - put_page(page); - -- if ( !rc ) -- rc = iommu_ret; -- - return rc; - } - diff --git a/xsa292.patch b/xsa292.patch deleted file mode 100644 index 198fd4f..0000000 --- a/xsa292.patch +++ /dev/null @@ -1,95 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: properly flush TLB in switch_cr3_cr4() - -The CR3 values used for contexts run with PCID enabled uniformly have -CR3.NOFLUSH set, resulting in the CR3 write itself to not cause any -flushing at all. When the second CR4 write is skipped or doesn't do any -flushing, there's nothing so far which would purge TLB entries which may -have accumulated again if the PCID doesn't change; the "just in case" -flush only affects the case where the PCID actually changes. (There may -be particularly many TLB entries re-accumulated in case of a watchdog -NMI kicking in during the critical time window.) - -Suppress the no-flush behavior of the CR3 write in this particular case. - -Similarly the second CR4 write may not cause any flushing of TLB entries -established again while the original PCID was still in use - it may get -performed because of unrelated bits changing. The flush of the old PCID -needs to happen nevertheless. - -At the same time also eliminate a possible race with lazy context -switch: Just like for CR4, CR3 may change at any time while interrupts -are enabled, due to the __sync_local_execstate() invocation from the -flush IPI handler. It is for that reason that the CR3 read, just like -the CR4 one, must happen only after interrupts have been turned off. - -This is XSA-292. - -Reported-by: Sergey Dyasli -Reported-by: Andrew Cooper -Tested-by: Sergey Dyasli -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper ---- -v3: Adjust comments. Drop old_cr4 from the PGE check in the expression - controlling the invocation of invpcid_flush_single_context(), as PGE - is always clear there. -v2: Decouple invpcid_flush_single_context() from 2nd CR4 write. - ---- a/xen/arch/x86/flushtlb.c -+++ b/xen/arch/x86/flushtlb.c -@@ -103,9 +103,8 @@ static void do_tlb_flush(void) - - void switch_cr3_cr4(unsigned long cr3, unsigned long cr4) - { -- unsigned long flags, old_cr4; -+ unsigned long flags, old_cr4, old_pcid; - u32 t; -- unsigned long old_pcid = cr3_pcid(read_cr3()); - - /* This non-reentrant function is sometimes called in interrupt context. */ - local_irq_save(flags); -@@ -133,15 +132,38 @@ void switch_cr3_cr4(unsigned long cr3, u - */ - invpcid_flush_all_nonglobals(); - -+ /* -+ * If we don't change PCIDs, the CR3 write below needs to flush this very -+ * PCID, even when a full flush was performed above, as we are currently -+ * accumulating TLB entries again from the old address space. -+ * NB: Clearing the bit when we don't use PCID is benign (as it is clear -+ * already in that case), but allows the if() to be more simple. -+ */ -+ old_pcid = cr3_pcid(read_cr3()); -+ if ( old_pcid == cr3_pcid(cr3) ) -+ cr3 &= ~X86_CR3_NOFLUSH; -+ - write_cr3(cr3); - - if ( old_cr4 != cr4 ) - write_cr4(cr4); -- else if ( old_pcid != cr3_pcid(cr3) ) -- /* -- * Make sure no TLB entries related to the old PCID created between -- * flushing the TLB and writing the new %cr3 value remain in the TLB. -- */ -+ -+ /* -+ * Make sure no TLB entries related to the old PCID created between -+ * flushing the TLB and writing the new %cr3 value remain in the TLB. -+ * -+ * The write to CR4 just above has performed a wider flush in certain -+ * cases, which therefore get excluded here. Since that write is -+ * conditional, note in particular that it won't be skipped if PCIDE -+ * transitions from 1 to 0. This is because the CR4 write further up will -+ * have been skipped in this case, as PCIDE and PGE won't both be set at -+ * the same time. -+ * -+ * Note also that PGE is always clear in old_cr4. -+ */ -+ if ( old_pcid != cr3_pcid(cr3) && -+ !(cr4 & X86_CR4_PGE) && -+ (old_cr4 & X86_CR4_PCIDE) <= (cr4 & X86_CR4_PCIDE) ) - invpcid_flush_single_context(old_pcid); - - post_flush(t); diff --git a/xsa293-4.11-1.patch b/xsa293-4.11-1.patch deleted file mode 100644 index 0b8499f..0000000 --- a/xsa293-4.11-1.patch +++ /dev/null @@ -1,317 +0,0 @@ -From: Andrew Cooper -Subject: x86/pv: Rewrite guest %cr4 handling from scratch - -The PV cr4 logic is almost impossible to follow, and leaks bits into guest -context which definitely shouldn't be visible (in particular, VMXE). - -The biggest problem however, and source of the complexity, is that it derives -new real and guest cr4 values from the current value in hardware - this is -context dependent and an inappropriate source of information. - -Rewrite the cr4 logic to be invariant of the current value in hardware. - -First of all, modify write_ptbase() to always use mmu_cr4_features for IDLE -and HVM contexts. mmu_cr4_features *is* the correct value to use, and makes -the ASSERT() obviously redundant. - -For PV guests, curr->arch.pv.ctrlreg[4] remains the guests view of cr4, but -all logic gets reworked in terms of this and mmu_cr4_features only. - -Two masks are introduced; bits which the guest has control over, and bits -which are forwarded from Xen's settings. One guest-visible change here is -that Xen's VMXE setting is no longer visible at all. - -pv_make_cr4() follows fairly closely from pv_guest_cr4_to_real_cr4(), but -deliberately starts with mmu_cr4_features, and only alters the minimal subset -of bits. - -The boot-time {compat_,}pv_cr4_mask variables are removed, as they are a -remnant of the pre-CPUID policy days. pv_fixup_guest_cr4() gains a related -derivation from the policy. - -Another guest visible change here is that a 32bit PV guest can now flip -FSGSBASE in its view of CR4. While the {RD,WR}{FS,GS}BASE instructions are -unusable outside of a 64bit code segment, the ability to modify FSGSBASE -matches real hardware behaviour, and avoids the need for any 32bit/64bit -differences in the logic. - -Overall, this patch shouldn't have a practical change in guest behaviour. -VMXE will disappear from view, and an inquisitive 32bit kernel can now see -FSGSBASE changing, but this new logic is otherwise bug-compatible with before. - -This is part of XSA-293 - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c -index b1e50d1..675152a 100644 ---- a/xen/arch/x86/domain.c -+++ b/xen/arch/x86/domain.c -@@ -733,49 +733,6 @@ int arch_domain_soft_reset(struct domain *d) - return ret; - } - --/* -- * These are the masks of CR4 bits (subject to hardware availability) which a -- * PV guest may not legitimiately attempt to modify. -- */ --static unsigned long __read_mostly pv_cr4_mask, compat_pv_cr4_mask; -- --static int __init init_pv_cr4_masks(void) --{ -- unsigned long common_mask = ~X86_CR4_TSD; -- -- /* -- * All PV guests may attempt to modify TSD, DE and OSXSAVE. -- */ -- if ( cpu_has_de ) -- common_mask &= ~X86_CR4_DE; -- if ( cpu_has_xsave ) -- common_mask &= ~X86_CR4_OSXSAVE; -- -- pv_cr4_mask = compat_pv_cr4_mask = common_mask; -- -- /* -- * 64bit PV guests may attempt to modify FSGSBASE. -- */ -- if ( cpu_has_fsgsbase ) -- pv_cr4_mask &= ~X86_CR4_FSGSBASE; -- -- return 0; --} --__initcall(init_pv_cr4_masks); -- --unsigned long pv_guest_cr4_fixup(const struct vcpu *v, unsigned long guest_cr4) --{ -- unsigned long hv_cr4 = real_cr4_to_pv_guest_cr4(read_cr4()); -- unsigned long mask = is_pv_32bit_vcpu(v) ? compat_pv_cr4_mask : pv_cr4_mask; -- -- if ( (guest_cr4 & mask) != (hv_cr4 & mask) ) -- printk(XENLOG_G_WARNING -- "d%d attempted to change %pv's CR4 flags %08lx -> %08lx\n", -- current->domain->domain_id, v, hv_cr4, guest_cr4); -- -- return (hv_cr4 & mask) | (guest_cr4 & ~mask); --} -- - #define xen_vcpu_guest_context vcpu_guest_context - #define fpu_ctxt fpu_ctxt.x - CHECK_FIELD_(struct, vcpu_guest_context, fpu_ctxt); -@@ -789,7 +746,7 @@ int arch_set_info_guest( - struct domain *d = v->domain; - unsigned long cr3_gfn; - struct page_info *cr3_page; -- unsigned long flags, cr4; -+ unsigned long flags; - unsigned int i; - int rc = 0, compat; - -@@ -978,9 +935,8 @@ int arch_set_info_guest( - v->arch.pv_vcpu.ctrlreg[0] &= X86_CR0_TS; - v->arch.pv_vcpu.ctrlreg[0] |= read_cr0() & ~X86_CR0_TS; - -- cr4 = v->arch.pv_vcpu.ctrlreg[4]; -- v->arch.pv_vcpu.ctrlreg[4] = cr4 ? pv_guest_cr4_fixup(v, cr4) : -- real_cr4_to_pv_guest_cr4(mmu_cr4_features); -+ v->arch.pv_vcpu.ctrlreg[4] = -+ pv_fixup_guest_cr4(v, v->arch.pv_vcpu.ctrlreg[4]); - - memset(v->arch.debugreg, 0, sizeof(v->arch.debugreg)); - for ( i = 0; i < 8; i++ ) -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 6509035..08634b7 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -505,33 +505,13 @@ void make_cr3(struct vcpu *v, mfn_t mfn) - v->arch.cr3 |= get_pcid_bits(v, false); - } - --unsigned long pv_guest_cr4_to_real_cr4(const struct vcpu *v) --{ -- const struct domain *d = v->domain; -- unsigned long cr4; -- -- cr4 = v->arch.pv_vcpu.ctrlreg[4] & ~X86_CR4_DE; -- cr4 |= mmu_cr4_features & (X86_CR4_PSE | X86_CR4_SMEP | X86_CR4_SMAP | -- X86_CR4_OSXSAVE | X86_CR4_FSGSBASE); -- -- if ( d->arch.pv_domain.pcid ) -- cr4 |= X86_CR4_PCIDE; -- else if ( !d->arch.pv_domain.xpti ) -- cr4 |= X86_CR4_PGE; -- -- cr4 |= d->arch.vtsc ? X86_CR4_TSD : 0; -- -- return cr4; --} -- - void write_ptbase(struct vcpu *v) - { - struct cpu_info *cpu_info = get_cpu_info(); - unsigned long new_cr4; - - new_cr4 = (is_pv_vcpu(v) && !is_idle_vcpu(v)) -- ? pv_guest_cr4_to_real_cr4(v) -- : ((read_cr4() & ~(X86_CR4_PCIDE | X86_CR4_TSD)) | X86_CR4_PGE); -+ ? pv_make_cr4(v) : mmu_cr4_features; - - if ( is_pv_vcpu(v) && v->domain->arch.pv_domain.xpti ) - { -@@ -550,8 +530,6 @@ void write_ptbase(struct vcpu *v) - switch_cr3_cr4(v->arch.cr3, new_cr4); - cpu_info->pv_cr3 = 0; - } -- -- ASSERT(is_pv_vcpu(v) || read_cr4() == mmu_cr4_features); - } - - /* -diff --git a/xen/arch/x86/pv/domain.c b/xen/arch/x86/pv/domain.c -index b75ff6b..3965959 100644 ---- a/xen/arch/x86/pv/domain.c -+++ b/xen/arch/x86/pv/domain.c -@@ -97,6 +97,52 @@ static void release_compat_l4(struct vcpu *v) - v->arch.guest_table_user = pagetable_null(); - } - -+unsigned long pv_fixup_guest_cr4(const struct vcpu *v, unsigned long cr4) -+{ -+ const struct cpuid_policy *p = v->domain->arch.cpuid; -+ -+ /* Discard attempts to set guest controllable bits outside of the policy. */ -+ cr4 &= ~((p->basic.tsc ? 0 : X86_CR4_TSD) | -+ (p->basic.de ? 0 : X86_CR4_DE) | -+ (p->feat.fsgsbase ? 0 : X86_CR4_FSGSBASE) | -+ (p->basic.xsave ? 0 : X86_CR4_OSXSAVE)); -+ -+ /* Masks expected to be disjoint sets. */ -+ BUILD_BUG_ON(PV_CR4_GUEST_MASK & PV_CR4_GUEST_VISIBLE_MASK); -+ -+ /* -+ * A guest sees the policy subset of its own choice of guest controllable -+ * bits, and a subset of Xen's choice of certain hardware settings. -+ */ -+ return ((cr4 & PV_CR4_GUEST_MASK) | -+ (mmu_cr4_features & PV_CR4_GUEST_VISIBLE_MASK)); -+} -+ -+unsigned long pv_make_cr4(const struct vcpu *v) -+{ -+ const struct domain *d = v->domain; -+ unsigned long cr4 = mmu_cr4_features & -+ ~(X86_CR4_PCIDE | X86_CR4_PGE | X86_CR4_TSD); -+ -+ /* -+ * PCIDE or PGE depends on the PCID/XPTI settings, but must not both be -+ * set, as it impacts the safety of TLB flushing. -+ */ -+ if ( d->arch.pv_domain.pcid ) -+ cr4 |= X86_CR4_PCIDE; -+ else if ( !d->arch.pv_domain.xpti ) -+ cr4 |= X86_CR4_PGE; -+ -+ /* -+ * TSD is needed if either the guest has elected to use it, or Xen is -+ * virtualising the TSC value the guest sees. -+ */ -+ if ( d->arch.vtsc || (v->arch.pv_vcpu.ctrlreg[4] & X86_CR4_TSD) ) -+ cr4 |= X86_CR4_TSD; -+ -+ return cr4; -+} -+ - int switch_compat(struct domain *d) - { - struct vcpu *v; -@@ -191,7 +237,7 @@ int pv_vcpu_initialise(struct vcpu *v) - /* PV guests by default have a 100Hz ticker. */ - v->periodic_period = MILLISECS(10); - -- v->arch.pv_vcpu.ctrlreg[4] = real_cr4_to_pv_guest_cr4(mmu_cr4_features); -+ v->arch.pv_vcpu.ctrlreg[4] = pv_fixup_guest_cr4(v, 0); - - if ( is_pv_32bit_domain(d) ) - { -diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c -index ce2ec76..4abbc14 100644 ---- a/xen/arch/x86/pv/emul-priv-op.c -+++ b/xen/arch/x86/pv/emul-priv-op.c -@@ -32,6 +32,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -785,8 +786,8 @@ static int write_cr(unsigned int reg, unsigned long val, - } - - case 4: /* Write CR4 */ -- curr->arch.pv_vcpu.ctrlreg[4] = pv_guest_cr4_fixup(curr, val); -- write_cr4(pv_guest_cr4_to_real_cr4(curr)); -+ curr->arch.pv_vcpu.ctrlreg[4] = pv_fixup_guest_cr4(curr, val); -+ write_cr4(pv_make_cr4(curr)); - ctxt_switch_levelling(curr); - return X86EMUL_OKAY; - } -diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h -index ec81d78..c8aa8a5 100644 ---- a/xen/include/asm-x86/domain.h -+++ b/xen/include/asm-x86/domain.h -@@ -610,17 +610,6 @@ bool update_secondary_system_time(struct vcpu *, - void vcpu_show_execution_state(struct vcpu *); - void vcpu_show_registers(const struct vcpu *); - --/* Clean up CR4 bits that are not under guest control. */ --unsigned long pv_guest_cr4_fixup(const struct vcpu *, unsigned long guest_cr4); -- --/* Convert between guest-visible and real CR4 values. */ --unsigned long pv_guest_cr4_to_real_cr4(const struct vcpu *v); -- --#define real_cr4_to_pv_guest_cr4(c) \ -- ((c) & ~(X86_CR4_PGE | X86_CR4_PSE | X86_CR4_TSD | \ -- X86_CR4_OSXSAVE | X86_CR4_SMEP | \ -- X86_CR4_FSGSBASE | X86_CR4_SMAP | X86_CR4_PCIDE)) -- - #define domain_max_vcpus(d) (is_hvm_domain(d) ? HVM_MAX_VCPUS : MAX_VIRT_CPUS) - - static inline struct vcpu_guest_context *alloc_vcpu_guest_context(void) -diff --git a/xen/include/asm-x86/pv/domain.h b/xen/include/asm-x86/pv/domain.h -index 4fea764..4e4710c 100644 ---- a/xen/include/asm-x86/pv/domain.h -+++ b/xen/include/asm-x86/pv/domain.h -@@ -59,6 +59,23 @@ int pv_vcpu_initialise(struct vcpu *v); - void pv_domain_destroy(struct domain *d); - int pv_domain_initialise(struct domain *d); - -+/* -+ * Bits which a PV guest can toggle in its view of cr4. Some are loaded into -+ * hardware, while some are fully emulated. -+ */ -+#define PV_CR4_GUEST_MASK \ -+ (X86_CR4_TSD | X86_CR4_DE | X86_CR4_FSGSBASE | X86_CR4_OSXSAVE) -+ -+/* Bits which a PV guest may observe from the real hardware settings. */ -+#define PV_CR4_GUEST_VISIBLE_MASK \ -+ (X86_CR4_PAE | X86_CR4_MCE | X86_CR4_OSFXSR | X86_CR4_OSXMMEXCPT) -+ -+/* Given a new cr4 value, construct the resulting guest-visible cr4 value. */ -+unsigned long pv_fixup_guest_cr4(const struct vcpu *v, unsigned long cr4); -+ -+/* Create a cr4 value to load into hardware, based on vcpu settings. */ -+unsigned long pv_make_cr4(const struct vcpu *v); -+ - #else /* !CONFIG_PV */ - - #include -@@ -68,6 +85,8 @@ static inline int pv_vcpu_initialise(struct vcpu *v) { return -EOPNOTSUPP; } - static inline void pv_domain_destroy(struct domain *d) {} - static inline int pv_domain_initialise(struct domain *d) { return -EOPNOTSUPP; } - -+static inline unsigned long pv_make_cr4(const struct vcpu *v) { return ~0ul; } -+ - #endif /* CONFIG_PV */ - - void paravirt_ctxt_switch_from(struct vcpu *v); diff --git a/xsa293-4.11-2.patch b/xsa293-4.11-2.patch deleted file mode 100644 index a005021..0000000 --- a/xsa293-4.11-2.patch +++ /dev/null @@ -1,260 +0,0 @@ -From: Andrew Cooper -Subject: x86/pv: Don't have %cr4.fsgsbase active behind a guest kernels back - -Currently, a 64bit PV guest can appear to set and clear FSGSBASE in %cr4, but -the bit remains set in hardware. Therefore, the {RD,WR}{FS,GS}BASE are usable -even when the guest kernel believes that they are disabled. - -The FSGSBASE feature isn't currently supported in Linux, and its context -switch path has some optimisations which rely on userspace being unable to use -the WR{FS,GS}BASE instructions. Xen's current behaviour undermines this -expectation. - -In 64bit PV guest context, always load the guest kernels setting of FSGSBASE -into %cr4. This requires adjusting how Xen uses the {RD,WR}{FS,GS}BASE -instructions. - - * Delete the cpu_has_fsgsbase helper. It is no longer safe, as users need to - check %cr4 directly. - * The raw __rd{fs,gs}base() helpers are only safe to use when %cr4.fsgsbase - is set. Comment this property. - * The {rd,wr}{fs,gs}{base,shadow}() and read_msr() helpers are updated to use - the current %cr4 value to determine which mechanism to use. - * toggle_guest_mode() and save_segments() are update to avoid reading - fs/gsbase if the values in hardware cannot be stale WRT struct vcpu. A - consequence of this is that the write_cr() path needs to cache the current - bases, as subsequent context switches will skip saving the values. - * write_cr4() is updated to ensure that the shadow %cr4.fsgsbase value is - observed in a safe way WRT the hardware setting, if an interrupt happens to - hit in the middle. - * pv_make_cr4() is updated for 64bit PV guests to use the guest kernels - choice of FSGSBASE. - -This is part of XSA-293 - -Reported-by: Andy Lutomirski -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c -index 675152a..29f892c 100644 ---- a/xen/arch/x86/domain.c -+++ b/xen/arch/x86/domain.c -@@ -1433,7 +1433,8 @@ static void save_segments(struct vcpu *v) - regs->fs = read_sreg(fs); - regs->gs = read_sreg(gs); - -- if ( cpu_has_fsgsbase && !is_pv_32bit_vcpu(v) ) -+ /* %fs/%gs bases can only be stale if WR{FS,GS}BASE are usable. */ -+ if ( (read_cr4() & X86_CR4_FSGSBASE) && !is_pv_32bit_vcpu(v) ) - { - v->arch.pv_vcpu.fs_base = __rdfsbase(); - if ( v->arch.flags & TF_kernel_mode ) -diff --git a/xen/arch/x86/pv/domain.c b/xen/arch/x86/pv/domain.c -index 3965959..228a174 100644 ---- a/xen/arch/x86/pv/domain.c -+++ b/xen/arch/x86/pv/domain.c -@@ -140,6 +140,16 @@ unsigned long pv_make_cr4(const struct vcpu *v) - if ( d->arch.vtsc || (v->arch.pv_vcpu.ctrlreg[4] & X86_CR4_TSD) ) - cr4 |= X86_CR4_TSD; - -+ /* -+ * The {RD,WR}{FS,GS}BASE are only useable in 64bit code segments. While -+ * we must not have CR4.FSGSBASE set behind the back of a 64bit PV kernel, -+ * we do leave it set in 32bit PV context to speed up Xen's context switch -+ * path. -+ */ -+ if ( !is_pv_32bit_domain(d) && -+ !(v->arch.pv_vcpu.ctrlreg[4] & X86_CR4_FSGSBASE) ) -+ cr4 &= ~X86_CR4_FSGSBASE; -+ - return cr4; - } - -@@ -375,7 +385,8 @@ void toggle_guest_mode(struct vcpu *v) - { - ASSERT(!is_pv_32bit_vcpu(v)); - -- if ( cpu_has_fsgsbase ) -+ /* %fs/%gs bases can only be stale if WR{FS,GS}BASE are usable. */ -+ if ( read_cr4() & X86_CR4_FSGSBASE ) - { - if ( v->arch.flags & TF_kernel_mode ) - v->arch.pv_vcpu.gs_base_kernel = __rdgsbase(); -diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c -index 4abbc14..312c1ee 100644 ---- a/xen/arch/x86/pv/emul-priv-op.c -+++ b/xen/arch/x86/pv/emul-priv-op.c -@@ -786,6 +786,17 @@ static int write_cr(unsigned int reg, unsigned long val, - } - - case 4: /* Write CR4 */ -+ /* -+ * If this write will disable FSGSBASE, refresh Xen's idea of the -+ * guest bases now that they can no longer change. -+ */ -+ if ( (curr->arch.pv_vcpu.ctrlreg[4] & X86_CR4_FSGSBASE) && -+ !(val & X86_CR4_FSGSBASE) ) -+ { -+ curr->arch.pv_vcpu.fs_base = __rdfsbase(); -+ curr->arch.pv_vcpu.gs_base_kernel = __rdgsbase(); -+ } -+ - curr->arch.pv_vcpu.ctrlreg[4] = pv_fixup_guest_cr4(curr, val); - write_cr4(pv_make_cr4(curr)); - ctxt_switch_levelling(curr); -@@ -835,14 +846,15 @@ static int read_msr(unsigned int reg, uint64_t *val, - case MSR_FS_BASE: - if ( is_pv_32bit_domain(currd) ) - break; -- *val = cpu_has_fsgsbase ? __rdfsbase() : curr->arch.pv_vcpu.fs_base; -+ *val = (read_cr4() & X86_CR4_FSGSBASE) ? __rdfsbase() -+ : curr->arch.pv_vcpu.fs_base; - return X86EMUL_OKAY; - - case MSR_GS_BASE: - if ( is_pv_32bit_domain(currd) ) - break; -- *val = cpu_has_fsgsbase ? __rdgsbase() -- : curr->arch.pv_vcpu.gs_base_kernel; -+ *val = (read_cr4() & X86_CR4_FSGSBASE) ? __rdgsbase() -+ : curr->arch.pv_vcpu.gs_base_kernel; - return X86EMUL_OKAY; - - case MSR_SHADOW_GS_BASE: -diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c -index ecb0149..a353d76 100644 ---- a/xen/arch/x86/setup.c -+++ b/xen/arch/x86/setup.c -@@ -1567,7 +1567,7 @@ void __init noreturn __start_xen(unsigned long mbi_p) - - cr4_pv32_mask = mmu_cr4_features & XEN_CR4_PV32_BITS; - -- if ( cpu_has_fsgsbase ) -+ if ( boot_cpu_has(X86_FEATURE_FSGSBASE) ) - set_in_cr4(X86_CR4_FSGSBASE); - - if ( opt_invpcid && cpu_has_invpcid ) -diff --git a/xen/include/asm-x86/cpufeature.h b/xen/include/asm-x86/cpufeature.h -index b237da1..861cb0a 100644 ---- a/xen/include/asm-x86/cpufeature.h -+++ b/xen/include/asm-x86/cpufeature.h -@@ -90,7 +90,6 @@ - #define cpu_has_xsaves boot_cpu_has(X86_FEATURE_XSAVES) - - /* CPUID level 0x00000007:0.ebx */ --#define cpu_has_fsgsbase boot_cpu_has(X86_FEATURE_FSGSBASE) - #define cpu_has_bmi1 boot_cpu_has(X86_FEATURE_BMI1) - #define cpu_has_hle boot_cpu_has(X86_FEATURE_HLE) - #define cpu_has_avx2 boot_cpu_has(X86_FEATURE_AVX2) -diff --git a/xen/include/asm-x86/msr.h b/xen/include/asm-x86/msr.h -index afbeb7f..1ba6ee3 100644 ---- a/xen/include/asm-x86/msr.h -+++ b/xen/include/asm-x86/msr.h -@@ -120,6 +120,14 @@ static inline uint64_t rdtsc_ordered(void) - : "=a" (low), "=d" (high) \ - : "c" (counter)) - -+/* -+ * On hardware supporting FSGSBASE, the value loaded into hardware is the -+ * guest kernel's choice for 64bit PV guests (Xen's choice for Idle, HVM and -+ * 32bit PV). -+ * -+ * Therefore, the {RD,WR}{FS,GS}BASE instructions are only safe to use if -+ * %cr4.fsgsbase is set. -+ */ - static inline unsigned long __rdfsbase(void) - { - unsigned long base; -@@ -150,7 +158,7 @@ static inline unsigned long rdfsbase(void) - { - unsigned long base; - -- if ( cpu_has_fsgsbase ) -+ if ( read_cr4() & X86_CR4_FSGSBASE ) - return __rdfsbase(); - - rdmsrl(MSR_FS_BASE, base); -@@ -162,7 +170,7 @@ static inline unsigned long rdgsbase(void) - { - unsigned long base; - -- if ( cpu_has_fsgsbase ) -+ if ( read_cr4() & X86_CR4_FSGSBASE ) - return __rdgsbase(); - - rdmsrl(MSR_GS_BASE, base); -@@ -174,7 +182,7 @@ static inline unsigned long rdgsshadow(void) - { - unsigned long base; - -- if ( cpu_has_fsgsbase ) -+ if ( read_cr4() & X86_CR4_FSGSBASE ) - { - asm volatile ( "swapgs" ); - base = __rdgsbase(); -@@ -188,7 +196,7 @@ static inline unsigned long rdgsshadow(void) - - static inline void wrfsbase(unsigned long base) - { -- if ( cpu_has_fsgsbase ) -+ if ( read_cr4() & X86_CR4_FSGSBASE ) - #ifdef HAVE_AS_FSGSBASE - asm volatile ( "wrfsbase %0" :: "r" (base) ); - #else -@@ -200,7 +208,7 @@ static inline void wrfsbase(unsigned long base) - - static inline void wrgsbase(unsigned long base) - { -- if ( cpu_has_fsgsbase ) -+ if ( read_cr4() & X86_CR4_FSGSBASE ) - #ifdef HAVE_AS_FSGSBASE - asm volatile ( "wrgsbase %0" :: "r" (base) ); - #else -@@ -212,7 +220,7 @@ static inline void wrgsbase(unsigned long base) - - static inline void wrgsshadow(unsigned long base) - { -- if ( cpu_has_fsgsbase ) -+ if ( read_cr4() & X86_CR4_FSGSBASE ) - { - asm volatile ( "swapgs\n\t" - #ifdef HAVE_AS_FSGSBASE -diff --git a/xen/include/asm-x86/processor.h b/xen/include/asm-x86/processor.h -index 2bd9e69..8e253dc 100644 ---- a/xen/include/asm-x86/processor.h -+++ b/xen/include/asm-x86/processor.h -@@ -305,11 +305,31 @@ static inline unsigned long read_cr4(void) - - static inline void write_cr4(unsigned long val) - { -+ struct cpu_info *info = get_cpu_info(); -+ - /* No global pages in case of PCIDs enabled! */ - ASSERT(!(val & X86_CR4_PGE) || !(val & X86_CR4_PCIDE)); - -- get_cpu_info()->cr4 = val; -- asm volatile ( "mov %0,%%cr4" : : "r" (val) ); -+ /* -+ * On hardware supporting FSGSBASE, the value in %cr4 is the kernel's -+ * choice for 64bit PV guests, which impacts whether Xen can use the -+ * instructions. -+ * -+ * The {rd,wr}{fs,gs}base() helpers use info->cr4 to work out whether it -+ * is safe to execute the {RD,WR}{FS,GS}BASE instruction, falling back to -+ * the MSR path if not. Some users require interrupt safety. -+ * -+ * If FSGSBASE is currently or about to become clear, reflect this in -+ * info->cr4 before updating %cr4, so an interrupt which hits in the -+ * middle won't observe FSGSBASE set in info->cr4 but clear in %cr4. -+ */ -+ info->cr4 = val & (info->cr4 | ~X86_CR4_FSGSBASE); -+ -+ asm volatile ( "mov %[val], %%cr4" -+ : "+m" (info->cr4) /* Force ordering without a barrier. */ -+ : [val] "r" (val) ); -+ -+ info->cr4 = val; - } - - /* Clear and set 'TS' bit respectively */ diff --git a/xsa294-4.11.patch b/xsa294-4.11.patch deleted file mode 100644 index a0784d9..0000000 --- a/xsa294-4.11.patch +++ /dev/null @@ -1,71 +0,0 @@ -From: Jan Beulich -Subject: x86/pv: _toggle_guest_pt() may not skip TLB flush for shadow mode guests - -For shadow mode guests (e.g. PV ones forced into that mode as L1TF -mitigation, or during migration) update_cr3() -> sh_update_cr3() may -result in a change to the (shadow) root page table (compared to the -previous one when running the same vCPU with the same PCID). This can, -first and foremost, be a result of memory pressure on the shadow memory -pool of the domain. Shadow code legitimately relies on the original -(prior to commit 5c81d260c2 ["xen/x86: use PCID feature"]) behavior of -the subsequent CR3 write to flush the TLB of entries still left from -walks with an earlier, different (shadow) root page table. - -Restore the flushing behavior, also for the second CR3 write on the exit -path to guest context when XPTI is active. For the moment accept that -this will introduce more flushes than are strictly necessary - no flush -would be needed when the (shadow) root page table doesn't actually -change, but this information isn't readily (i.e. without introducing a -layering violation) available here. - -This is XSA-294. - -Reported-by: XXX PERSON -Signed-off-by: Jan Beulich -Tested-by: Juergen Gross -Reviewed-by: Andrew Cooper - -diff --git a/xen/arch/x86/pv/domain.c b/xen/arch/x86/pv/domain.c -index b75ff6b..528413a 100644 ---- a/xen/arch/x86/pv/domain.c -+++ b/xen/arch/x86/pv/domain.c -@@ -296,21 +296,35 @@ int pv_domain_initialise(struct domain *d) - static void _toggle_guest_pt(struct vcpu *v) - { - const struct domain *d = v->domain; -+ struct cpu_info *cpu_info = get_cpu_info(); -+ unsigned long cr3; - - v->arch.flags ^= TF_kernel_mode; - update_cr3(v); - if ( d->arch.pv_domain.xpti ) - { -- struct cpu_info *cpu_info = get_cpu_info(); -- - cpu_info->root_pgt_changed = true; - cpu_info->pv_cr3 = __pa(this_cpu(root_pgt)) | - (d->arch.pv_domain.pcid - ? get_pcid_bits(v, true) : 0); - } - -- /* Don't flush user global mappings from the TLB. Don't tick TLB clock. */ -- write_cr3(v->arch.cr3); -+ /* -+ * Don't flush user global mappings from the TLB. Don't tick TLB clock. -+ * -+ * In shadow mode, though, update_cr3() may need to be accompanied by a -+ * 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. The XPTI CR3 write, if enabled, -+ * will then need to be a flushing one too. -+ */ -+ cr3 = v->arch.cr3; -+ if ( shadow_mode_enabled(d) ) -+ { -+ cr3 &= ~X86_CR3_NOFLUSH; -+ cpu_info->pv_cr3 &= ~X86_CR3_NOFLUSH; -+ } -+ write_cr3(cr3); - - if ( !(v->arch.flags & TF_kernel_mode) ) - return; From ba63a02fbc348bb04d2b0d95d1818ae458d3560f Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 4 Apr 2019 21:00:20 +0100 Subject: [PATCH 002/194] remove traces of efiming and efi_flags logic from spec file --- xen.spec | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/xen.spec b/xen.spec index 7835edc..6b9e4f7 100644 --- a/xen.spec +++ b/xen.spec @@ -46,15 +46,6 @@ %ifnarch x86_64 aarch64 %{ix86} %define build_efi 0 %endif -%if %build_efi && "%dist" < ".fc26" -%ifarch x86_64 -%define efiming 1 -%else -%define efiming 0 -%endif -%else -%define efiming 0 -%endif %if "%dist" >= ".fc20" %define with_systemd_presets 1 %else @@ -177,10 +168,6 @@ ExclusiveArch: %{ix86} x86_64 armv7hl aarch64 %if %with_ocaml BuildRequires: ocaml, ocaml-findlib %endif -# efi image needs an ld that has -mi386pep option -%if %efiming -BuildRequires: mingw64-binutils -%endif %if %with_systemd_presets Requires(post): systemd Requires(preun): systemd @@ -350,9 +337,6 @@ cp -v %{SOURCE21} xen/.config %if !%build_ocaml %define ocaml_flags OCAML_TOOLS=n %endif -%if %efiming -%define efi_flags LD_EFI=/usr/x86_64-w64-mingw32/bin/ld -%endif %if %build_efi mkdir -p dist/install/boot/efi/efi/fedora %endif @@ -371,16 +355,15 @@ export EXTRA_CFLAGS_QEMU_XEN="$RPM_OPT_FLAGS" export PYTHON="/usr/bin/python2" %if %build_hyp %if %build_crosshyp -%define efi_flags LD_EFI=false -XEN_TARGET_ARCH=x86_64 make %{?_smp_mflags} %{?efi_flags} 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'`" +XEN_TARGET_ARCH=x86_64 make %{?_smp_mflags} 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 %{?_smp_mflags} %{?efi_flags} prefix=/usr xen CC="gcc `echo $RPM_OPT_FLAGS | sed -e 's/-mfloat-abi=hard//g' -e 's/-march=armv7-a//g'`" +make %{?_smp_mflags} 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 %{?_smp_mflags} %{?efi_flags} prefix=/usr xen CC="gcc $RPM_OPT_FLAGS" +make %{?_smp_mflags} prefix=/usr xen CC="gcc $RPM_OPT_FLAGS" %else -make %{?_smp_mflags} %{?efi_flags} 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'`" +make %{?_smp_mflags} 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 @@ -883,6 +866,7 @@ fi kdd is now xen-kdd, xenmon.py is now xenmon, fsimage.so is now xenfsimage.so fs libdir is now xenfsimage libdir xen-ringwatch xen-bugtool have been dropped +- remove remaining traces of efiming and efi_flags logic * Tue Mar 05 2019 Michael Young - 4.11.1-4 - xen: various flaws (#1685577) From 995f98f00005c045b58d0524f7841267898d8094 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Fri, 5 Apr 2019 20:55:26 +0100 Subject: [PATCH 003/194] switch from python2 to python3 --- xen.python.env.patch | 2 +- xen.python3.patch | 1749 ++++++++++++++++++++++++++++++++++++++++++ xen.spec | 19 +- 3 files changed, 1761 insertions(+), 9 deletions(-) create mode 100644 xen.python3.patch diff --git a/xen.python.env.patch b/xen.python.env.patch index 8844b18..3b8c1e6 100644 --- a/xen.python.env.patch +++ b/xen.python.env.patch @@ -37,7 +37,7 @@ +++ xen-4.11.0/tools/misc/xencov_split 2018-09-18 21:56:07.397893895 +0100 @@ -1,4 +1,4 @@ -#!/usr/bin/python -+#!/usr/bin/python2 ++#!/usr/bin/python3 import sys, os, os.path as path, struct, errno from optparse import OptionParser diff --git a/xen.python3.patch b/xen.python3.patch new file mode 100644 index 0000000..a89b4d9 --- /dev/null +++ b/xen.python3.patch @@ -0,0 +1,1749 @@ +From a9047a722ba5de38e7c1d762ffcfb74c36725fe2 Mon Sep 17 00:00:00 2001 +From: Andrew Cooper +Date: Mon, 11 Mar 2019 19:18:40 +0000 +Subject: [PATCH] tools/xen-foreign: Update python scripts to be Py3 compatible + +The issues are: + * dict.has_key() was completely removed in Py3 + * dict.keys() is an iterable rather than list in Py3, so .sort() doesn't work. + * list.sort(cmp=) was deprecated in Py2.4 and removed in Py3. + +The has_key() issue is trivially fixed by switching to using the in keyword. +The sorting issue could be trivially fixed, but take the opportunity to +improve the code. + +The reason for the sorting is to ensure that "unsigned long" gets replaced +before "long", and the only reason sorting is necessary is because +inttypes[arch] is needlessly a dictionary. Update inttypes[arch] to be a list +of tuples rather than a dictionary, and process them in list order. + +Reported-by: George Dunlap +Signed-off-by: Andrew Cooper +Acked-by: Wei Liu +--- + tools/include/xen-foreign/mkchecker.py | 2 +- + tools/include/xen-foreign/mkheader.py | 58 +++++++++++++------------- + 2 files changed, 29 insertions(+), 31 deletions(-) + +diff --git a/tools/include/xen-foreign/mkchecker.py b/tools/include/xen-foreign/mkchecker.py +index fdad869a91..199b0eebbc 100644 +--- a/tools/include/xen-foreign/mkchecker.py ++++ b/tools/include/xen-foreign/mkchecker.py +@@ -37,7 +37,7 @@ for struct in structs: + f.write('\tprintf("%%-25s |", "%s");\n' % struct); + for a in archs: + s = struct + "_" + a; +- if compat_arches.has_key(a): ++ if a in compat_arches: + compat = compat_arches[a] + c = struct + "_" + compat; + else: +diff --git a/tools/include/xen-foreign/mkheader.py b/tools/include/xen-foreign/mkheader.py +index 97e0c7a984..fb268f0dce 100644 +--- a/tools/include/xen-foreign/mkheader.py ++++ b/tools/include/xen-foreign/mkheader.py +@@ -17,13 +17,13 @@ header = {}; + footer = {}; + + #arm +-inttypes["arm32"] = { +- "unsigned long" : "__danger_unsigned_long_on_arm32", +- "long" : "__danger_long_on_arm32", +- "xen_pfn_t" : "uint64_t", +- "xen_ulong_t" : "uint64_t", +- "uint64_t" : "__align8__ uint64_t", +-}; ++inttypes["arm32"] = [ ++ ("unsigned long", "__danger_unsigned_long_on_arm32"), ++ ("long", "__danger_long_on_arm32"), ++ ("xen_pfn_t", "uint64_t"), ++ ("xen_ulong_t", "uint64_t"), ++ ("uint64_t", "__align8__ uint64_t"), ++] + header["arm32"] = """ + #define __arm___ARM32 1 + #if defined(__GNUC__) && !defined(__STRICT_ANSI__) +@@ -38,13 +38,13 @@ footer["arm32"] = """ + #undef __DECL_REG + """ + +-inttypes["arm64"] = { +- "unsigned long" : "__danger_unsigned_long_on_arm64", +- "long" : "__danger_long_on_arm64", +- "xen_pfn_t" : "uint64_t", +- "xen_ulong_t" : "uint64_t", +- "uint64_t" : "__align8__ uint64_t", +-}; ++inttypes["arm64"] = [ ++ ("unsigned long", "__danger_unsigned_long_on_arm64"), ++ ("long", "__danger_long_on_arm64"), ++ ("xen_pfn_t", "uint64_t"), ++ ("xen_ulong_t", "uint64_t"), ++ ("uint64_t", "__align8__ uint64_t"), ++] + header["arm64"] = """ + #define __aarch64___ARM64 1 + #if defined(__GNUC__) && !defined(__STRICT_ANSI__) +@@ -60,12 +60,12 @@ footer["arm64"] = """ + """ + + # x86_32 +-inttypes["x86_32"] = { +- "unsigned long" : "uint32_t", +- "long" : "uint32_t", +- "xen_pfn_t" : "uint32_t", +- "xen_ulong_t" : "uint32_t", +-}; ++inttypes["x86_32"] = [ ++ ("unsigned long", "uint32_t"), ++ ("long", "uint32_t"), ++ ("xen_pfn_t", "uint32_t"), ++ ("xen_ulong_t", "uint32_t"), ++] + header["x86_32"] = """ + #define __DECL_REG_LO8(which) uint32_t e ## which ## x + #define __DECL_REG_LO16(name) uint32_t e ## name +@@ -79,12 +79,12 @@ footer["x86_32"] = """ + """; + + # x86_64 +-inttypes["x86_64"] = { +- "unsigned long" : "__align8__ uint64_t", +- "long" : "__align8__ uint64_t", +- "xen_pfn_t" : "__align8__ uint64_t", +- "xen_ulong_t" : "__align8__ uint64_t", +-}; ++inttypes["x86_64"] = [ ++ ("unsigned long", "__align8__ uint64_t"), ++ ("long", "__align8__ uint64_t"), ++ ("xen_pfn_t", "__align8__ uint64_t"), ++ ("xen_ulong_t", "__align8__ uint64_t"), ++] + header["x86_64"] = """ + #if defined(__GNUC__) && !defined(__STRICT_ANSI__) + # define __DECL_REG(name) union { uint64_t r ## name, e ## name; } +@@ -205,10 +205,8 @@ for struct in structs: + output = re.sub("\\b(%s)_t\\b" % struct, "\\1_%s_t" % arch, output); + + # replace: integer types +-integers = inttypes[arch].keys(); +-integers.sort(lambda a, b: cmp(len(b),len(a))); +-for type in integers: +- output = re.sub("\\b%s\\b" % type, inttypes[arch][type], output); ++for old, new in inttypes[arch]: ++ output = re.sub("\\b%s\\b" % old, new, output) + + # print results + f = open(outfile, "w"); +-- +2.17.2 + +From 72288c156662e764cc47605f51842b4d0fcc3582 Mon Sep 17 00:00:00 2001 +From: Wei Liu +Date: Mon, 11 Mar 2019 17:16:45 +0000 +Subject: [PATCH 1/8] Update Python requirement to 2.6 + +CentOS 5, which was the reason for the 2.4 restriction, is EOL. CentOS +6 ships 2.6. + +Bump the version to 2.6 in README. Update configure. + +Signed-off-by: Wei Liu +Acked-by: Andrew Cooper +--- + README | 4 ++-- + tools/configure | 8 ++++---- + tools/configure.ac | 2 +- + 3 files changed, 7 insertions(+), 7 deletions(-) + +diff --git a/README b/README +index 6346825bc0..d00715c2fd 100644 +--- a/README ++++ b/README +@@ -46,7 +46,7 @@ provided by your OS distributor: + - GCC 4.8 or later + - GNU Binutils 2.24 or later + * Development install of zlib (e.g., zlib-dev) +- * Development install of Python 2, v2.4 or later (e.g., python-dev) ++ * Development install of Python 2, v2.6 or later (e.g., python-dev) + * Development install of curses (e.g., libncurses-dev) + * Development install of openssl (e.g., openssl-dev) + * Development install of x11 (e.g. xorg-x11-dev) +@@ -177,7 +177,7 @@ Python Runtime Libraries + + Various tools, such as pygrub, have the following runtime dependencies: + +- * Python 2, v2.4 or later. ++ * Python 2, v2.6 or later. + URL: http://www.python.org/ + Debian: python + +diff --git a/tools/configure b/tools/configure +index acc857510e..d0065b811d 100755 +--- a/tools/configure ++++ b/tools/configure +@@ -7002,15 +7002,15 @@ if test x"${PYTHONPATH}" = x"no" + then + as_fn_error $? "Unable to find $PYTHON, please install $PYTHON" "$LINENO" 5 + fi +-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for python version >= 2.3 " >&5 +-$as_echo_n "checking for python version >= 2.3 ... " >&6; } +-`$PYTHON -c 'import sys; sys.exit(eval("sys.version_info < (2, 3)"))'` ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for python version >= 2.6 " >&5 ++$as_echo_n "checking for python version >= 2.6 ... " >&6; } ++`$PYTHON -c 'import sys; sys.exit(eval("sys.version_info < (2, 6)"))'` + if test "$?" != "0" + then + python_version=`$PYTHON -V 2>&1` + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + $as_echo "no" >&6; } +- as_fn_error $? "$python_version is too old, minimum required version is 2.3" "$LINENO" 5 ++ as_fn_error $? "$python_version is too old, minimum required version is 2.6" "$LINENO" 5 + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 + $as_echo "yes" >&6; } +diff --git a/tools/configure.ac b/tools/configure.ac +index 1499344ce6..c9fd69ddfa 100644 +--- a/tools/configure.ac ++++ b/tools/configure.ac +@@ -358,7 +358,7 @@ AS_IF([echo "$PYTHON" | grep -q "^/"], [ + ],[test -z "$PYTHON"], [PYTHON="python"], + [AC_MSG_ERROR([PYTHON specified, but is not an absolute path])]) + AX_PATH_PROG_OR_FAIL([PYTHONPATH], [$PYTHON]) +-AX_CHECK_PYTHON_VERSION([2], [3]) ++AX_CHECK_PYTHON_VERSION([2], [6]) + + AS_IF([test "$cross_compiling" != yes], [ + AX_CHECK_PYTHON_DEVEL() +-- +2.17.2 + + +From e81209fd4ea705f4de4b61d05e6ab0aea9bb0b88 Mon Sep 17 00:00:00 2001 +From: Wei Liu +Date: Mon, 11 Mar 2019 12:58:05 +0000 +Subject: [PATCH 2/8] pygrub/fsimage: drop unused struct + +Signed-off-by: Wei Liu +Reviewed-by: Andrew Cooper +--- + tools/pygrub/src/fsimage/fsimage.c | 7 ------- + 1 file changed, 7 deletions(-) + +diff --git a/tools/pygrub/src/fsimage/fsimage.c b/tools/pygrub/src/fsimage/fsimage.c +index 743a3fb7b8..780207791c 100644 +--- a/tools/pygrub/src/fsimage/fsimage.c ++++ b/tools/pygrub/src/fsimage/fsimage.c +@@ -43,13 +43,6 @@ typedef struct fsimage_file { + fsi_file_t *file; + } fsimage_file_t; + +-struct foo { +- int ref; +- int size; +- long hash; +- int state; +-}; +- + static PyObject * + fsimage_file_read(fsimage_file_t *file, PyObject *args, PyObject *kwargs) + { +-- +2.17.2 + + +From b9e1368af14ded6aee3bdf64e8329628b16291ff Mon Sep 17 00:00:00 2001 +From: Wei Liu +Date: Mon, 11 Mar 2019 12:55:29 +0000 +Subject: [PATCH 3/8] pygrub: change tabs into spaces + +Not sure why Python 2 never complained, but Python 3 does. + +Change tabs to spaces. + +Signed-off-by: Wei Liu +Reviewed-by: Andrew Cooper +--- + tools/pygrub/src/pygrub | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub +index 52a8965ad9..1189b1ca48 100755 +--- a/tools/pygrub/src/pygrub ++++ b/tools/pygrub/src/pygrub +@@ -858,7 +858,7 @@ if __name__ == "__main__": + output_directory = a + + if debug: +- logging.basicConfig(level=logging.DEBUG) ++ logging.basicConfig(level=logging.DEBUG) + + + try: +@@ -917,7 +917,7 @@ if __name__ == "__main__": + # IOErrors raised by fsimage.open + # RuntimeErrors raised by run_grub if no menu.lst present + if debug: +- traceback.print_exc() ++ traceback.print_exc() + fs = None + continue + +-- +2.17.2 + + +From 16cc3362aed39e3093419b9df6ec73269071d063 Mon Sep 17 00:00:00 2001 +From: Wei Liu +Date: Tue, 5 Mar 2019 12:32:06 +0000 +Subject: [PATCH 4/8] build/m4: make python_devel.m4 work with both python 2 + and 3 + +Do the following: + +1. Change the form of "print". +2. Use AC_CHECK_FUNC to avoid the need to generate library name. +3. Remove unused stuff. + +Signed-off-by: Wei Liu +Reviewed-by: Anthony PERARD +--- + m4/python_devel.m4 | 23 ++++++----------- + tools/configure | 64 ++++++---------------------------------------- + 2 files changed, 16 insertions(+), 71 deletions(-) + +diff --git a/m4/python_devel.m4 b/m4/python_devel.m4 +index 05ea4ef7e2..f9cb23aee1 100644 +--- a/m4/python_devel.m4 ++++ b/m4/python_devel.m4 +@@ -1,38 +1,31 @@ + AC_DEFUN([AX_CHECK_PYTHON_DEVEL], [ + ac_previous_cppflags=$CPPFLAGS + ac_previous_ldflags=$LDFLAGS +-ac_python_version=`$PYTHON -c 'import distutils.sysconfig; \ +- print distutils.sysconfig.get_config_var("VERSION")'` + AC_PATH_PROG([pyconfig], [$PYTHON-config], [no]) + AS_IF([test x"$pyconfig" = x"no"], [ + dnl For those that don't have python-config + CPPFLAGS="$CFLAGS `$PYTHON -c 'import distutils.sysconfig; \ + print "-I" + distutils.sysconfig.get_config_var("INCLUDEPY")'`" + CPPFLAGS="$CPPFLAGS `$PYTHON -c 'import distutils.sysconfig; \ +- print distutils.sysconfig.get_config_var("CFLAGS")'`" +- PYTHON_LIBS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ +- print distutils.sysconfig.get_config_var("LIBS")'`" +- PYTHON_LIBS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ +- print distutils.sysconfig.get_config_var("SYSLIBS")'`" ++ print(distutils.sysconfig.get_config_var("CFLAGS"))'`" + LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ +- print "-L" + distutils.sysconfig.get_python_lib(plat_specific=1,\ +- standard_lib=1) + "/config"'`" ++ print("-L" + distutils.sysconfig.get_python_lib(plat_specific=1,\ ++ standard_lib=1) + "/config")'`" + LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ +- print distutils.sysconfig.get_config_var("LINKFORSHARED")'`" ++ print(distutils.sysconfig.get_config_var("LINKFORSHARED"))'`" + LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ +- print distutils.sysconfig.get_config_var("LDFLAGS")'`" ++ print(distutils.sysconfig.get_config_var("LDFLAGS"))'`" + ], [ + dnl If python-config is found use it + CPPFLAGS="$CFLAGS `$PYTHON-config --cflags`" + LDFLAGS="$LDFLAGS `$PYTHON-config --ldflags`" +- PYTHON_LIBS="$LIBS `$PYTHON-config --libs`" + ]) + + AC_CHECK_HEADER([Python.h], [], + [AC_MSG_ERROR([Unable to find Python development headers])],) +-AC_CHECK_LIB(python$ac_python_version, PyArg_ParseTuple, [], +- [AC_MSG_ERROR([Unable to find a suitable python development library])], +- [$PYTHON_LIBS]) ++AC_CHECK_FUNC([PyArg_ParseTuple], [], ++ [AC_MSG_ERROR([Unable to find a suitable python development library])]) ++ + CPPFLAGS=$ac_previous_cppflags + LDFLAGS=$ac_previous_ldflags + ]) +diff --git a/tools/configure b/tools/configure +index d0065b811d..e1fa5d6b0f 100755 +--- a/tools/configure ++++ b/tools/configure +@@ -7418,8 +7418,6 @@ if test "$cross_compiling" != yes; then : + + ac_previous_cppflags=$CPPFLAGS + ac_previous_ldflags=$LDFLAGS +-ac_python_version=`$PYTHON -c 'import distutils.sysconfig; \ +- print distutils.sysconfig.get_config_var("VERSION")'` + # Extract the first word of "$PYTHON-config", so it can be a program name with args. + set dummy $PYTHON-config; ac_word=$2 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +@@ -7466,24 +7464,19 @@ if test x"$pyconfig" = x"no"; then : + CPPFLAGS="$CFLAGS `$PYTHON -c 'import distutils.sysconfig; \ + print "-I" + distutils.sysconfig.get_config_var("INCLUDEPY")'`" + CPPFLAGS="$CPPFLAGS `$PYTHON -c 'import distutils.sysconfig; \ +- print distutils.sysconfig.get_config_var("CFLAGS")'`" +- PYTHON_LIBS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ +- print distutils.sysconfig.get_config_var("LIBS")'`" +- PYTHON_LIBS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ +- print distutils.sysconfig.get_config_var("SYSLIBS")'`" ++ print(distutils.sysconfig.get_config_var("CFLAGS"))'`" + LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ +- print "-L" + distutils.sysconfig.get_python_lib(plat_specific=1,\ +- standard_lib=1) + "/config"'`" ++ print("-L" + distutils.sysconfig.get_python_lib(plat_specific=1,\ ++ standard_lib=1) + "/config")'`" + LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ +- print distutils.sysconfig.get_config_var("LINKFORSHARED")'`" ++ print(distutils.sysconfig.get_config_var("LINKFORSHARED"))'`" + LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ +- print distutils.sysconfig.get_config_var("LDFLAGS")'`" ++ print(distutils.sysconfig.get_config_var("LDFLAGS"))'`" + + else + + CPPFLAGS="$CFLAGS `$PYTHON-config --cflags`" + LDFLAGS="$LDFLAGS `$PYTHON-config --ldflags`" +- PYTHON_LIBS="$LIBS `$PYTHON-config --libs`" + + fi + +@@ -7495,55 +7488,14 @@ else + fi + + +-as_ac_Lib=`$as_echo "ac_cv_lib_python$ac_python_version''_PyArg_ParseTuple" | $as_tr_sh` +-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for PyArg_ParseTuple in -lpython$ac_python_version" >&5 +-$as_echo_n "checking for PyArg_ParseTuple in -lpython$ac_python_version... " >&6; } +-if eval \${$as_ac_Lib+:} false; then : +- $as_echo_n "(cached) " >&6 +-else +- ac_check_lib_save_LIBS=$LIBS +-LIBS="-lpython$ac_python_version $PYTHON_LIBS $LIBS" +-cat confdefs.h - <<_ACEOF >conftest.$ac_ext +-/* end confdefs.h. */ +- +-/* Override any GCC internal prototype to avoid an error. +- Use char because int might match the return type of a GCC +- builtin and then its argument prototype would still apply. */ +-#ifdef __cplusplus +-extern "C" +-#endif +-char PyArg_ParseTuple (); +-int +-main () +-{ +-return PyArg_ParseTuple (); +- ; +- return 0; +-} +-_ACEOF +-if ac_fn_c_try_link "$LINENO"; then : +- eval "$as_ac_Lib=yes" +-else +- eval "$as_ac_Lib=no" +-fi +-rm -f core conftest.err conftest.$ac_objext \ +- conftest$ac_exeext conftest.$ac_ext +-LIBS=$ac_check_lib_save_LIBS +-fi +-eval ac_res=\$$as_ac_Lib +- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +-$as_echo "$ac_res" >&6; } +-if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +- cat >>confdefs.h <<_ACEOF +-#define `$as_echo "HAVE_LIBpython$ac_python_version" | $as_tr_cpp` 1 +-_ACEOF +- +- LIBS="-lpython$ac_python_version $LIBS" ++ac_fn_c_check_func "$LINENO" "PyArg_ParseTuple" "ac_cv_func_PyArg_ParseTuple" ++if test "x$ac_cv_func_PyArg_ParseTuple" = xyes; then : + + else + as_fn_error $? "Unable to find a suitable python development library" "$LINENO" 5 + fi + ++ + CPPFLAGS=$ac_previous_cppflags + LDFLAGS=$ac_previous_ldflags + +-- +2.17.2 + + +From 660d2dd863802ef464c90b32f187cb65861f8185 Mon Sep 17 00:00:00 2001 +From: Wei Liu +Date: Thu, 7 Mar 2019 12:33:38 +0000 +Subject: [PATCH 5/8] libxl: make python scripts work with python 2.6 and up + +Go through transformations suggested by 2to3 and pick the necessary +ones. + +Signed-off-by: Wei Liu +Reviewed-by: Andrew Cooper +--- + tools/libxl/gentest.py | 4 +++- + tools/libxl/gentypes.py | 12 +++++++----- + tools/libxl/idl.py | 15 ++++++++------- + 3 files changed, 18 insertions(+), 13 deletions(-) + +diff --git a/tools/libxl/gentest.py b/tools/libxl/gentest.py +index 989959fc68..1cc7eebc82 100644 +--- a/tools/libxl/gentest.py ++++ b/tools/libxl/gentest.py +@@ -1,5 +1,7 @@ + #!/usr/bin/python + ++from __future__ import print_function ++ + import os + import sys + import re +@@ -86,7 +88,7 @@ def gen_rand_init(ty, v, indent = " ", parent = None): + + if __name__ == '__main__': + if len(sys.argv) < 3: +- print >>sys.stderr, "Usage: gentest.py " ++ print("Usage: gentest.py ", file=sys.stderr) + sys.exit(1) + + random.seed(os.getenv('LIBXL_TESTIDL_SEED')) +diff --git a/tools/libxl/gentypes.py b/tools/libxl/gentypes.py +index 88e5c5f30e..6417c9dd8c 100644 +--- a/tools/libxl/gentypes.py ++++ b/tools/libxl/gentypes.py +@@ -1,5 +1,7 @@ + #!/usr/bin/python + ++from __future__ import print_function ++ + import sys + import re + +@@ -576,14 +578,14 @@ def libxl_C_enum_from_string(ty, str, e, indent = " "): + + if __name__ == '__main__': + if len(sys.argv) != 6: +- print >>sys.stderr, "Usage: gentypes.py
" ++ print("Usage: gentypes.py
", file=sys.stderr) + sys.exit(1) + + (_, idlname, header, header_private, header_json, impl) = sys.argv + + (builtins,types) = idl.parse(idlname) + +- print "outputting libxl type definitions to %s" % header ++ print("outputting libxl type definitions to %s" % header) + + f = open(header, "w") + +@@ -633,7 +635,7 @@ if __name__ == '__main__': + f.write("""#endif /* %s */\n""" % (header_define)) + f.close() + +- print "outputting libxl JSON definitions to %s" % header_json ++ print("outputting libxl JSON definitions to %s" % header_json) + + f = open(header_json, "w") + +@@ -657,7 +659,7 @@ if __name__ == '__main__': + f.write("""#endif /* %s */\n""" % header_json_define) + f.close() + +- print "outputting libxl type internal definitions to %s" % header_private ++ print("outputting libxl type internal definitions to %s" % header_private) + + f = open(header_private, "w") + +@@ -683,7 +685,7 @@ if __name__ == '__main__': + f.write("""#endif /* %s */\n""" % header_json_define) + f.close() + +- print "outputting libxl type implementations to %s" % impl ++ print("outputting libxl type implementations to %s" % impl) + + f = open(impl, "w") + f.write(""" +diff --git a/tools/libxl/idl.py b/tools/libxl/idl.py +index 2a7f3c44fe..d7367503b4 100644 +--- a/tools/libxl/idl.py ++++ b/tools/libxl/idl.py +@@ -1,3 +1,5 @@ ++from __future__ import print_function ++ + import sys + + PASS_BY_VALUE = 1 +@@ -11,7 +13,7 @@ DIR_BOTH = 3 + _default_namespace = "" + def namespace(s): + if type(s) != str: +- raise TypeError, "Require a string for the default namespace." ++ raise TypeError("Require a string for the default namespace.") + global _default_namespace + _default_namespace = s + +@@ -346,7 +348,7 @@ class OrderedDict(dict): + return [(x,self[x]) for x in self.__ordered] + + def parse(f): +- print >>sys.stderr, "Parsing %s" % f ++ print("Parsing %s" % f, file=sys.stderr) + + globs = {} + locs = OrderedDict() +@@ -362,11 +364,10 @@ def parse(f): + globs[n] = t + + try: +- execfile(f, globs, locs) +- except SyntaxError,e: +- raise SyntaxError, \ +- "Errors were found at line %d while processing %s:\n\t%s"\ +- %(e.lineno,f,e.text) ++ exec(compile(open(f).read(), f, 'exec'), globs, locs) ++ except SyntaxError as e: ++ raise SyntaxError("Errors were found at line %d while processing %s:\n\t%s" ++ % (e.lineno, f, e.text)) + + types = [t for t in locs.ordered_values() if isinstance(t,Type)] + +-- +2.17.2 + + +From 0aabd89dcfee9ee2a6caaa2ec7a475daf5cada53 Mon Sep 17 00:00:00 2001 +From: Wei Liu +Date: Thu, 7 Mar 2019 12:45:47 +0000 +Subject: [PATCH 6/8] pygrub: make python scripts work with 2.6 and up + +Run 2to3 and pick the sensible suggestions. + +Import print_function and absolute_import so 2.6 can work. + +There has never been a curses.wrapper module according to 2.x and 3.x +doc, only a function, so "import curses.wrapper" is not correct. It +happened to work because 2.x implemented a (undocumented) module. + +We only need to import curses to make curses.wrapper available to +pygrub. + +Signed-off-by: Wei Liu +Reviewed-by: Andrew Cooper +--- + tools/pygrub/src/ExtLinuxConf.py | 19 +++++---- + tools/pygrub/src/GrubConf.py | 39 ++++++++++-------- + tools/pygrub/src/LiloConf.py | 19 +++++---- + tools/pygrub/src/pygrub | 71 ++++++++++++++++---------------- + 4 files changed, 78 insertions(+), 70 deletions(-) + +diff --git a/tools/pygrub/src/ExtLinuxConf.py b/tools/pygrub/src/ExtLinuxConf.py +index d1789bf020..9fd635b9cf 100644 +--- a/tools/pygrub/src/ExtLinuxConf.py ++++ b/tools/pygrub/src/ExtLinuxConf.py +@@ -10,9 +10,11 @@ + # along with this program; If not, see . + # + ++from __future__ import print_function, absolute_import ++ + import sys, re, os + import logging +-import GrubConf ++from . import GrubConf + + class ExtLinuxImage(object): + def __init__(self, lines, path): +@@ -32,7 +34,8 @@ class ExtLinuxImage(object): + self.lines = [] + self.path = path + self.root = "" +- map(self.set_from_line, lines) ++ for line in lines: ++ self.set_from_line(line) + + def set_from_line(self, line, replace = None): + (com, arg) = GrubConf.grub_exact_split(line, 2) +@@ -67,7 +70,7 @@ class ExtLinuxImage(object): + setattr(self, "initrd", a.replace("initrd=", "")) + arg = arg.replace(a, "") + +- if com is not None and self.commands.has_key(com): ++ if com is not None and com in self.commands: + if self.commands[com] is not None: + setattr(self, self.commands[com], re.sub('^"(.+)"$', r"\1", arg.strip())) + else: +@@ -136,7 +139,7 @@ class ExtLinuxConfigFile(object): + def parse(self, buf = None): + if buf is None: + if self.filename is None: +- raise ValueError, "No config file defined to parse!" ++ raise ValueError("No config file defined to parse!") + + f = open(self.filename, 'r') + lines = f.readlines() +@@ -167,7 +170,7 @@ class ExtLinuxConfigFile(object): + + (com, arg) = GrubConf.grub_exact_split(l, 2) + com = com.lower() +- if self.commands.has_key(com): ++ if com in self.commands: + if self.commands[com] is not None: + setattr(self, self.commands[com], arg.strip()) + else: +@@ -207,8 +210,8 @@ class ExtLinuxConfigFile(object): + + if __name__ == "__main__": + if len(sys.argv) < 2: +- raise RuntimeError, "Need a configuration file to read" ++ raise RuntimeError("Need a configuration file to read") + g = ExtLinuxConfigFile(sys.argv[1]) + for i in g.images: +- print i +- print g.default ++ print(i) ++ print(g.default) +diff --git a/tools/pygrub/src/GrubConf.py b/tools/pygrub/src/GrubConf.py +index dc810d55cb..f8d3799dc0 100644 +--- a/tools/pygrub/src/GrubConf.py ++++ b/tools/pygrub/src/GrubConf.py +@@ -12,6 +12,8 @@ + # along with this program; If not, see . + # + ++from __future__ import print_function, absolute_import ++ + import os, sys + import logging + import re +@@ -44,7 +46,7 @@ def get_path(s): + return (None, s) + idx = s.find(')') + if idx == -1: +- raise ValueError, "Unable to find matching ')'" ++ raise ValueError("Unable to find matching ')'") + d = s[:idx] + return (GrubDiskPart(d), s[idx + 1:]) + +@@ -100,7 +102,8 @@ class _GrubImage(object): + " initrd: %s\n" %(self.title, self.root, self.kernel, + self.args, self.initrd)) + def _parse(self, lines): +- map(self.set_from_line, lines) ++ for line in lines: ++ self.set_from_line(line) + + def reset(self, lines): + self._root = self._initrd = self._kernel = self._args = None +@@ -141,7 +144,7 @@ class GrubImage(_GrubImage): + def set_from_line(self, line, replace = None): + (com, arg) = grub_exact_split(line, 2) + +- if self.commands.has_key(com): ++ if com in self.commands: + if self.commands[com] is not None: + setattr(self, self.commands[com], arg.strip()) + else: +@@ -177,7 +180,7 @@ class _GrubConfigFile(object): + self.parse() + + def parse(self, buf = None): +- raise RuntimeError, "unimplemented parse function" ++ raise RuntimeError("unimplemented parse function") + + def hasPasswordAccess(self): + return self.passwordAccess +@@ -201,7 +204,7 @@ class _GrubConfigFile(object): + import crypt + if crypt.crypt(password, pwd[1]) == pwd[1]: + return True +- except Exception, e: ++ except Exception as e: + self.passExc = "Can't verify password: %s" % str(e) + return False + +@@ -213,7 +216,7 @@ class _GrubConfigFile(object): + + def set(self, line): + (com, arg) = grub_exact_split(line, 2) +- if self.commands.has_key(com): ++ if com in self.commands: + if self.commands[com] is not None: + setattr(self, self.commands[com], arg.strip()) + else: +@@ -233,7 +236,7 @@ class _GrubConfigFile(object): + self._default = val + + if self._default < 0: +- raise ValueError, "default must be positive number" ++ raise ValueError("default must be positive number") + default = property(_get_default, _set_default) + + def set_splash(self, val): +@@ -265,7 +268,7 @@ class GrubConfigFile(_GrubConfigFile): + def parse(self, buf = None): + if buf is None: + if self.filename is None: +- raise ValueError, "No config file defined to parse!" ++ raise ValueError("No config file defined to parse!") + + f = open(self.filename, 'r') + lines = f.readlines() +@@ -296,7 +299,7 @@ class GrubConfigFile(_GrubConfigFile): + continue + + (com, arg) = grub_exact_split(l, 2) +- if self.commands.has_key(com): ++ if com in self.commands: + if self.commands[com] is not None: + setattr(self, self.commands[com], arg.strip()) + else: +@@ -328,7 +331,7 @@ class Grub2Image(_GrubImage): + if com == "set": + (com,arg) = grub2_handle_set(arg) + +- if self.commands.has_key(com): ++ if com in self.commands: + if self.commands[com] is not None: + setattr(self, self.commands[com], arg.strip()) + else: +@@ -364,7 +367,7 @@ class Grub2ConfigFile(_GrubConfigFile): + def parse(self, buf = None): + if buf is None: + if self.filename is None: +- raise ValueError, "No config file defined to parse!" ++ raise ValueError("No config file defined to parse!") + + f = open(self.filename, 'r') + lines = f.readlines() +@@ -398,7 +401,7 @@ class Grub2ConfigFile(_GrubConfigFile): + title_match = re.match('^menuentry ["\'](.*?)["\'] (.*){', l) + if title_match: + if img is not None: +- raise RuntimeError, "syntax error: cannot nest menuentry (%d %s)" % (len(img),img) ++ raise RuntimeError("syntax error: cannot nest menuentry (%d %s)" % (len(img),img)) + img = [] + title = title_match.group(1) + continue +@@ -413,7 +416,7 @@ class Grub2ConfigFile(_GrubConfigFile): + menu_level -= 1 + continue + else: +- raise RuntimeError, "syntax error: closing brace without menuentry" ++ raise RuntimeError("syntax error: closing brace without menuentry") + + self.add_image(Grub2Image(title, img)) + img = None +@@ -428,7 +431,7 @@ class Grub2ConfigFile(_GrubConfigFile): + if com == "set": + (com,arg) = grub2_handle_set(arg) + +- if self.commands.has_key(com): ++ if com in self.commands: + if self.commands[com] is not None: + arg_strip = arg.strip() + if arg_strip == "${saved_entry}" or arg_strip == "${next_entry}": +@@ -443,7 +446,7 @@ class Grub2ConfigFile(_GrubConfigFile): + logging.warning("Unknown directive %s" %(com,)) + + if img is not None: +- raise RuntimeError, "syntax error: end of file with open menuentry(%d %s)" % (len(img),img) ++ raise RuntimeError("syntax error: end of file with open menuentry(%d %s)" % (len(img),img)) + + if self.hasPassword(): + self.setPasswordAccess(False) +@@ -462,12 +465,12 @@ class Grub2ConfigFile(_GrubConfigFile): + + if __name__ == "__main__": + if len(sys.argv) < 3: +- raise RuntimeError, "Need a grub version (\"grub\" or \"grub2\") and a grub.conf or grub.cfg to read" ++ raise RuntimeError('Need a grub version ("grub" or "grub2") and a grub.conf or grub.cfg to read') + if sys.argv[1] == "grub": + g = GrubConfigFile(sys.argv[2]) + elif sys.argv[1] == "grub2": + g = Grub2ConfigFile(sys.argv[2]) + else: +- raise RuntimeError, "Unknown config type %s" % sys.argv[1] ++ raise RuntimeError("Unknown config type %s" % sys.argv[1]) + for i in g.images: +- print i #, i.title, i.root, i.kernel, i.args, i.initrd ++ print(i) #, i.title, i.root, i.kernel, i.args, i.initrd +diff --git a/tools/pygrub/src/LiloConf.py b/tools/pygrub/src/LiloConf.py +index 2cb649f115..e3bfcb5244 100644 +--- a/tools/pygrub/src/LiloConf.py ++++ b/tools/pygrub/src/LiloConf.py +@@ -2,9 +2,11 @@ + #LiloConf.py + # + ++from __future__ import print_function, absolute_import ++ + import sys, re, os + import logging +-import GrubConf ++from . import GrubConf + + class LiloImage(object): + def __init__(self, lines, path): +@@ -24,12 +26,13 @@ class LiloImage(object): + self.lines = [] + self.path = path + self.root = "" +- map(self.set_from_line, lines) ++ for line in lines: ++ self.set_from_line(line) + + def set_from_line(self, line, replace = None): + (com, arg) = GrubConf.grub_exact_split(line, 2) + +- if self.commands.has_key(com): ++ if com in self.commands: + if self.commands[com] is not None: + setattr(self, self.commands[com], re.sub('^"(.+)"$', r"\1", arg.strip())) + else: +@@ -97,7 +100,7 @@ class LiloConfigFile(object): + def parse(self, buf = None): + if buf is None: + if self.filename is None: +- raise ValueError, "No config file defined to parse!" ++ raise ValueError("No config file defined to parse!") + + f = open(self.filename, 'r') + lines = f.readlines() +@@ -127,7 +130,7 @@ class LiloConfigFile(object): + continue + + (com, arg) = GrubConf.grub_exact_split(l, 2) +- if self.commands.has_key(com): ++ if com in self.commands: + if self.commands[com] is not None: + setattr(self, self.commands[com], arg.strip()) + else: +@@ -170,8 +173,8 @@ class LiloConfigFile(object): + + if __name__ == "__main__": + if len(sys.argv) < 2: +- raise RuntimeError, "Need a lilo.conf to read" ++ raise RuntimeError("Need a lilo.conf to read") + g = LiloConfigFile(sys.argv[1]) + for i in g.images: +- print i #, i.title, i.root, i.kernel, i.args, i.initrd +- print g.default ++ print(i) #, i.title, i.root, i.kernel, i.args, i.initrd ++ print(g.default) +diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub +index 1189b1ca48..dbdce315c6 100755 +--- a/tools/pygrub/src/pygrub ++++ b/tools/pygrub/src/pygrub +@@ -12,13 +12,15 @@ + # along with this program; If not, see . + # + ++from __future__ import print_function ++ + import os, sys, string, struct, tempfile, re, traceback, stat, errno + import copy + import logging + import platform + import xen.lowlevel.xc + +-import curses, _curses, curses.wrapper, curses.textpad, curses.ascii ++import curses, _curses, curses.textpad, curses.ascii + import getopt + + import xenfsimage +@@ -77,7 +79,7 @@ def get_solaris_slice(file, offset): + buf = os.read(fd, 512) + os.close(fd) + if struct.unpack(">sys.stderr, "Using %s to parse %s" % (parser,f) ++ print("Using %s to parse %s" % (parser,f), file=sys.stderr) + self.cf = parser() + self.cf.filename = f + break + if self.__dict__.get('cf', None) is None: +- raise RuntimeError, "couldn't find bootloader config file in the image provided." ++ raise RuntimeError("couldn't find bootloader config file in the image provided.") + f = fs.open_file(self.cf.filename) + # limit read size to avoid pathological cases + buf = f.read(FS_READ_MAX) +@@ -628,11 +627,11 @@ def run_grub(file, entry, fs, cfg_args): + if list_entries: + for i in range(len(g.cf.images)): + img = g.cf.images[i] +- print "title: %s" % img.title +- print " root: %s" % img.root +- print " kernel: %s" % img.kernel[1] +- print " args: %s" % img.args +- print " initrd: %s" % img.initrd[1] ++ print("title: %s" % img.title) ++ print(" root: %s" % img.root) ++ print(" kernel: %s" % img.kernel[1]) ++ print(" args: %s" % img.args) ++ print(" initrd: %s" % img.initrd[1]) + + if interactive and not list_entries: + curses.wrapper(run_main) +@@ -646,7 +645,7 @@ def run_grub(file, entry, fs, cfg_args): + sel = idx + + if sel == -1: +- print "No kernel image selected!" ++ print("No kernel image selected!") + sys.exit(1) + + try: +@@ -731,7 +730,7 @@ def format_sxp(kernel, ramdisk, args): + def format_simple(kernel, ramdisk, args, sep): + for check in (kernel, ramdisk, args): + if check is not None and sep in check: +- raise RuntimeError, "simple format cannot represent delimiter-containing value" ++ raise RuntimeError("simple format cannot represent delimiter-containing value") + s = ("kernel %s" % kernel) + sep + if ramdisk: + s += ("ramdisk %s" % ramdisk) + sep +@@ -744,7 +743,7 @@ if __name__ == "__main__": + sel = None + + def usage(): +- print >> sys.stderr, "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],) ++ 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): +@@ -755,8 +754,8 @@ if __name__ == "__main__": + sys.exit("The requested %s file does not exist" % file_type) + try: + datafile = fs.open_file(file_to_read) +- except Exception, e: +- print >>sys.stderr, e ++ 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) +@@ -769,8 +768,8 @@ if __name__ == "__main__": + return ret + try: + os.write(tfd, data) +- except Exception, e: +- print >>sys.stderr, e ++ except Exception as e: ++ print(e, file=sys.stderr) + os.close(tfd) + os.unlink(ret) + del datafile +@@ -834,7 +833,7 @@ if __name__ == "__main__": + try: + part_offs = [ int(a) ] + except ValueError: +- print "offset value must be an integer" ++ print("offset value must be an integer") + usage() + sys.exit(1) + elif o in ("--entry",): +@@ -847,13 +846,13 @@ if __name__ == "__main__": + debug = True + elif o in ("--output-format",): + if a not in ["sxp", "simple", "simple0"]: +- print "unknown output format %s" % a ++ print("unknown output format %s" % a) + usage() + sys.exit(1) + output_format = a + elif o in ("--output-directory",): + if not os.path.isdir(a): +- print "%s is not an existing directory" % a ++ print("%s is not an existing directory" % a) + sys.exit(1) + output_directory = a + +@@ -862,8 +861,8 @@ if __name__ == "__main__": + + + try: +- os.makedirs(output_directory, 0700) +- except OSError,e: ++ os.makedirs(output_directory, 0o700) ++ except OSError as e: + if (e.errno == errno.EEXIST) and os.path.isdir(output_directory): + pass + else: +@@ -877,10 +876,10 @@ if __name__ == "__main__": + # debug + if isconfig: + chosencfg = run_grub(file, entry, fs, incfg["args"]) +- print " kernel: %s" % chosencfg["kernel"] ++ print(" kernel: %s" % chosencfg["kernel"]) + if chosencfg["ramdisk"]: +- print " initrd: %s" % chosencfg["ramdisk"] +- print " args: %s" % chosencfg["args"] ++ print(" initrd: %s" % chosencfg["ramdisk"]) ++ print(" args: %s" % chosencfg["args"]) + sys.exit(0) + + # if boot filesystem is set then pass to fsimage.open +@@ -926,7 +925,7 @@ if __name__ == "__main__": + + # Did looping through partitions find us a kernel? + if fs is None: +- raise RuntimeError, "Unable to find partition containing kernel" ++ raise RuntimeError("Unable to find partition containing kernel") + + bootcfg["kernel"] = copy_from_image(fs, chosencfg["kernel"], "kernel", + output_directory, not_really) +-- +2.17.2 + + +From 83a204e6951c6358f995da3b60dd61224e9d41ac Mon Sep 17 00:00:00 2001 +From: Wei Liu +Date: Tue, 5 Mar 2019 14:13:17 +0000 +Subject: [PATCH 7/8] pygrub/fsimage: make it work with python 3 + +With the help of two porting guides and cpython source code: + +1. Use PyBytes to replace PyString counterparts. +2. Use PyVarObject_HEAD_INIT. +3. Remove usage of Py_FindMethod. +4. Use new module initialisation routine. + +For #3, Py_FindMethod was removed, yet an alternative wasn't +documented. The code is the result of reverse-engineering cpython +commit 6116d4a1d1 + +https://docs.python.org/3/howto/cporting.html +http://python3porting.com/cextensions.html + +Signed-off-by: Wei Liu +Reviewed-by: Andrew Cooper +--- + tools/pygrub/src/fsimage/fsimage.c | 123 ++++++++++++++++------------- + 1 file changed, 69 insertions(+), 54 deletions(-) + +diff --git a/tools/pygrub/src/fsimage/fsimage.c b/tools/pygrub/src/fsimage/fsimage.c +index 780207791c..2ebbbe35df 100644 +--- a/tools/pygrub/src/fsimage/fsimage.c ++++ b/tools/pygrub/src/fsimage/fsimage.c +@@ -26,12 +26,6 @@ + #include + #include + +-#if (PYTHON_API_VERSION >= 1011) +-#define PY_PAD 0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L +-#else +-#define PY_PAD 0L,0L,0L,0L +-#endif +- + typedef struct fsimage_fs { + PyObject_HEAD + fsi_t *fs; +@@ -59,12 +53,24 @@ fsimage_file_read(fsimage_file_t *file, PyObject *args, PyObject *kwargs) + + bufsize = size ? size : 4096; + +- if ((buffer = PyString_FromStringAndSize(NULL, bufsize)) == NULL) ++ buffer = ++#if PY_MAJOR_VERSION < 3 ++ PyString_FromStringAndSize(NULL, bufsize); ++#else ++ PyBytes_FromStringAndSize(NULL, bufsize); ++#endif ++ ++ if (buffer == NULL) + return (NULL); + + while (1) { + int err; +- void *buf = PyString_AS_STRING(buffer) + bytesread; ++ void *buf = ++#if PY_MAJOR_VERSION < 3 ++ PyString_AS_STRING(buffer) + bytesread; ++#else ++ PyBytes_AS_STRING(buffer) + bytesread; ++#endif + + err = fsi_pread_file(file->file, buf, bufsize, + bytesread + offset); +@@ -84,12 +90,20 @@ fsimage_file_read(fsimage_file_t *file, PyObject *args, PyObject *kwargs) + if (bufsize == 0) + break; + } else { ++#if PY_MAJOR_VERSION < 3 + if (_PyString_Resize(&buffer, bytesread + bufsize) < 0) ++#else ++ if (_PyBytes_Resize(&buffer, bytesread + bufsize) < 0) ++#endif + return (NULL); + } + } + ++#if PY_MAJOR_VERSION < 3 + _PyString_Resize(&buffer, bytesread); ++#else ++ _PyBytes_Resize(&buffer, bytesread); ++#endif + return (buffer); + } + +@@ -106,11 +120,13 @@ static struct PyMethodDef fsimage_file_methods[] = { + { NULL, NULL, 0, NULL } + }; + ++#if PY_MAJOR_VERSION < 3 + static PyObject * + fsimage_file_getattr(fsimage_file_t *file, char *name) + { + return (Py_FindMethod(fsimage_file_methods, (PyObject *)file, name)); + } ++#endif + + static void + fsimage_file_dealloc(fsimage_file_t *file) +@@ -123,29 +139,18 @@ fsimage_file_dealloc(fsimage_file_t *file) + + static char fsimage_file_type__doc__[] = "Filesystem image file"; + PyTypeObject fsimage_file_type = { +- PyObject_HEAD_INIT(&PyType_Type) +- 0, /* ob_size */ +- "xenfsimage.file", /* tp_name */ +- sizeof(fsimage_file_t), /* tp_size */ +- 0, /* tp_itemsize */ +- (destructor) fsimage_file_dealloc, /* tp_dealloc */ +- 0, /* tp_print */ +- (getattrfunc) fsimage_file_getattr, /* tp_getattr */ +- 0, /* tp_setattr */ +- 0, /* tp_compare */ +- 0, /* tp_repr */ +- 0, /* tp_as_number */ +- 0, /* tp_as_sequence */ +- 0, /* tp_as_mapping */ +- 0, /* tp_hash */ +- 0, /* tp_call */ +- 0, /* tp_str */ +- 0, /* tp_getattro */ +- 0, /* tp_setattro */ +- 0, /* tp_as_buffer */ +- Py_TPFLAGS_DEFAULT, /* tp_flags */ +- fsimage_file_type__doc__, +- PY_PAD ++ PyVarObject_HEAD_INIT(&PyType_Type, 0) ++ .tp_name = "xenfsimage.file", ++ .tp_basicsize = sizeof(fsimage_file_t), ++ .tp_dealloc = (destructor) fsimage_file_dealloc, ++#if PY_MAJOR_VERSION < 3 ++ .tp_getattr = (getattrfunc) fsimage_file_getattr, ++#endif ++ .tp_flags = Py_TPFLAGS_DEFAULT, ++ .tp_doc = fsimage_file_type__doc__, ++#if PY_MAJOR_VERSION >= 3 ++ .tp_methods = fsimage_file_methods, ++#endif + }; + + static PyObject * +@@ -208,11 +213,13 @@ static struct PyMethodDef fsimage_fs_methods[] = { + { NULL, NULL, 0, NULL } + }; + ++#if PY_MAJOR_VERSION < 3 + static PyObject * + fsimage_fs_getattr(fsimage_fs_t *fs, char *name) + { + return (Py_FindMethod(fsimage_fs_methods, (PyObject *)fs, name)); + } ++#endif + + static void + fsimage_fs_dealloc (fsimage_fs_t *fs) +@@ -225,29 +232,18 @@ fsimage_fs_dealloc (fsimage_fs_t *fs) + PyDoc_STRVAR(fsimage_fs_type__doc__, "Filesystem image"); + + PyTypeObject fsimage_fs_type = { +- PyObject_HEAD_INIT(&PyType_Type) +- 0, /* ob_size */ +- "xenfsimage.fs", /* tp_name */ +- sizeof(fsimage_fs_t), /* tp_size */ +- 0, /* tp_itemsize */ +- (destructor) fsimage_fs_dealloc, /* tp_dealloc */ +- 0, /* tp_print */ +- (getattrfunc) fsimage_fs_getattr, /* tp_getattr */ +- 0, /* tp_setattr */ +- 0, /* tp_compare */ +- 0, /* tp_repr */ +- 0, /* tp_as_number */ +- 0, /* tp_as_sequence */ +- 0, /* tp_as_mapping */ +- 0, /* tp_hash */ +- 0, /* tp_call */ +- 0, /* tp_str */ +- 0, /* tp_getattro */ +- 0, /* tp_setattro */ +- 0, /* tp_as_buffer */ +- Py_TPFLAGS_DEFAULT, /* tp_flags */ +- fsimage_fs_type__doc__, +- PY_PAD ++ PyVarObject_HEAD_INIT(&PyType_Type, 0) ++ .tp_name = "xenfsimage.fs", ++ .tp_basicsize = sizeof(fsimage_fs_t), ++ .tp_dealloc = (destructor) fsimage_fs_dealloc, ++#if PY_MAJOR_VERSION < 3 ++ .tp_getattr = (getattrfunc) fsimage_fs_getattr, ++#endif ++ .tp_flags = Py_TPFLAGS_DEFAULT, ++ .tp_doc = fsimage_fs_type__doc__, ++#if PY_MAJOR_VERSION >= 3 ++ .tp_methods = fsimage_fs_methods, ++#endif + }; + + static PyObject * +@@ -309,8 +305,27 @@ static struct PyMethodDef fsimage_module_methods[] = { + { NULL, NULL, 0, NULL } + }; + ++#if PY_MAJOR_VERSION >= 3 ++static struct PyModuleDef fsimage_module_def = { ++ PyModuleDef_HEAD_INIT, ++ .m_name = "xenfsimage", ++ .m_size = -1, ++ .m_methods = fsimage_module_methods, ++}; ++#endif ++ + PyMODINIT_FUNC ++#if PY_MAJOR_VERSION >= 3 ++PyInit_xenfsimage(void) ++#else + initxenfsimage(void) ++#endif + { ++#if PY_MAJOR_VERSION < 3 + Py_InitModule("xenfsimage", fsimage_module_methods); ++#else ++ if (PyType_Ready(&fsimage_fs_type) < 0 || PyType_Ready(&fsimage_file_type) < 0) ++ return NULL; ++ return PyModule_Create(&fsimage_module_def); ++#endif + } +-- +2.17.2 + + +From 9b0bc91b3b32856df014fab9de40f463c89a8b1e Mon Sep 17 00:00:00 2001 +From: Wei Liu +Date: Mon, 11 Mar 2019 17:19:19 +0000 +Subject: [PATCH 8/8] README: remove requirement on Python 2 + +Now that all python scripts are compatible with Python 2.6 and above, +remove the restriction. + +Signed-off-by: Wei Liu +Acked-by: Andrew Cooper +--- + README | 10 ++-------- + 1 file changed, 2 insertions(+), 8 deletions(-) + +diff --git a/README b/README +index d00715c2fd..23e4f7c3dc 100644 +--- a/README ++++ b/README +@@ -46,7 +46,7 @@ provided by your OS distributor: + - GCC 4.8 or later + - GNU Binutils 2.24 or later + * Development install of zlib (e.g., zlib-dev) +- * Development install of Python 2, v2.6 or later (e.g., python-dev) ++ * Development install of Python 2.6 or later (e.g., python-dev) + * Development install of curses (e.g., libncurses-dev) + * Development install of openssl (e.g., openssl-dev) + * Development install of x11 (e.g. xorg-x11-dev) +@@ -177,16 +177,10 @@ Python Runtime Libraries + + Various tools, such as pygrub, have the following runtime dependencies: + +- * Python 2, v2.6 or later. ++ * Python 2.6 or later. + URL: http://www.python.org/ + Debian: python + +-Note that the build system expects `python` to be python2. If your system +-has `python` pointing to python3 (as in the case of Arch Linux or Anaconda), +-you'll need to specify a path to a python2 binary when running configure: +- +- PYTHON=/usr/bin/python2 ./configure +- + Intel(R) Trusted Execution Technology Support + ============================================= + +-- +2.17.2 + +From 38a85fe8d98b54da7f842f8d78bf8c54f7747735 Mon Sep 17 00:00:00 2001 +From: Wei Liu +Date: Wed, 13 Mar 2019 13:54:48 +0000 +Subject: [PATCH] build/m4: fix python library detection on Ubuntu systems + +16cc3362aed doesn't work on Ubuntu with gcc (but it does work with +clang). Work around it by manipulating LIBS. + +Signed-off-by: Wei Liu +Acked-by: Andrew Cooper +--- + m4/python_devel.m4 | 7 +++++++ + tools/configure | 7 +++++++ + 2 files changed, 14 insertions(+) + +diff --git a/m4/python_devel.m4 b/m4/python_devel.m4 +index f9cb23aee1..7f26381376 100644 +--- a/m4/python_devel.m4 ++++ b/m4/python_devel.m4 +@@ -1,6 +1,7 @@ + AC_DEFUN([AX_CHECK_PYTHON_DEVEL], [ + ac_previous_cppflags=$CPPFLAGS + ac_previous_ldflags=$LDFLAGS ++ac_previous_libs=$LIBS + AC_PATH_PROG([pyconfig], [$PYTHON-config], [no]) + AS_IF([test x"$pyconfig" = x"no"], [ + dnl For those that don't have python-config +@@ -15,10 +16,15 @@ AS_IF([test x"$pyconfig" = x"no"], [ + print(distutils.sysconfig.get_config_var("LINKFORSHARED"))'`" + LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ + print(distutils.sysconfig.get_config_var("LDFLAGS"))'`" ++ LIBS="$LIBS `$PYTHON -c 'import distutils.sysconfig; \ ++ print(distutils.sysconfig.get_config_var("LIBS"))'`" ++ LIBS="$LIBS `$PYTHON -c 'import distutils.sysconfig; \ ++ print(distutils.sysconfig.get_config_var("SYSLIBS"))'`" + ], [ + dnl If python-config is found use it + CPPFLAGS="$CFLAGS `$PYTHON-config --cflags`" + LDFLAGS="$LDFLAGS `$PYTHON-config --ldflags`" ++ LIBS="$LIBS `$PYTHON-config --libs`" + ]) + + AC_CHECK_HEADER([Python.h], [], +@@ -28,4 +34,5 @@ AC_CHECK_FUNC([PyArg_ParseTuple], [], + + CPPFLAGS=$ac_previous_cppflags + LDFLAGS=$ac_previous_ldflags ++LIBS=$ac_previous_libs + ]) +diff --git a/tools/configure b/tools/configure +index 9ff879548a..b66d3f6fba 100755 +--- a/tools/configure ++++ b/tools/configure +@@ -7418,6 +7418,7 @@ if test "$cross_compiling" != yes; then : + + ac_previous_cppflags=$CPPFLAGS + ac_previous_ldflags=$LDFLAGS ++ac_previous_libs=$LIBS + # Extract the first word of "$PYTHON-config", so it can be a program name with args. + set dummy $PYTHON-config; ac_word=$2 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +@@ -7472,11 +7473,16 @@ if test x"$pyconfig" = x"no"; then : + print(distutils.sysconfig.get_config_var("LINKFORSHARED"))'`" + LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ + print(distutils.sysconfig.get_config_var("LDFLAGS"))'`" ++ LIBS="$LIBS `$PYTHON -c 'import distutils.sysconfig; \ ++ print(distutils.sysconfig.get_config_var("LIBS"))'`" ++ LIBS="$LIBS `$PYTHON -c 'import distutils.sysconfig; \ ++ print(distutils.sysconfig.get_config_var("SYSLIBS"))'`" + + else + + CPPFLAGS="$CFLAGS `$PYTHON-config --cflags`" + LDFLAGS="$LDFLAGS `$PYTHON-config --ldflags`" ++ LIBS="$LIBS `$PYTHON-config --libs`" + + fi + +@@ -7498,6 +7504,7 @@ fi + + CPPFLAGS=$ac_previous_cppflags + LDFLAGS=$ac_previous_ldflags ++LIBS=$ac_previous_libs + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether Python setup.py brokenly enables -D_FORTIFY_SOURCE" >&5 +-- +2.17.2 + +From 485079e816788d70169f45579e1f5a8f909dc1b3 Mon Sep 17 00:00:00 2001 +From: Wei Liu +Date: Mon, 1 Apr 2019 11:32:35 +0100 +Subject: [PATCH 1/4] pygrub: fix message in grub parser + +The code suggests 0 is allowed. Zero is not a positive number. + +Signed-off-by: Wei Liu +Acked-by: Andrew Cooper +--- + tools/pygrub/src/GrubConf.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/pygrub/src/GrubConf.py b/tools/pygrub/src/GrubConf.py +index f8d3799dc0..0204d410ac 100644 +--- a/tools/pygrub/src/GrubConf.py ++++ b/tools/pygrub/src/GrubConf.py +@@ -236,7 +236,7 @@ class _GrubConfigFile(object): + self._default = val + + if self._default < 0: +- raise ValueError("default must be positive number") ++ raise ValueError("default must be non-negative number") + default = property(_get_default, _set_default) + + def set_splash(self, val): +-- +2.17.2 + + +From 767ba397d34848c7e0c4e9cdfc5efa4e0cb61442 Mon Sep 17 00:00:00 2001 +From: Wei Liu +Date: Mon, 1 Apr 2019 11:32:36 +0100 +Subject: [PATCH 2/4] pygrub/grub: always use integer for default entry + +The original code set the default to either a string or an integer +(0) and relies on a Python 2 specific behaviour to work (integer is +allowed to be compared to string in Python 2 but not 3). + +Always use integer. The caller (pygrub) already has code to handle +that. + +Reported-by: M A Young +Signed-off-by: Wei Liu +Acked-by: Andrew Cooper +--- + tools/pygrub/src/GrubConf.py | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/tools/pygrub/src/GrubConf.py b/tools/pygrub/src/GrubConf.py +index 0204d410ac..594139bac7 100644 +--- a/tools/pygrub/src/GrubConf.py ++++ b/tools/pygrub/src/GrubConf.py +@@ -233,7 +233,11 @@ class _GrubConfigFile(object): + if val == "saved": + self._default = 0 + else: +- self._default = val ++ try: ++ self._default = int(val) ++ except ValueError: ++ logging.warning("Invalid value %s, setting default to 0" %(val,)) ++ self._default = 0 + + if self._default < 0: + raise ValueError("default must be non-negative number") +-- +2.17.2 + + +From ff915c8cacc264ae1380d51fea07267b8308d7ba Mon Sep 17 00:00:00 2001 +From: Wei Liu +Date: Mon, 1 Apr 2019 11:32:37 +0100 +Subject: [PATCH 3/4] pygrub: encode / decode string in Python 3 + +String is unicode in 3 but bytes in 2. We need to call encode / decode +function when using Python 3. + +Reported-by: M A Young +Signed-off-by: Wei Liu +Acked-by: Andrew Cooper +--- + tools/pygrub/src/pygrub | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub +index dbdce315c6..ce7ab0eb8c 100755 +--- a/tools/pygrub/src/pygrub ++++ b/tools/pygrub/src/pygrub +@@ -457,7 +457,10 @@ class Grub: + # limit read size to avoid pathological cases + buf = f.read(FS_READ_MAX) + del f +- self.cf.parse(buf) ++ if sys.version_info[0] < 3: ++ self.cf.parse(buf) ++ else: ++ self.cf.parse(buf.decode()) + + def image_index(self): + if isinstance(self.cf.default, int): +@@ -960,5 +963,8 @@ if __name__ == "__main__": + ostring = format_simple(bootcfg["kernel"], bootcfg["ramdisk"], args, "\0") + + sys.stdout.flush() +- os.write(fd, ostring) ++ if sys.version_info[0] < 3: ++ os.write(fd, ostring) ++ else: ++ os.write(fd, ostring.encode()) + +-- +2.17.2 + + +From a57a1b26ec0ae31f924cf2bbcf479637d007be44 Mon Sep 17 00:00:00 2001 +From: Wei Liu +Date: Mon, 1 Apr 2019 11:32:38 +0100 +Subject: [PATCH 4/4] tools/ocaml: make python scripts 2 and 3 compatible + +1. Explicitly import reduce because that's required in 3. +2. Change print to function. +3. Eliminate invocations of has_key. + +Signed-off-by: M A Young +Signed-off-by: Wei Liu +Reviewed-by: Andrew Cooper +Acked-by: Christian Lindig +--- + tools/ocaml/libs/xentoollog/genlevels.py | 5 ++++- + tools/ocaml/libs/xl/genwrap.py | 17 ++++++++++------- + 2 files changed, 14 insertions(+), 8 deletions(-) + +diff --git a/tools/ocaml/libs/xentoollog/genlevels.py b/tools/ocaml/libs/xentoollog/genlevels.py +index 8c233c59b1..f9cf853e26 100755 +--- a/tools/ocaml/libs/xentoollog/genlevels.py ++++ b/tools/ocaml/libs/xentoollog/genlevels.py +@@ -1,6 +1,9 @@ + #!/usr/bin/python + ++from __future__ import print_function ++ + import sys ++from functools import reduce + + def read_levels(): + f = open('../../../libs/toollog/include/xentoollog.h', 'r') +@@ -93,7 +96,7 @@ def autogen_header(open_comment, close_comment): + + if __name__ == '__main__': + if len(sys.argv) < 3: +- print >>sys.stderr, "Usage: genlevels.py " ++ print("Usage: genlevels.py ", file=sys.stderr) + sys.exit(1) + + levels, olevels = read_levels() +diff --git a/tools/ocaml/libs/xl/genwrap.py b/tools/ocaml/libs/xl/genwrap.py +index 815c1cb0e3..7bf26bdcd8 100644 +--- a/tools/ocaml/libs/xl/genwrap.py ++++ b/tools/ocaml/libs/xl/genwrap.py +@@ -1,6 +1,9 @@ + #!/usr/bin/python + ++from __future__ import print_function ++ + import sys,os ++from functools import reduce + + import idl + +@@ -78,7 +81,7 @@ def ocaml_type_of(ty): + elif isinstance(ty,idl.Array): + return "%s array" % ocaml_type_of(ty.elem_type) + elif isinstance(ty,idl.Builtin): +- if not builtins.has_key(ty.typename): ++ if ty.typename not in builtins: + raise NotImplementedError("Unknown Builtin %s (%s)" % (ty.typename, type(ty))) + typename,_,_ = builtins[ty.typename] + if not typename: +@@ -251,7 +254,7 @@ def gen_ocaml_ml(ty, interface, indent=""): + else: + s += "\texternal default : ctx -> %sunit -> t = \"stub_libxl_%s_init\"\n" % (union_args, ty.rawname) + +- if functions.has_key(ty.rawname): ++ if ty.rawname in functions: + for name,args in functions[ty.rawname]: + s += "\texternal %s : " % name + s += " -> ".join(args) +@@ -278,7 +281,7 @@ def c_val(ty, c, o, indent="", parent = None): + else: + s += "%s = Int_val(%s);" % (c, o) + elif isinstance(ty,idl.Builtin): +- if not builtins.has_key(ty.typename): ++ if ty.typename not in builtins: + raise NotImplementedError("Unknown Builtin %s (%s)" % (ty.typename, type(ty))) + _,fn,_ = builtins[ty.typename] + if not fn: +@@ -375,7 +378,7 @@ def ocaml_Val(ty, o, c, indent="", parent = None): + else: + s += "%s = Val_int(%s);" % (o, c) + elif isinstance(ty,idl.Builtin): +- if not builtins.has_key(ty.typename): ++ if ty.typename not in builtins: + raise NotImplementedError("Unknown Builtin %s (%s)" % (ty.typename, type(ty))) + _,_,fn = builtins[ty.typename] + if not fn: +@@ -520,7 +523,7 @@ def autogen_header(open_comment, close_comment): + + if __name__ == '__main__': + if len(sys.argv) < 4: +- print >>sys.stderr, "Usage: genwrap.py " ++ print("Usage: genwrap.py ", file=sys.stderr) + sys.exit(1) + + (_,types) = idl.parse(sys.argv[1]) +@@ -533,7 +536,7 @@ if __name__ == '__main__': + + for t in blacklist: + if t not in [ty.rawname for ty in types]: +- print "unknown type %s in blacklist" % t ++ print("unknown type %s in blacklist" % t) + + types = [ty for ty in types if not ty.rawname in blacklist] + +@@ -564,7 +567,7 @@ if __name__ == '__main__': + cinc.write("\n") + cinc.write(gen_Val_ocaml(ty)) + cinc.write("\n") +- if functions.has_key(ty.rawname): ++ if ty.rawname in functions: + cinc.write(gen_c_stub_prototype(ty, functions[ty.rawname])) + cinc.write("\n") + if ty.init_fn is not None: +-- +2.17.2 + diff --git a/xen.spec b/xen.spec index 6b9e4f7..b122ac8 100644 --- a/xen.spec +++ b/xen.spec @@ -112,6 +112,7 @@ Patch39: qemu.trad.CVE-2017-9330.patch Patch41: xen.drop.brctl.patch Patch45: xen.python.env.patch Patch46: xen.gcc9.fixes.patch +Patch47: xen.python3.patch %if %build_qemutrad @@ -124,7 +125,7 @@ BuildRequires: seabios-bin ipxe-roms-qemu BuildRequires: dev86 %endif %endif -BuildRequires: python2-devel ncurses-devel +BuildRequires: python3-devel ncurses-devel BuildRequires: perl-interpreter perl-generators %ifarch %{ix86} x86_64 # so that x86_64 builds pick up glibc32 correctly @@ -157,7 +158,7 @@ BuildRequires: gcc-x86_64-linux-gnu %endif BuildRequires: gcc Requires: iproute -Requires: python2-lxml +Requires: python3-lxml Requires: xen-runtime = %{version}-%{release} # Not strictly a dependency, but kpartx is by far the most useful tool right # now for accessing domU data from within a dom0 so bring it in when the user @@ -309,6 +310,7 @@ manage Xen virtual machines. %patch41 -p1 %patch45 -p1 %patch46 -p1 +%patch47 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -352,7 +354,7 @@ mkdir -p dist/install%{_libdir}/ocaml/stublibs export EXTRA_CFLAGS_XEN_TOOLS="$RPM_OPT_FLAGS" export EXTRA_CFLAGS_QEMU_TRADITIONAL="$RPM_OPT_FLAGS" export EXTRA_CFLAGS_QEMU_XEN="$RPM_OPT_FLAGS" -export PYTHON="/usr/bin/python2" +export PYTHON="/usr/bin/python3" %if %build_hyp %if %build_crosshyp XEN_TARGET_ARCH=x86_64 make %{?_smp_mflags} 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'`" @@ -634,8 +636,8 @@ fi %files %doc COPYING README %{_bindir}/xencons -%{python2_sitearch}/%{name} -%{python2_sitearch}/xen-*.egg-info +%{python3_sitearch}/%{name} +%{python3_sitearch}/xen-*.egg-info # Guest autostart links %dir %attr(0700,root,root) %{_sysconfdir}/%{name}/auto @@ -708,9 +710,9 @@ fi %{_mandir}/man7/xl-numa-placement.7.gz %endif -%{python2_sitearch}/xenfsimage.so -%{python2_sitearch}/grub -%{python2_sitearch}/pygrub-*.egg-info +%{python3_sitearch}/xenfsimage*.so +%{python3_sitearch}/grub +%{python3_sitearch}/pygrub-*.egg-info # The firmware %ifarch %{ix86} x86_64 @@ -867,6 +869,7 @@ fi fs libdir is now xenfsimage libdir xen-ringwatch xen-bugtool have been dropped - remove remaining traces of efiming and efi_flags logic +- switch from python2 to python3 * Tue Mar 05 2019 Michael Young - 4.11.1-4 - xen: various flaws (#1685577) From 852b076a0e9b1a196a814c632e357950b743a8f7 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Fri, 5 Apr 2019 21:08:20 +0100 Subject: [PATCH 004/194] drop systemd_postun and renumber patches --- .gitignore | 2 +- sources | 2 +- xen.spec | 35 ++++++++++------------------------- 3 files changed, 12 insertions(+), 27 deletions(-) diff --git a/.gitignore b/.gitignore index 130be34..7d62d29 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.11.1.tar.gz +/xen-4.12.0.tar.gz diff --git a/sources b/sources index f2266d9..b5efa23 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.11.1.tar.gz) = c1655c5decdaed95a2b9a99652318cfc72f6cfdae957cfe60d635f7787e8850f33e8fafc4c4b8d61fb579c9b9d93028a6382903e71808a0418b931e76d72a649 +SHA512 (xen-4.12.0.tar.gz) = 0ce366dcac607c9b592c5e9c0f40652eef743913b246bed4b0c380b8d59ac23a6adcd05befec37fc799a61476f47df76d4911cbf1da6ceb51441c546bf2464de diff --git a/xen.spec b/xen.spec index b122ac8..56421b0 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.12.0 -Release: 0%{?dist} +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,10 +109,10 @@ Patch36: qemu.trad.CVE-2017-7718.patch Patch37: droplibvirtconflict.patch Patch38: qemu.trad.CVE-2017-8309.patch Patch39: qemu.trad.CVE-2017-9330.patch -Patch41: xen.drop.brctl.patch -Patch45: xen.python.env.patch -Patch46: xen.gcc9.fixes.patch -Patch47: xen.python3.patch +Patch40: xen.drop.brctl.patch +Patch41: xen.python.env.patch +Patch42: xen.gcc9.fixes.patch +Patch43: xen.python3.patch %if %build_qemutrad @@ -172,7 +172,6 @@ BuildRequires: ocaml, ocaml-findlib %if %with_systemd_presets Requires(post): systemd Requires(preun): systemd -Requires(postun): systemd BuildRequires: systemd %endif BuildRequires: systemd-devel @@ -307,10 +306,10 @@ manage Xen virtual machines. %patch34 -p1 %patch37 -p1 %patch3 -p1 +%patch40 -p1 %patch41 -p1 -%patch45 -p1 -%patch46 -p1 -%patch47 -p1 +%patch42 -p1 +%patch43 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -544,11 +543,6 @@ if [ $1 == 0 ]; then fi %endif -%if %with_systemd_presets -%postun -%systemd_postun -%endif - %post runtime %if %with_systemd_presets %systemd_post xenstored.service xenconsoled.service @@ -569,11 +563,6 @@ if [ $1 == 0 ]; then fi %endif -%if %with_systemd_presets -%postun runtime -%systemd_postun -%endif - %posttrans runtime if [ ! -L /usr/lib/xen -a -d /usr/lib/xen -a -z "$(ls -A /usr/lib/xen)" ]; then rmdir /usr/lib/xen @@ -624,11 +613,6 @@ if [ $1 == 0 ]; then /bin/systemctl disable oxenstored.service fi %endif - -%if %with_systemd_presets -%postun ocaml -%systemd_postun -%endif %endif # Base package only contains XenD/xm python stuff @@ -858,7 +842,7 @@ fi %endif %changelog -* Thu Apr 04 2019 Michael Young - 4.12.0-0 +* Fri Apr 05 2019 Michael Young - 4.12.0-1 - update to 4.12.0 (#1694695) remove patches for issues now fixed upstream replace xen.use.fedora.ipxe.patch with --with-system-ipxe @@ -870,6 +854,7 @@ fi xen-ringwatch xen-bugtool have been dropped - remove remaining traces of efiming and efi_flags logic - switch from python2 to python3 +- drop systemd_postun and renumber patches * Tue Mar 05 2019 Michael Young - 4.11.1-4 - xen: various flaws (#1685577) From 6ea38f207f89d2e0c98bffd4b79d7a2f5bd4db6d Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 14 May 2019 21:53:52 +0100 Subject: [PATCH 005/194] Microarchitectural Data Sampling speculative side channel [XSA-297, CVE-2018-12126, CVE-2018-12127, CVE-2018-12130, CVE-2019-11091] additional patches so above applies cleanly work around grub2 issues in dom0 --- ...1338e1d8a32e46c808321323c4ad8fc5ba01.patch | 71 ++++ ...ebee98e61dfe3b8b1eb71043ad5220b3cfd3.patch | 185 ++++++++++ xen.spec | 74 +++- xsa297-4.12-1.patch | 163 +++++++++ xsa297-4.12-2.patch | 54 +++ xsa297-4.12-3.patch | 109 ++++++ xsa297-4.12-4.patch | 55 +++ xsa297-4.12-5.patch | 147 ++++++++ xsa297-4.12-6.patch | 134 ++++++++ xsa297-4.12-7.patch | 316 ++++++++++++++++++ 10 files changed, 1307 insertions(+), 1 deletion(-) create mode 100644 xen.git-e25d1338e1d8a32e46c808321323c4ad8fc5ba01.patch create mode 100644 xen.git-e3a1ebee98e61dfe3b8b1eb71043ad5220b3cfd3.patch create mode 100644 xsa297-4.12-1.patch create mode 100644 xsa297-4.12-2.patch create mode 100644 xsa297-4.12-3.patch create mode 100644 xsa297-4.12-4.patch create mode 100644 xsa297-4.12-5.patch create mode 100644 xsa297-4.12-6.patch create mode 100644 xsa297-4.12-7.patch diff --git a/xen.git-e25d1338e1d8a32e46c808321323c4ad8fc5ba01.patch b/xen.git-e25d1338e1d8a32e46c808321323c4ad8fc5ba01.patch new file mode 100644 index 0000000..ece6829 --- /dev/null +++ b/xen.git-e25d1338e1d8a32e46c808321323c4ad8fc5ba01.patch @@ -0,0 +1,71 @@ +From e25d1338e1d8a32e46c808321323c4ad8fc5ba01 Mon Sep 17 00:00:00 2001 +From: Andrew Cooper +Date: Fri, 3 May 2019 10:44:58 +0200 +Subject: [PATCH] x86/msr: Shorten ARCH_CAPABILITIES_* constants + +They are unnecesserily verbose, and ARCH_CAPS_* is already the more common +version. + +Signed-off-by: Andrew Cooper +Acked-by: Jan Beulich +master commit: ba27aaa88548c824a47dcf5609288ee1c05d2946 +master date: 2019-03-18 16:26:40 +0000 +--- + xen/arch/x86/spec_ctrl.c | 10 +++++----- + xen/include/asm-x86/msr-index.h | 4 ++-- + 2 files changed, 7 insertions(+), 7 deletions(-) + +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index ad72ecd3a5..22bfc5a5e8 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -230,8 +230,8 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + (_7d0 & cpufeat_mask(X86_FEATURE_L1D_FLUSH)) ? " L1D_FLUSH" : "", + (_7d0 & cpufeat_mask(X86_FEATURE_SSBD)) ? " SSBD" : "", + (e8b & cpufeat_mask(X86_FEATURE_IBPB)) ? " IBPB" : "", +- (caps & ARCH_CAPABILITIES_IBRS_ALL) ? " IBRS_ALL" : "", +- (caps & ARCH_CAPABILITIES_RDCL_NO) ? " RDCL_NO" : "", ++ (caps & ARCH_CAPS_IBRS_ALL) ? " IBRS_ALL" : "", ++ (caps & ARCH_CAPS_RDCL_NO) ? " RDCL_NO" : "", + (caps & ARCH_CAPS_RSBA) ? " RSBA" : "", + (caps & ARCH_CAPS_SKIP_L1DFL) ? " SKIP_L1DFL": "", + (caps & ARCH_CAPS_SSB_NO) ? " SSB_NO" : ""); +@@ -549,7 +549,7 @@ static __init void l1tf_calculations(uint64_t caps) + } + + /* Any processor advertising RDCL_NO should be not vulnerable to L1TF. */ +- if ( caps & ARCH_CAPABILITIES_RDCL_NO ) ++ if ( caps & ARCH_CAPS_RDCL_NO ) + cpu_has_bug_l1tf = false; + + if ( cpu_has_bug_l1tf && hit_default ) +@@ -613,9 +613,9 @@ int8_t __read_mostly opt_xpti_domu = -1; + static __init void xpti_init_default(uint64_t caps) + { + if ( boot_cpu_data.x86_vendor == X86_VENDOR_AMD ) +- caps = ARCH_CAPABILITIES_RDCL_NO; ++ caps = ARCH_CAPS_RDCL_NO; + +- if ( caps & ARCH_CAPABILITIES_RDCL_NO ) ++ if ( caps & ARCH_CAPS_RDCL_NO ) + { + if ( opt_xpti_hwdom < 0 ) + opt_xpti_hwdom = 0; +diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h +index c6e1d8768f..11512d4250 100644 +--- a/xen/include/asm-x86/msr-index.h ++++ b/xen/include/asm-x86/msr-index.h +@@ -42,8 +42,8 @@ + #define PRED_CMD_IBPB (_AC(1, ULL) << 0) + + #define MSR_ARCH_CAPABILITIES 0x0000010a +-#define ARCH_CAPABILITIES_RDCL_NO (_AC(1, ULL) << 0) +-#define ARCH_CAPABILITIES_IBRS_ALL (_AC(1, ULL) << 1) ++#define ARCH_CAPS_RDCL_NO (_AC(1, ULL) << 0) ++#define ARCH_CAPS_IBRS_ALL (_AC(1, ULL) << 1) + #define ARCH_CAPS_RSBA (_AC(1, ULL) << 2) + #define ARCH_CAPS_SKIP_L1DFL (_AC(1, ULL) << 3) + #define ARCH_CAPS_SSB_NO (_AC(1, ULL) << 4) +-- +2.11.0 + diff --git a/xen.git-e3a1ebee98e61dfe3b8b1eb71043ad5220b3cfd3.patch b/xen.git-e3a1ebee98e61dfe3b8b1eb71043ad5220b3cfd3.patch new file mode 100644 index 0000000..c0862cd --- /dev/null +++ b/xen.git-e3a1ebee98e61dfe3b8b1eb71043ad5220b3cfd3.patch @@ -0,0 +1,185 @@ +From e3a1ebee98e61dfe3b8b1eb71043ad5220b3cfd3 Mon Sep 17 00:00:00 2001 +From: Andrew Cooper +Date: Fri, 3 May 2019 10:39:29 +0200 +Subject: [PATCH] x86/tsx: Implement controls for RTM force-abort mode + +The CPUID bit and MSR are deliberately not exposed to guests, because they +won't exist on newer processors. As vPMU isn't security supported, the +misbehaviour of PCR3 isn't expected to impact production deployments. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +master commit: 6be613f29b4205349275d24367bd4c82fb2960dd +master date: 2019-03-12 17:05:21 +0000 +--- + docs/misc/xen-command-line.pandoc | 17 ++++++++++++++++- + tools/misc/xen-cpuid.c | 2 ++ + xen/arch/x86/cpu/intel.c | 3 +++ + xen/arch/x86/cpu/vpmu.c | 3 +++ + xen/arch/x86/msr.c | 4 ++++ + xen/include/asm-x86/cpufeature.h | 3 +++ + xen/include/asm-x86/msr-index.h | 3 +++ + xen/include/asm-x86/vpmu.h | 1 + + xen/include/public/arch-x86/cpufeatureset.h | 1 + + 9 files changed, 36 insertions(+), 1 deletion(-) + +diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc +index 742555616d..6db82f302e 100644 +--- a/docs/misc/xen-command-line.pandoc ++++ b/docs/misc/xen-command-line.pandoc +@@ -2109,7 +2109,7 @@ Use Virtual Processor ID support if available. This prevents the need for TLB + flushes on VM entry and exit, increasing performance. + + ### vpmu (x86) +- = List of [ , bts, ipc, arch ] ++ = List of [ , bts, ipc, arch, rtm-abort= ] + + Applicability: x86. Default: false + +@@ -2142,6 +2142,21 @@ provide access to a wealth of low level processor information. + + * The `arch` option allows access to the pre-defined architectural events. + ++* The `rtm-abort` boolean controls a trade-off between working Restricted ++ Transactional Memory, and working performance counters. ++ ++ All processors released to date (Q1 2019) supporting Transactional Memory ++ Extensions suffer an erratum which has been addressed in microcode. ++ ++ Processors based on the Skylake microarchitecture with up-to-date ++ microcode internally use performance counter 3 to work around the erratum. ++ A consequence is that the counter gets reprogrammed whenever an `XBEGIN` ++ instruction is executed. ++ ++ An alternative mode exists where PCR3 behaves as before, at the cost of ++ `XBEGIN` unconditionally aborting. Enabling `rtm-abort` mode will ++ activate this alternative mode. ++ + *Warning:* + As the virtualisation is not 100% safe, don't use the vpmu flag on + production systems (see http://xenbits.xen.org/xsa/advisory-163.html)! +diff --git a/tools/misc/xen-cpuid.c b/tools/misc/xen-cpuid.c +index 6e7ca8b9a4..d87a72e3e6 100644 +--- a/tools/misc/xen-cpuid.c ++++ b/tools/misc/xen-cpuid.c +@@ -146,6 +146,8 @@ static const char *str_7d0[32] = + { + [ 2] = "avx512_4vnniw", [ 3] = "avx512_4fmaps", + ++ /* 12 */ [13] = "tsx-force-abort", ++ + [26] = "ibrsb", [27] = "stibp", + [28] = "l1d_flush", [29] = "arch_caps", + /* 30 */ [31] = "ssbd", +diff --git a/xen/arch/x86/cpu/intel.c b/xen/arch/x86/cpu/intel.c +index 65fa3d611f..29c6b87512 100644 +--- a/xen/arch/x86/cpu/intel.c ++++ b/xen/arch/x86/cpu/intel.c +@@ -286,6 +286,9 @@ static void Intel_errata_workarounds(struct cpuinfo_x86 *c) + if (c->x86 == 6 && cpu_has_clflush && + (c->x86_model == 29 || c->x86_model == 46 || c->x86_model == 47)) + __set_bit(X86_FEATURE_CLFLUSH_MONITOR, c->x86_capability); ++ ++ if (cpu_has_tsx_force_abort && opt_rtm_abort) ++ wrmsrl(MSR_TSX_FORCE_ABORT, TSX_FORCE_ABORT_RTM); + } + + +diff --git a/xen/arch/x86/cpu/vpmu.c b/xen/arch/x86/cpu/vpmu.c +index 8324d62f11..8f6daf13fd 100644 +--- a/xen/arch/x86/cpu/vpmu.c ++++ b/xen/arch/x86/cpu/vpmu.c +@@ -45,6 +45,7 @@ CHECK_pmu_params; + static unsigned int __read_mostly opt_vpmu_enabled; + unsigned int __read_mostly vpmu_mode = XENPMU_MODE_OFF; + unsigned int __read_mostly vpmu_features = 0; ++bool __read_mostly opt_rtm_abort; + + static DEFINE_SPINLOCK(vpmu_lock); + static unsigned vpmu_count; +@@ -73,6 +74,8 @@ static int __init parse_vpmu_params(const char *s) + vpmu_features |= XENPMU_FEATURE_IPC_ONLY; + else if ( !cmdline_strcmp(s, "arch") ) + vpmu_features |= XENPMU_FEATURE_ARCH_ONLY; ++ else if ( (val = parse_boolean("rtm-abort", s, ss)) >= 0 ) ++ opt_rtm_abort = val; + else + rc = -EINVAL; + +diff --git a/xen/arch/x86/msr.c b/xen/arch/x86/msr.c +index 9bb38b6d66..4df4a59f4d 100644 +--- a/xen/arch/x86/msr.c ++++ b/xen/arch/x86/msr.c +@@ -131,6 +131,8 @@ int guest_rdmsr(const struct vcpu *v, uint32_t msr, uint64_t *val) + case MSR_PRED_CMD: + case MSR_FLUSH_CMD: + /* Write-only */ ++ case MSR_TSX_FORCE_ABORT: ++ /* Not offered to guests. */ + goto gp_fault; + + case MSR_SPEC_CTRL: +@@ -230,6 +232,8 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) + case MSR_INTEL_PLATFORM_INFO: + case MSR_ARCH_CAPABILITIES: + /* Read-only */ ++ case MSR_TSX_FORCE_ABORT: ++ /* Not offered to guests. */ + goto gp_fault; + + case MSR_AMD_PATCHLOADER: +diff --git a/xen/include/asm-x86/cpufeature.h b/xen/include/asm-x86/cpufeature.h +index 1fb9af4b19..745801f3c0 100644 +--- a/xen/include/asm-x86/cpufeature.h ++++ b/xen/include/asm-x86/cpufeature.h +@@ -112,6 +112,9 @@ + /* CPUID level 0x80000007.edx */ + #define cpu_has_itsc boot_cpu_has(X86_FEATURE_ITSC) + ++/* CPUID level 0x00000007:0.edx */ ++#define cpu_has_tsx_force_abort boot_cpu_has(X86_FEATURE_TSX_FORCE_ABORT) ++ + /* Synthesized. */ + #define cpu_has_arch_perfmon boot_cpu_has(X86_FEATURE_ARCH_PERFMON) + #define cpu_has_cpuid_faulting boot_cpu_has(X86_FEATURE_CPUID_FAULTING) +diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h +index 24d783a72d..c6e1d8768f 100644 +--- a/xen/include/asm-x86/msr-index.h ++++ b/xen/include/asm-x86/msr-index.h +@@ -51,6 +51,9 @@ + #define MSR_FLUSH_CMD 0x0000010b + #define FLUSH_CMD_L1D (_AC(1, ULL) << 0) + ++#define MSR_TSX_FORCE_ABORT 0x0000010f ++#define TSX_FORCE_ABORT_RTM (_AC(1, ULL) << 0) ++ + /* Intel MSRs. Some also available on other CPUs */ + #define MSR_IA32_PERFCTR0 0x000000c1 + #define MSR_IA32_A_PERFCTR0 0x000004c1 +diff --git a/xen/include/asm-x86/vpmu.h b/xen/include/asm-x86/vpmu.h +index 5e778ab7ba..1287b9fb6e 100644 +--- a/xen/include/asm-x86/vpmu.h ++++ b/xen/include/asm-x86/vpmu.h +@@ -125,6 +125,7 @@ static inline int vpmu_do_rdmsr(unsigned int msr, uint64_t *msr_content) + + extern unsigned int vpmu_mode; + extern unsigned int vpmu_features; ++extern bool opt_rtm_abort; + + /* Context switch */ + static inline void vpmu_switch_from(struct vcpu *prev) +diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h +index fbc68fa29f..2bcc5487ac 100644 +--- a/xen/include/public/arch-x86/cpufeatureset.h ++++ b/xen/include/public/arch-x86/cpufeatureset.h +@@ -242,6 +242,7 @@ XEN_CPUFEATURE(IBPB, 8*32+12) /*A IBPB support only (no IBRS, used by + /* Intel-defined CPU features, CPUID level 0x00000007:0.edx, word 9 */ + XEN_CPUFEATURE(AVX512_4VNNIW, 9*32+ 2) /*A AVX512 Neural Network Instructions */ + XEN_CPUFEATURE(AVX512_4FMAPS, 9*32+ 3) /*A AVX512 Multiply Accumulation Single Precision */ ++XEN_CPUFEATURE(TSX_FORCE_ABORT, 9*32+13) /* MSR_TSX_FORCE_ABORT.RTM_ABORT */ + XEN_CPUFEATURE(IBRSB, 9*32+26) /*A IBRS and IBPB support (used by Intel) */ + XEN_CPUFEATURE(STIBP, 9*32+27) /*A STIBP */ + XEN_CPUFEATURE(L1D_FLUSH, 9*32+28) /*S MSR_FLUSH_CMD and L1D flush. */ +-- +2.11.0 + diff --git a/xen.spec b/xen.spec index 56421b0..d8c49ee 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.12.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 @@ -113,6 +113,15 @@ Patch40: xen.drop.brctl.patch Patch41: xen.python.env.patch Patch42: xen.gcc9.fixes.patch Patch43: xen.python3.patch +Patch44: xen.git-e25d1338e1d8a32e46c808321323c4ad8fc5ba01.patch +Patch45: xen.git-e3a1ebee98e61dfe3b8b1eb71043ad5220b3cfd3.patch +Patch46: xsa297-4.12-1.patch +Patch47: xsa297-4.12-2.patch +Patch48: xsa297-4.12-3.patch +Patch49: xsa297-4.12-4.patch +Patch50: xsa297-4.12-5.patch +Patch51: xsa297-4.12-6.patch +Patch52: xsa297-4.12-7.patch %if %build_qemutrad @@ -310,6 +319,15 @@ 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 @@ -578,9 +596,33 @@ fi if [ $1 == 1 -a -f /sbin/grub2-mkconfig ]; then if [ -f /boot/grub2/grub.cfg ]; then /sbin/grub2-mkconfig -o /boot/grub2/grub.cfg + sed -i -e '/insmod module2/d' /boot/grub2/grub.cfg + if [ -d /usr/lib/grub/i386-pc ]; then + if [ ! -d /boot/grub2/i386-pc ]; then + mkdir /boot/grub2/i386-pc + fi + if [ -f /usr/lib/grub/i386-pc/relocator.mod -a ! -f /boot/grub2/i386-pc/relocator.mod ]; then + cp -p /usr/lib/grub/i386-pc/relocator.mod /boot/grub2/i386-pc/relocator.mod + fi + if [ -f /usr/lib/grub/i386-pc/multiboot2.mod -a ! -f /boot/grub2/i386-pc/multiboot2.mod ]; then + cp -p /usr/lib/grub/i386-pc/multiboot2.mod /boot/grub2/i386-pc/multiboot2.mod + fi + fi fi if [ -f /boot/efi/EFI/fedora/grub.cfg ]; then /sbin/grub2-mkconfig -o /boot/efi/EFI/fedora/grub.cfg + sed -i -e '/insmod module2/d' /boot/efi/EFI/fedora/grub.cfg + if [ -d /usr/lib/grub/x86_64-efi ]; then + if [ ! -d /boot/efi/EFI/fedora/x86_64-efi ]; then + mkdir /boot/efi/EFI/fedora/x86_64-efi + fi + if [ -f /usr/lib/grub/x86_64-efi/relocator.mod -a ! -f /boot/efi/EFI/fedora/x86_64-efi/relocator.mod ]; then + cp -p /usr/lib/grub/x86_64-efi/relocator.mod /boot/efi/EFI/fedora/x86_64-efi/relocator.mod + fi + if [ -f /usr/lib/grub/x86_64-efi/multiboot2.mod -a ! -f /boot/efi/EFI/fedora/x86_64-efi/multiboot2.mod ]; then + cp -p /usr/lib/grub/x86_64-efi/multiboot2.mod /boot/efi/EFI/fedora/x86_64-efi/multiboot2.mod + fi + fi fi fi @@ -588,9 +630,33 @@ fi if [ -f /sbin/grub2-mkconfig ]; then if [ -f /boot/grub2/grub.cfg ]; then /sbin/grub2-mkconfig -o /boot/grub2/grub.cfg + sed -i -e '/insmod module2/d' /boot/grub2/grub.cfg + if [ -d /usr/lib/grub/i386-pc -a $1 == 1 ]; then + if [ ! -d /boot/grub2/i386-pc ]; then + mkdir /boot/grub2/i386-pc + fi + if [ -f /usr/lib/grub/i386-pc/relocator.mod -a ! -f /boot/grub2/i386-pc/relocator.mod ]; then + cp -p /usr/lib/grub/i386-pc/relocator.mod /boot/grub2/i386-pc/relocator.mod + fi + if [ -f /usr/lib/grub/i386-pc/multiboot2.mod -a ! -f /boot/grub2/i386-pc/multiboot2.mod ]; then + cp -p /usr/lib/grub/i386-pc/multiboot2.mod /boot/grub2/i386-pc/multiboot2.mod + fi + fi fi if [ -f /boot/efi/EFI/fedora/grub.cfg ]; then /sbin/grub2-mkconfig -o /boot/efi/EFI/fedora/grub.cfg + sed -i -e '/insmod module2/d' /boot/efi/EFI/fedora/grub.cfg + if [ -d /usr/lib/grub/x86_64-efi -a $1 == 1 ]; then + if [ ! -d /boot/efi/EFI/fedora/x86_64-efi ]; then + mkdir /boot/efi/EFI/fedora/x86_64-efi + fi + if [ -f /usr/lib/grub/x86_64-efi/relocator.mod -a ! -f /boot/efi/EFI/fedora/x86_64-efi/relocator.mod ]; then + cp -p /usr/lib/grub/x86_64-efi/relocator.mod /boot/efi/EFI/fedora/x86_64-efi/relocator.mod + fi + if [ -f /usr/lib/grub/x86_64-efi/multiboot2.mod -a ! -f /boot/efi/EFI/fedora/x86_64-efi/multiboot2.mod ]; then + cp -p /usr/lib/grub/x86_64-efi/multiboot2.mod /boot/efi/EFI/fedora/x86_64-efi/multiboot2.mod + fi + fi fi fi %endif @@ -842,6 +908,12 @@ fi %endif %changelog +* Tue May 14 2019 Michael Young - 4.12.0-2 +- Microarchitectural Data Sampling speculative side channel [XSA-297, + CVE-2018-12126, CVE-2018-12127, CVE-2018-12130, CVE-2019-11091] +- additional patches so above applies cleanly +- work around grub2 issues in dom0 + * Fri Apr 05 2019 Michael Young - 4.12.0-1 - update to 4.12.0 (#1694695) remove patches for issues now fixed upstream diff --git a/xsa297-4.12-1.patch b/xsa297-4.12-1.patch new file mode 100644 index 0000000..51da965 --- /dev/null +++ b/xsa297-4.12-1.patch @@ -0,0 +1,163 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Reposition the XPTI command line parsing logic + +It has ended up in the middle of the mitigation calculation logic. Move it to +be beside the other command line parsing. + +No functional change. + +Signed-off-by: Andrew Cooper +Acked-by: Jan Beulich + +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 1171c02..99310c8 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -167,6 +167,73 @@ static int __init parse_spec_ctrl(const char *s) + } + custom_param("spec-ctrl", parse_spec_ctrl); + ++int8_t __read_mostly opt_xpti_hwdom = -1; ++int8_t __read_mostly opt_xpti_domu = -1; ++ ++static __init void xpti_init_default(uint64_t caps) ++{ ++ if ( boot_cpu_data.x86_vendor == X86_VENDOR_AMD ) ++ caps = ARCH_CAPS_RDCL_NO; ++ ++ if ( caps & ARCH_CAPS_RDCL_NO ) ++ { ++ if ( opt_xpti_hwdom < 0 ) ++ opt_xpti_hwdom = 0; ++ if ( opt_xpti_domu < 0 ) ++ opt_xpti_domu = 0; ++ } ++ else ++ { ++ if ( opt_xpti_hwdom < 0 ) ++ opt_xpti_hwdom = 1; ++ if ( opt_xpti_domu < 0 ) ++ opt_xpti_domu = 1; ++ } ++} ++ ++static __init int parse_xpti(const char *s) ++{ ++ const char *ss; ++ int val, rc = 0; ++ ++ /* Interpret 'xpti' alone in its positive boolean form. */ ++ if ( *s == '\0' ) ++ opt_xpti_hwdom = opt_xpti_domu = 1; ++ ++ do { ++ ss = strchr(s, ','); ++ if ( !ss ) ++ ss = strchr(s, '\0'); ++ ++ switch ( parse_bool(s, ss) ) ++ { ++ case 0: ++ opt_xpti_hwdom = opt_xpti_domu = 0; ++ break; ++ ++ case 1: ++ opt_xpti_hwdom = opt_xpti_domu = 1; ++ break; ++ ++ default: ++ if ( !strcmp(s, "default") ) ++ opt_xpti_hwdom = opt_xpti_domu = -1; ++ else if ( (val = parse_boolean("dom0", s, ss)) >= 0 ) ++ opt_xpti_hwdom = val; ++ else if ( (val = parse_boolean("domu", s, ss)) >= 0 ) ++ opt_xpti_domu = val; ++ else if ( *s ) ++ rc = -EINVAL; ++ break; ++ } ++ ++ s = ss + 1; ++ } while ( *ss ); ++ ++ return rc; ++} ++custom_param("xpti", parse_xpti); ++ + int8_t __read_mostly opt_pv_l1tf_hwdom = -1; + int8_t __read_mostly opt_pv_l1tf_domu = -1; + +@@ -627,73 +694,6 @@ static __init void l1tf_calculations(uint64_t caps) + : (3ul << (paddr_bits - 2)))); + } + +-int8_t __read_mostly opt_xpti_hwdom = -1; +-int8_t __read_mostly opt_xpti_domu = -1; +- +-static __init void xpti_init_default(uint64_t caps) +-{ +- if ( boot_cpu_data.x86_vendor == X86_VENDOR_AMD ) +- caps = ARCH_CAPS_RDCL_NO; +- +- if ( caps & ARCH_CAPS_RDCL_NO ) +- { +- if ( opt_xpti_hwdom < 0 ) +- opt_xpti_hwdom = 0; +- if ( opt_xpti_domu < 0 ) +- opt_xpti_domu = 0; +- } +- else +- { +- if ( opt_xpti_hwdom < 0 ) +- opt_xpti_hwdom = 1; +- if ( opt_xpti_domu < 0 ) +- opt_xpti_domu = 1; +- } +-} +- +-static __init int parse_xpti(const char *s) +-{ +- const char *ss; +- int val, rc = 0; +- +- /* Interpret 'xpti' alone in its positive boolean form. */ +- if ( *s == '\0' ) +- opt_xpti_hwdom = opt_xpti_domu = 1; +- +- do { +- ss = strchr(s, ','); +- if ( !ss ) +- ss = strchr(s, '\0'); +- +- switch ( parse_bool(s, ss) ) +- { +- case 0: +- opt_xpti_hwdom = opt_xpti_domu = 0; +- break; +- +- case 1: +- opt_xpti_hwdom = opt_xpti_domu = 1; +- break; +- +- default: +- if ( !strcmp(s, "default") ) +- opt_xpti_hwdom = opt_xpti_domu = -1; +- else if ( (val = parse_boolean("dom0", s, ss)) >= 0 ) +- opt_xpti_hwdom = val; +- else if ( (val = parse_boolean("domu", s, ss)) >= 0 ) +- opt_xpti_domu = val; +- else if ( *s ) +- rc = -EINVAL; +- break; +- } +- +- s = ss + 1; +- } while ( *ss ); +- +- return rc; +-} +-custom_param("xpti", parse_xpti); +- + void __init init_speculation_mitigations(void) + { + enum ind_thunk thunk = THUNK_DEFAULT; diff --git a/xsa297-4.12-2.patch b/xsa297-4.12-2.patch new file mode 100644 index 0000000..d03279a --- /dev/null +++ b/xsa297-4.12-2.patch @@ -0,0 +1,54 @@ +From: Andrew Cooper +Subject: x86/msr: Definitions for MSR_INTEL_CORE_THREAD_COUNT + +This is a model specific register which details the current configuration +cores and threads in the package. Because of how Hyperthread and Core +configuration works works in firmware, the MSR it is de-facto constant and +will remain unchanged until the next system reset. + +It is a read only MSR (so unilaterally reject writes), but for now retain its +leaky-on-read properties. Further CPUID/MSR work is required before we can +start virtualising a consistent topology to the guest, and retaining the old +behaviour is the safest course of action. + +Signed-off-by: Andrew Cooper +Acked-by: Jan Beulich + +diff --git a/xen/arch/x86/msr.c b/xen/arch/x86/msr.c +index 4df4a59..a7f67d9 100644 +--- a/xen/arch/x86/msr.c ++++ b/xen/arch/x86/msr.c +@@ -200,6 +200,10 @@ int guest_rdmsr(const struct vcpu *v, uint32_t msr, uint64_t *val) + ARRAY_SIZE(msrs->dr_mask))]; + break; + ++ /* ++ * TODO: Implement when we have better topology representation. ++ case MSR_INTEL_CORE_THREAD_COUNT: ++ */ + default: + return X86EMUL_UNHANDLEABLE; + } +@@ -229,6 +233,7 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) + { + uint64_t rsvd; + ++ case MSR_INTEL_CORE_THREAD_COUNT: + case MSR_INTEL_PLATFORM_INFO: + case MSR_ARCH_CAPABILITIES: + /* Read-only */ +diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h +index 11512d4..389f95f 100644 +--- a/xen/include/asm-x86/msr-index.h ++++ b/xen/include/asm-x86/msr-index.h +@@ -32,6 +32,10 @@ + #define EFER_KNOWN_MASK (EFER_SCE | EFER_LME | EFER_LMA | EFER_NX | \ + EFER_SVME | EFER_FFXSE) + ++#define MSR_INTEL_CORE_THREAD_COUNT 0x00000035 ++#define MSR_CTC_THREAD_MASK 0x0000ffff ++#define MSR_CTC_CORE_MASK 0xffff0000 ++ + /* Speculation Controls. */ + #define MSR_SPEC_CTRL 0x00000048 + #define SPEC_CTRL_IBRS (_AC(1, ULL) << 0) diff --git a/xsa297-4.12-3.patch b/xsa297-4.12-3.patch new file mode 100644 index 0000000..7b43266 --- /dev/null +++ b/xsa297-4.12-3.patch @@ -0,0 +1,109 @@ +From: Andrew Cooper +Subject: x86/boot: Detect the firmware SMT setting correctly on Intel hardware + +While boot_cpu_data.x86_num_siblings is an accurate value to use on AMD +hardware, it isn't on Intel when the user has disabled Hyperthreading in the +firmware. As a result, a user which has chosen to disable HT still gets +nagged on L1TF-vulnerable hardware when they haven't chosen an explicit +smt= setting. + +Make use of the largely-undocumented MSR_INTEL_CORE_THREAD_COUNT which in +practice exists since Nehalem, when booting on real hardware. Fall back to +using the ACPI table APIC IDs. + +While adjusting this logic, fix a latent bug in amd_get_topology(). The +thread count field in CPUID.0x8000001e.ebx is documented as 8 bits wide, +rather than 2 bits wide. + +Signed-off-by: Andrew Cooper +Acked-by: Jan Beulich + +diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c +index c790416..b1debac 100644 +--- a/xen/arch/x86/cpu/amd.c ++++ b/xen/arch/x86/cpu/amd.c +@@ -507,7 +507,7 @@ static void amd_get_topology(struct cpuinfo_x86 *c) + u32 eax, ebx, ecx, edx; + + cpuid(0x8000001e, &eax, &ebx, &ecx, &edx); +- c->x86_num_siblings = ((ebx >> 8) & 0x3) + 1; ++ c->x86_num_siblings = ((ebx >> 8) & 0xff) + 1; + + if (c->x86 < 0x17) + c->compute_unit_id = ebx & 0xFF; +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 99310c8..e49ab3f 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -368,6 +368,45 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + #endif + } + ++static bool __init check_smt_enabled(void) ++{ ++ uint64_t val; ++ unsigned int cpu; ++ ++ /* ++ * x86_num_siblings defaults to 1 in the absence of other information, and ++ * is adjusted based on other topology information found in CPUID leaves. ++ * ++ * On AMD hardware, it will be the current SMT configuration. On Intel ++ * hardware, it will represent the maximum capability, rather than the ++ * current configuration. ++ */ ++ if ( boot_cpu_data.x86_num_siblings < 2 ) ++ return false; ++ ++ /* ++ * Intel Nehalem and later hardware does have an MSR which reports the ++ * current count of cores/threads in the package. ++ * ++ * At the time of writing, it is almost completely undocumented, so isn't ++ * virtualised reliably. ++ */ ++ if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && !cpu_has_hypervisor && ++ !rdmsr_safe(MSR_INTEL_CORE_THREAD_COUNT, val) ) ++ return (MASK_EXTR(val, MSR_CTC_CORE_MASK) != ++ MASK_EXTR(val, MSR_CTC_THREAD_MASK)); ++ ++ /* ++ * Search over the CPUs reported in the ACPI tables. Any whose APIC ID ++ * has a non-zero thread id component indicates that SMT is active. ++ */ ++ for_each_present_cpu ( cpu ) ++ if ( x86_cpu_to_apicid[cpu] & (boot_cpu_data.x86_num_siblings - 1) ) ++ return true; ++ ++ return false; ++} ++ + /* Calculate whether Retpoline is known-safe on this CPU. */ + static bool __init retpoline_safe(uint64_t caps) + { +@@ -697,12 +736,14 @@ static __init void l1tf_calculations(uint64_t caps) + void __init init_speculation_mitigations(void) + { + enum ind_thunk thunk = THUNK_DEFAULT; +- bool use_spec_ctrl = false, ibrs = false; ++ bool use_spec_ctrl = false, ibrs = false, hw_smt_enabled; + uint64_t caps = 0; + + if ( boot_cpu_has(X86_FEATURE_ARCH_CAPS) ) + rdmsrl(MSR_ARCH_CAPABILITIES, caps); + ++ hw_smt_enabled = check_smt_enabled(); ++ + /* + * Has the user specified any custom BTI mitigations? If so, follow their + * instructions exactly and disable all heuristics. +@@ -873,8 +914,7 @@ void __init init_speculation_mitigations(void) + * However, if we are on affected hardware, with HT enabled, and the user + * hasn't explicitly chosen whether to use HT or not, nag them to do so. + */ +- if ( opt_smt == -1 && cpu_has_bug_l1tf && !pv_shim && +- boot_cpu_data.x86_num_siblings > 1 ) ++ if ( opt_smt == -1 && cpu_has_bug_l1tf && !pv_shim && hw_smt_enabled ) + warning_add( + "Booted on L1TF-vulnerable hardware with SMT/Hyperthreading\n" + "enabled. Please assess your configuration and choose an\n" diff --git a/xsa297-4.12-4.patch b/xsa297-4.12-4.patch new file mode 100644 index 0000000..a7e2fe2 --- /dev/null +++ b/xsa297-4.12-4.patch @@ -0,0 +1,55 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Misc non-functional cleanup + + * Identify BTI in the spec_ctrl_{enter,exit}_idle() comments, as other + mitigations will shortly appear. + * Use alternative_input() and cover the lack of memory cobber with a further + barrier. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h +index 779da2b..20ee112 100644 +--- a/xen/include/asm-x86/spec_ctrl.h ++++ b/xen/include/asm-x86/spec_ctrl.h +@@ -68,6 +68,8 @@ static always_inline void spec_ctrl_enter_idle(struct cpu_info *info) + uint32_t val = 0; + + /* ++ * Branch Target Injection: ++ * + * Latch the new shadow value, then enable shadowing, then update the MSR. + * There are no SMP issues here; only local processor ordering concerns. + */ +@@ -75,8 +77,9 @@ static always_inline void spec_ctrl_enter_idle(struct cpu_info *info) + barrier(); + info->spec_ctrl_flags |= SCF_use_shadow; + barrier(); +- asm volatile ( ALTERNATIVE("", "wrmsr", X86_FEATURE_SC_MSR_IDLE) +- :: "a" (val), "c" (MSR_SPEC_CTRL), "d" (0) : "memory" ); ++ alternative_input("", "wrmsr", X86_FEATURE_SC_MSR_IDLE, ++ "a" (val), "c" (MSR_SPEC_CTRL), "d" (0)); ++ barrier(); + } + + /* WARNING! `ret`, `call *`, `jmp *` not safe before this call. */ +@@ -85,13 +88,16 @@ static always_inline void spec_ctrl_exit_idle(struct cpu_info *info) + uint32_t val = info->xen_spec_ctrl; + + /* ++ * Branch Target Injection: ++ * + * Disable shadowing before updating the MSR. There are no SMP issues + * here; only local processor ordering concerns. + */ + info->spec_ctrl_flags &= ~SCF_use_shadow; + barrier(); +- asm volatile ( ALTERNATIVE("", "wrmsr", X86_FEATURE_SC_MSR_IDLE) +- :: "a" (val), "c" (MSR_SPEC_CTRL), "d" (0) : "memory" ); ++ alternative_input("", "wrmsr", X86_FEATURE_SC_MSR_IDLE, ++ "a" (val), "c" (MSR_SPEC_CTRL), "d" (0)); ++ barrier(); + } + + #endif /* __ASSEMBLY__ */ diff --git a/xsa297-4.12-5.patch b/xsa297-4.12-5.patch new file mode 100644 index 0000000..172a992 --- /dev/null +++ b/xsa297-4.12-5.patch @@ -0,0 +1,147 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: CPUID/MSR definitions for Microarchitectural Data + Sampling + +The MD_CLEAR feature can be automatically offered to guests. No +infrastructure is needed in Xen to support the guest making use of it. + +This is part of XSA-297, CVE-2018-12126, CVE-2018-12127, CVE-2018-12130, CVE-2019-11091. + +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 6db82f3..f80d8d8 100644 +--- a/docs/misc/xen-command-line.pandoc ++++ b/docs/misc/xen-command-line.pandoc +@@ -483,7 +483,7 @@ accounting for hardware capabilities as enumerated via CPUID. + + Currently accepted: + +-The Speculation Control hardware features `ibrsb`, `stibp`, `ibpb`, ++The Speculation Control hardware features `md-clear`, `ibrsb`, `stibp`, `ibpb`, + `l1d-flush` and `ssbd` are used by default if available and applicable. They can + be ignored, e.g. `no-ibrsb`, at which point Xen won't use them itself, and + won't offer them to guests. +diff --git a/tools/libxl/libxl_cpuid.c b/tools/libxl/libxl_cpuid.c +index 52e16c2..5a1702d 100644 +--- a/tools/libxl/libxl_cpuid.c ++++ b/tools/libxl/libxl_cpuid.c +@@ -202,6 +202,7 @@ int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str) + + {"avx512-4vnniw",0x00000007, 0, CPUID_REG_EDX, 2, 1}, + {"avx512-4fmaps",0x00000007, 0, CPUID_REG_EDX, 3, 1}, ++ {"md-clear", 0x00000007, 0, CPUID_REG_EDX, 10, 1}, + {"ibrsb", 0x00000007, 0, CPUID_REG_EDX, 26, 1}, + {"stibp", 0x00000007, 0, CPUID_REG_EDX, 27, 1}, + {"l1d-flush", 0x00000007, 0, CPUID_REG_EDX, 28, 1}, +diff --git a/tools/misc/xen-cpuid.c b/tools/misc/xen-cpuid.c +index d87a72e..f67ecd3 100644 +--- a/tools/misc/xen-cpuid.c ++++ b/tools/misc/xen-cpuid.c +@@ -146,6 +146,7 @@ static const char *str_7d0[32] = + { + [ 2] = "avx512_4vnniw", [ 3] = "avx512_4fmaps", + ++ [10] = "md-clear", + /* 12 */ [13] = "tsx-force-abort", + + [26] = "ibrsb", [27] = "stibp", +diff --git a/xen/arch/x86/cpuid.c b/xen/arch/x86/cpuid.c +index ab0aab6..3efad9c 100644 +--- a/xen/arch/x86/cpuid.c ++++ b/xen/arch/x86/cpuid.c +@@ -29,7 +29,12 @@ static int __init parse_xen_cpuid(const char *s) + if ( !ss ) + ss = strchr(s, '\0'); + +- if ( (val = parse_boolean("ibpb", s, ss)) >= 0 ) ++ if ( (val = parse_boolean("md-clear", s, ss)) >= 0 ) ++ { ++ if ( !val ) ++ setup_clear_cpu_cap(X86_FEATURE_MD_CLEAR); ++ } ++ else if ( (val = parse_boolean("ibpb", s, ss)) >= 0 ) + { + if ( !val ) + setup_clear_cpu_cap(X86_FEATURE_IBPB); +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index e49ab3f..a573b02 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -291,17 +291,19 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + printk("Speculative mitigation facilities:\n"); + + /* Hardware features which pertain to speculative mitigations. */ +- 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", + (_7d0 & cpufeat_mask(X86_FEATURE_IBRSB)) ? " IBRS/IBPB" : "", + (_7d0 & cpufeat_mask(X86_FEATURE_STIBP)) ? " STIBP" : "", + (_7d0 & cpufeat_mask(X86_FEATURE_L1D_FLUSH)) ? " L1D_FLUSH" : "", + (_7d0 & cpufeat_mask(X86_FEATURE_SSBD)) ? " SSBD" : "", ++ (_7d0 & cpufeat_mask(X86_FEATURE_MD_CLEAR)) ? " MD_CLEAR" : "", + (e8b & cpufeat_mask(X86_FEATURE_IBPB)) ? " IBPB" : "", + (caps & ARCH_CAPS_IBRS_ALL) ? " IBRS_ALL" : "", + (caps & ARCH_CAPS_RDCL_NO) ? " RDCL_NO" : "", + (caps & ARCH_CAPS_RSBA) ? " RSBA" : "", + (caps & ARCH_CAPS_SKIP_L1DFL) ? " SKIP_L1DFL": "", +- (caps & ARCH_CAPS_SSB_NO) ? " SSB_NO" : ""); ++ (caps & ARCH_CAPS_SSB_NO) ? " SSB_NO" : "", ++ (caps & ARCH_CAPS_MDS_NO) ? " MDS_NO" : ""); + + /* Compiled-in support which pertains to mitigations. */ + if ( IS_ENABLED(CONFIG_INDIRECT_THUNK) || IS_ENABLED(CONFIG_SHADOW_PAGING) ) +@@ -339,23 +341,25 @@ 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\n", ++ printk(" Support for HVM VMs:%s%s%s%s%s\n", + (boot_cpu_has(X86_FEATURE_SC_MSR_HVM) || + boot_cpu_has(X86_FEATURE_SC_RSB_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" : ""); ++ opt_eager_fpu ? " EAGER_FPU" : "", ++ boot_cpu_has(X86_FEATURE_MD_CLEAR) ? " MD_CLEAR" : ""); + + #endif + #ifdef CONFIG_PV +- printk(" Support for PV VMs:%s%s%s%s\n", ++ printk(" Support for PV VMs:%s%s%s%s%s\n", + (boot_cpu_has(X86_FEATURE_SC_MSR_PV) || + boot_cpu_has(X86_FEATURE_SC_RSB_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" : ""); ++ opt_eager_fpu ? " EAGER_FPU" : "", ++ boot_cpu_has(X86_FEATURE_MD_CLEAR) ? " MD_CLEAR" : ""); + + printk(" XPTI (64-bit PV only): Dom0 %s, DomU %s (with%s PCID)\n", + opt_xpti_hwdom ? "enabled" : "disabled", +diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h +index 389f95f..637259b 100644 +--- a/xen/include/asm-x86/msr-index.h ++++ b/xen/include/asm-x86/msr-index.h +@@ -51,6 +51,7 @@ + #define ARCH_CAPS_RSBA (_AC(1, ULL) << 2) + #define ARCH_CAPS_SKIP_L1DFL (_AC(1, ULL) << 3) + #define ARCH_CAPS_SSB_NO (_AC(1, ULL) << 4) ++#define ARCH_CAPS_MDS_NO (_AC(1, ULL) << 5) + + #define MSR_FLUSH_CMD 0x0000010b + #define FLUSH_CMD_L1D (_AC(1, ULL) << 0) +diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h +index 2bcc548..55231d4 100644 +--- a/xen/include/public/arch-x86/cpufeatureset.h ++++ b/xen/include/public/arch-x86/cpufeatureset.h +@@ -242,6 +242,7 @@ XEN_CPUFEATURE(IBPB, 8*32+12) /*A IBPB support only (no IBRS, used by + /* Intel-defined CPU features, CPUID level 0x00000007:0.edx, word 9 */ + XEN_CPUFEATURE(AVX512_4VNNIW, 9*32+ 2) /*A AVX512 Neural Network Instructions */ + XEN_CPUFEATURE(AVX512_4FMAPS, 9*32+ 3) /*A AVX512 Multiply Accumulation Single Precision */ ++XEN_CPUFEATURE(MD_CLEAR, 9*32+10) /*A VERW clears microarchitectural buffers */ + XEN_CPUFEATURE(TSX_FORCE_ABORT, 9*32+13) /* MSR_TSX_FORCE_ABORT.RTM_ABORT */ + XEN_CPUFEATURE(IBRSB, 9*32+26) /*A IBRS and IBPB support (used by Intel) */ + XEN_CPUFEATURE(STIBP, 9*32+27) /*A STIBP */ diff --git a/xsa297-4.12-6.patch b/xsa297-4.12-6.patch new file mode 100644 index 0000000..48023a8 --- /dev/null +++ b/xsa297-4.12-6.patch @@ -0,0 +1,134 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Infrastructure to use VERW to flush pipeline buffers + +Three synthetic features are introduced, as we need individual control of +each, depending on circumstances. A later change will enable them at +appropriate points. + +The verw_sel field doesn't strictly need to live in struct cpu_info. It lives +there because there is a convenient hole it can fill, and it reduces the +complexity of the SPEC_CTRL_EXIT_TO_{PV,HVM} assembly by avoiding the need for +any temporary stack maintenance. + +This is part of XSA-297, CVE-2018-12126, CVE-2018-12127, CVE-2018-12130, CVE-2019-11091. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/x86_64/asm-offsets.c b/xen/arch/x86/x86_64/asm-offsets.c +index 052228c..33930ce 100644 +--- a/xen/arch/x86/x86_64/asm-offsets.c ++++ b/xen/arch/x86/x86_64/asm-offsets.c +@@ -110,6 +110,7 @@ void __dummy__(void) + BLANK(); + + OFFSET(CPUINFO_guest_cpu_user_regs, struct cpu_info, guest_cpu_user_regs); ++ OFFSET(CPUINFO_verw_sel, struct cpu_info, verw_sel); + OFFSET(CPUINFO_current_vcpu, struct cpu_info, current_vcpu); + OFFSET(CPUINFO_cr4, struct cpu_info, cr4); + OFFSET(CPUINFO_xen_cr3, struct cpu_info, xen_cr3); +diff --git a/xen/include/asm-x86/cpufeatures.h b/xen/include/asm-x86/cpufeatures.h +index 0c06274..ba55245 100644 +--- a/xen/include/asm-x86/cpufeatures.h ++++ b/xen/include/asm-x86/cpufeatures.h +@@ -31,3 +31,6 @@ XEN_CPUFEATURE(SC_RSB_PV, (FSCAPINTS+0)*32+18) /* RSB overwrite needed for + XEN_CPUFEATURE(SC_RSB_HVM, (FSCAPINTS+0)*32+19) /* RSB overwrite needed for HVM */ + XEN_CPUFEATURE(SC_MSR_IDLE, (FSCAPINTS+0)*32+21) /* (SC_MSR_PV || SC_MSR_HVM) && default_xen_spec_ctrl */ + XEN_CPUFEATURE(XEN_LBR, (FSCAPINTS+0)*32+22) /* Xen uses MSR_DEBUGCTL.LBR */ ++XEN_CPUFEATURE(SC_VERW_PV, (FSCAPINTS+0)*32+23) /* VERW used by Xen for PV */ ++XEN_CPUFEATURE(SC_VERW_HVM, (FSCAPINTS+0)*32+24) /* VERW used by Xen for HVM */ ++XEN_CPUFEATURE(SC_VERW_IDLE, (FSCAPINTS+0)*32+25) /* VERW used by Xen for idle */ +diff --git a/xen/include/asm-x86/current.h b/xen/include/asm-x86/current.h +index 5bd64b2..f3508c3 100644 +--- a/xen/include/asm-x86/current.h ++++ b/xen/include/asm-x86/current.h +@@ -38,6 +38,7 @@ struct vcpu; + struct cpu_info { + struct cpu_user_regs guest_cpu_user_regs; + unsigned int processor_id; ++ unsigned int verw_sel; + struct vcpu *current_vcpu; + unsigned long per_cpu_offset; + unsigned long cr4; +diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h +index 20ee112..ba03bb4 100644 +--- a/xen/include/asm-x86/spec_ctrl.h ++++ b/xen/include/asm-x86/spec_ctrl.h +@@ -60,6 +60,13 @@ static inline void init_shadow_spec_ctrl_state(void) + info->shadow_spec_ctrl = 0; + info->xen_spec_ctrl = default_xen_spec_ctrl; + info->spec_ctrl_flags = default_spec_ctrl_flags; ++ ++ /* ++ * For least latency, the VERW selector should be a writeable data ++ * descriptor resident in the cache. __HYPERVISOR_DS32 shares a cache ++ * line with __HYPERVISOR_CS, so is expected to be very cache-hot. ++ */ ++ info->verw_sel = __HYPERVISOR_DS32; + } + + /* WARNING! `ret`, `call *`, `jmp *` not safe after this call. */ +@@ -80,6 +87,22 @@ static always_inline void spec_ctrl_enter_idle(struct cpu_info *info) + alternative_input("", "wrmsr", X86_FEATURE_SC_MSR_IDLE, + "a" (val), "c" (MSR_SPEC_CTRL), "d" (0)); + barrier(); ++ ++ /* ++ * Microarchitectural Store Buffer Data Sampling: ++ * ++ * On vulnerable systems, store buffer entries are statically partitioned ++ * between active threads. When entering idle, our store buffer entries ++ * are re-partitioned to allow the other threads to use them. ++ * ++ * Flush the buffers to ensure that no sensitive data of ours can be ++ * leaked by a sibling after it gets our store buffer entries. ++ * ++ * Note: VERW must be encoded with a memory operand, as it is only that ++ * form which causes a flush. ++ */ ++ alternative_input("", "verw %[sel]", X86_FEATURE_SC_VERW_IDLE, ++ [sel] "m" (info->verw_sel)); + } + + /* WARNING! `ret`, `call *`, `jmp *` not safe before this call. */ +@@ -98,6 +121,17 @@ static always_inline void spec_ctrl_exit_idle(struct cpu_info *info) + alternative_input("", "wrmsr", X86_FEATURE_SC_MSR_IDLE, + "a" (val), "c" (MSR_SPEC_CTRL), "d" (0)); + barrier(); ++ ++ /* ++ * Microarchitectural Store Buffer Data Sampling: ++ * ++ * On vulnerable systems, store buffer entries are statically partitioned ++ * between active threads. When exiting idle, the other threads store ++ * buffer entries are re-partitioned to give us some. ++ * ++ * We now have store buffer entries with stale data from sibling threads. ++ * A flush if necessary will be performed on the return to guest path. ++ */ + } + + #endif /* __ASSEMBLY__ */ +diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h +index 803f7ce..c60093b 100644 +--- a/xen/include/asm-x86/spec_ctrl_asm.h ++++ b/xen/include/asm-x86/spec_ctrl_asm.h +@@ -241,12 +241,16 @@ + /* 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 ++ DO_SPEC_CTRL_EXIT_TO_GUEST, X86_FEATURE_SC_MSR_PV; \ ++ 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 ++ 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. diff --git a/xsa297-4.12-7.patch b/xsa297-4.12-7.patch new file mode 100644 index 0000000..8a70687 --- /dev/null +++ b/xsa297-4.12-7.patch @@ -0,0 +1,316 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Introduce options to control VERW flushing + +The Microarchitectural Data Sampling vulnerability is split into categories +with subtly different properties: + + MLPDS - Microarchitectural Load Port Data Sampling + MSBDS - Microarchitectural Store Buffer Data Sampling + MFBDS - Microarchitectural Fill Buffer Data Sampling + MDSUM - Microarchitectural Data Sampling Uncacheable Memory + +MDSUM is a special case of the other three, and isn't distinguished further. + +These issues pertain to three microarchitectural buffers. The Load Ports, the +Store Buffers and the Fill Buffers. Each of these structures are flushed by +the new enhanced VERW functionality, but the conditions under which flushing +is necessary vary. + +For this concise overview of the issues and default logic, the abbreviations +SP (Store Port), FB (Fill Buffer), LP (Load Port) and HT (Hyperthreading) are +used for brevity: + + * Vulnerable hardware is divided into two categories - parts which suffer + from SP only, and parts with any other combination of vulnerabilities. + + * SP only has an HT interaction when the thread goes idle, due to the static + partitioning of resources. LP and FB have HT interactions at all points, + due to the competitive sharing of resources. All issues potentially leak + data across the return-to-guest transition. + + * The microcode which implements VERW flushing also extends MSR_FLUSH_CMD, so + we don't need to do both on the HVM return-to-guest path. However, some + parts are not vulnerable to L1TF (therefore have no MSR_FLUSH_CMD), but are + vulnerable to MDS, so do require VERW on the HVM path. + +Note that we deliberately support mds=1 even without MD_CLEAR in case the +microcode has been updated but the feature bit not exposed. + +This is part of XSA-297, CVE-2018-12126, CVE-2018-12127, CVE-2018-12130, CVE-2019-11091. + +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 f80d8d8..85081fd 100644 +--- a/docs/misc/xen-command-line.pandoc ++++ b/docs/misc/xen-command-line.pandoc +@@ -1895,7 +1895,7 @@ 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}=, ++> `= List of [ , xen=, {pv,hvm,msr-sc,rsb,md-clear}=, + > bti-thunk=retpoline|lfence|jmp, {ibrs,ibpb,ssbd,eager-fpu, + > l1d-flush}= ]` + +@@ -1919,9 +1919,10 @@ 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=` and `rsb=` 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. ++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. + + * `pv=` and `hvm=` offer control over all suboptions for PV and HVM guests + respectively. +@@ -1930,6 +1931,11 @@ protect itself, and Xen's ability to virtualise support for 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. ++* `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.* + + 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 a573b02..0509ac8 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -35,6 +35,8 @@ static bool __initdata opt_msr_sc_pv = true; + static bool __initdata opt_msr_sc_hvm = true; + static bool __initdata opt_rsb_pv = true; + 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; + + /* Cmdline controls for Xen's speculative settings. */ + static enum ind_thunk { +@@ -59,6 +61,9 @@ paddr_t __read_mostly l1tf_addr_mask, __read_mostly l1tf_safe_maddr; + static bool __initdata cpu_has_bug_l1tf; + static unsigned int __initdata l1d_maxphysaddr; + ++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 int __init parse_spec_ctrl(const char *s) + { + const char *ss; +@@ -94,6 +99,8 @@ static int __init 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_thunk = THUNK_JMP; + opt_ibrs = 0; +@@ -116,11 +123,13 @@ static int __init parse_spec_ctrl(const char *s) + { + opt_msr_sc_pv = val; + opt_rsb_pv = val; ++ opt_md_clear_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; + } + else if ( (val = parse_boolean("msr-sc", s, ss)) >= 0 ) + { +@@ -132,6 +141,12 @@ static int __init parse_spec_ctrl(const char *s) + opt_rsb_pv = val; + opt_rsb_hvm = val; + } ++ else if ( (val = parse_boolean("md-clear", s, ss)) >= 0 || ++ (val = parse_boolean("mds", s, ss)) >= 0 ) ++ { ++ opt_md_clear_pv = val; ++ opt_md_clear_hvm = val; ++ } + + /* Xen's speculative sidechannel mitigation settings. */ + else if ( !strncmp(s, "bti-thunk=", 10) ) +@@ -317,7 +332,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, Other:%s%s\n", ++ printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s, Other:%s%s%s\n", + thunk == THUNK_NONE ? "N/A" : + thunk == THUNK_RETPOLINE ? "RETPOLINE" : + thunk == THUNK_LFENCE ? "LFENCE" : +@@ -327,7 +342,8 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + !boot_cpu_has(X86_FEATURE_SSBD) ? "" : + (default_xen_spec_ctrl & SPEC_CTRL_SSBD) ? " SSBD+" : " SSBD-", + opt_ibpb ? " IBPB" : "", +- opt_l1d_flush ? " L1D_FLUSH" : ""); ++ opt_l1d_flush ? " L1D_FLUSH" : "", ++ opt_md_clear_pv || opt_md_clear_hvm ? " VERW" : ""); + + /* L1TF diagnostics, printed if vulnerable or PV shadowing is in use. */ + if ( cpu_has_bug_l1tf || opt_pv_l1tf_hwdom || opt_pv_l1tf_domu ) +@@ -737,6 +753,107 @@ static __init void l1tf_calculations(uint64_t caps) + : (3ul << (paddr_bits - 2)))); + } + ++/* Calculate whether this CPU is vulnerable to MDS. */ ++static __init void mds_calculations(uint64_t caps) ++{ ++ /* MDS 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; ++ ++ /* Any processor advertising MDS_NO should be not vulnerable to MDS. */ ++ if ( caps & ARCH_CAPS_MDS_NO ) ++ return; ++ ++ switch ( boot_cpu_data.x86_model ) ++ { ++ /* ++ * Core processors since at least Nehalem are vulnerable. ++ */ ++ case 0x1f: /* Auburndale / Havendale */ ++ case 0x1e: /* Nehalem */ ++ case 0x1a: /* Nehalem EP */ ++ case 0x2e: /* Nehalem EX */ ++ case 0x25: /* Westmere */ ++ case 0x2c: /* Westmere EP */ ++ case 0x2f: /* Westmere EX */ ++ case 0x2a: /* SandyBridge */ ++ case 0x2d: /* SandyBridge EP/EX */ ++ case 0x3a: /* IvyBridge */ ++ case 0x3e: /* IvyBridge EP/EX */ ++ case 0x3c: /* Haswell */ ++ case 0x3f: /* Haswell EX/EP */ ++ case 0x45: /* Haswell D */ ++ case 0x46: /* Haswell H */ ++ case 0x3d: /* Broadwell */ ++ case 0x47: /* Broadwell H */ ++ case 0x4f: /* Broadwell EP/EX */ ++ case 0x56: /* Broadwell D */ ++ case 0x4e: /* Skylake M */ ++ case 0x5e: /* Skylake D */ ++ cpu_has_bug_mds = true; ++ break; ++ ++ /* ++ * Some Core processors have per-stepping vulnerability. ++ */ ++ case 0x55: /* Skylake-X / Cascade Lake */ ++ if ( boot_cpu_data.x86_mask <= 5 ) ++ cpu_has_bug_mds = true; ++ break; ++ ++ case 0x8e: /* Kaby / Coffee / Whiskey Lake M */ ++ if ( boot_cpu_data.x86_mask <= 0xb ) ++ cpu_has_bug_mds = true; ++ break; ++ ++ case 0x9e: /* Kaby / Coffee / Whiskey Lake D */ ++ if ( boot_cpu_data.x86_mask <= 0xc ) ++ cpu_has_bug_mds = true; ++ break; ++ ++ /* ++ * Very old and very new Atom processors are not vulnerable. ++ */ ++ case 0x1c: /* Pineview */ ++ case 0x26: /* Lincroft */ ++ case 0x27: /* Penwell */ ++ case 0x35: /* Cloverview */ ++ case 0x36: /* Cedarview */ ++ case 0x7a: /* Goldmont */ ++ break; ++ ++ /* ++ * Middling Atom processors are vulnerable to just the Store Buffer ++ * aspect. ++ */ ++ case 0x37: /* Baytrail / Valleyview (Silvermont) */ ++ case 0x4a: /* Merrifield */ ++ case 0x4c: /* Cherrytrail / Brasswell */ ++ case 0x4d: /* Avaton / Rangely (Silvermont) */ ++ case 0x5a: /* Moorefield */ ++ case 0x5d: ++ case 0x65: ++ case 0x6e: ++ case 0x75: ++ /* ++ * Knights processors (which are based on the Silvermont/Airmont ++ * microarchitecture) are similarly only affected by the Store Buffer ++ * aspect. ++ */ ++ case 0x57: /* Knights Landing */ ++ case 0x85: /* Knights Mill */ ++ cpu_has_bug_msbds_only = true; ++ break; ++ ++ default: ++ printk("Unrecognised CPU model %#x - assuming vulnerable to MDS\n", ++ boot_cpu_data.x86_model); ++ cpu_has_bug_mds = true; ++ break; ++ } ++} ++ + void __init init_speculation_mitigations(void) + { + enum ind_thunk thunk = THUNK_DEFAULT; +@@ -924,6 +1041,47 @@ void __init init_speculation_mitigations(void) + "enabled. Please assess your configuration and choose an\n" + "explicit 'smt=' setting. See XSA-273.\n"); + ++ mds_calculations(caps); ++ ++ /* ++ * 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)); ++ ++ /* ++ * 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. ++ * ++ * 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. ++ */ ++ 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); ++ ++ /* ++ * Warn the user if they are on MLPDS/MFBDS-vulnerable hardware with HT ++ * active and no explicit SMT choice. ++ */ ++ if ( opt_smt == -1 && cpu_has_bug_mds && hw_smt_enabled ) ++ warning_add( ++ "Booted on MLPDS/MFBDS-vulnerable hardware with SMT/Hyperthreading\n" ++ "enabled. Mitigations will not be fully effective. Please\n" ++ "choose an explicit smt= setting. See XSA-297.\n"); ++ + print_details(thunk, caps); + + /* From e41f4de6799bd7798f3058e6b2f294baf686eb88 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 14 May 2019 22:51:28 +0100 Subject: [PATCH 006/194] Microarchitectural Data Sampling speculative side channel [XSA-297, CVE-2018-12126, CVE-2018-12127, CVE-2018-12130, CVE-2019-11091] additional patches so above applies cleanly work around grub2 issues in dom0 --- ...fbdd62724577febeff11ae50d440992a8f11.patch | 71 +++ ...feb7131e66ed9186ad8766c9582502c98998.patch | 464 ++++++++++++++++++ ...e159a6b1c379a0dac918120267b606d4defd.patch | 194 ++++++++ xen.spec | 76 ++- xsa297-4.11-1.patch | 163 ++++++ xsa297-4.11-2.patch | 54 ++ xsa297-4.11-3.patch | 109 ++++ xsa297-4.11-4.patch | 55 +++ xsa297-4.11-5.patch | 141 ++++++ xsa297-4.11-6.patch | 134 +++++ xsa297-4.11-7.patch | 316 ++++++++++++ 11 files changed, 1776 insertions(+), 1 deletion(-) create mode 100644 xen.git-0825fbdd62724577febeff11ae50d440992a8f11.patch create mode 100644 xen.git-e202feb7131e66ed9186ad8766c9582502c98998.patch create mode 100644 xen.git-fc46e159a6b1c379a0dac918120267b606d4defd.patch create mode 100644 xsa297-4.11-1.patch create mode 100644 xsa297-4.11-2.patch create mode 100644 xsa297-4.11-3.patch create mode 100644 xsa297-4.11-4.patch create mode 100644 xsa297-4.11-5.patch create mode 100644 xsa297-4.11-6.patch create mode 100644 xsa297-4.11-7.patch diff --git a/xen.git-0825fbdd62724577febeff11ae50d440992a8f11.patch b/xen.git-0825fbdd62724577febeff11ae50d440992a8f11.patch new file mode 100644 index 0000000..0bfbb2d --- /dev/null +++ b/xen.git-0825fbdd62724577febeff11ae50d440992a8f11.patch @@ -0,0 +1,71 @@ +From 0825fbdd62724577febeff11ae50d440992a8f11 Mon Sep 17 00:00:00 2001 +From: Andrew Cooper +Date: Fri, 3 May 2019 10:55:10 +0200 +Subject: [PATCH] x86/msr: Shorten ARCH_CAPABILITIES_* constants + +They are unnecesserily verbose, and ARCH_CAPS_* is already the more common +version. + +Signed-off-by: Andrew Cooper +Acked-by: Jan Beulich +master commit: ba27aaa88548c824a47dcf5609288ee1c05d2946 +master date: 2019-03-18 16:26:40 +0000 +--- + xen/arch/x86/spec_ctrl.c | 10 +++++----- + xen/include/asm-x86/msr-index.h | 4 ++-- + 2 files changed, 7 insertions(+), 7 deletions(-) + +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index e641894f17..27b1158d84 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -286,8 +286,8 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + (_7d0 & cpufeat_mask(X86_FEATURE_L1D_FLUSH)) ? " L1D_FLUSH" : "", + (_7d0 & cpufeat_mask(X86_FEATURE_SSBD)) ? " SSBD" : "", + (e8b & cpufeat_mask(X86_FEATURE_IBPB)) ? " IBPB" : "", +- (caps & ARCH_CAPABILITIES_IBRS_ALL) ? " IBRS_ALL" : "", +- (caps & ARCH_CAPABILITIES_RDCL_NO) ? " RDCL_NO" : "", ++ (caps & ARCH_CAPS_IBRS_ALL) ? " IBRS_ALL" : "", ++ (caps & ARCH_CAPS_RDCL_NO) ? " RDCL_NO" : "", + (caps & ARCH_CAPS_RSBA) ? " RSBA" : "", + (caps & ARCH_CAPS_SKIP_L1DFL) ? " SKIP_L1DFL": "", + (caps & ARCH_CAPS_SSB_NO) ? " SSB_NO" : ""); +@@ -598,7 +598,7 @@ static __init void l1tf_calculations(uint64_t caps) + } + + /* Any processor advertising RDCL_NO should be not vulnerable to L1TF. */ +- if ( caps & ARCH_CAPABILITIES_RDCL_NO ) ++ if ( caps & ARCH_CAPS_RDCL_NO ) + cpu_has_bug_l1tf = false; + + if ( cpu_has_bug_l1tf && hit_default ) +@@ -662,9 +662,9 @@ int8_t __read_mostly opt_xpti_domu = -1; + static __init void xpti_init_default(uint64_t caps) + { + if ( boot_cpu_data.x86_vendor == X86_VENDOR_AMD ) +- caps = ARCH_CAPABILITIES_RDCL_NO; ++ caps = ARCH_CAPS_RDCL_NO; + +- if ( caps & ARCH_CAPABILITIES_RDCL_NO ) ++ if ( caps & ARCH_CAPS_RDCL_NO ) + { + if ( opt_xpti_hwdom < 0 ) + opt_xpti_hwdom = 0; +diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h +index d13308ffe0..7588fc1567 100644 +--- a/xen/include/asm-x86/msr-index.h ++++ b/xen/include/asm-x86/msr-index.h +@@ -44,8 +44,8 @@ + #define PRED_CMD_IBPB (_AC(1, ULL) << 0) + + #define MSR_ARCH_CAPABILITIES 0x0000010a +-#define ARCH_CAPABILITIES_RDCL_NO (_AC(1, ULL) << 0) +-#define ARCH_CAPABILITIES_IBRS_ALL (_AC(1, ULL) << 1) ++#define ARCH_CAPS_RDCL_NO (_AC(1, ULL) << 0) ++#define ARCH_CAPS_IBRS_ALL (_AC(1, ULL) << 1) + #define ARCH_CAPS_RSBA (_AC(1, ULL) << 2) + #define ARCH_CAPS_SKIP_L1DFL (_AC(1, ULL) << 3) + #define ARCH_CAPS_SSB_NO (_AC(1, ULL) << 4) +-- +2.11.0 + diff --git a/xen.git-e202feb7131e66ed9186ad8766c9582502c98998.patch b/xen.git-e202feb7131e66ed9186ad8766c9582502c98998.patch new file mode 100644 index 0000000..dfd2ccd --- /dev/null +++ b/xen.git-e202feb7131e66ed9186ad8766c9582502c98998.patch @@ -0,0 +1,464 @@ +From e202feb7131e66ed9186ad8766c9582502c98998 Mon Sep 17 00:00:00 2001 +From: Andrew Cooper +Date: Fri, 1 Feb 2019 11:34:35 +0100 +Subject: [PATCH] xen/cmdline: Fix buggy strncmp(s, LITERAL, ss - s) construct + +When the command line parsing was updated to use const strings and no longer +tokenise with NUL characters, string matches could no longer be made with +strcmp(). + +Unfortunately, the replacement was buggy. strncmp(s, "opt", ss - s) matches +"o", "op" and "opt" on the command line, as ss - s may be shorter than the +passed literal. Furthermore, parse_bool() is affected by this, so substrings +such as "d", "e" and "o" are considered valid, with the latter being ambiguous +between "on" and "off". + +Introduce a new strcmp-like function for the task, which looks for exact +string matches, but declares success when the NUL of the literal matches a +comma, colon or semicolon in the command line fragment. + +No change to the intended parsing functionality, but fixes cases where a +partial string on the command line will inadvertently trigger options. + +A few areas were more than just a trivial change: + + * parse_irq_vector_map_param() gained some style corrections. + * parse_vpmu_params() was rewritten to use the normal list-of-options form, + rather than just fixing up parse_vpmu_param() and leaving the parsing being + hard to follow. + * Instead of making the trivial fix of adding an explicit length check in + parse_bool(), use the length to select which token to we search for, which + is more efficient than the previous linear search over all possible tokens. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +Acked-by: Julien Grall +master commit: 2ddf7e3e341df3ccf21613ff7ffd4b7693abe9e9 +master date: 2019-01-15 12:58:34 +0000 +--- + xen/arch/x86/cpu/vpmu.c | 49 ++++++++-------------- + xen/arch/x86/irq.c | 12 +++--- + xen/arch/x86/psr.c | 4 +- + xen/arch/x86/spec_ctrl.c | 12 +++--- + xen/arch/x86/x86_64/mmconfig-shared.c | 4 +- + xen/common/efi/boot.c | 4 +- + xen/common/kernel.c | 79 ++++++++++++++++++++++++++++------- + xen/drivers/cpufreq/cpufreq.c | 6 +-- + xen/drivers/passthrough/iommu.c | 28 ++++++------- + xen/drivers/passthrough/pci.c | 4 +- + xen/include/xen/lib.h | 7 ++++ + 11 files changed, 124 insertions(+), 85 deletions(-) + +diff --git a/xen/arch/x86/cpu/vpmu.c b/xen/arch/x86/cpu/vpmu.c +index b978e05613..2be61606b4 100644 +--- a/xen/arch/x86/cpu/vpmu.c ++++ b/xen/arch/x86/cpu/vpmu.c +@@ -61,42 +61,31 @@ static unsigned vpmu_count; + + static DEFINE_PER_CPU(struct vcpu *, last_vcpu); + +-static int parse_vpmu_param(const char *s, unsigned int len) +-{ +- if ( !*s || !len ) +- return 0; +- if ( !strncmp(s, "bts", len) ) +- vpmu_features |= XENPMU_FEATURE_INTEL_BTS; +- else if ( !strncmp(s, "ipc", len) ) +- vpmu_features |= XENPMU_FEATURE_IPC_ONLY; +- else if ( !strncmp(s, "arch", len) ) +- vpmu_features |= XENPMU_FEATURE_ARCH_ONLY; +- else +- return 1; +- return 0; +-} +- + static int __init parse_vpmu_params(const char *s) + { +- const char *sep, *p = s; ++ const char *ss; + + switch ( parse_bool(s, NULL) ) + { + case 0: + break; + default: +- for ( ; ; ) +- { +- sep = strchr(p, ','); +- if ( sep == NULL ) +- sep = strchr(p, 0); +- if ( parse_vpmu_param(p, sep - p) ) +- goto error; +- if ( !*sep ) +- /* reached end of flags */ +- break; +- p = sep + 1; +- } ++ do { ++ ss = strchr(s, ','); ++ if ( !ss ) ++ ss = strchr(s, '\0'); ++ ++ if ( !cmdline_strcmp(s, "bts") ) ++ vpmu_features |= XENPMU_FEATURE_INTEL_BTS; ++ else if ( !cmdline_strcmp(s, "ipc") ) ++ vpmu_features |= XENPMU_FEATURE_IPC_ONLY; ++ else if ( !cmdline_strcmp(s, "arch") ) ++ vpmu_features |= XENPMU_FEATURE_ARCH_ONLY; ++ else ++ return -EINVAL; ++ ++ s = ss + 1; ++ } while ( *ss ); + /* fall through */ + case 1: + /* Default VPMU mode */ +@@ -105,10 +94,6 @@ static int __init parse_vpmu_params(const char *s) + break; + } + return 0; +- +- error: +- printk("VPMU: unknown flags: %s - vpmu disabled!\n", s); +- return -EINVAL; + } + + void vpmu_lvtpc_update(uint32_t val) +diff --git a/xen/arch/x86/irq.c b/xen/arch/x86/irq.c +index 87ef2e801f..0ceb9b9a1c 100644 +--- a/xen/arch/x86/irq.c ++++ b/xen/arch/x86/irq.c +@@ -70,12 +70,12 @@ static int __init parse_irq_vector_map_param(const char *s) + if ( !ss ) + ss = strchr(s, '\0'); + +- if ( !strncmp(s, "none", ss - s)) +- opt_irq_vector_map=OPT_IRQ_VECTOR_MAP_NONE; +- else if ( !strncmp(s, "global", ss - s)) +- opt_irq_vector_map=OPT_IRQ_VECTOR_MAP_GLOBAL; +- else if ( !strncmp(s, "per-device", ss - s)) +- opt_irq_vector_map=OPT_IRQ_VECTOR_MAP_PERDEV; ++ if ( !cmdline_strcmp(s, "none") ) ++ opt_irq_vector_map = OPT_IRQ_VECTOR_MAP_NONE; ++ else if ( !cmdline_strcmp(s, "global") ) ++ opt_irq_vector_map = OPT_IRQ_VECTOR_MAP_GLOBAL; ++ else if ( !cmdline_strcmp(s, "per-device") ) ++ opt_irq_vector_map = OPT_IRQ_VECTOR_MAP_PERDEV; + else + rc = -EINVAL; + +diff --git a/xen/arch/x86/psr.c b/xen/arch/x86/psr.c +index 0ba8ef88d4..5866a261e3 100644 +--- a/xen/arch/x86/psr.c ++++ b/xen/arch/x86/psr.c +@@ -591,13 +591,13 @@ static int __init parse_psr_param(const char *s) + if ( val_delim > ss ) + val_delim = ss; + +- if ( *val_delim && !strncmp(s, "rmid_max", val_delim - s) ) ++ if ( *val_delim && !cmdline_strcmp(s, "rmid_max") ) + { + opt_rmid_max = simple_strtoul(val_delim + 1, &q, 0); + if ( *q && *q != ',' ) + rc = -EINVAL; + } +- else if ( *val_delim && !strncmp(s, "cos_max", val_delim - s) ) ++ else if ( *val_delim && !cmdline_strcmp(s, "cos_max") ) + { + opt_cos_max = simple_strtoul(val_delim + 1, &q, 0); + if ( *q && *q != ',' ) +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index eb480c1f08..e641894f17 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -83,11 +83,11 @@ static int __init parse_bti(const char *s) + { + s += 6; + +- if ( !strncmp(s, "retpoline", ss - s) ) ++ if ( !cmdline_strcmp(s, "retpoline") ) + opt_thunk = THUNK_RETPOLINE; +- else if ( !strncmp(s, "lfence", ss - s) ) ++ else if ( !cmdline_strcmp(s, "lfence") ) + opt_thunk = THUNK_LFENCE; +- else if ( !strncmp(s, "jmp", ss - s) ) ++ else if ( !cmdline_strcmp(s, "jmp") ) + opt_thunk = THUNK_JMP; + else + rc = -EINVAL; +@@ -194,11 +194,11 @@ static int __init parse_spec_ctrl(const char *s) + { + s += 10; + +- if ( !strncmp(s, "retpoline", ss - s) ) ++ if ( !cmdline_strcmp(s, "retpoline") ) + opt_thunk = THUNK_RETPOLINE; +- else if ( !strncmp(s, "lfence", ss - s) ) ++ else if ( !cmdline_strcmp(s, "lfence") ) + opt_thunk = THUNK_LFENCE; +- else if ( !strncmp(s, "jmp", ss - s) ) ++ else if ( !cmdline_strcmp(s, "jmp") ) + opt_thunk = THUNK_JMP; + else + rc = -EINVAL; +diff --git a/xen/arch/x86/x86_64/mmconfig-shared.c b/xen/arch/x86/x86_64/mmconfig-shared.c +index 7c3b7fd30b..01b5720445 100644 +--- a/xen/arch/x86/x86_64/mmconfig-shared.c ++++ b/xen/arch/x86/x86_64/mmconfig-shared.c +@@ -46,8 +46,8 @@ static int __init parse_mmcfg(const char *s) + case 1: + break; + default: +- if ( !strncmp(s, "amd_fam10", ss - s) || +- !strncmp(s, "amd-fam10", ss - s) ) ++ if ( !cmdline_strcmp(s, "amd_fam10") || ++ !cmdline_strcmp(s, "amd-fam10") ) + pci_probe |= PCI_CHECK_ENABLE_AMD_MMCONF; + else + rc = -EINVAL; +diff --git a/xen/common/efi/boot.c b/xen/common/efi/boot.c +index 6be0b3986f..a9917f31f1 100644 +--- a/xen/common/efi/boot.c ++++ b/xen/common/efi/boot.c +@@ -1323,14 +1323,14 @@ static int __init parse_efi_param(const char *s) + if ( !ss ) + ss = strchr(s, '\0'); + +- if ( !strncmp(s, "rs", ss - s) ) ++ if ( !cmdline_strcmp(s, "rs") ) + { + if ( val ) + __set_bit(EFI_RS, &efi_flags); + else + __clear_bit(EFI_RS, &efi_flags); + } +- else if ( !strncmp(s, "attr=uc", ss - s) ) ++ else if ( !cmdline_strcmp(s, "attr=uc") ) + efi_map_uc = val; + else + rc = -EINVAL; +diff --git a/xen/common/kernel.c b/xen/common/kernel.c +index 5766a0f784..053c31d391 100644 +--- a/xen/common/kernel.c ++++ b/xen/common/kernel.c +@@ -221,25 +221,51 @@ void __init cmdline_parse(const char *cmdline) + + int parse_bool(const char *s, const char *e) + { +- unsigned int len; ++ size_t len = e ? ({ ASSERT(e >= s); e - s; }) : strlen(s); + +- len = e ? ({ ASSERT(e >= s); e - s; }) : strlen(s); +- if ( !len ) +- return -1; ++ switch ( len ) ++ { ++ case 1: ++ if ( *s == '1' ) ++ return 1; ++ if ( *s == '0' ) ++ return 0; ++ break; + +- if ( !strncmp("no", s, len) || +- !strncmp("off", s, len) || +- !strncmp("false", s, len) || +- !strncmp("disable", s, len) || +- !strncmp("0", s, len) ) +- return 0; ++ case 2: ++ if ( !strncmp("on", s, 2) ) ++ return 1; ++ if ( !strncmp("no", s, 2) ) ++ return 0; ++ break; ++ ++ case 3: ++ if ( !strncmp("yes", s, 3) ) ++ return 1; ++ if ( !strncmp("off", s, 3) ) ++ return 0; ++ break; ++ ++ case 4: ++ if ( !strncmp("true", s, 4) ) ++ return 1; ++ break; ++ ++ case 5: ++ if ( !strncmp("false", s, 5) ) ++ return 0; ++ break; + +- if ( !strncmp("yes", s, len) || +- !strncmp("on", s, len) || +- !strncmp("true", s, len) || +- !strncmp("enable", s, len) || +- !strncmp("1", s, len) ) +- return 1; ++ case 6: ++ if ( !strncmp("enable", s, 6) ) ++ return 1; ++ break; ++ ++ case 7: ++ if ( !strncmp("disable", s, 7) ) ++ return 0; ++ break; ++ } + + return -1; + } +@@ -271,6 +297,27 @@ int parse_boolean(const char *name, const char *s, const char *e) + return -1; + } + ++int cmdline_strcmp(const char *frag, const char *name) ++{ ++ for ( ; ; frag++, name++ ) ++ { ++ unsigned char f = *frag, n = *name; ++ int res = f - n; ++ ++ if ( res || n == '\0' ) ++ { ++ /* ++ * NUL in 'name' matching a comma, colon or semicolon in 'frag' ++ * implies success. ++ */ ++ if ( n == '\0' && (f == ',' || f == ':' || f == ';') ) ++ res = 0; ++ ++ return res; ++ } ++ } ++} ++ + unsigned int tainted; + + /** +diff --git a/xen/drivers/cpufreq/cpufreq.c b/xen/drivers/cpufreq/cpufreq.c +index 212f48f9f4..6152a045d9 100644 +--- a/xen/drivers/cpufreq/cpufreq.c ++++ b/xen/drivers/cpufreq/cpufreq.c +@@ -73,7 +73,7 @@ static int __init setup_cpufreq_option(const char *str) + arg = strchr(str, '\0'); + choice = parse_bool(str, arg); + +- if ( choice < 0 && !strncmp(str, "dom0-kernel", arg - str) ) ++ if ( choice < 0 && !cmdline_strcmp(str, "dom0-kernel") ) + { + xen_processor_pmbits &= ~XEN_PROCESSOR_PM_PX; + cpufreq_controller = FREQCTL_dom0_kernel; +@@ -81,14 +81,14 @@ static int __init setup_cpufreq_option(const char *str) + return 0; + } + +- if ( choice == 0 || !strncmp(str, "none", arg - str) ) ++ if ( choice == 0 || !cmdline_strcmp(str, "none") ) + { + xen_processor_pmbits &= ~XEN_PROCESSOR_PM_PX; + cpufreq_controller = FREQCTL_none; + return 0; + } + +- if ( choice > 0 || !strncmp(str, "xen", arg - str) ) ++ if ( choice > 0 || !cmdline_strcmp(str, "xen") ) + { + xen_processor_pmbits |= XEN_PROCESSOR_PM_PX; + cpufreq_controller = FREQCTL_xen; +diff --git a/xen/drivers/passthrough/iommu.c b/xen/drivers/passthrough/iommu.c +index 2c44fabf99..f9b13b018c 100644 +--- a/xen/drivers/passthrough/iommu.c ++++ b/xen/drivers/passthrough/iommu.c +@@ -95,36 +95,36 @@ static int __init parse_iommu_param(const char *s) + b = parse_bool(s, ss); + if ( b >= 0 ) + iommu_enable = b; +- else if ( !strncmp(s, "force", ss - s) || +- !strncmp(s, "required", ss - s) ) ++ else if ( !cmdline_strcmp(s, "force") || ++ !cmdline_strcmp(s, "required") ) + force_iommu = val; +- else if ( !strncmp(s, "workaround_bios_bug", ss - s) ) ++ else if ( !cmdline_strcmp(s, "workaround_bios_bug") ) + iommu_workaround_bios_bug = val; +- else if ( !strncmp(s, "igfx", ss - s) ) ++ else if ( !cmdline_strcmp(s, "igfx") ) + iommu_igfx = val; +- else if ( !strncmp(s, "verbose", ss - s) ) ++ else if ( !cmdline_strcmp(s, "verbose") ) + iommu_verbose = val; +- else if ( !strncmp(s, "snoop", ss - s) ) ++ else if ( !cmdline_strcmp(s, "snoop") ) + iommu_snoop = val; +- else if ( !strncmp(s, "qinval", ss - s) ) ++ else if ( !cmdline_strcmp(s, "qinval") ) + iommu_qinval = val; +- else if ( !strncmp(s, "intremap", ss - s) ) ++ else if ( !cmdline_strcmp(s, "intremap") ) + iommu_intremap = val; +- else if ( !strncmp(s, "intpost", ss - s) ) ++ else if ( !cmdline_strcmp(s, "intpost") ) + iommu_intpost = val; +- else if ( !strncmp(s, "debug", ss - s) ) ++ else if ( !cmdline_strcmp(s, "debug") ) + { + iommu_debug = val; + if ( val ) + iommu_verbose = 1; + } +- else if ( !strncmp(s, "amd-iommu-perdev-intremap", ss - s) ) ++ else if ( !cmdline_strcmp(s, "amd-iommu-perdev-intremap") ) + amd_iommu_perdev_intremap = val; +- else if ( !strncmp(s, "dom0-passthrough", ss - s) ) ++ else if ( !cmdline_strcmp(s, "dom0-passthrough") ) + iommu_passthrough = val; +- else if ( !strncmp(s, "dom0-strict", ss - s) ) ++ else if ( !cmdline_strcmp(s, "dom0-strict") ) + iommu_dom0_strict = val; +- else if ( !strncmp(s, "sharept", ss - s) ) ++ else if ( !cmdline_strcmp(s, "sharept") ) + iommu_hap_pt_share = val; + else + rc = -EINVAL; +diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c +index 1db69d5b99..f51cae7f4e 100644 +--- a/xen/drivers/passthrough/pci.c ++++ b/xen/drivers/passthrough/pci.c +@@ -212,12 +212,12 @@ static int __init parse_pci_param(const char *s) + if ( !ss ) + ss = strchr(s, '\0'); + +- if ( !strncmp(s, "serr", ss - s) ) ++ if ( !cmdline_strcmp(s, "serr") ) + { + cmd_mask = PCI_COMMAND_SERR; + brctl_mask = PCI_BRIDGE_CTL_SERR | PCI_BRIDGE_CTL_DTMR_SERR; + } +- else if ( !strncmp(s, "perr", ss - s) ) ++ else if ( !cmdline_strcmp(s, "perr") ) + { + cmd_mask = PCI_COMMAND_PARITY; + brctl_mask = PCI_BRIDGE_CTL_PARITY; +diff --git a/xen/include/xen/lib.h b/xen/include/xen/lib.h +index 1d9771340c..750f809968 100644 +--- a/xen/include/xen/lib.h ++++ b/xen/include/xen/lib.h +@@ -81,6 +81,13 @@ int parse_bool(const char *s, const char *e); + */ + int parse_boolean(const char *name, const char *s, const char *e); + ++/** ++ * Very similar to strcmp(), but will declare a match if the NUL in 'name' ++ * lines up with comma, colon or semicolon in 'frag'. Designed for picking ++ * exact string matches out of a delimited command line list. ++ */ ++int cmdline_strcmp(const char *frag, const char *name); ++ + /*#define DEBUG_TRACE_DUMP*/ + #ifdef DEBUG_TRACE_DUMP + extern void debugtrace_dump(void); +-- +2.11.0 + diff --git a/xen.git-fc46e159a6b1c379a0dac918120267b606d4defd.patch b/xen.git-fc46e159a6b1c379a0dac918120267b606d4defd.patch new file mode 100644 index 0000000..200a2a9 --- /dev/null +++ b/xen.git-fc46e159a6b1c379a0dac918120267b606d4defd.patch @@ -0,0 +1,194 @@ +From fc46e159a6b1c379a0dac918120267b606d4defd Mon Sep 17 00:00:00 2001 +From: Andrew Cooper +Date: Mon, 18 Mar 2019 17:08:25 +0100 +Subject: [PATCH] x86/tsx: Implement controls for RTM force-abort mode + +The CPUID bit and MSR are deliberately not exposed to guests, because they +won't exist on newer processors. As vPMU isn't security supported, the +misbehaviour of PCR3 isn't expected to impact production deployments. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +master commit: 6be613f29b4205349275d24367bd4c82fb2960dd +master date: 2019-03-12 17:05:21 +0000 +--- + docs/misc/xen-command-line.markdown | 17 ++++++++++++++++- + tools/misc/xen-cpuid.c | 2 ++ + xen/arch/x86/cpu/intel.c | 3 +++ + xen/arch/x86/cpu/vpmu.c | 5 +++++ + xen/arch/x86/msr.c | 4 ++++ + xen/include/asm-x86/cpufeature.h | 3 +++ + xen/include/asm-x86/msr-index.h | 3 +++ + xen/include/asm-x86/vpmu.h | 1 + + xen/include/public/arch-x86/cpufeatureset.h | 1 + + 9 files changed, 38 insertions(+), 1 deletion(-) + +diff --git a/docs/misc/xen-command-line.markdown b/docs/misc/xen-command-line.markdown +index 8046cc8333..8e243808a1 100644 +--- a/docs/misc/xen-command-line.markdown ++++ b/docs/misc/xen-command-line.markdown +@@ -2008,7 +2008,7 @@ Use Virtual Processor ID support if available. This prevents the need for TLB + flushes on VM entry and exit, increasing performance. + + ### vpmu (x86) +-> `= ( | { bts | ipc | arch [, ...] } )` ++> `= ( | { bts | ipc | arch | rtm-abort= [, ...] } )` + + > Default: `off` + +@@ -2034,6 +2034,21 @@ in the Pre-Defined Architectural Performance Events table from the Intel 64 + and IA-32 Architectures Software Developer's Manual, Volume 3B, System + Programming Guide, Part 2. + ++vpmu=rtm-abort controls a trade-off between working Restricted Transactional ++Memory, and working performance counters. ++ ++All processors released to date (Q1 2019) supporting Transactional Memory ++Extensions suffer an erratum which has been addressed in microcode. ++ ++Processors based on the Skylake microarchitecture with up-to-date ++microcode internally use performance counter 3 to work around the erratum. ++A consequence is that the counter gets reprogrammed whenever an `XBEGIN` ++instruction is executed. ++ ++An alternative mode exists where PCR3 behaves as before, at the cost of ++`XBEGIN` unconditionally aborting. Enabling `rtm-abort` mode will ++activate this alternative mode. ++ + If a boolean is not used, combinations of flags are allowed, comma separated. + For example, vpmu=arch,bts. + +diff --git a/tools/misc/xen-cpuid.c b/tools/misc/xen-cpuid.c +index 3888b4e158..0ac903a931 100644 +--- a/tools/misc/xen-cpuid.c ++++ b/tools/misc/xen-cpuid.c +@@ -142,6 +142,8 @@ static const char *str_7d0[32] = + { + [ 2] = "avx512_4vnniw", [ 3] = "avx512_4fmaps", + ++ /* 12 */ [13] = "tsx-force-abort", ++ + [26] = "ibrsb", [27] = "stibp", + [28] = "l1d_flush", [29] = "arch_caps", + /* 30 */ [31] = "ssbd", +diff --git a/xen/arch/x86/cpu/intel.c b/xen/arch/x86/cpu/intel.c +index 9477965321..8e23ed6379 100644 +--- a/xen/arch/x86/cpu/intel.c ++++ b/xen/arch/x86/cpu/intel.c +@@ -287,6 +287,9 @@ static void Intel_errata_workarounds(struct cpuinfo_x86 *c) + if (c->x86 == 6 && cpu_has_clflush && + (c->x86_model == 29 || c->x86_model == 46 || c->x86_model == 47)) + __set_bit(X86_FEATURE_CLFLUSH_MONITOR, c->x86_capability); ++ ++ if (cpu_has_tsx_force_abort && opt_rtm_abort) ++ wrmsrl(MSR_TSX_FORCE_ABORT, TSX_FORCE_ABORT_RTM); + } + + +diff --git a/xen/arch/x86/cpu/vpmu.c b/xen/arch/x86/cpu/vpmu.c +index 2be61606b4..639ae0ca63 100644 +--- a/xen/arch/x86/cpu/vpmu.c ++++ b/xen/arch/x86/cpu/vpmu.c +@@ -53,6 +53,7 @@ CHECK_pmu_params; + static unsigned int __read_mostly opt_vpmu_enabled; + unsigned int __read_mostly vpmu_mode = XENPMU_MODE_OFF; + unsigned int __read_mostly vpmu_features = 0; ++bool __read_mostly opt_rtm_abort; + static int parse_vpmu_params(const char *s); + custom_param("vpmu", parse_vpmu_params); + +@@ -71,6 +72,8 @@ static int __init parse_vpmu_params(const char *s) + break; + default: + do { ++ int val; ++ + ss = strchr(s, ','); + if ( !ss ) + ss = strchr(s, '\0'); +@@ -81,6 +84,8 @@ static int __init parse_vpmu_params(const char *s) + vpmu_features |= XENPMU_FEATURE_IPC_ONLY; + else if ( !cmdline_strcmp(s, "arch") ) + vpmu_features |= XENPMU_FEATURE_ARCH_ONLY; ++ else if ( (val = parse_boolean("rtm-abort", s, ss)) >= 0 ) ++ opt_rtm_abort = val; + else + return -EINVAL; + +diff --git a/xen/arch/x86/msr.c b/xen/arch/x86/msr.c +index 1a591dd2b5..b49fbd8077 100644 +--- a/xen/arch/x86/msr.c ++++ b/xen/arch/x86/msr.c +@@ -152,6 +152,8 @@ int guest_rdmsr(const struct vcpu *v, uint32_t msr, uint64_t *val) + case MSR_PRED_CMD: + case MSR_FLUSH_CMD: + /* Write-only */ ++ case MSR_TSX_FORCE_ABORT: ++ /* Not offered to guests. */ + goto gp_fault; + + case MSR_SPEC_CTRL: +@@ -203,6 +205,8 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) + case MSR_INTEL_PLATFORM_INFO: + case MSR_ARCH_CAPABILITIES: + /* Read-only */ ++ case MSR_TSX_FORCE_ABORT: ++ /* Not offered to guests. */ + goto gp_fault; + + case MSR_AMD_PATCHLOADER: +diff --git a/xen/include/asm-x86/cpufeature.h b/xen/include/asm-x86/cpufeature.h +index 861cb0af93..1c699a8def 100644 +--- a/xen/include/asm-x86/cpufeature.h ++++ b/xen/include/asm-x86/cpufeature.h +@@ -106,6 +106,9 @@ + /* CPUID level 0x80000007.edx */ + #define cpu_has_itsc boot_cpu_has(X86_FEATURE_ITSC) + ++/* CPUID level 0x00000007:0.edx */ ++#define cpu_has_tsx_force_abort boot_cpu_has(X86_FEATURE_TSX_FORCE_ABORT) ++ + /* Synthesized. */ + #define cpu_has_arch_perfmon boot_cpu_has(X86_FEATURE_ARCH_PERFMON) + #define cpu_has_cpuid_faulting boot_cpu_has(X86_FEATURE_CPUID_FAULTING) +diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h +index 7235623c86..d13308ffe0 100644 +--- a/xen/include/asm-x86/msr-index.h ++++ b/xen/include/asm-x86/msr-index.h +@@ -53,6 +53,9 @@ + #define MSR_FLUSH_CMD 0x0000010b + #define FLUSH_CMD_L1D (_AC(1, ULL) << 0) + ++#define MSR_TSX_FORCE_ABORT 0x0000010f ++#define TSX_FORCE_ABORT_RTM (_AC(1, ULL) << 0) ++ + /* Intel MSRs. Some also available on other CPUs */ + #define MSR_IA32_PERFCTR0 0x000000c1 + #define MSR_IA32_A_PERFCTR0 0x000004c1 +diff --git a/xen/include/asm-x86/vpmu.h b/xen/include/asm-x86/vpmu.h +index 5e778ab7ba..1287b9fb6e 100644 +--- a/xen/include/asm-x86/vpmu.h ++++ b/xen/include/asm-x86/vpmu.h +@@ -125,6 +125,7 @@ static inline int vpmu_do_rdmsr(unsigned int msr, uint64_t *msr_content) + + extern unsigned int vpmu_mode; + extern unsigned int vpmu_features; ++extern bool opt_rtm_abort; + + /* Context switch */ + static inline void vpmu_switch_from(struct vcpu *prev) +diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h +index 6c82816fd3..aa2656d792 100644 +--- a/xen/include/public/arch-x86/cpufeatureset.h ++++ b/xen/include/public/arch-x86/cpufeatureset.h +@@ -242,6 +242,7 @@ XEN_CPUFEATURE(IBPB, 8*32+12) /*A IBPB support only (no IBRS, used by + /* Intel-defined CPU features, CPUID level 0x00000007:0.edx, word 9 */ + XEN_CPUFEATURE(AVX512_4VNNIW, 9*32+ 2) /*A AVX512 Neural Network Instructions */ + XEN_CPUFEATURE(AVX512_4FMAPS, 9*32+ 3) /*A AVX512 Multiply Accumulation Single Precision */ ++XEN_CPUFEATURE(TSX_FORCE_ABORT, 9*32+13) /* MSR_TSX_FORCE_ABORT.RTM_ABORT */ + XEN_CPUFEATURE(IBRSB, 9*32+26) /*A IBRS and IBPB support (used by Intel) */ + XEN_CPUFEATURE(STIBP, 9*32+27) /*A STIBP */ + XEN_CPUFEATURE(L1D_FLUSH, 9*32+28) /*S MSR_FLUSH_CMD and L1D flush. */ +-- +2.11.0 + diff --git a/xen.spec b/xen.spec index b11a535..7ba43c4 100644 --- a/xen.spec +++ b/xen.spec @@ -67,7 +67,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.11.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 @@ -137,6 +137,16 @@ Patch54: xsa292.patch Patch55: xsa293-4.11-1.patch Patch56: xsa293-4.11-2.patch Patch57: xsa294-4.11.patch +Patch58: xen.git-0825fbdd62724577febeff11ae50d440992a8f11.patch +Patch59: xen.git-e202feb7131e66ed9186ad8766c9582502c98998.patch +Patch60: xen.git-fc46e159a6b1c379a0dac918120267b606d4defd.patch +Patch61: xsa297-4.11-1.patch +Patch62: xsa297-4.11-2.patch +Patch63: xsa297-4.11-3.patch +Patch64: xsa297-4.11-4.patch +Patch65: xsa297-4.11-5.patch +Patch66: xsa297-4.11-6.patch +Patch67: xsa297-4.11-7.patch %if %build_qemutrad @@ -354,6 +364,16 @@ manage Xen virtual machines. %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 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -633,9 +653,33 @@ fi if [ $1 == 1 -a -f /sbin/grub2-mkconfig ]; then if [ -f /boot/grub2/grub.cfg ]; then /sbin/grub2-mkconfig -o /boot/grub2/grub.cfg + sed -i -e '/insmod module2/d' /boot/grub2/grub.cfg + if [ -d /usr/lib/grub/i386-pc ]; then + if [ ! -d /boot/grub2/i386-pc ]; then + mkdir /boot/grub2/i386-pc + fi + if [ -f /usr/lib/grub/i386-pc/relocator.mod -a ! -f /boot/grub2/i386-pc/relocator.mod ]; then + cp -p /usr/lib/grub/i386-pc/relocator.mod /boot/grub2/i386-pc/relocator.mod + fi + if [ -f /usr/lib/grub/i386-pc/multiboot2.mod -a ! -f /boot/grub2/i386-pc/multiboot2.mod ]; then + cp -p /usr/lib/grub/i386-pc/multiboot2.mod /boot/grub2/i386-pc/multiboot2.mod + fi + fi fi if [ -f /boot/efi/EFI/fedora/grub.cfg ]; then /sbin/grub2-mkconfig -o /boot/efi/EFI/fedora/grub.cfg + sed -i -e '/insmod module2/d' /boot/efi/EFI/fedora/grub.cfg + if [ -d /usr/lib/grub/x86_64-efi ]; then + if [ ! -d /boot/efi/EFI/fedora/x86_64-efi ]; then + mkdir /boot/efi/EFI/fedora/x86_64-efi + fi + if [ -f /usr/lib/grub/x86_64-efi/relocator.mod -a ! -f /boot/efi/EFI/fedora/x86_64-efi/relocator.mod ]; then + cp -p /usr/lib/grub/x86_64-efi/relocator.mod /boot/efi/EFI/fedora/x86_64-efi/relocator.mod + fi + if [ -f /usr/lib/grub/x86_64-efi/multiboot2.mod -a ! -f /boot/efi/EFI/fedora/x86_64-efi/multiboot2.mod ]; then + cp -p /usr/lib/grub/x86_64-efi/multiboot2.mod /boot/efi/EFI/fedora/x86_64-efi/multiboot2.mod + fi + fi fi fi @@ -643,9 +687,33 @@ fi if [ -f /sbin/grub2-mkconfig ]; then if [ -f /boot/grub2/grub.cfg ]; then /sbin/grub2-mkconfig -o /boot/grub2/grub.cfg + sed -i -e '/insmod module2/d' /boot/grub2/grub.cfg + if [ -d /usr/lib/grub/i386-pc -a $1 == 1 ]; then + if [ ! -d /boot/grub2/i386-pc ]; then + mkdir /boot/grub2/i386-pc + fi + if [ -f /usr/lib/grub/i386-pc/relocator.mod -a ! -f /boot/grub2/i386-pc/relocator.mod ]; then + cp -p /usr/lib/grub/i386-pc/relocator.mod /boot/grub2/i386-pc/relocator.mod + fi + if [ -f /usr/lib/grub/i386-pc/multiboot2.mod -a ! -f /boot/grub2/i386-pc/multiboot2.mod ]; then + cp -p /usr/lib/grub/i386-pc/multiboot2.mod /boot/grub2/i386-pc/multiboot2.mod + fi + fi fi if [ -f /boot/efi/EFI/fedora/grub.cfg ]; then /sbin/grub2-mkconfig -o /boot/efi/EFI/fedora/grub.cfg + sed -i -e '/insmod module2/d' /boot/efi/EFI/fedora/grub.cfg + if [ -d /usr/lib/grub/x86_64-efi -a $1 == 1 ]; then + if [ ! -d /boot/efi/EFI/fedora/x86_64-efi ]; then + mkdir /boot/efi/EFI/fedora/x86_64-efi + fi + if [ -f /usr/lib/grub/x86_64-efi/relocator.mod -a ! -f /boot/efi/EFI/fedora/x86_64-efi/relocator.mod ]; then + cp -p /usr/lib/grub/x86_64-efi/relocator.mod /boot/efi/EFI/fedora/x86_64-efi/relocator.mod + fi + if [ -f /usr/lib/grub/x86_64-efi/multiboot2.mod -a ! -f /boot/efi/EFI/fedora/x86_64-efi/multiboot2.mod ]; then + cp -p /usr/lib/grub/x86_64-efi/multiboot2.mod /boot/efi/EFI/fedora/x86_64-efi/multiboot2.mod + fi + fi fi fi %endif @@ -905,6 +973,12 @@ fi %endif %changelog +* Tue May 14 2019 Michael Young - 4.11.1-5 +- Microarchitectural Data Sampling speculative side channel [XSA-297, + CVE-2018-12126, CVE-2018-12127, CVE-2018-12130, CVE-2019-11091] +- additional patches so above applies cleanly +- work around grub2 issues in dom0 + * Tue Mar 05 2019 Michael Young - 4.11.1-4 - xen: various flaws (#1685577) grant table transfer issues on large hosts [XSA-284] diff --git a/xsa297-4.11-1.patch b/xsa297-4.11-1.patch new file mode 100644 index 0000000..5dd5035 --- /dev/null +++ b/xsa297-4.11-1.patch @@ -0,0 +1,163 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Reposition the XPTI command line parsing logic + +It has ended up in the middle of the mitigation calculation logic. Move it to +be beside the other command line parsing. + +No functional change. + +Signed-off-by: Andrew Cooper +Acked-by: Jan Beulich + +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 8fa6c10..949bbda 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -223,6 +223,73 @@ static int __init parse_spec_ctrl(const char *s) + } + custom_param("spec-ctrl", parse_spec_ctrl); + ++int8_t __read_mostly opt_xpti_hwdom = -1; ++int8_t __read_mostly opt_xpti_domu = -1; ++ ++static __init void xpti_init_default(uint64_t caps) ++{ ++ if ( boot_cpu_data.x86_vendor == X86_VENDOR_AMD ) ++ caps = ARCH_CAPS_RDCL_NO; ++ ++ if ( caps & ARCH_CAPS_RDCL_NO ) ++ { ++ if ( opt_xpti_hwdom < 0 ) ++ opt_xpti_hwdom = 0; ++ if ( opt_xpti_domu < 0 ) ++ opt_xpti_domu = 0; ++ } ++ else ++ { ++ if ( opt_xpti_hwdom < 0 ) ++ opt_xpti_hwdom = 1; ++ if ( opt_xpti_domu < 0 ) ++ opt_xpti_domu = 1; ++ } ++} ++ ++static __init int parse_xpti(const char *s) ++{ ++ const char *ss; ++ int val, rc = 0; ++ ++ /* Interpret 'xpti' alone in its positive boolean form. */ ++ if ( *s == '\0' ) ++ opt_xpti_hwdom = opt_xpti_domu = 1; ++ ++ do { ++ ss = strchr(s, ','); ++ if ( !ss ) ++ ss = strchr(s, '\0'); ++ ++ switch ( parse_bool(s, ss) ) ++ { ++ case 0: ++ opt_xpti_hwdom = opt_xpti_domu = 0; ++ break; ++ ++ case 1: ++ opt_xpti_hwdom = opt_xpti_domu = 1; ++ break; ++ ++ default: ++ if ( !strcmp(s, "default") ) ++ opt_xpti_hwdom = opt_xpti_domu = -1; ++ else if ( (val = parse_boolean("dom0", s, ss)) >= 0 ) ++ opt_xpti_hwdom = val; ++ else if ( (val = parse_boolean("domu", s, ss)) >= 0 ) ++ opt_xpti_domu = val; ++ else if ( *s ) ++ rc = -EINVAL; ++ break; ++ } ++ ++ s = ss + 1; ++ } while ( *ss ); ++ ++ return rc; ++} ++custom_param("xpti", parse_xpti); ++ + int8_t __read_mostly opt_pv_l1tf_hwdom = -1; + int8_t __read_mostly opt_pv_l1tf_domu = -1; + +@@ -676,73 +743,6 @@ static __init void l1tf_calculations(uint64_t caps) + : (3ul << (paddr_bits - 2)))); + } + +-int8_t __read_mostly opt_xpti_hwdom = -1; +-int8_t __read_mostly opt_xpti_domu = -1; +- +-static __init void xpti_init_default(uint64_t caps) +-{ +- if ( boot_cpu_data.x86_vendor == X86_VENDOR_AMD ) +- caps = ARCH_CAPS_RDCL_NO; +- +- if ( caps & ARCH_CAPS_RDCL_NO ) +- { +- if ( opt_xpti_hwdom < 0 ) +- opt_xpti_hwdom = 0; +- if ( opt_xpti_domu < 0 ) +- opt_xpti_domu = 0; +- } +- else +- { +- if ( opt_xpti_hwdom < 0 ) +- opt_xpti_hwdom = 1; +- if ( opt_xpti_domu < 0 ) +- opt_xpti_domu = 1; +- } +-} +- +-static __init int parse_xpti(const char *s) +-{ +- const char *ss; +- int val, rc = 0; +- +- /* Interpret 'xpti' alone in its positive boolean form. */ +- if ( *s == '\0' ) +- opt_xpti_hwdom = opt_xpti_domu = 1; +- +- do { +- ss = strchr(s, ','); +- if ( !ss ) +- ss = strchr(s, '\0'); +- +- switch ( parse_bool(s, ss) ) +- { +- case 0: +- opt_xpti_hwdom = opt_xpti_domu = 0; +- break; +- +- case 1: +- opt_xpti_hwdom = opt_xpti_domu = 1; +- break; +- +- default: +- if ( !strcmp(s, "default") ) +- opt_xpti_hwdom = opt_xpti_domu = -1; +- else if ( (val = parse_boolean("dom0", s, ss)) >= 0 ) +- opt_xpti_hwdom = val; +- else if ( (val = parse_boolean("domu", s, ss)) >= 0 ) +- opt_xpti_domu = val; +- else if ( *s ) +- rc = -EINVAL; +- break; +- } +- +- s = ss + 1; +- } while ( *ss ); +- +- return rc; +-} +-custom_param("xpti", parse_xpti); +- + void __init init_speculation_mitigations(void) + { + enum ind_thunk thunk = THUNK_DEFAULT; diff --git a/xsa297-4.11-2.patch b/xsa297-4.11-2.patch new file mode 100644 index 0000000..7c6c006 --- /dev/null +++ b/xsa297-4.11-2.patch @@ -0,0 +1,54 @@ +From: Andrew Cooper +Subject: x86/msr: Definitions for MSR_INTEL_CORE_THREAD_COUNT + +This is a model specific register which details the current configuration +cores and threads in the package. Because of how Hyperthread and Core +configuration works works in firmware, the MSR it is de-facto constant and +will remain unchanged until the next system reset. + +It is a read only MSR (so unilaterally reject writes), but for now retain its +leaky-on-read properties. Further CPUID/MSR work is required before we can +start virtualising a consistent topology to the guest, and retaining the old +behaviour is the safest course of action. + +Signed-off-by: Andrew Cooper +Acked-by: Jan Beulich + +diff --git a/xen/arch/x86/msr.c b/xen/arch/x86/msr.c +index b49fbd8..153f36b 100644 +--- a/xen/arch/x86/msr.c ++++ b/xen/arch/x86/msr.c +@@ -180,6 +180,10 @@ int guest_rdmsr(const struct vcpu *v, uint32_t msr, uint64_t *val) + _MSR_MISC_FEATURES_CPUID_FAULTING; + break; + ++ /* ++ * TODO: Implement when we have better topology representation. ++ case MSR_INTEL_CORE_THREAD_COUNT: ++ */ + default: + return X86EMUL_UNHANDLEABLE; + } +@@ -202,6 +206,7 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) + { + uint64_t rsvd; + ++ case MSR_INTEL_CORE_THREAD_COUNT: + case MSR_INTEL_PLATFORM_INFO: + case MSR_ARCH_CAPABILITIES: + /* Read-only */ +diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h +index 7588fc1..7cddfca 100644 +--- a/xen/include/asm-x86/msr-index.h ++++ b/xen/include/asm-x86/msr-index.h +@@ -34,6 +34,10 @@ + #define EFER_KNOWN_MASK (EFER_SCE | EFER_LME | EFER_LMA | EFER_NX | \ + EFER_SVME | EFER_LMSLE | EFER_FFXSE) + ++#define MSR_INTEL_CORE_THREAD_COUNT 0x00000035 ++#define MSR_CTC_THREAD_MASK 0x0000ffff ++#define MSR_CTC_CORE_MASK 0xffff0000 ++ + /* Speculation Controls. */ + #define MSR_SPEC_CTRL 0x00000048 + #define SPEC_CTRL_IBRS (_AC(1, ULL) << 0) diff --git a/xsa297-4.11-3.patch b/xsa297-4.11-3.patch new file mode 100644 index 0000000..a6e59ef --- /dev/null +++ b/xsa297-4.11-3.patch @@ -0,0 +1,109 @@ +From: Andrew Cooper +Subject: x86/boot: Detect the firmware SMT setting correctly on Intel hardware + +While boot_cpu_data.x86_num_siblings is an accurate value to use on AMD +hardware, it isn't on Intel when the user has disabled Hyperthreading in the +firmware. As a result, a user which has chosen to disable HT still gets +nagged on L1TF-vulnerable hardware when they haven't chosen an explicit +smt= setting. + +Make use of the largely-undocumented MSR_INTEL_CORE_THREAD_COUNT which in +practice exists since Nehalem, when booting on real hardware. Fall back to +using the ACPI table APIC IDs. + +While adjusting this logic, fix a latent bug in amd_get_topology(). The +thread count field in CPUID.0x8000001e.ebx is documented as 8 bits wide, +rather than 2 bits wide. + +Signed-off-by: Andrew Cooper +Acked-by: Jan Beulich + +diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c +index 76078b5..894b892 100644 +--- a/xen/arch/x86/cpu/amd.c ++++ b/xen/arch/x86/cpu/amd.c +@@ -505,7 +505,7 @@ static void amd_get_topology(struct cpuinfo_x86 *c) + u32 eax, ebx, ecx, edx; + + cpuid(0x8000001e, &eax, &ebx, &ecx, &edx); +- c->x86_num_siblings = ((ebx >> 8) & 0x3) + 1; ++ c->x86_num_siblings = ((ebx >> 8) & 0xff) + 1; + + if (c->x86 < 0x17) + c->compute_unit_id = ebx & 0xFF; +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 949bbda..ac1be4a 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -417,6 +417,45 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + opt_pv_l1tf_domu ? "enabled" : "disabled"); + } + ++static bool __init check_smt_enabled(void) ++{ ++ uint64_t val; ++ unsigned int cpu; ++ ++ /* ++ * x86_num_siblings defaults to 1 in the absence of other information, and ++ * is adjusted based on other topology information found in CPUID leaves. ++ * ++ * On AMD hardware, it will be the current SMT configuration. On Intel ++ * hardware, it will represent the maximum capability, rather than the ++ * current configuration. ++ */ ++ if ( boot_cpu_data.x86_num_siblings < 2 ) ++ return false; ++ ++ /* ++ * Intel Nehalem and later hardware does have an MSR which reports the ++ * current count of cores/threads in the package. ++ * ++ * At the time of writing, it is almost completely undocumented, so isn't ++ * virtualised reliably. ++ */ ++ if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && !cpu_has_hypervisor && ++ !rdmsr_safe(MSR_INTEL_CORE_THREAD_COUNT, val) ) ++ return (MASK_EXTR(val, MSR_CTC_CORE_MASK) != ++ MASK_EXTR(val, MSR_CTC_THREAD_MASK)); ++ ++ /* ++ * Search over the CPUs reported in the ACPI tables. Any whose APIC ID ++ * has a non-zero thread id component indicates that SMT is active. ++ */ ++ for_each_present_cpu ( cpu ) ++ if ( x86_cpu_to_apicid[cpu] & (boot_cpu_data.x86_num_siblings - 1) ) ++ return true; ++ ++ return false; ++} ++ + /* Calculate whether Retpoline is known-safe on this CPU. */ + static bool __init retpoline_safe(uint64_t caps) + { +@@ -746,12 +785,14 @@ static __init void l1tf_calculations(uint64_t caps) + void __init init_speculation_mitigations(void) + { + enum ind_thunk thunk = THUNK_DEFAULT; +- bool use_spec_ctrl = false, ibrs = false; ++ bool use_spec_ctrl = false, ibrs = false, hw_smt_enabled; + uint64_t caps = 0; + + if ( boot_cpu_has(X86_FEATURE_ARCH_CAPS) ) + rdmsrl(MSR_ARCH_CAPABILITIES, caps); + ++ hw_smt_enabled = check_smt_enabled(); ++ + /* + * Has the user specified any custom BTI mitigations? If so, follow their + * instructions exactly and disable all heuristics. +@@ -927,8 +968,7 @@ void __init init_speculation_mitigations(void) + * However, if we are on affected hardware, with HT enabled, and the user + * hasn't explicitly chosen whether to use HT or not, nag them to do so. + */ +- if ( opt_smt == -1 && cpu_has_bug_l1tf && !pv_shim && +- boot_cpu_data.x86_num_siblings > 1 ) ++ if ( opt_smt == -1 && cpu_has_bug_l1tf && !pv_shim && hw_smt_enabled ) + warning_add( + "Booted on L1TF-vulnerable hardware with SMT/Hyperthreading\n" + "enabled. Please assess your configuration and choose an\n" diff --git a/xsa297-4.11-4.patch b/xsa297-4.11-4.patch new file mode 100644 index 0000000..e6acc9e --- /dev/null +++ b/xsa297-4.11-4.patch @@ -0,0 +1,55 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Misc non-functional cleanup + + * Identify BTI in the spec_ctrl_{enter,exit}_idle() comments, as other + mitigations will shortly appear. + * Use alternative_input() and cover the lack of memory cobber with a further + barrier. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h +index c846354..4983071 100644 +--- a/xen/include/asm-x86/spec_ctrl.h ++++ b/xen/include/asm-x86/spec_ctrl.h +@@ -61,6 +61,8 @@ static always_inline void spec_ctrl_enter_idle(struct cpu_info *info) + uint32_t val = 0; + + /* ++ * Branch Target Injection: ++ * + * Latch the new shadow value, then enable shadowing, then update the MSR. + * There are no SMP issues here; only local processor ordering concerns. + */ +@@ -68,8 +70,9 @@ static always_inline void spec_ctrl_enter_idle(struct cpu_info *info) + barrier(); + info->spec_ctrl_flags |= SCF_use_shadow; + barrier(); +- asm volatile ( ALTERNATIVE(ASM_NOP3, "wrmsr", X86_FEATURE_SC_MSR_IDLE) +- :: "a" (val), "c" (MSR_SPEC_CTRL), "d" (0) : "memory" ); ++ alternative_input(ASM_NOP3, "wrmsr", X86_FEATURE_SC_MSR_IDLE, ++ "a" (val), "c" (MSR_SPEC_CTRL), "d" (0)); ++ barrier(); + } + + /* WARNING! `ret`, `call *`, `jmp *` not safe before this call. */ +@@ -78,13 +81,16 @@ static always_inline void spec_ctrl_exit_idle(struct cpu_info *info) + uint32_t val = info->xen_spec_ctrl; + + /* ++ * Branch Target Injection: ++ * + * Disable shadowing before updating the MSR. There are no SMP issues + * here; only local processor ordering concerns. + */ + info->spec_ctrl_flags &= ~SCF_use_shadow; + barrier(); +- asm volatile ( ALTERNATIVE(ASM_NOP3, "wrmsr", X86_FEATURE_SC_MSR_IDLE) +- :: "a" (val), "c" (MSR_SPEC_CTRL), "d" (0) : "memory" ); ++ alternative_input(ASM_NOP3, "wrmsr", X86_FEATURE_SC_MSR_IDLE, ++ "a" (val), "c" (MSR_SPEC_CTRL), "d" (0)); ++ barrier(); + } + + #endif /* !__X86_SPEC_CTRL_H__ */ diff --git a/xsa297-4.11-5.patch b/xsa297-4.11-5.patch new file mode 100644 index 0000000..f03c3d8 --- /dev/null +++ b/xsa297-4.11-5.patch @@ -0,0 +1,141 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: CPUID/MSR definitions for Microarchitectural Data + Sampling + +The MD_CLEAR feature can be automatically offered to guests. No +infrastructure is needed in Xen to support the guest making use of it. + +This is part of XSA-297, CVE-2018-12126, CVE-2018-12127, CVE-2018-12130, CVE-2019-11091. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/docs/misc/xen-command-line.markdown b/docs/misc/xen-command-line.markdown +index 8e24380..8260dfb 100644 +--- a/docs/misc/xen-command-line.markdown ++++ b/docs/misc/xen-command-line.markdown +@@ -489,7 +489,7 @@ accounting for hardware capabilities as enumerated via CPUID. + + Currently accepted: + +-The Speculation Control hardware features `ibrsb`, `stibp`, `ibpb`, ++The Speculation Control hardware features `md-clear`, `ibrsb`, `stibp`, `ibpb`, + `l1d-flush` and `ssbd` are used by default if available and applicable. They can + be ignored, e.g. `no-ibrsb`, at which point Xen won't use them itself, and + won't offer them to guests. +diff --git a/tools/libxl/libxl_cpuid.c b/tools/libxl/libxl_cpuid.c +index 52e16c2..5a1702d 100644 +--- a/tools/libxl/libxl_cpuid.c ++++ b/tools/libxl/libxl_cpuid.c +@@ -202,6 +202,7 @@ int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str) + + {"avx512-4vnniw",0x00000007, 0, CPUID_REG_EDX, 2, 1}, + {"avx512-4fmaps",0x00000007, 0, CPUID_REG_EDX, 3, 1}, ++ {"md-clear", 0x00000007, 0, CPUID_REG_EDX, 10, 1}, + {"ibrsb", 0x00000007, 0, CPUID_REG_EDX, 26, 1}, + {"stibp", 0x00000007, 0, CPUID_REG_EDX, 27, 1}, + {"l1d-flush", 0x00000007, 0, CPUID_REG_EDX, 28, 1}, +diff --git a/tools/misc/xen-cpuid.c b/tools/misc/xen-cpuid.c +index 0ac903a..16697c4 100644 +--- a/tools/misc/xen-cpuid.c ++++ b/tools/misc/xen-cpuid.c +@@ -142,6 +142,7 @@ static const char *str_7d0[32] = + { + [ 2] = "avx512_4vnniw", [ 3] = "avx512_4fmaps", + ++ [10] = "md-clear", + /* 12 */ [13] = "tsx-force-abort", + + [26] = "ibrsb", [27] = "stibp", +diff --git a/xen/arch/x86/cpuid.c b/xen/arch/x86/cpuid.c +index 5cc89e2..497bd2a 100644 +--- a/xen/arch/x86/cpuid.c ++++ b/xen/arch/x86/cpuid.c +@@ -28,7 +28,12 @@ static int __init parse_xen_cpuid(const char *s) + if ( !ss ) + ss = strchr(s, '\0'); + +- if ( (val = parse_boolean("ibpb", s, ss)) >= 0 ) ++ if ( (val = parse_boolean("md-clear", s, ss)) >= 0 ) ++ { ++ if ( !val ) ++ setup_clear_cpu_cap(X86_FEATURE_MD_CLEAR); ++ } ++ else if ( (val = parse_boolean("ibpb", s, ss)) >= 0 ) + { + if ( !val ) + setup_clear_cpu_cap(X86_FEATURE_IBPB); +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index ac1be4a..fdd90a8 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -347,17 +347,19 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + printk("Speculative mitigation facilities:\n"); + + /* Hardware features which pertain to speculative mitigations. */ +- 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", + (_7d0 & cpufeat_mask(X86_FEATURE_IBRSB)) ? " IBRS/IBPB" : "", + (_7d0 & cpufeat_mask(X86_FEATURE_STIBP)) ? " STIBP" : "", + (_7d0 & cpufeat_mask(X86_FEATURE_L1D_FLUSH)) ? " L1D_FLUSH" : "", + (_7d0 & cpufeat_mask(X86_FEATURE_SSBD)) ? " SSBD" : "", ++ (_7d0 & cpufeat_mask(X86_FEATURE_MD_CLEAR)) ? " MD_CLEAR" : "", + (e8b & cpufeat_mask(X86_FEATURE_IBPB)) ? " IBPB" : "", + (caps & ARCH_CAPS_IBRS_ALL) ? " IBRS_ALL" : "", + (caps & ARCH_CAPS_RDCL_NO) ? " RDCL_NO" : "", + (caps & ARCH_CAPS_RSBA) ? " RSBA" : "", + (caps & ARCH_CAPS_SKIP_L1DFL) ? " SKIP_L1DFL": "", +- (caps & ARCH_CAPS_SSB_NO) ? " SSB_NO" : ""); ++ (caps & ARCH_CAPS_SSB_NO) ? " SSB_NO" : "", ++ (caps & ARCH_CAPS_MDS_NO) ? " MDS_NO" : ""); + + /* Compiled-in support which pertains to mitigations. */ + if ( IS_ENABLED(CONFIG_INDIRECT_THUNK) || IS_ENABLED(CONFIG_SHADOW_PAGING) ) +@@ -394,19 +396,21 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + * Alternatives blocks for protecting against and/or virtualising + * mitigation support for guests. + */ +- printk(" Support for VMs: PV:%s%s%s%s, HVM:%s%s%s%s\n", ++ printk(" Support for VMs: PV:%s%s%s%s%s, HVM:%s%s%s%s%s\n", + (boot_cpu_has(X86_FEATURE_SC_MSR_PV) || + boot_cpu_has(X86_FEATURE_SC_RSB_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_SC_MSR_HVM) || + boot_cpu_has(X86_FEATURE_SC_RSB_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" : ""); ++ opt_eager_fpu ? " EAGER_FPU" : "", ++ boot_cpu_has(X86_FEATURE_MD_CLEAR) ? " MD_CLEAR" : ""); + + printk(" XPTI (64-bit PV only): Dom0 %s, DomU %s\n", + opt_xpti_hwdom ? "enabled" : "disabled", +diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h +index 7cddfca..b8151d2 100644 +--- a/xen/include/asm-x86/msr-index.h ++++ b/xen/include/asm-x86/msr-index.h +@@ -53,6 +53,7 @@ + #define ARCH_CAPS_RSBA (_AC(1, ULL) << 2) + #define ARCH_CAPS_SKIP_L1DFL (_AC(1, ULL) << 3) + #define ARCH_CAPS_SSB_NO (_AC(1, ULL) << 4) ++#define ARCH_CAPS_MDS_NO (_AC(1, ULL) << 5) + + #define MSR_FLUSH_CMD 0x0000010b + #define FLUSH_CMD_L1D (_AC(1, ULL) << 0) +diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h +index aa2656d..a14d8a7 100644 +--- a/xen/include/public/arch-x86/cpufeatureset.h ++++ b/xen/include/public/arch-x86/cpufeatureset.h +@@ -242,6 +242,7 @@ XEN_CPUFEATURE(IBPB, 8*32+12) /*A IBPB support only (no IBRS, used by + /* Intel-defined CPU features, CPUID level 0x00000007:0.edx, word 9 */ + XEN_CPUFEATURE(AVX512_4VNNIW, 9*32+ 2) /*A AVX512 Neural Network Instructions */ + XEN_CPUFEATURE(AVX512_4FMAPS, 9*32+ 3) /*A AVX512 Multiply Accumulation Single Precision */ ++XEN_CPUFEATURE(MD_CLEAR, 9*32+10) /*A VERW clears microarchitectural buffers */ + XEN_CPUFEATURE(TSX_FORCE_ABORT, 9*32+13) /* MSR_TSX_FORCE_ABORT.RTM_ABORT */ + XEN_CPUFEATURE(IBRSB, 9*32+26) /*A IBRS and IBPB support (used by Intel) */ + XEN_CPUFEATURE(STIBP, 9*32+27) /*A STIBP */ diff --git a/xsa297-4.11-6.patch b/xsa297-4.11-6.patch new file mode 100644 index 0000000..5d153a9 --- /dev/null +++ b/xsa297-4.11-6.patch @@ -0,0 +1,134 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Infrastructure to use VERW to flush pipeline buffers + +Three synthetic features are introduced, as we need individual control of +each, depending on circumstances. A later change will enable them at +appropriate points. + +The verw_sel field doesn't strictly need to live in struct cpu_info. It lives +there because there is a convenient hole it can fill, and it reduces the +complexity of the SPEC_CTRL_EXIT_TO_{PV,HVM} assembly by avoiding the need for +any temporary stack maintenance. + +This is part of XSA-297, CVE-2018-12126, CVE-2018-12127, CVE-2018-12130, CVE-2019-11091. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/x86_64/asm-offsets.c b/xen/arch/x86/x86_64/asm-offsets.c +index 5957c76..97cff49 100644 +--- a/xen/arch/x86/x86_64/asm-offsets.c ++++ b/xen/arch/x86/x86_64/asm-offsets.c +@@ -129,6 +129,7 @@ void __dummy__(void) + + OFFSET(CPUINFO_guest_cpu_user_regs, struct cpu_info, guest_cpu_user_regs); + OFFSET(CPUINFO_processor_id, struct cpu_info, processor_id); ++ OFFSET(CPUINFO_verw_sel, struct cpu_info, verw_sel); + OFFSET(CPUINFO_current_vcpu, struct cpu_info, current_vcpu); + OFFSET(CPUINFO_cr4, struct cpu_info, cr4); + OFFSET(CPUINFO_xen_cr3, struct cpu_info, xen_cr3); +diff --git a/xen/include/asm-x86/cpufeatures.h b/xen/include/asm-x86/cpufeatures.h +index 8e5cc53..96a5a01 100644 +--- a/xen/include/asm-x86/cpufeatures.h ++++ b/xen/include/asm-x86/cpufeatures.h +@@ -33,3 +33,6 @@ XEN_CPUFEATURE(SC_RSB_HVM, (FSCAPINTS+0)*32+19) /* RSB overwrite needed for + XEN_CPUFEATURE(NO_XPTI, (FSCAPINTS+0)*32+20) /* XPTI mitigation not in use */ + XEN_CPUFEATURE(SC_MSR_IDLE, (FSCAPINTS+0)*32+21) /* (SC_MSR_PV || SC_MSR_HVM) && default_xen_spec_ctrl */ + XEN_CPUFEATURE(XEN_LBR, (FSCAPINTS+0)*32+22) /* Xen uses MSR_DEBUGCTL.LBR */ ++XEN_CPUFEATURE(SC_VERW_PV, (FSCAPINTS+0)*32+23) /* VERW used by Xen for PV */ ++XEN_CPUFEATURE(SC_VERW_HVM, (FSCAPINTS+0)*32+24) /* VERW used by Xen for HVM */ ++XEN_CPUFEATURE(SC_VERW_IDLE, (FSCAPINTS+0)*32+25) /* VERW used by Xen for idle */ +diff --git a/xen/include/asm-x86/current.h b/xen/include/asm-x86/current.h +index 5bd64b2..f3508c3 100644 +--- a/xen/include/asm-x86/current.h ++++ b/xen/include/asm-x86/current.h +@@ -38,6 +38,7 @@ struct vcpu; + struct cpu_info { + struct cpu_user_regs guest_cpu_user_regs; + unsigned int processor_id; ++ unsigned int verw_sel; + struct vcpu *current_vcpu; + unsigned long per_cpu_offset; + unsigned long cr4; +diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h +index 4983071..333d180 100644 +--- a/xen/include/asm-x86/spec_ctrl.h ++++ b/xen/include/asm-x86/spec_ctrl.h +@@ -53,6 +53,13 @@ static inline void init_shadow_spec_ctrl_state(void) + info->shadow_spec_ctrl = 0; + info->xen_spec_ctrl = default_xen_spec_ctrl; + info->spec_ctrl_flags = default_spec_ctrl_flags; ++ ++ /* ++ * For least latency, the VERW selector should be a writeable data ++ * descriptor resident in the cache. __HYPERVISOR_DS32 shares a cache ++ * line with __HYPERVISOR_CS, so is expected to be very cache-hot. ++ */ ++ info->verw_sel = __HYPERVISOR_DS32; + } + + /* WARNING! `ret`, `call *`, `jmp *` not safe after this call. */ +@@ -73,6 +80,22 @@ static always_inline void spec_ctrl_enter_idle(struct cpu_info *info) + alternative_input(ASM_NOP3, "wrmsr", X86_FEATURE_SC_MSR_IDLE, + "a" (val), "c" (MSR_SPEC_CTRL), "d" (0)); + barrier(); ++ ++ /* ++ * Microarchitectural Store Buffer Data Sampling: ++ * ++ * On vulnerable systems, store buffer entries are statically partitioned ++ * between active threads. When entering idle, our store buffer entries ++ * are re-partitioned to allow the other threads to use them. ++ * ++ * Flush the buffers to ensure that no sensitive data of ours can be ++ * leaked by a sibling after it gets our store buffer entries. ++ * ++ * Note: VERW must be encoded with a memory operand, as it is only that ++ * form which causes a flush. ++ */ ++ alternative_input("", "verw %[sel]", X86_FEATURE_SC_VERW_IDLE, ++ [sel] "m" (info->verw_sel)); + } + + /* WARNING! `ret`, `call *`, `jmp *` not safe before this call. */ +@@ -91,6 +114,17 @@ static always_inline void spec_ctrl_exit_idle(struct cpu_info *info) + alternative_input(ASM_NOP3, "wrmsr", X86_FEATURE_SC_MSR_IDLE, + "a" (val), "c" (MSR_SPEC_CTRL), "d" (0)); + barrier(); ++ ++ /* ++ * Microarchitectural Store Buffer Data Sampling: ++ * ++ * On vulnerable systems, store buffer entries are statically partitioned ++ * between active threads. When exiting idle, the other threads store ++ * buffer entries are re-partitioned to give us some. ++ * ++ * We now have store buffer entries with stale data from sibling threads. ++ * A flush if necessary will be performed on the return to guest path. ++ */ + } + + #endif /* !__X86_SPEC_CTRL_H__ */ +diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h +index edace2a..9cc15e7 100644 +--- a/xen/include/asm-x86/spec_ctrl_asm.h ++++ b/xen/include/asm-x86/spec_ctrl_asm.h +@@ -245,12 +245,16 @@ + /* 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 ++ DO_SPEC_CTRL_EXIT_TO_GUEST, X86_FEATURE_SC_MSR_PV; \ ++ 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 ++ 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. diff --git a/xsa297-4.11-7.patch b/xsa297-4.11-7.patch new file mode 100644 index 0000000..940191d --- /dev/null +++ b/xsa297-4.11-7.patch @@ -0,0 +1,316 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Introduce options to control VERW flushing + +The Microarchitectural Data Sampling vulnerability is split into categories +with subtly different properties: + + MLPDS - Microarchitectural Load Port Data Sampling + MSBDS - Microarchitectural Store Buffer Data Sampling + MFBDS - Microarchitectural Fill Buffer Data Sampling + MDSUM - Microarchitectural Data Sampling Uncacheable Memory + +MDSUM is a special case of the other three, and isn't distinguished further. + +These issues pertain to three microarchitectural buffers. The Load Ports, the +Store Buffers and the Fill Buffers. Each of these structures are flushed by +the new enhanced VERW functionality, but the conditions under which flushing +is necessary vary. + +For this concise overview of the issues and default logic, the abbreviations +SP (Store Port), FB (Fill Buffer), LP (Load Port) and HT (Hyperthreading) are +used for brevity: + + * Vulnerable hardware is divided into two categories - parts which suffer + from SP only, and parts with any other combination of vulnerabilities. + + * SP only has an HT interaction when the thread goes idle, due to the static + partitioning of resources. LP and FB have HT interactions at all points, + due to the competitive sharing of resources. All issues potentially leak + data across the return-to-guest transition. + + * The microcode which implements VERW flushing also extends MSR_FLUSH_CMD, so + we don't need to do both on the HVM return-to-guest path. However, some + parts are not vulnerable to L1TF (therefore have no MSR_FLUSH_CMD), but are + vulnerable to MDS, so do require VERW on the HVM path. + +Note that we deliberately support mds=1 even without MD_CLEAR in case the +microcode has been updated but the feature bit not exposed. + +This is part of XSA-297, CVE-2018-12126, CVE-2018-12127, CVE-2018-12130, CVE-2019-11091. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/docs/misc/xen-command-line.markdown b/docs/misc/xen-command-line.markdown +index 8260dfb..8108bbf 100644 +--- a/docs/misc/xen-command-line.markdown ++++ b/docs/misc/xen-command-line.markdown +@@ -1800,7 +1800,7 @@ is being interpreted as a custom timeout in milliseconds. Zero or boolean + false disable the quirk workaround, which is also the default. + + ### spec-ctrl (x86) +-> `= List of [ , xen=, {pv,hvm,msr-sc,rsb}=, ++> `= List of [ , xen=, {pv,hvm,msr-sc,rsb,md-clear}=, + > bti-thunk=retpoline|lfence|jmp, {ibrs,ibpb,ssbd,eager-fpu, + > l1d-flush}= ]` + +@@ -1824,9 +1824,10 @@ 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=` and `rsb=` 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. ++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. + + * `pv=` and `hvm=` offer control over all suboptions for PV and HVM guests + respectively. +@@ -1835,6 +1836,11 @@ protect itself, and Xen's ability to virtualise support for 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. ++* `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.* + + 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 fdd90a8..10fcd77 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -34,6 +34,8 @@ static bool __initdata opt_msr_sc_pv = true; + static bool __initdata opt_msr_sc_hvm = true; + static bool __initdata opt_rsb_pv = true; + 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; + + /* Cmdline controls for Xen's speculative settings. */ + static enum ind_thunk { +@@ -58,6 +60,9 @@ paddr_t __read_mostly l1tf_addr_mask, __read_mostly l1tf_safe_maddr; + static bool __initdata cpu_has_bug_l1tf; + static unsigned int __initdata l1d_maxphysaddr; + ++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 int __init parse_bti(const char *s) + { + const char *ss; +@@ -150,6 +155,8 @@ static int __init 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_thunk = THUNK_JMP; + opt_ibrs = 0; +@@ -172,11 +179,13 @@ static int __init parse_spec_ctrl(const char *s) + { + opt_msr_sc_pv = val; + opt_rsb_pv = val; ++ opt_md_clear_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; + } + else if ( (val = parse_boolean("msr-sc", s, ss)) >= 0 ) + { +@@ -188,6 +197,12 @@ static int __init parse_spec_ctrl(const char *s) + opt_rsb_pv = val; + opt_rsb_hvm = val; + } ++ else if ( (val = parse_boolean("md-clear", s, ss)) >= 0 || ++ (val = parse_boolean("mds", s, ss)) >= 0 ) ++ { ++ opt_md_clear_pv = val; ++ opt_md_clear_hvm = val; ++ } + + /* Xen's speculative sidechannel mitigation settings. */ + else if ( !strncmp(s, "bti-thunk=", 10) ) +@@ -373,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, Other:%s%s\n", ++ printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s, Other:%s%s%s\n", + thunk == THUNK_NONE ? "N/A" : + thunk == THUNK_RETPOLINE ? "RETPOLINE" : + thunk == THUNK_LFENCE ? "LFENCE" : +@@ -383,7 +398,8 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + !boot_cpu_has(X86_FEATURE_SSBD) ? "" : + (default_xen_spec_ctrl & SPEC_CTRL_SSBD) ? " SSBD+" : " SSBD-", + opt_ibpb ? " IBPB" : "", +- opt_l1d_flush ? " L1D_FLUSH" : ""); ++ opt_l1d_flush ? " L1D_FLUSH" : "", ++ opt_md_clear_pv || opt_md_clear_hvm ? " VERW" : ""); + + /* L1TF diagnostics, printed if vulnerable or PV shadowing is in use. */ + if ( cpu_has_bug_l1tf || opt_pv_l1tf_hwdom || opt_pv_l1tf_domu ) +@@ -786,6 +802,107 @@ static __init void l1tf_calculations(uint64_t caps) + : (3ul << (paddr_bits - 2)))); + } + ++/* Calculate whether this CPU is vulnerable to MDS. */ ++static __init void mds_calculations(uint64_t caps) ++{ ++ /* MDS 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; ++ ++ /* Any processor advertising MDS_NO should be not vulnerable to MDS. */ ++ if ( caps & ARCH_CAPS_MDS_NO ) ++ return; ++ ++ switch ( boot_cpu_data.x86_model ) ++ { ++ /* ++ * Core processors since at least Nehalem are vulnerable. ++ */ ++ case 0x1f: /* Auburndale / Havendale */ ++ case 0x1e: /* Nehalem */ ++ case 0x1a: /* Nehalem EP */ ++ case 0x2e: /* Nehalem EX */ ++ case 0x25: /* Westmere */ ++ case 0x2c: /* Westmere EP */ ++ case 0x2f: /* Westmere EX */ ++ case 0x2a: /* SandyBridge */ ++ case 0x2d: /* SandyBridge EP/EX */ ++ case 0x3a: /* IvyBridge */ ++ case 0x3e: /* IvyBridge EP/EX */ ++ case 0x3c: /* Haswell */ ++ case 0x3f: /* Haswell EX/EP */ ++ case 0x45: /* Haswell D */ ++ case 0x46: /* Haswell H */ ++ case 0x3d: /* Broadwell */ ++ case 0x47: /* Broadwell H */ ++ case 0x4f: /* Broadwell EP/EX */ ++ case 0x56: /* Broadwell D */ ++ case 0x4e: /* Skylake M */ ++ case 0x5e: /* Skylake D */ ++ cpu_has_bug_mds = true; ++ break; ++ ++ /* ++ * Some Core processors have per-stepping vulnerability. ++ */ ++ case 0x55: /* Skylake-X / Cascade Lake */ ++ if ( boot_cpu_data.x86_mask <= 5 ) ++ cpu_has_bug_mds = true; ++ break; ++ ++ case 0x8e: /* Kaby / Coffee / Whiskey Lake M */ ++ if ( boot_cpu_data.x86_mask <= 0xb ) ++ cpu_has_bug_mds = true; ++ break; ++ ++ case 0x9e: /* Kaby / Coffee / Whiskey Lake D */ ++ if ( boot_cpu_data.x86_mask <= 0xc ) ++ cpu_has_bug_mds = true; ++ break; ++ ++ /* ++ * Very old and very new Atom processors are not vulnerable. ++ */ ++ case 0x1c: /* Pineview */ ++ case 0x26: /* Lincroft */ ++ case 0x27: /* Penwell */ ++ case 0x35: /* Cloverview */ ++ case 0x36: /* Cedarview */ ++ case 0x7a: /* Goldmont */ ++ break; ++ ++ /* ++ * Middling Atom processors are vulnerable to just the Store Buffer ++ * aspect. ++ */ ++ case 0x37: /* Baytrail / Valleyview (Silvermont) */ ++ case 0x4a: /* Merrifield */ ++ case 0x4c: /* Cherrytrail / Brasswell */ ++ case 0x4d: /* Avaton / Rangely (Silvermont) */ ++ case 0x5a: /* Moorefield */ ++ case 0x5d: ++ case 0x65: ++ case 0x6e: ++ case 0x75: ++ /* ++ * Knights processors (which are based on the Silvermont/Airmont ++ * microarchitecture) are similarly only affected by the Store Buffer ++ * aspect. ++ */ ++ case 0x57: /* Knights Landing */ ++ case 0x85: /* Knights Mill */ ++ cpu_has_bug_msbds_only = true; ++ break; ++ ++ default: ++ printk("Unrecognised CPU model %#x - assuming vulnerable to MDS\n", ++ boot_cpu_data.x86_model); ++ cpu_has_bug_mds = true; ++ break; ++ } ++} ++ + void __init init_speculation_mitigations(void) + { + enum ind_thunk thunk = THUNK_DEFAULT; +@@ -978,6 +1095,47 @@ void __init init_speculation_mitigations(void) + "enabled. Please assess your configuration and choose an\n" + "explicit 'smt=' setting. See XSA-273.\n"); + ++ mds_calculations(caps); ++ ++ /* ++ * 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)); ++ ++ /* ++ * 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. ++ * ++ * 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. ++ */ ++ 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); ++ ++ /* ++ * Warn the user if they are on MLPDS/MFBDS-vulnerable hardware with HT ++ * active and no explicit SMT choice. ++ */ ++ if ( opt_smt == -1 && cpu_has_bug_mds && hw_smt_enabled ) ++ warning_add( ++ "Booted on MLPDS/MFBDS-vulnerable hardware with SMT/Hyperthreading\n" ++ "enabled. Mitigations will not be fully effective. Please\n" ++ "choose an explicit smt= setting. See XSA-297.\n"); ++ + print_details(thunk, caps); + + /* From 0b88acdac09c25387d9cc9ee35821471c013f196 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Sun, 2 Jun 2019 00:17:26 +0100 Subject: [PATCH 007/194] fix HVM DomU boot on some chipsets fix expected FTBFS with Python 3.8 (#1704807) adjust grub2 workaround --- ...c15b981ba04c0709e6f25af3b76beb34cafa.patch | 51 ++++++++++ xen.python38.patch | 94 +++++++++++++++++++ xen.spec | 43 ++++----- 3 files changed, 163 insertions(+), 25 deletions(-) create mode 100644 xen.git-8457c15b981ba04c0709e6f25af3b76beb34cafa.patch create mode 100644 xen.python38.patch diff --git a/xen.git-8457c15b981ba04c0709e6f25af3b76beb34cafa.patch b/xen.git-8457c15b981ba04c0709e6f25af3b76beb34cafa.patch new file mode 100644 index 0000000..6e18188 --- /dev/null +++ b/xen.git-8457c15b981ba04c0709e6f25af3b76beb34cafa.patch @@ -0,0 +1,51 @@ +From 8457c15b981ba04c0709e6f25af3b76beb34cafa Mon Sep 17 00:00:00 2001 +From: Igor Druzhinin +Date: Wed, 15 May 2019 09:40:45 +0200 +Subject: [PATCH] x86/vmx: Fixup removals of MSR load/save list entries + +Commit 540d5422 ("x86/vmx: Support removing MSRs from the host/guest +load/save lists") introduced infrastructure finally exposed by +commit fd32dcfe ("x86/vmx: Don't leak EFER.NXE into guest context") +that led to a functional regression on Harpertown and earlier cores +(Gen 1 VT-x) due to MSR count being incorrectly set in VMCS. +As the result, as soon as guest EFER becomes equal to Xen EFER +(which eventually happens in almost every 64-bit VM) and its MSR +entry is supposed to be removed, a stale version of EFER is loaded +into a guest instead causing almost immediate guest failure. + +Signed-off-by: Igor Druzhinin +Reviewed-by: Jan Beulich +Reviewed-by: Andrew Cooper +Acked-by: Kevin Tian +master commit: e28c0ee3356f52f589bbae54e89aaed25c1f599d +master date: 2019-04-09 10:58:18 +0100 +--- + xen/arch/x86/hvm/vmx/vmcs.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/xen/arch/x86/hvm/vmx/vmcs.c b/xen/arch/x86/hvm/vmx/vmcs.c +index 74f2a08cfd..45d18493df 100644 +--- a/xen/arch/x86/hvm/vmx/vmcs.c ++++ b/xen/arch/x86/hvm/vmx/vmcs.c +@@ -1490,15 +1490,15 @@ int vmx_del_msr(struct vcpu *v, uint32_t msr, enum vmx_msr_list_type type) + switch ( type ) + { + case VMX_MSR_HOST: +- __vmwrite(VM_EXIT_MSR_LOAD_COUNT, vmx->host_msr_count--); ++ __vmwrite(VM_EXIT_MSR_LOAD_COUNT, --vmx->host_msr_count); + break; + + case VMX_MSR_GUEST: +- __vmwrite(VM_EXIT_MSR_STORE_COUNT, vmx->msr_save_count--); ++ __vmwrite(VM_EXIT_MSR_STORE_COUNT, --vmx->msr_save_count); + + /* Fallthrough */ + case VMX_MSR_GUEST_LOADONLY: +- __vmwrite(VM_ENTRY_MSR_LOAD_COUNT, vmx->msr_load_count--); ++ __vmwrite(VM_ENTRY_MSR_LOAD_COUNT, --vmx->msr_load_count); + break; + } + +-- +2.11.0 + diff --git a/xen.python38.patch b/xen.python38.patch new file mode 100644 index 0000000..30d5d21 --- /dev/null +++ b/xen.python38.patch @@ -0,0 +1,94 @@ +--- xen-4.12.0/m4/python_devel.m4.orig 2019-05-31 23:30:42.489738121 +0100 ++++ xen-4.12.0/m4/python_devel.m4 2019-06-01 17:02:38.886934441 +0100 +@@ -24,7 +24,8 @@ + dnl If python-config is found use it + CPPFLAGS="$CFLAGS `$PYTHON-config --cflags`" + LDFLAGS="$LDFLAGS `$PYTHON-config --ldflags`" +- LIBS="$LIBS `$PYTHON-config --libs`" ++ LIBSTMP="`$PYTHON-config --libs --embed`" || LIBSTMP="`$PYTHON-config --libs`" ++ LIBS="$LIBS $LIBSTMP" + ]) + + AC_CHECK_HEADER([Python.h], [], +--- xen-4.12.0/tools/configure.orig 2019-05-31 23:30:42.498738452 +0100 ++++ xen-4.12.0/tools/configure 2019-06-01 17:08:26.100727658 +0100 +@@ -7482,7 +7482,8 @@ + + CPPFLAGS="$CFLAGS `$PYTHON-config --cflags`" + LDFLAGS="$LDFLAGS `$PYTHON-config --ldflags`" +- LIBS="$LIBS `$PYTHON-config --libs`" ++ LIBSTMP="`$PYTHON-config --libs --embed`" || LIBSTMP="`$PYTHON-config --libs`" ++ LIBS="$LIBS $LIBSTMP" + + fi + +--- xen-4.12.0/tools/python/xen/lowlevel/xc/xc.c.orig 2019-04-01 12:03:23.000000000 +0100 ++++ xen-4.12.0/tools/python/xen/lowlevel/xc/xc.c 2019-06-01 17:58:39.567729630 +0100 +@@ -118,7 +118,8 @@ + PyObject *kwds) + { + uint32_t dom = 0, target = 0; +- int ret, i; ++ int ret; ++ unsigned int i; + PyObject *pyhandle = NULL; + struct xen_domctl_createdomain config = { + .handle = { +@@ -296,7 +297,7 @@ + + static PyObject *pyxc_domain_sethandle(XcObject *self, PyObject *args) + { +- int i; ++ unsigned int i; + uint32_t dom; + PyObject *pyhandle; + xen_domain_handle_t handle; +@@ -337,7 +338,8 @@ + PyObject *list, *info_dict, *pyhandle; + + uint32_t first_dom = 0; +- int max_doms = 1024, nr_doms, i, j; ++ int max_doms = 1024, nr_doms, i; ++ unsigned int j; + xc_dominfo_t *info; + + static char *kwd_list[] = { "first_dom", "max_doms", NULL }; +@@ -632,7 +634,8 @@ + { + uint32_t sbdf; + uint32_t max_sdevs, num_sdevs; +- int domid, seg, bus, dev, func, rc, i; ++ int domid, seg, bus, dev, func, rc; ++ unsigned int i; + PyObject *Pystr; + char *group_str; + char dev_str[9]; +@@ -972,7 +975,7 @@ + { + xc_physinfo_t pinfo; + char cpu_cap[128], virt_caps[128], *p; +- int i; ++ unsigned int i; + const char *virtcap_names[] = { "hvm", "hvm_directio" }; + + if ( xc_physinfo(self->xc_handle, &pinfo) != 0 ) +--- xen-4.12.0/tools/python/xen/lowlevel/xs/xs.c.orig 2019-04-01 12:03:23.000000000 +0100 ++++ xen-4.12.0/tools/python/xen/lowlevel/xs/xs.c 2019-06-01 18:59:46.316760561 +0100 +@@ -186,7 +186,7 @@ + Py_END_ALLOW_THREADS + + if (xsval) { +- int i; ++ unsigned int i; + PyObject *val = PyList_New(xsval_n); + for (i = 0; i < xsval_n; i++) + #if PY_MAJOR_VERSION >= 3 +@@ -276,7 +276,7 @@ + struct xs_handle *xh = xshandle(self); + struct xs_permissions *perms; + unsigned int perms_n = 0; +- int i; ++ unsigned int i; + + xs_transaction_t th; + char *thstr; diff --git a/xen.spec b/xen.spec index d8c49ee..b281887 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.12.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 @@ -122,6 +122,8 @@ Patch49: xsa297-4.12-4.patch Patch50: xsa297-4.12-5.patch Patch51: xsa297-4.12-6.patch Patch52: xsa297-4.12-7.patch +Patch53: xen.git-8457c15b981ba04c0709e6f25af3b76beb34cafa.patch +Patch54: xen.python38.patch %if %build_qemutrad @@ -328,6 +330,8 @@ manage Xen virtual machines. %patch50 -p1 %patch51 -p1 %patch52 -p1 +%patch53 -p1 +%patch54 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -597,6 +601,14 @@ if [ $1 == 1 -a -f /sbin/grub2-mkconfig ]; then if [ -f /boot/grub2/grub.cfg ]; then /sbin/grub2-mkconfig -o /boot/grub2/grub.cfg sed -i -e '/insmod module2/d' /boot/grub2/grub.cfg + fi + if [ -f /boot/efi/EFI/fedora/grub.cfg ]; then + /sbin/grub2-mkconfig -o /boot/efi/EFI/fedora/grub.cfg + sed -i -e '/insmod module2/d' /boot/efi/EFI/fedora/grub.cfg + fi +fi +if [ -f /sbin/grub2-mkconfig ]; then + if [ -f /boot/grub2/grub.cfg ]; then if [ -d /usr/lib/grub/i386-pc ]; then if [ ! -d /boot/grub2/i386-pc ]; then mkdir /boot/grub2/i386-pc @@ -610,8 +622,6 @@ if [ $1 == 1 -a -f /sbin/grub2-mkconfig ]; then fi fi if [ -f /boot/efi/EFI/fedora/grub.cfg ]; then - /sbin/grub2-mkconfig -o /boot/efi/EFI/fedora/grub.cfg - sed -i -e '/insmod module2/d' /boot/efi/EFI/fedora/grub.cfg if [ -d /usr/lib/grub/x86_64-efi ]; then if [ ! -d /boot/efi/EFI/fedora/x86_64-efi ]; then mkdir /boot/efi/EFI/fedora/x86_64-efi @@ -631,32 +641,10 @@ if [ -f /sbin/grub2-mkconfig ]; then if [ -f /boot/grub2/grub.cfg ]; then /sbin/grub2-mkconfig -o /boot/grub2/grub.cfg sed -i -e '/insmod module2/d' /boot/grub2/grub.cfg - if [ -d /usr/lib/grub/i386-pc -a $1 == 1 ]; then - if [ ! -d /boot/grub2/i386-pc ]; then - mkdir /boot/grub2/i386-pc - fi - if [ -f /usr/lib/grub/i386-pc/relocator.mod -a ! -f /boot/grub2/i386-pc/relocator.mod ]; then - cp -p /usr/lib/grub/i386-pc/relocator.mod /boot/grub2/i386-pc/relocator.mod - fi - if [ -f /usr/lib/grub/i386-pc/multiboot2.mod -a ! -f /boot/grub2/i386-pc/multiboot2.mod ]; then - cp -p /usr/lib/grub/i386-pc/multiboot2.mod /boot/grub2/i386-pc/multiboot2.mod - fi - fi fi if [ -f /boot/efi/EFI/fedora/grub.cfg ]; then /sbin/grub2-mkconfig -o /boot/efi/EFI/fedora/grub.cfg sed -i -e '/insmod module2/d' /boot/efi/EFI/fedora/grub.cfg - if [ -d /usr/lib/grub/x86_64-efi -a $1 == 1 ]; then - if [ ! -d /boot/efi/EFI/fedora/x86_64-efi ]; then - mkdir /boot/efi/EFI/fedora/x86_64-efi - fi - if [ -f /usr/lib/grub/x86_64-efi/relocator.mod -a ! -f /boot/efi/EFI/fedora/x86_64-efi/relocator.mod ]; then - cp -p /usr/lib/grub/x86_64-efi/relocator.mod /boot/efi/EFI/fedora/x86_64-efi/relocator.mod - fi - if [ -f /usr/lib/grub/x86_64-efi/multiboot2.mod -a ! -f /boot/efi/EFI/fedora/x86_64-efi/multiboot2.mod ]; then - cp -p /usr/lib/grub/x86_64-efi/multiboot2.mod /boot/efi/EFI/fedora/x86_64-efi/multiboot2.mod - fi - fi fi fi %endif @@ -908,6 +896,11 @@ fi %endif %changelog +* Sat Jun 01 2019 Michael Young - 4.12.0-3 +- fix HVM DomU boot on some chipsets +- fix expected FTBFS with Python 3.8 (#1704807) +- adjust grub2 workaround + * Tue May 14 2019 Michael Young - 4.12.0-2 - Microarchitectural Data Sampling speculative side channel [XSA-297, CVE-2018-12126, CVE-2018-12127, CVE-2018-12130, CVE-2019-11091] From 780a4075dcb0c5a4335d9b1db759f9badfc39532 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Sat, 15 Jun 2019 19:13:39 +0100 Subject: [PATCH 008/194] Unlimited Arm Atomics Operations [XSA-295] (#1720760) --- xen.spec | 45 +++- xsa295-4.11-01.patch | 84 +++++++ xsa295-4.11-02.patch | 90 +++++++ xsa295-4.11-03.patch | 71 ++++++ xsa295-4.11-04.patch | 171 +++++++++++++ xsa295-4.11-05.patch | 202 +++++++++++++++ xsa295-4.11-06.patch | 442 +++++++++++++++++++++++++++++++++ xsa295-4.11-07.patch | 83 +++++++ xsa295-4.11-08.patch | 145 +++++++++++ xsa295-4.11-09.patch | 135 ++++++++++ xsa295-4.11-10.patch | 255 +++++++++++++++++++ xsa295-4.11-11.patch | 258 +++++++++++++++++++ xsa295-4.11-12.patch | 30 +++ xsa295-4.11-13.patch | 138 +++++++++++ xsa295-4.11-14.patch | 280 +++++++++++++++++++++ xsa295-4.11-15.patch | 112 +++++++++ xsa295-4.11-16.patch | 277 +++++++++++++++++++++ xsa295-4.11-17.patch | 93 +++++++ xsa295-4.11-18.patch | 577 +++++++++++++++++++++++++++++++++++++++++++ xsa295-4.11-19.patch | 84 +++++++ xsa295-4.11-20.patch | 199 +++++++++++++++ 21 files changed, 3770 insertions(+), 1 deletion(-) create mode 100644 xsa295-4.11-01.patch create mode 100644 xsa295-4.11-02.patch create mode 100644 xsa295-4.11-03.patch create mode 100644 xsa295-4.11-04.patch create mode 100644 xsa295-4.11-05.patch create mode 100644 xsa295-4.11-06.patch create mode 100644 xsa295-4.11-07.patch create mode 100644 xsa295-4.11-08.patch create mode 100644 xsa295-4.11-09.patch create mode 100644 xsa295-4.11-10.patch create mode 100644 xsa295-4.11-11.patch create mode 100644 xsa295-4.11-12.patch create mode 100644 xsa295-4.11-13.patch create mode 100644 xsa295-4.11-14.patch create mode 100644 xsa295-4.11-15.patch create mode 100644 xsa295-4.11-16.patch create mode 100644 xsa295-4.11-17.patch create mode 100644 xsa295-4.11-18.patch create mode 100644 xsa295-4.11-19.patch create mode 100644 xsa295-4.11-20.patch diff --git a/xen.spec b/xen.spec index 7ba43c4..6c4fbd2 100644 --- a/xen.spec +++ b/xen.spec @@ -67,7 +67,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.11.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 @@ -147,6 +147,26 @@ Patch64: xsa297-4.11-4.patch Patch65: xsa297-4.11-5.patch Patch66: xsa297-4.11-6.patch Patch67: xsa297-4.11-7.patch +Patch68: xsa295-4.11-01.patch +Patch69: xsa295-4.11-02.patch +Patch70: xsa295-4.11-03.patch +Patch71: xsa295-4.11-04.patch +Patch72: xsa295-4.11-05.patch +Patch73: xsa295-4.11-06.patch +Patch74: xsa295-4.11-07.patch +Patch75: xsa295-4.11-08.patch +Patch76: xsa295-4.11-09.patch +Patch77: xsa295-4.11-10.patch +Patch78: xsa295-4.11-11.patch +Patch79: xsa295-4.11-12.patch +Patch80: xsa295-4.11-13.patch +Patch81: xsa295-4.11-14.patch +Patch82: xsa295-4.11-15.patch +Patch83: xsa295-4.11-16.patch +Patch84: xsa295-4.11-17.patch +Patch85: xsa295-4.11-18.patch +Patch86: xsa295-4.11-19.patch +Patch87: xsa295-4.11-20.patch %if %build_qemutrad @@ -374,6 +394,26 @@ manage Xen virtual machines. %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 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -973,6 +1013,9 @@ fi %endif %changelog +* Sat Jun 15 2019 Michael Young - 4.11.1-6 +- Unlimited Arm Atomics Operations [XSA-295] (#1720760) + * Tue May 14 2019 Michael Young - 4.11.1-5 - Microarchitectural Data Sampling speculative side channel [XSA-297, CVE-2018-12126, CVE-2018-12127, CVE-2018-12130, CVE-2019-11091] diff --git a/xsa295-4.11-01.patch b/xsa295-4.11-01.patch new file mode 100644 index 0000000..05ecdf1 --- /dev/null +++ b/xsa295-4.11-01.patch @@ -0,0 +1,84 @@ +From 66db8e4c095491ae795c8eebafd778b2dab6513d Mon Sep 17 00:00:00 2001 +From: Jan Beulich +Date: Tue, 12 Mar 2019 14:40:24 +0100 +Subject: [PATCH v2 4.11 01/20] events: drop arch_evtchn_inject() + +Have the only user call vcpu_mark_events_pending() instead, at the same +time arranging for correct ordering of the writes (evtchn_pending_sel +should be written before evtchn_upcall_pending). + +Signed-off-by: Jan Beulich +Reviewed-by: Andrew Cooper +Reviewed-by: Julien Grall +--- + xen/arch/arm/vgic.c | 5 ----- + xen/arch/arm/vgic/vgic.c | 5 ----- + xen/common/domain.c | 3 +-- + xen/include/xen/event.h | 3 --- + 4 files changed, 1 insertion(+), 15 deletions(-) + +diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c +index 3fafdd0b66..07c704edff 100644 +--- a/xen/arch/arm/vgic.c ++++ b/xen/arch/arm/vgic.c +@@ -597,11 +597,6 @@ out: + return; + } + +-void arch_evtchn_inject(struct vcpu *v) +-{ +- vgic_inject_irq(v->domain, v, v->domain->arch.evtchn_irq, true); +-} +- + bool vgic_evtchn_irq_pending(struct vcpu *v) + { + struct pending_irq *p; +diff --git a/xen/arch/arm/vgic/vgic.c b/xen/arch/arm/vgic/vgic.c +index a35449bf11..bd4e268e57 100644 +--- a/xen/arch/arm/vgic/vgic.c ++++ b/xen/arch/arm/vgic/vgic.c +@@ -692,11 +692,6 @@ void vgic_kick_vcpus(struct domain *d) + } + } + +-void arch_evtchn_inject(struct vcpu *v) +-{ +- vgic_inject_irq(v->domain, v, v->domain->arch.evtchn_irq, true); +-} +- + bool vgic_evtchn_irq_pending(struct vcpu *v) + { + struct vgic_irq *irq; +diff --git a/xen/common/domain.c b/xen/common/domain.c +index 6cbf135457..f0458c200f 100644 +--- a/xen/common/domain.c ++++ b/xen/common/domain.c +@@ -1239,10 +1239,9 @@ int map_vcpu_info(struct vcpu *v, unsigned long gfn, unsigned offset) + * Mark everything as being pending just to make sure nothing gets + * lost. The domain will get a spurious event, but it can cope. + */ +- vcpu_info(v, evtchn_upcall_pending) = 1; + for ( i = 0; i < BITS_PER_EVTCHN_WORD(d); i++ ) + set_bit(i, &vcpu_info(v, evtchn_pending_sel)); +- arch_evtchn_inject(v); ++ vcpu_mark_events_pending(v); + + return 0; + } +diff --git a/xen/include/xen/event.h b/xen/include/xen/event.h +index ebb879e88d..b8152a9831 100644 +--- a/xen/include/xen/event.h ++++ b/xen/include/xen/event.h +@@ -83,9 +83,6 @@ int guest_enabled_event(struct vcpu *v, uint32_t virq); + /* Notify remote end of a Xen-attached event channel.*/ + void notify_via_xen_event_channel(struct domain *ld, int lport); + +-/* Inject an event channel notification into the guest */ +-void arch_evtchn_inject(struct vcpu *v); +- + /* + * Internal event channel object storage. + * +-- +2.17.1 + diff --git a/xsa295-4.11-02.patch b/xsa295-4.11-02.patch new file mode 100644 index 0000000..6d5daa9 --- /dev/null +++ b/xsa295-4.11-02.patch @@ -0,0 +1,90 @@ +From 6284dd531adc18c71180b30f8c027c8ac13af945 Mon Sep 17 00:00:00 2001 +From: Jan Beulich +Date: Tue, 12 Mar 2019 14:40:56 +0100 +Subject: [PATCH v2 4.11 02/20] common: avoid atomic read-modify-write accesses + in map_vcpu_info() + +There's no need to set the evtchn_pending_sel bits one by one. Simply +write full words with all ones. + +For Arm this requires extending write_atomic() to also handle 64-bit +values; for symmetry read_atomic() gets adjusted as well. + +Signed-off-by: Jan Beulich +Reviewed-by: Andrew Cooper +Reviewed-by: Julien Grall +--- + xen/common/domain.c | 9 ++++++--- + xen/include/asm-arm/atomic.h | 15 +++++++++++++++ + 2 files changed, 21 insertions(+), 3 deletions(-) + +diff --git a/xen/common/domain.c b/xen/common/domain.c +index f0458c200f..6ee2bba753 100644 +--- a/xen/common/domain.c ++++ b/xen/common/domain.c +@@ -1186,7 +1186,6 @@ int map_vcpu_info(struct vcpu *v, unsigned long gfn, unsigned offset) + void *mapping; + vcpu_info_t *new_info; + struct page_info *page; +- int i; + + if ( offset > (PAGE_SIZE - sizeof(vcpu_info_t)) ) + return -EINVAL; +@@ -1239,8 +1238,12 @@ int map_vcpu_info(struct vcpu *v, unsigned long gfn, unsigned offset) + * Mark everything as being pending just to make sure nothing gets + * lost. The domain will get a spurious event, but it can cope. + */ +- for ( i = 0; i < BITS_PER_EVTCHN_WORD(d); i++ ) +- set_bit(i, &vcpu_info(v, evtchn_pending_sel)); ++#ifdef CONFIG_COMPAT ++ if ( !has_32bit_shinfo(d) ) ++ write_atomic(&new_info->native.evtchn_pending_sel, ~0); ++ else ++#endif ++ write_atomic(&vcpu_info(v, evtchn_pending_sel), ~0); + vcpu_mark_events_pending(v); + + return 0; +diff --git a/xen/include/asm-arm/atomic.h b/xen/include/asm-arm/atomic.h +index afb3eeea5b..7b4c987fa7 100644 +--- a/xen/include/asm-arm/atomic.h ++++ b/xen/include/asm-arm/atomic.h +@@ -55,6 +55,19 @@ build_atomic_write(write_int_atomic, "", WORD, int, "r") + #if defined (CONFIG_ARM_64) + build_atomic_read(read_u64_atomic, "", "", uint64_t, "=r") + build_atomic_write(write_u64_atomic, "", "", uint64_t, "r") ++#elif defined (CONFIG_ARM_32) ++static inline uint64_t read_u64_atomic(const volatile uint64_t *addr) ++{ ++ uint64_t val; ++ ++ asm volatile ( "ldrd %0,%H0,%1" : "=r" (val) : "m" (*addr) ); ++ ++ return val; ++} ++static inline void write_u64_atomic(volatile uint64_t *addr, uint64_t val) ++{ ++ asm volatile ( "strd %1,%H1,%0" : "=m" (*addr) : "r" (val) ); ++} + #endif + + build_add_sized(add_u8_sized, "b", BYTE, uint8_t, "ri") +@@ -69,6 +82,7 @@ void __bad_atomic_size(void); + case 1: __x = (typeof(*p))read_u8_atomic((uint8_t *)p); break; \ + case 2: __x = (typeof(*p))read_u16_atomic((uint16_t *)p); break; \ + case 4: __x = (typeof(*p))read_u32_atomic((uint32_t *)p); break; \ ++ case 8: __x = (typeof(*p))read_u64_atomic((uint64_t *)p); break; \ + default: __x = 0; __bad_atomic_size(); break; \ + } \ + __x; \ +@@ -80,6 +94,7 @@ void __bad_atomic_size(void); + case 1: write_u8_atomic((uint8_t *)p, (uint8_t)__x); break; \ + case 2: write_u16_atomic((uint16_t *)p, (uint16_t)__x); break; \ + case 4: write_u32_atomic((uint32_t *)p, (uint32_t)__x); break; \ ++ case 8: write_u64_atomic((uint64_t *)p, (uint64_t)__x); break; \ + default: __bad_atomic_size(); break; \ + } \ + __x; \ +-- +2.17.1 + diff --git a/xsa295-4.11-03.patch b/xsa295-4.11-03.patch new file mode 100644 index 0000000..3c4756e --- /dev/null +++ b/xsa295-4.11-03.patch @@ -0,0 +1,71 @@ +From 71df6499812025175e37781e161d90c0e3b43f3c Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:16 +0100 +Subject: [PATCH v2 4.11 03/20] xen/arm: Add an isb() before reading CNTPCT_EL0 + to prevent re-ordering + +Per D8.2.1 in ARM DDI 0487C.a, "a read to CNTPCT_EL0 can occur +speculatively and out of order relative to other instructions executed +on the same PE." + +Add an instruction barrier to get accurate number of cycles when +requested in get_cycles(). For the other users of CNPCT_EL0, replace by +a call to get_cycles(). + +This is part of XSA-295. + +Signed-off-by: Julien Grall +Acked-by: Stefano Stabellini +--- + xen/arch/arm/time.c | 4 ++-- + xen/include/asm-arm/time.h | 5 ++++- + 2 files changed, 6 insertions(+), 3 deletions(-) + +diff --git a/xen/arch/arm/time.c b/xen/arch/arm/time.c +index c11fcfeadd..a15b4a0dc7 100644 +--- a/xen/arch/arm/time.c ++++ b/xen/arch/arm/time.c +@@ -149,7 +149,7 @@ void __init preinit_xen_time(void) + if ( res ) + panic("Timer: Cannot initialize platform timer"); + +- boot_count = READ_SYSREG64(CNTPCT_EL0); ++ boot_count = get_cycles(); + } + + static void __init init_dt_xen_time(void) +@@ -190,7 +190,7 @@ int __init init_xen_time(void) + /* Return number of nanoseconds since boot */ + s_time_t get_s_time(void) + { +- uint64_t ticks = READ_SYSREG64(CNTPCT_EL0) - boot_count; ++ uint64_t ticks = get_cycles() - boot_count; + return ticks_to_ns(ticks); + } + +diff --git a/xen/include/asm-arm/time.h b/xen/include/asm-arm/time.h +index 5b9a31de91..ca30406669 100644 +--- a/xen/include/asm-arm/time.h ++++ b/xen/include/asm-arm/time.h +@@ -1,6 +1,8 @@ + #ifndef __ARM_TIME_H__ + #define __ARM_TIME_H__ + ++#include ++ + #define DT_MATCH_TIMER \ + DT_MATCH_COMPATIBLE("arm,armv7-timer"), \ + DT_MATCH_COMPATIBLE("arm,armv8-timer") +@@ -9,7 +11,8 @@ typedef unsigned long cycles_t; + + static inline cycles_t get_cycles (void) + { +- return 0; ++ isb(); ++ return READ_SYSREG64(CNTPCT_EL0); + } + + /* List of timer's IRQ */ +-- +2.17.1 + diff --git a/xsa295-4.11-04.patch b/xsa295-4.11-04.patch new file mode 100644 index 0000000..2ed0362 --- /dev/null +++ b/xsa295-4.11-04.patch @@ -0,0 +1,171 @@ +From 7e481e7147f81cc739611670e30a03c9d7e06c5b Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:17 +0100 +Subject: [PATCH v2 4.11 04/20] xen/grant_table: Rework the prototype of + _set_status* for lisibility + +It is not clear from the parameters name whether domid and gt_version +correspond to the local or remote domain. A follow-up patch will make +them more confusing. + +So rename domid (resp. gt_version) to ldomid (resp. rgt_version). At +the same time re-order the parameters to hopefully make it more +readable. + +This is part of XSA-295. + +Suggested-by: Jan Beulich +Signed-off-by: Julien Grall +Reviewed-by: Jan Beulich +Acked-by: Stefano Stabellini +--- + xen/common/grant_table.c | 57 ++++++++++++++++++++-------------------- + 1 file changed, 28 insertions(+), 29 deletions(-) + +diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c +index 656fad1b42..c3a806fe47 100644 +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -652,11 +652,11 @@ static unsigned int nr_grant_entries(struct grant_table *gt) + return 0; + } + +-static int _set_status_v1(domid_t domid, ++static int _set_status_v1(const grant_entry_header_t *shah, ++ struct active_grant_entry *act, + int readonly, + int mapflag, +- grant_entry_header_t *shah, +- struct active_grant_entry *act) ++ domid_t ldomid) + { + int rc = GNTST_okay; + union grant_combo scombo, prev_scombo, new_scombo; +@@ -691,11 +691,11 @@ static int _set_status_v1(domid_t domid, + if ( !act->pin && + (((scombo.shorts.flags & mask) != + GTF_permit_access) || +- (scombo.shorts.domid != domid)) ) ++ (scombo.shorts.domid != ldomid)) ) + PIN_FAIL(done, GNTST_general_error, + "Bad flags (%x) or dom (%d); expected d%d\n", + scombo.shorts.flags, scombo.shorts.domid, +- domid); ++ ldomid); + + new_scombo = scombo; + new_scombo.shorts.flags |= GTF_reading; +@@ -724,12 +724,12 @@ done: + return rc; + } + +-static int _set_status_v2(domid_t domid, ++static int _set_status_v2(const grant_entry_header_t *shah, ++ grant_status_t *status, ++ struct active_grant_entry *act, + int readonly, + int mapflag, +- grant_entry_header_t *shah, +- struct active_grant_entry *act, +- grant_status_t *status) ++ domid_t ldomid) + { + int rc = GNTST_okay; + union grant_combo scombo; +@@ -755,10 +755,10 @@ static int _set_status_v2(domid_t domid, + if ( !act->pin && + ( (((flags & mask) != GTF_permit_access) && + ((flags & mask) != GTF_transitive)) || +- (id != domid)) ) ++ (id != ldomid)) ) + PIN_FAIL(done, GNTST_general_error, + "Bad flags (%x) or dom (%d); expected d%d, flags %x\n", +- flags, id, domid, mask); ++ flags, id, ldomid, mask); + + if ( readonly ) + { +@@ -785,14 +785,14 @@ static int _set_status_v2(domid_t domid, + { + if ( (((flags & mask) != GTF_permit_access) && + ((flags & mask) != GTF_transitive)) || +- (id != domid) || ++ (id != ldomid) || + (!readonly && (flags & GTF_readonly)) ) + { + gnttab_clear_flag(_GTF_writing, status); + gnttab_clear_flag(_GTF_reading, status); + PIN_FAIL(done, GNTST_general_error, + "Unstable flags (%x) or dom (%d); expected d%d (r/w: %d)\n", +- flags, id, domid, !readonly); ++ flags, id, ldomid, !readonly); + } + } + else +@@ -810,19 +810,19 @@ done: + } + + +-static int _set_status(unsigned gt_version, +- domid_t domid, ++static int _set_status(const grant_entry_header_t *shah, ++ grant_status_t *status, ++ unsigned rgt_version, ++ struct active_grant_entry *act, + int readonly, + int mapflag, +- grant_entry_header_t *shah, +- struct active_grant_entry *act, +- grant_status_t *status) ++ domid_t ldomid) + { + +- if ( gt_version == 1 ) +- return _set_status_v1(domid, readonly, mapflag, shah, act); ++ if ( rgt_version == 1 ) ++ return _set_status_v1(shah, act, readonly, mapflag, ldomid); + else +- return _set_status_v2(domid, readonly, mapflag, shah, act, status); ++ return _set_status_v2(shah, status, act, readonly, mapflag, ldomid); + } + + static struct active_grant_entry *grant_map_exists(const struct domain *ld, +@@ -994,9 +994,9 @@ map_grant_ref( + (!(op->flags & GNTMAP_readonly) && + !(act->pin & (GNTPIN_hstw_mask|GNTPIN_devw_mask))) ) + { +- if ( (rc = _set_status(rgt->gt_version, ld->domain_id, +- op->flags & GNTMAP_readonly, +- 1, shah, act, status) ) != GNTST_okay ) ++ if ( (rc = _set_status(shah, status, rgt->gt_version, act, ++ op->flags & GNTMAP_readonly, 1, ++ ld->domain_id) != GNTST_okay) ) + goto act_release_out; + + if ( !act->pin ) +@@ -2452,8 +2452,8 @@ acquire_grant_for_copy( + { + if ( (!old_pin || (!readonly && + !(old_pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)))) && +- (rc = _set_status_v2(ldom, readonly, 0, shah, act, +- status)) != GNTST_okay ) ++ (rc = _set_status_v2(shah, status, act, readonly, 0, ++ ldom)) != GNTST_okay ) + goto unlock_out; + + if ( !allow_transitive ) +@@ -2553,9 +2553,8 @@ acquire_grant_for_copy( + else if ( !old_pin || + (!readonly && !(old_pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask))) ) + { +- if ( (rc = _set_status(rgt->gt_version, ldom, +- readonly, 0, shah, act, +- status) ) != GNTST_okay ) ++ if ( (rc = _set_status(shah, status, rgt->gt_version, act, ++ readonly, 0, ldom)) != GNTST_okay ) + goto unlock_out; + + td = rd; +-- +2.17.1 + diff --git a/xsa295-4.11-05.patch b/xsa295-4.11-05.patch new file mode 100644 index 0000000..dc10440 --- /dev/null +++ b/xsa295-4.11-05.patch @@ -0,0 +1,202 @@ +From 7e536ea72c87b76f13144d3713d5696d94b777f5 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:18 +0100 +Subject: [PATCH v2 4.11 05/20] xen/arm64: bitops: Rewrite bitop helpers in C + +This is part of XSA-295. + +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +Signed-off-by: Stefano Stabellini +--- + xen/arch/arm/README.LinuxPrimitives | 1 - + xen/arch/arm/arm64/lib/bitops.S | 67 --------------------- + xen/arch/arm/arm64/lib/bitops.c | 90 +++++++++++++++++++++++++++++ + 3 files changed, 90 insertions(+), 68 deletions(-) + delete mode 100644 xen/arch/arm/arm64/lib/bitops.S + create mode 100644 xen/arch/arm/arm64/lib/bitops.c + +diff --git a/xen/arch/arm/README.LinuxPrimitives b/xen/arch/arm/README.LinuxPrimitives +index 028e8721f9..891667a5da 100644 +--- a/xen/arch/arm/README.LinuxPrimitives ++++ b/xen/arch/arm/README.LinuxPrimitives +@@ -8,7 +8,6 @@ arm64: + + bitops: last sync @ v3.16-rc6 (last commit: 8715466b6027) + +-linux/arch/arm64/lib/bitops.S xen/arch/arm/arm64/lib/bitops.S + linux/arch/arm64/include/asm/bitops.h xen/include/asm-arm/arm64/bitops.h + + --------------------------------------------------------------------- +diff --git a/xen/arch/arm/arm64/lib/bitops.S b/xen/arch/arm/arm64/lib/bitops.S +deleted file mode 100644 +index 6471dd1875..0000000000 +--- a/xen/arch/arm/arm64/lib/bitops.S ++++ /dev/null +@@ -1,67 +0,0 @@ +-/* +- * Based on linux/arch/arm64/lib/bitops.h which in turn is +- * Based on arch/arm/lib/bitops.h +- * +- * Copyright (C) 2013 ARM Ltd. +- * +- * 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 distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program. If not, see . +- */ +- +-/* +- * x0: bits 4:0 bit offset +- * bits 31:5 word offset +- * x1: address +- */ +- .macro bitop, name, instr +-ENTRY( \name ) +- and w3, w0, #31 // Get bit offset +- eor w0, w0, w3 // Clear low bits +- mov x2, #1 +- add x1, x1, x0, lsr #3 // Get word offset +- lsl x3, x2, x3 // Create mask +-1: ldxr w2, [x1] +- \instr w2, w2, w3 +- stxr w0, w2, [x1] +- cbnz w0, 1b +- ret +-ENDPROC(\name ) +- .endm +- +- .macro testop, name, instr +-ENTRY( \name ) +- and w3, w0, #31 // Get bit offset +- eor w0, w0, w3 // Clear low bits +- mov x2, #1 +- add x1, x1, x0, lsr #3 // Get word offset +- lsl x4, x2, x3 // Create mask +-1: ldxr w2, [x1] +- lsr w0, w2, w3 // Save old value of bit +- \instr w2, w2, w4 // toggle bit +- stlxr w5, w2, [x1] +- cbnz w5, 1b +- dmb ish +- and w0, w0, #1 +-3: ret +-ENDPROC(\name ) +- .endm +- +-/* +- * Atomic bit operations. +- */ +- bitop change_bit, eor +- bitop clear_bit, bic +- bitop set_bit, orr +- +- testop test_and_change_bit, eor +- testop test_and_clear_bit, bic +- testop test_and_set_bit, orr +diff --git a/xen/arch/arm/arm64/lib/bitops.c b/xen/arch/arm/arm64/lib/bitops.c +new file mode 100644 +index 0000000000..b1c681c642 +--- /dev/null ++++ b/xen/arch/arm/arm64/lib/bitops.c +@@ -0,0 +1,90 @@ ++/* ++ * Copyright (C) 2018 ARM Ltd. ++ * ++ * 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 distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include ++#include ++ ++/* ++ * The atomic bit operations pass the number of bit in a signed number ++ * (not sure why). This has the drawback to increase the complexity of ++ * the resulting assembly. ++ * ++ * To generate simpler code, the number of bit (nr) will be cast to ++ * unsigned int. ++ * ++ * XXX: Rework the interface to use unsigned int. ++ */ ++ ++#define bitop(name, instr) \ ++void name(int nr, volatile void *p) \ ++{ \ ++ volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ ++ const uint32_t mask = BIT_MASK((unsigned int)nr); \ ++ unsigned long res, tmp; \ ++ \ ++ do \ ++ { \ ++ asm volatile ("// " __stringify(name) "\n" \ ++ " ldxr %w2, %1\n" \ ++ " " __stringify(instr) " %w2, %w2, %w3\n" \ ++ " stxr %w0, %w2, %1\n" \ ++ : "=&r" (res), "+Q" (*ptr), "=&r" (tmp) \ ++ : "r" (mask)); \ ++ } while ( res ); \ ++} \ ++ ++#define testop(name, instr) \ ++int name(int nr, volatile void *p) \ ++{ \ ++ volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ ++ unsigned int bit = (unsigned int)nr % BITS_PER_WORD; \ ++ const uint32_t mask = BIT_MASK(bit); \ ++ unsigned long res, tmp; \ ++ unsigned long oldbit; \ ++ \ ++ do \ ++ { \ ++ asm volatile ("// " __stringify(name) "\n" \ ++ " ldxr %w3, %2\n" \ ++ " lsr %w1, %w3, %w5 // Save old value of bit\n" \ ++ " " __stringify(instr) " %w3, %w3, %w4 // Toggle bit\n" \ ++ " stlxr %w0, %w3, %2\n" \ ++ : "=&r" (res), "=&r" (oldbit), "+Q" (*ptr), "=&r" (tmp) \ ++ : "r" (mask), "r" (bit) \ ++ : "memory"); \ ++ } while ( res ); \ ++ \ ++ dmb(ish); \ ++ \ ++ return oldbit & 1; \ ++} ++ ++bitop(change_bit, eor) ++bitop(clear_bit, bic) ++bitop(set_bit, orr) ++ ++testop(test_and_change_bit, eor) ++testop(test_and_clear_bit, bic) ++testop(test_and_set_bit, orr) ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-file-style: "BSD" ++ * c-basic-offset: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +-- +2.17.1 + diff --git a/xsa295-4.11-06.patch b/xsa295-4.11-06.patch new file mode 100644 index 0000000..c723d79 --- /dev/null +++ b/xsa295-4.11-06.patch @@ -0,0 +1,442 @@ +From d7519197af2489a856fd928541e32b29a74f85ba Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:19 +0100 +Subject: [PATCH v2 4.11 06/20] xen/arm32: bitops: Rewrite bitop helpers in C + +This is part of XSA-295. + +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +Signed-off-by: Stefano Stabellini +--- + xen/arch/arm/README.LinuxPrimitives | 14 +--- + xen/arch/arm/arm32/lib/Makefile | 5 +- + xen/arch/arm/arm32/lib/bitops.c | 98 +++++++++++++++++++++++ + xen/arch/arm/arm32/lib/bitops.h | 104 ------------------------- + xen/arch/arm/arm32/lib/changebit.S | 14 ---- + xen/arch/arm/arm32/lib/clearbit.S | 14 ---- + xen/arch/arm/arm32/lib/setbit.S | 15 ---- + xen/arch/arm/arm32/lib/testchangebit.S | 15 ---- + xen/arch/arm/arm32/lib/testclearbit.S | 15 ---- + xen/arch/arm/arm32/lib/testsetbit.S | 15 ---- + xen/include/asm-arm/arm32/bitops.h | 19 ++--- + 11 files changed, 108 insertions(+), 220 deletions(-) + create mode 100644 xen/arch/arm/arm32/lib/bitops.c + delete mode 100644 xen/arch/arm/arm32/lib/bitops.h + delete mode 100644 xen/arch/arm/arm32/lib/changebit.S + delete mode 100644 xen/arch/arm/arm32/lib/clearbit.S + delete mode 100644 xen/arch/arm/arm32/lib/setbit.S + delete mode 100644 xen/arch/arm/arm32/lib/testchangebit.S + delete mode 100644 xen/arch/arm/arm32/lib/testclearbit.S + delete mode 100644 xen/arch/arm/arm32/lib/testsetbit.S + +diff --git a/xen/arch/arm/README.LinuxPrimitives b/xen/arch/arm/README.LinuxPrimitives +index 891667a5da..664a9f89ed 100644 +--- a/xen/arch/arm/README.LinuxPrimitives ++++ b/xen/arch/arm/README.LinuxPrimitives +@@ -68,19 +68,9 @@ arm32 + + bitops: last sync @ v3.16-rc6 (last commit: c32ffce0f66e) + +-linux/arch/arm/lib/bitops.h xen/arch/arm/arm32/lib/bitops.h +-linux/arch/arm/lib/changebit.S xen/arch/arm/arm32/lib/changebit.S +-linux/arch/arm/lib/clearbit.S xen/arch/arm/arm32/lib/clearbit.S + linux/arch/arm/lib/findbit.S xen/arch/arm/arm32/lib/findbit.S +-linux/arch/arm/lib/setbit.S xen/arch/arm/arm32/lib/setbit.S +-linux/arch/arm/lib/testchangebit.S xen/arch/arm/arm32/lib/testchangebit.S +-linux/arch/arm/lib/testclearbit.S xen/arch/arm/arm32/lib/testclearbit.S +-linux/arch/arm/lib/testsetbit.S xen/arch/arm/arm32/lib/testsetbit.S +- +-for i in bitops.h changebit.S clearbit.S findbit.S setbit.S testchangebit.S \ +- testclearbit.S testsetbit.S; do +- diff -u ../linux/arch/arm/lib/$i xen/arch/arm/arm32/lib/$i; +-done ++ ++diff -u ../linux/arch/arm/lib/findbit.S xen/arch/arm/arm32/lib/findbit.S + + --------------------------------------------------------------------- + +diff --git a/xen/arch/arm/arm32/lib/Makefile b/xen/arch/arm/arm32/lib/Makefile +index e9fbc595b9..b1457c89dc 100644 +--- a/xen/arch/arm/arm32/lib/Makefile ++++ b/xen/arch/arm/arm32/lib/Makefile +@@ -1,6 +1,5 @@ + obj-y += memcpy.o memmove.o memset.o memchr.o memzero.o +-obj-y += findbit.o setbit.o +-obj-y += setbit.o clearbit.o changebit.o +-obj-y += testsetbit.o testclearbit.o testchangebit.o ++obj-y += findbit.o ++obj-y += bitops.o + obj-y += strchr.o strrchr.o + obj-y += lib1funcs.o lshrdi3.o div64.o +diff --git a/xen/arch/arm/arm32/lib/bitops.c b/xen/arch/arm/arm32/lib/bitops.c +new file mode 100644 +index 0000000000..c69bb53037 +--- /dev/null ++++ b/xen/arch/arm/arm32/lib/bitops.c +@@ -0,0 +1,98 @@ ++/* ++ * Copyright (C) 2018 ARM Ltd. ++ * ++ * 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 distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include ++#include ++#include ++ ++/* ++ * The atomic bit operations pass the number of bit in a signed number ++ * (not sure why). This has the drawback to increase the complexity of ++ * the resulting assembly. ++ * ++ * To generate simpler code, the number of bit (nr) will be cast to ++ * unsigned int. ++ * ++ * XXX: Rework the interface to use unsigned int. ++ */ ++ ++#define bitop(name, instr) \ ++void name(int nr, volatile void *p) \ ++{ \ ++ volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ ++ const uint32_t mask = BIT_MASK((unsigned int)nr); \ ++ unsigned long res, tmp; \ ++ \ ++ ASSERT(((vaddr_t)p & 0x3) == 0); \ ++ prefetchw((const void *)ptr); \ ++ \ ++ do \ ++ { \ ++ asm volatile ("// " __stringify(name) "\n" \ ++ " ldrex %2, %1\n" \ ++ " " __stringify(instr) " %2, %2, %3\n" \ ++ " strex %0, %2, %1\n" \ ++ : "=&r" (res), "+Qo" (*ptr), "=&r" (tmp) \ ++ : "r" (mask)); \ ++ } while ( res ); \ ++} ++ ++#define testop(name, instr) \ ++int name(int nr, volatile void *p) \ ++{ \ ++ volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ ++ unsigned int bit = (unsigned int)nr % BITS_PER_WORD; \ ++ const uint32_t mask = BIT_MASK(bit); \ ++ unsigned long res, tmp; \ ++ int oldbit; \ ++ \ ++ ASSERT(((vaddr_t)p & 0x3) == 0); \ ++ smp_mb(); \ ++ \ ++ prefetchw((const void *)ptr); \ ++ \ ++ do \ ++ { \ ++ asm volatile ("// " __stringify(name) "\n" \ ++ " ldrex %3, %2\n" \ ++ " lsr %1, %3, %5 // Save old value of bit\n" \ ++ " " __stringify(instr) " %3, %3, %4 // Toggle bit\n" \ ++ " strex %0, %3, %2\n" \ ++ : "=&r" (res), "=&r" (oldbit), "+Qo" (*ptr), "=&r" (tmp) \ ++ : "r" (mask), "r" (bit)); \ ++ } while ( res ); \ ++ \ ++ smp_mb(); \ ++ \ ++ return oldbit & 1; \ ++} \ ++ ++bitop(change_bit, eor) ++bitop(clear_bit, bic) ++bitop(set_bit, orr) ++ ++testop(test_and_change_bit, eor) ++testop(test_and_clear_bit, bic) ++testop(test_and_set_bit, orr) ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-file-style: "BSD" ++ * c-basic-offset: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +diff --git a/xen/arch/arm/arm32/lib/bitops.h b/xen/arch/arm/arm32/lib/bitops.h +deleted file mode 100644 +index d5e13476f4..0000000000 +--- a/xen/arch/arm/arm32/lib/bitops.h ++++ /dev/null +@@ -1,104 +0,0 @@ +- +-#if __LINUX_ARM_ARCH__ >= 6 +- .macro bitop, name, instr +-ENTRY( \name ) +-UNWIND( .fnstart ) +- ands ip, r1, #3 +- strneb r1, [ip] @ assert word-aligned +- mov r2, #1 +- and r3, r0, #31 @ Get bit offset +- mov r0, r0, lsr #5 +- add r1, r1, r0, lsl #2 @ Get word offset +-#if __LINUX_ARM_ARCH__ >= 7 && defined(CONFIG_SMP) +- .arch_extension mp +- ALT_SMP(W(pldw) [r1]) +- ALT_UP(W(nop)) +-#endif +- mov r3, r2, lsl r3 +-1: ldrex r2, [r1] +- \instr r2, r2, r3 +- strex r0, r2, [r1] +- cmp r0, #0 +- bne 1b +- bx lr +-UNWIND( .fnend ) +-ENDPROC(\name ) +- .endm +- +- .macro testop, name, instr, store +-ENTRY( \name ) +-UNWIND( .fnstart ) +- ands ip, r1, #3 +- strneb r1, [ip] @ assert word-aligned +- mov r2, #1 +- and r3, r0, #31 @ Get bit offset +- mov r0, r0, lsr #5 +- add r1, r1, r0, lsl #2 @ Get word offset +- mov r3, r2, lsl r3 @ create mask +- smp_dmb +-#if __LINUX_ARM_ARCH__ >= 7 && defined(CONFIG_SMP) +- .arch_extension mp +- ALT_SMP(W(pldw) [r1]) +- ALT_UP(W(nop)) +-#endif +-1: ldrex r2, [r1] +- ands r0, r2, r3 @ save old value of bit +- \instr r2, r2, r3 @ toggle bit +- strex ip, r2, [r1] +- cmp ip, #0 +- bne 1b +- smp_dmb +- cmp r0, #0 +- movne r0, #1 +-2: bx lr +-UNWIND( .fnend ) +-ENDPROC(\name ) +- .endm +-#else +- .macro bitop, name, instr +-ENTRY( \name ) +-UNWIND( .fnstart ) +- ands ip, r1, #3 +- strneb r1, [ip] @ assert word-aligned +- and r2, r0, #31 +- mov r0, r0, lsr #5 +- mov r3, #1 +- mov r3, r3, lsl r2 +- save_and_disable_irqs ip +- ldr r2, [r1, r0, lsl #2] +- \instr r2, r2, r3 +- str r2, [r1, r0, lsl #2] +- restore_irqs ip +- mov pc, lr +-UNWIND( .fnend ) +-ENDPROC(\name ) +- .endm +- +-/** +- * testop - implement a test_and_xxx_bit operation. +- * @instr: operational instruction +- * @store: store instruction +- * +- * Note: we can trivially conditionalise the store instruction +- * to avoid dirtying the data cache. +- */ +- .macro testop, name, instr, store +-ENTRY( \name ) +-UNWIND( .fnstart ) +- ands ip, r1, #3 +- strneb r1, [ip] @ assert word-aligned +- and r3, r0, #31 +- mov r0, r0, lsr #5 +- save_and_disable_irqs ip +- ldr r2, [r1, r0, lsl #2]! +- mov r0, #1 +- tst r2, r0, lsl r3 +- \instr r2, r2, r0, lsl r3 +- \store r2, [r1] +- moveq r0, #0 +- restore_irqs ip +- mov pc, lr +-UNWIND( .fnend ) +-ENDPROC(\name ) +- .endm +-#endif +diff --git a/xen/arch/arm/arm32/lib/changebit.S b/xen/arch/arm/arm32/lib/changebit.S +deleted file mode 100644 +index 6e4ae7594a..0000000000 +--- a/xen/arch/arm/arm32/lib/changebit.S ++++ /dev/null +@@ -1,14 +0,0 @@ +-/* +- * linux/arch/arm/lib/changebit.S +- * +- * Copyright (C) 1995-1996 Russell King +- * +- * 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. +- */ +-#include "assembler.h" +-#include "bitops.h" +- .text +- +-bitop _change_bit, eor +diff --git a/xen/arch/arm/arm32/lib/clearbit.S b/xen/arch/arm/arm32/lib/clearbit.S +deleted file mode 100644 +index fda553f246..0000000000 +--- a/xen/arch/arm/arm32/lib/clearbit.S ++++ /dev/null +@@ -1,14 +0,0 @@ +-/* +- * linux/arch/arm/lib/clearbit.S +- * +- * Copyright (C) 1995-1996 Russell King +- * +- * 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. +- */ +-#include "assembler.h" +-#include "bitops.h" +- .text +- +-bitop _clear_bit, bic +diff --git a/xen/arch/arm/arm32/lib/setbit.S b/xen/arch/arm/arm32/lib/setbit.S +deleted file mode 100644 +index d52f0ab65c..0000000000 +--- a/xen/arch/arm/arm32/lib/setbit.S ++++ /dev/null +@@ -1,15 +0,0 @@ +-/* +- * linux/arch/arm/lib/setbit.S +- * +- * Copyright (C) 1995-1996 Russell King +- * +- * 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. +- */ +- +-#include "assembler.h" +-#include "bitops.h" +- .text +- +-bitop _set_bit, orr +diff --git a/xen/arch/arm/arm32/lib/testchangebit.S b/xen/arch/arm/arm32/lib/testchangebit.S +deleted file mode 100644 +index d83b04c057..0000000000 +--- a/xen/arch/arm/arm32/lib/testchangebit.S ++++ /dev/null +@@ -1,15 +0,0 @@ +-/* +- * linux/arch/arm/lib/testchangebit.S +- * +- * Copyright (C) 1995-1996 Russell King +- * +- * 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. +- */ +- +-#include "assembler.h" +-#include "bitops.h" +- .text +- +-testop _test_and_change_bit, eor, str +diff --git a/xen/arch/arm/arm32/lib/testclearbit.S b/xen/arch/arm/arm32/lib/testclearbit.S +deleted file mode 100644 +index 6f5b7b92d1..0000000000 +--- a/xen/arch/arm/arm32/lib/testclearbit.S ++++ /dev/null +@@ -1,15 +0,0 @@ +-/* +- * linux/arch/arm/lib/testclearbit.S +- * +- * Copyright (C) 1995-1996 Russell King +- * +- * 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. +- */ +- +-#include "assembler.h" +-#include "bitops.h" +- .text +- +-testop _test_and_clear_bit, bicne, strne +diff --git a/xen/arch/arm/arm32/lib/testsetbit.S b/xen/arch/arm/arm32/lib/testsetbit.S +deleted file mode 100644 +index 30425a842a..0000000000 +--- a/xen/arch/arm/arm32/lib/testsetbit.S ++++ /dev/null +@@ -1,15 +0,0 @@ +-/* +- * linux/arch/arm/lib/testsetbit.S +- * +- * Copyright (C) 1995-1996 Russell King +- * +- * 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. +- */ +- +-#include "assembler.h" +-#include "bitops.h" +- .text +- +-testop _test_and_set_bit, orreq, streq +diff --git a/xen/include/asm-arm/arm32/bitops.h b/xen/include/asm-arm/arm32/bitops.h +index 8be3564540..67c4c3f55c 100644 +--- a/xen/include/asm-arm/arm32/bitops.h ++++ b/xen/include/asm-arm/arm32/bitops.h +@@ -1,19 +1,12 @@ + #ifndef _ARM_ARM32_BITOPS_H + #define _ARM_ARM32_BITOPS_H + +-extern void _set_bit(int nr, volatile void * p); +-extern void _clear_bit(int nr, volatile void * p); +-extern void _change_bit(int nr, volatile void * p); +-extern int _test_and_set_bit(int nr, volatile void * p); +-extern int _test_and_clear_bit(int nr, volatile void * p); +-extern int _test_and_change_bit(int nr, volatile void * p); +- +-#define set_bit(n,p) _set_bit(n,p) +-#define clear_bit(n,p) _clear_bit(n,p) +-#define change_bit(n,p) _change_bit(n,p) +-#define test_and_set_bit(n,p) _test_and_set_bit(n,p) +-#define test_and_clear_bit(n,p) _test_and_clear_bit(n,p) +-#define test_and_change_bit(n,p) _test_and_change_bit(n,p) ++extern void set_bit(int nr, volatile void * p); ++extern void clear_bit(int nr, volatile void * p); ++extern void change_bit(int nr, volatile void * p); ++extern int test_and_set_bit(int nr, volatile void * p); ++extern int test_and_clear_bit(int nr, volatile void * p); ++extern int test_and_change_bit(int nr, volatile void * p); + + #define flsl fls + +-- +2.17.1 + diff --git a/xsa295-4.11-07.patch b/xsa295-4.11-07.patch new file mode 100644 index 0000000..3a80b80 --- /dev/null +++ b/xsa295-4.11-07.patch @@ -0,0 +1,83 @@ +From f2d572a74dbf78c3e0c4ae2ef87dae0110d2f3f3 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:20 +0100 +Subject: [PATCH v2 4.11 07/20] xen/arm: bitops: Consolidate prototypes in one + place + +The prototype are the same between arm32 and arm64. Consolidate them in +asm-arm/bitops.h. + +This change will help the introductions of new helpers in a follow-up +patch. + +This is part of XSA-295. + +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +--- + xen/include/asm-arm/arm32/bitops.h | 7 ------- + xen/include/asm-arm/arm64/bitops.h | 10 ---------- + xen/include/asm-arm/bitops.h | 8 ++++++++ + 3 files changed, 8 insertions(+), 17 deletions(-) + +diff --git a/xen/include/asm-arm/arm32/bitops.h b/xen/include/asm-arm/arm32/bitops.h +index 67c4c3f55c..57938a5874 100644 +--- a/xen/include/asm-arm/arm32/bitops.h ++++ b/xen/include/asm-arm/arm32/bitops.h +@@ -1,13 +1,6 @@ + #ifndef _ARM_ARM32_BITOPS_H + #define _ARM_ARM32_BITOPS_H + +-extern void set_bit(int nr, volatile void * p); +-extern void clear_bit(int nr, volatile void * p); +-extern void change_bit(int nr, volatile void * p); +-extern int test_and_set_bit(int nr, volatile void * p); +-extern int test_and_clear_bit(int nr, volatile void * p); +-extern int test_and_change_bit(int nr, volatile void * p); +- + #define flsl fls + + /* +diff --git a/xen/include/asm-arm/arm64/bitops.h b/xen/include/asm-arm/arm64/bitops.h +index 6bf1922680..6cc224ad13 100644 +--- a/xen/include/asm-arm/arm64/bitops.h ++++ b/xen/include/asm-arm/arm64/bitops.h +@@ -1,16 +1,6 @@ + #ifndef _ARM_ARM64_BITOPS_H + #define _ARM_ARM64_BITOPS_H + +-/* +- * Little endian assembly atomic bitops. +- */ +-extern void set_bit(int nr, volatile void *p); +-extern void clear_bit(int nr, volatile void *p); +-extern void change_bit(int nr, volatile void *p); +-extern int test_and_set_bit(int nr, volatile void *p); +-extern int test_and_clear_bit(int nr, volatile void *p); +-extern int test_and_change_bit(int nr, volatile void *p); +- + /* Based on linux/include/asm-generic/bitops/builtin-__ffs.h */ + /** + * __ffs - find first bit in word. +diff --git a/xen/include/asm-arm/bitops.h b/xen/include/asm-arm/bitops.h +index 1cbfb9edb2..c69b08adf6 100644 +--- a/xen/include/asm-arm/bitops.h ++++ b/xen/include/asm-arm/bitops.h +@@ -38,6 +38,14 @@ + # error "unknown ARM variant" + #endif + ++/* Atomics bitops */ ++void set_bit(int nr, volatile void *p); ++void clear_bit(int nr, volatile void *p); ++void change_bit(int nr, volatile void *p); ++int test_and_set_bit(int nr, volatile void *p); ++int test_and_clear_bit(int nr, volatile void *p); ++int test_and_change_bit(int nr, volatile void *p); ++ + /** + * __test_and_set_bit - Set a bit and return its old value + * @nr: Bit to set +-- +2.17.1 + diff --git a/xsa295-4.11-08.patch b/xsa295-4.11-08.patch new file mode 100644 index 0000000..f756ff3 --- /dev/null +++ b/xsa295-4.11-08.patch @@ -0,0 +1,145 @@ +From 550682d9a82f7e32e64b38b78ee58f17ab0f9559 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Wed, 22 May 2019 13:37:53 -0700 +Subject: [PATCH v2 4.11 08/20] xen/arm64: cmpxchg: Simplify the cmpxchg + implementation + +The only difference between each case of the cmpxchg is the size of +used. Rather than duplicating the code, provide a macro to generate each +cases. + +This makes the code easier to read and modify. + +This is part of XSA-295. + +Signed-off-by; Julien Grall +Reviewed-by: Stefano Stabellini +Signed-off-by: Stefano Stabellini +--- + xen/include/asm-arm/arm64/cmpxchg.h | 98 +++++++++++------------------ + 1 file changed, 36 insertions(+), 62 deletions(-) + +diff --git a/xen/include/asm-arm/arm64/cmpxchg.h b/xen/include/asm-arm/arm64/cmpxchg.h +index ae42b2f5ff..393fbca6a5 100644 +--- a/xen/include/asm-arm/arm64/cmpxchg.h ++++ b/xen/include/asm-arm/arm64/cmpxchg.h +@@ -61,80 +61,54 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size + __ret; \ + }) + +-extern void __bad_cmpxchg(volatile void *ptr, int size); ++extern unsigned long __bad_cmpxchg(volatile void *ptr, int size); ++ ++#define __CMPXCHG_CASE(w, sz, name) \ ++static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ ++ unsigned long old, \ ++ unsigned long new) \ ++{ \ ++ unsigned long res, oldval; \ ++ \ ++ do { \ ++ asm volatile("// __cmpxchg_case_" #name "\n" \ ++ " ldxr" #sz " %" #w "1, %2\n" \ ++ " mov %w0, #0\n" \ ++ " cmp %" #w "1, %" #w "3\n" \ ++ " b.ne 1f\n" \ ++ " stxr" #sz " %w0, %" #w "4, %2\n" \ ++ "1:\n" \ ++ : "=&r" (res), "=&r" (oldval), \ ++ "+Q" (*(unsigned long *)ptr) \ ++ : "Ir" (old), "r" (new) \ ++ : "cc"); \ ++ } while (res); \ ++ \ ++ return oldval; \ ++} ++ ++__CMPXCHG_CASE(w, b, 1) ++__CMPXCHG_CASE(w, h, 2) ++__CMPXCHG_CASE(w, , 4) ++__CMPXCHG_CASE( , , 8) + + static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, + unsigned long new, int size) + { +- unsigned long oldval = 0, res; +- + switch (size) { + case 1: +- do { +- asm volatile("// __cmpxchg1\n" +- " ldxrb %w1, %2\n" +- " mov %w0, #0\n" +- " cmp %w1, %w3\n" +- " b.ne 1f\n" +- " stxrb %w0, %w4, %2\n" +- "1:\n" +- : "=&r" (res), "=&r" (oldval), "+Q" (*(u8 *)ptr) +- : "Ir" (old), "r" (new) +- : "cc"); +- } while (res); +- break; +- ++ return __cmpxchg_case_1(ptr, old, new); + case 2: +- do { +- asm volatile("// __cmpxchg2\n" +- " ldxrh %w1, %2\n" +- " mov %w0, #0\n" +- " cmp %w1, %w3\n" +- " b.ne 1f\n" +- " stxrh %w0, %w4, %2\n" +- "1:\n" +- : "=&r" (res), "=&r" (oldval), "+Q" (*(u16 *)ptr) +- : "Ir" (old), "r" (new) +- : "cc"); +- } while (res); +- break; +- ++ return __cmpxchg_case_2(ptr, old, new); + case 4: +- do { +- asm volatile("// __cmpxchg4\n" +- " ldxr %w1, %2\n" +- " mov %w0, #0\n" +- " cmp %w1, %w3\n" +- " b.ne 1f\n" +- " stxr %w0, %w4, %2\n" +- "1:\n" +- : "=&r" (res), "=&r" (oldval), "+Q" (*(u32 *)ptr) +- : "Ir" (old), "r" (new) +- : "cc"); +- } while (res); +- break; +- ++ return __cmpxchg_case_4(ptr, old, new); + case 8: +- do { +- asm volatile("// __cmpxchg8\n" +- " ldxr %1, %2\n" +- " mov %w0, #0\n" +- " cmp %1, %3\n" +- " b.ne 1f\n" +- " stxr %w0, %4, %2\n" +- "1:\n" +- : "=&r" (res), "=&r" (oldval), "+Q" (*(u64 *)ptr) +- : "Ir" (old), "r" (new) +- : "cc"); +- } while (res); +- break; +- ++ return __cmpxchg_case_8(ptr, old, new); + default: +- __bad_cmpxchg(ptr, size); +- oldval = 0; ++ return __bad_cmpxchg(ptr, size); + } + +- return oldval; ++ ASSERT_UNREACHABLE(); + } + + static inline unsigned long __cmpxchg_mb(volatile void *ptr, unsigned long old, +-- +2.17.1 + diff --git a/xsa295-4.11-09.patch b/xsa295-4.11-09.patch new file mode 100644 index 0000000..24a81f3 --- /dev/null +++ b/xsa295-4.11-09.patch @@ -0,0 +1,135 @@ +From b824f26e89e6c9637eefea42c16eb632b33ad6ba Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:22 +0100 +Subject: [PATCH v2 4.11 09/20] xen/arm32: cmpxchg: Simplify the cmpxchg + implementation + +The only difference between each case of the cmpxchg is the size of +used. Rather than duplicating the code, provide a macro to generate each +cases. + +This makes the code easier to read and modify. + +While doing the rework, the case for 64-bit cmpxchg is removed. This is +unused today (already commented) and it would not be possible to use +it directly. + +This is part of XSA-295. + +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +--- + xen/include/asm-arm/arm32/cmpxchg.h | 84 +++++++++++------------------ + 1 file changed, 31 insertions(+), 53 deletions(-) + +diff --git a/xen/include/asm-arm/arm32/cmpxchg.h b/xen/include/asm-arm/arm32/cmpxchg.h +index 03e0bed3a6..471a9e3a3f 100644 +--- a/xen/include/asm-arm/arm32/cmpxchg.h ++++ b/xen/include/asm-arm/arm32/cmpxchg.h +@@ -52,72 +52,50 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size + * indicated by comparing RETURN with OLD. + */ + +-extern void __bad_cmpxchg(volatile void *ptr, int size); ++extern unsigned long __bad_cmpxchg(volatile void *ptr, int size); ++ ++#define __CMPXCHG_CASE(sz, name) \ ++static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ ++ unsigned long old, \ ++ unsigned long new) \ ++{ \ ++ unsigned long oldval, res; \ ++ \ ++ do { \ ++ asm volatile("@ __cmpxchg_case_" #name "\n" \ ++ " ldrex" #sz " %1, [%2]\n" \ ++ " mov %0, #0\n" \ ++ " teq %1, %3\n" \ ++ " strex" #sz "eq %0, %4, [%2]\n" \ ++ : "=&r" (res), "=&r" (oldval) \ ++ : "r" (ptr), "Ir" (old), "r" (new) \ ++ : "memory", "cc"); \ ++ } while (res); \ ++ \ ++ return oldval; \ ++} ++ ++__CMPXCHG_CASE(b, 1) ++__CMPXCHG_CASE(h, 2) ++__CMPXCHG_CASE( , 4) + + static always_inline unsigned long __cmpxchg( + volatile void *ptr, unsigned long old, unsigned long new, int size) + { +- unsigned long oldval, res; +- + prefetchw((const void *)ptr); + + switch (size) { + case 1: +- do { +- asm volatile("@ __cmpxchg1\n" +- " ldrexb %1, [%2]\n" +- " mov %0, #0\n" +- " teq %1, %3\n" +- " strexbeq %0, %4, [%2]\n" +- : "=&r" (res), "=&r" (oldval) +- : "r" (ptr), "Ir" (old), "r" (new) +- : "memory", "cc"); +- } while (res); +- break; ++ return __cmpxchg_case_1(ptr, old, new); + case 2: +- do { +- asm volatile("@ __cmpxchg2\n" +- " ldrexh %1, [%2]\n" +- " mov %0, #0\n" +- " teq %1, %3\n" +- " strexheq %0, %4, [%2]\n" +- : "=&r" (res), "=&r" (oldval) +- : "r" (ptr), "Ir" (old), "r" (new) +- : "memory", "cc"); +- } while (res); +- break; ++ return __cmpxchg_case_2(ptr, old, new); + case 4: +- do { +- asm volatile("@ __cmpxchg4\n" +- " ldrex %1, [%2]\n" +- " mov %0, #0\n" +- " teq %1, %3\n" +- " strexeq %0, %4, [%2]\n" +- : "=&r" (res), "=&r" (oldval) +- : "r" (ptr), "Ir" (old), "r" (new) +- : "memory", "cc"); +- } while (res); +- break; +-#if 0 +- case 8: +- do { +- asm volatile("@ __cmpxchg8\n" +- " ldrexd %1, [%2]\n" +- " mov %0, #0\n" +- " teq %1, %3\n" +- " strexdeq %0, %4, [%2]\n" +- : "=&r" (res), "=&r" (oldval) +- : "r" (ptr), "Ir" (old), "r" (new) +- : "memory", "cc"); +- } while (res); +- break; +-#endif ++ return __cmpxchg_case_4(ptr, old, new); + default: +- __bad_cmpxchg(ptr, size); +- oldval = 0; ++ return __bad_cmpxchg(ptr, size); + } + +- return oldval; ++ ASSERT_UNREACHABLE(); + } + + static always_inline unsigned long __cmpxchg_mb(volatile void *ptr, +-- +2.17.1 + diff --git a/xsa295-4.11-10.patch b/xsa295-4.11-10.patch new file mode 100644 index 0000000..07d571c --- /dev/null +++ b/xsa295-4.11-10.patch @@ -0,0 +1,255 @@ +From 307aee21ae39696bffcaf1f1e5a1434ec6a1908b Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:23 +0100 +Subject: [PATCH v2 4.11 10/20] xen/arm: bitops: Implement a new set of helpers + that can timeout + +Exclusive load-store atomics should only be used between trusted +threads. As not all the guests are trusted, it may be possible to DoS +Xen when updating shared memory with guest atomically. + +To prevent the infinite loop, we introduce a new set of helpers that can +timeout. The timeout is based on the maximum number of iterations. + +They will be used in follow-up patch to make atomic operations +on shared memory safe. + +This is part of XSA-295. + +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +--- + xen/arch/arm/arm32/lib/bitops.c | 52 ++++++++++++++++++++++++++++----- + xen/arch/arm/arm64/lib/bitops.c | 52 ++++++++++++++++++++++++++++----- + xen/include/asm-arm/bitops.h | 28 +++++++++++++++++- + 3 files changed, 117 insertions(+), 15 deletions(-) + +diff --git a/xen/arch/arm/arm32/lib/bitops.c b/xen/arch/arm/arm32/lib/bitops.c +index c69bb53037..08750314fc 100644 +--- a/xen/arch/arm/arm32/lib/bitops.c ++++ b/xen/arch/arm/arm32/lib/bitops.c +@@ -30,7 +30,8 @@ + */ + + #define bitop(name, instr) \ +-void name(int nr, volatile void *p) \ ++static always_inline bool int_##name(int nr, volatile void *p, bool timeout,\ ++ unsigned int max_try) \ + { \ + volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ + const uint32_t mask = BIT_MASK((unsigned int)nr); \ +@@ -47,17 +48,33 @@ void name(int nr, volatile void *p) \ + " strex %0, %2, %1\n" \ + : "=&r" (res), "+Qo" (*ptr), "=&r" (tmp) \ + : "r" (mask)); \ +- } while ( res ); \ ++ \ ++ if ( !res ) \ ++ break; \ ++ } while ( !timeout || ((--max_try) > 0) ); \ ++ \ ++ return !res; \ ++} \ ++ \ ++void name(int nr, volatile void *p) \ ++{ \ ++ if ( !int_##name(nr, p, false, 0) ) \ ++ ASSERT_UNREACHABLE(); \ ++} \ ++ \ ++bool name##_timeout(int nr, volatile void *p, unsigned int max_try) \ ++{ \ ++ return int_##name(nr, p, true, max_try); \ + } + + #define testop(name, instr) \ +-int name(int nr, volatile void *p) \ ++static always_inline bool int_##name(int nr, volatile void *p, int *oldbit, \ ++ bool timeout, unsigned int max_try) \ + { \ + volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ + unsigned int bit = (unsigned int)nr % BITS_PER_WORD; \ + const uint32_t mask = BIT_MASK(bit); \ + unsigned long res, tmp; \ +- int oldbit; \ + \ + ASSERT(((vaddr_t)p & 0x3) == 0); \ + smp_mb(); \ +@@ -71,14 +88,35 @@ int name(int nr, volatile void *p) \ + " lsr %1, %3, %5 // Save old value of bit\n" \ + " " __stringify(instr) " %3, %3, %4 // Toggle bit\n" \ + " strex %0, %3, %2\n" \ +- : "=&r" (res), "=&r" (oldbit), "+Qo" (*ptr), "=&r" (tmp) \ ++ : "=&r" (res), "=&r" (*oldbit), "+Qo" (*ptr), "=&r" (tmp) \ + : "r" (mask), "r" (bit)); \ +- } while ( res ); \ ++ \ ++ if ( !res ) \ ++ break; \ ++ } while ( !timeout || ((--max_try) > 0) ); \ + \ + smp_mb(); \ + \ +- return oldbit & 1; \ ++ *oldbit &= 1; \ ++ \ ++ return !res; \ ++} \ ++ \ ++int name(int nr, volatile void *p) \ ++{ \ ++ int oldbit; \ ++ \ ++ if ( !int_##name(nr, p, &oldbit, false, 0) ) \ ++ ASSERT_UNREACHABLE(); \ ++ \ ++ return oldbit; \ + } \ ++ \ ++bool name##_timeout(int nr, volatile void *p, \ ++ int *oldbit, unsigned int max_try) \ ++{ \ ++ return int_##name(nr, p, oldbit, true, max_try); \ ++} + + bitop(change_bit, eor) + bitop(clear_bit, bic) +diff --git a/xen/arch/arm/arm64/lib/bitops.c b/xen/arch/arm/arm64/lib/bitops.c +index b1c681c642..78bf4ed8c5 100644 +--- a/xen/arch/arm/arm64/lib/bitops.c ++++ b/xen/arch/arm/arm64/lib/bitops.c +@@ -29,7 +29,8 @@ + */ + + #define bitop(name, instr) \ +-void name(int nr, volatile void *p) \ ++static always_inline bool int_##name(int nr, volatile void *p, bool timeout,\ ++ unsigned int max_try) \ + { \ + volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ + const uint32_t mask = BIT_MASK((unsigned int)nr); \ +@@ -43,17 +44,33 @@ void name(int nr, volatile void *p) \ + " stxr %w0, %w2, %1\n" \ + : "=&r" (res), "+Q" (*ptr), "=&r" (tmp) \ + : "r" (mask)); \ +- } while ( res ); \ ++ \ ++ if ( !res ) \ ++ break; \ ++ } while ( !timeout || ((--max_try) > 0) ); \ ++ \ ++ return !res; \ + } \ ++ \ ++void name(int nr, volatile void *p) \ ++{ \ ++ if ( !int_##name(nr, p, false, 0) ) \ ++ ASSERT_UNREACHABLE(); \ ++} \ ++ \ ++bool name##_timeout(int nr, volatile void *p, unsigned int max_try) \ ++{ \ ++ return int_##name(nr, p, true, max_try); \ ++} + + #define testop(name, instr) \ +-int name(int nr, volatile void *p) \ ++static always_inline bool int_##name(int nr, volatile void *p, int *oldbit, \ ++ bool timeout, unsigned int max_try) \ + { \ + volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ + unsigned int bit = (unsigned int)nr % BITS_PER_WORD; \ + const uint32_t mask = BIT_MASK(bit); \ + unsigned long res, tmp; \ +- unsigned long oldbit; \ + \ + do \ + { \ +@@ -62,14 +79,35 @@ int name(int nr, volatile void *p) \ + " lsr %w1, %w3, %w5 // Save old value of bit\n" \ + " " __stringify(instr) " %w3, %w3, %w4 // Toggle bit\n" \ + " stlxr %w0, %w3, %2\n" \ +- : "=&r" (res), "=&r" (oldbit), "+Q" (*ptr), "=&r" (tmp) \ ++ : "=&r" (res), "=&r" (*oldbit), "+Q" (*ptr), "=&r" (tmp) \ + : "r" (mask), "r" (bit) \ + : "memory"); \ +- } while ( res ); \ ++ \ ++ if ( !res ) \ ++ break; \ ++ } while ( !timeout || ((--max_try) > 0) ); \ + \ + dmb(ish); \ + \ +- return oldbit & 1; \ ++ *oldbit &= 1; \ ++ \ ++ return !res; \ ++} \ ++ \ ++int name(int nr, volatile void *p) \ ++{ \ ++ int oldbit; \ ++ \ ++ if ( !int_##name(nr, p, &oldbit, false, 0) ) \ ++ ASSERT_UNREACHABLE(); \ ++ \ ++ return oldbit; \ ++} \ ++ \ ++bool name##_timeout(int nr, volatile void *p, \ ++ int *oldbit, unsigned int max_try) \ ++{ \ ++ return int_##name(nr, p, oldbit, true, max_try); \ + } + + bitop(change_bit, eor) +diff --git a/xen/include/asm-arm/bitops.h b/xen/include/asm-arm/bitops.h +index c69b08adf6..f6782b33be 100644 +--- a/xen/include/asm-arm/bitops.h ++++ b/xen/include/asm-arm/bitops.h +@@ -38,7 +38,14 @@ + # error "unknown ARM variant" + #endif + +-/* Atomics bitops */ ++/* ++ * Atomic bitops ++ * ++ * The helpers below *should* only be used on memory shared between ++ * trusted threads or we know the memory cannot be accessed by another ++ * thread. ++ */ ++ + void set_bit(int nr, volatile void *p); + void clear_bit(int nr, volatile void *p); + void change_bit(int nr, volatile void *p); +@@ -46,6 +53,25 @@ int test_and_set_bit(int nr, volatile void *p); + int test_and_clear_bit(int nr, volatile void *p); + int test_and_change_bit(int nr, volatile void *p); + ++/* ++ * The helpers below may fail to update the memory if the action takes ++ * too long. ++ * ++ * @max_try: Maximum number of iterations ++ * ++ * The helpers will return true when the update has succeeded (i.e no ++ * timeout) and false if the update has failed. ++ */ ++bool set_bit_timeout(int nr, volatile void *p, unsigned int max_try); ++bool clear_bit_timeout(int nr, volatile void *p, unsigned int max_try); ++bool change_bit_timeout(int nr, volatile void *p, unsigned int max_try); ++bool test_and_set_bit_timeout(int nr, volatile void *p, ++ int *oldbit, unsigned int max_try); ++bool test_and_clear_bit_timeout(int nr, volatile void *p, ++ int *oldbit, unsigned int max_try); ++bool test_and_change_bit_timeout(int nr, volatile void *p, ++ int *oldbit, unsigned int max_try); ++ + /** + * __test_and_set_bit - Set a bit and return its old value + * @nr: Bit to set +-- +2.17.1 + diff --git a/xsa295-4.11-11.patch b/xsa295-4.11-11.patch new file mode 100644 index 0000000..3357343 --- /dev/null +++ b/xsa295-4.11-11.patch @@ -0,0 +1,258 @@ +From 94736d1a81c259acb16eccb1b2f2f7286bf71180 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Wed, 22 May 2019 13:39:17 -0700 +Subject: [PATCH v2 4.11 11/20] xen/arm: cmpxchg: Provide a new helper that can + timeout + +Exclusive load-store atomics should only be used between trusted +threads. As not all the guests are trusted, it may be possible to DoS +Xen when updating shared memory with guest atomically. + +To prevent the infinite loop, we introduce a new helper that can timeout. +The timeout is based on the maximum number of iterations. + +It will be used in follow-up patch to make atomic operations on shared +memory safe. + +This is part of XSA-295. + +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +Signed-off-by: Stefano Stabellini +--- + xen/include/asm-arm/arm32/cmpxchg.h | 63 +++++++++++++++++++++----- + xen/include/asm-arm/arm64/cmpxchg.h | 70 ++++++++++++++++++++++------- + 2 files changed, 106 insertions(+), 27 deletions(-) + +diff --git a/xen/include/asm-arm/arm32/cmpxchg.h b/xen/include/asm-arm/arm32/cmpxchg.h +index 471a9e3a3f..49ca2a0d7a 100644 +--- a/xen/include/asm-arm/arm32/cmpxchg.h ++++ b/xen/include/asm-arm/arm32/cmpxchg.h +@@ -55,11 +55,14 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size + extern unsigned long __bad_cmpxchg(volatile void *ptr, int size); + + #define __CMPXCHG_CASE(sz, name) \ +-static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ +- unsigned long old, \ +- unsigned long new) \ ++static inline bool __cmpxchg_case_##name(volatile void *ptr, \ ++ unsigned long *old, \ ++ unsigned long new, \ ++ bool timeout, \ ++ unsigned int max_try) \ + { \ +- unsigned long oldval, res; \ ++ unsigned long oldval; \ ++ unsigned long res; \ + \ + do { \ + asm volatile("@ __cmpxchg_case_" #name "\n" \ +@@ -68,29 +71,35 @@ static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ + " teq %1, %3\n" \ + " strex" #sz "eq %0, %4, [%2]\n" \ + : "=&r" (res), "=&r" (oldval) \ +- : "r" (ptr), "Ir" (old), "r" (new) \ ++ : "r" (ptr), "Ir" (*old), "r" (new) \ + : "memory", "cc"); \ +- } while (res); \ + \ +- return oldval; \ ++ if (!res) \ ++ break; \ ++ } while (!timeout || ((--max_try) > 0)); \ ++ \ ++ *old = oldval; \ ++ \ ++ return !res; \ + } + + __CMPXCHG_CASE(b, 1) + __CMPXCHG_CASE(h, 2) + __CMPXCHG_CASE( , 4) + +-static always_inline unsigned long __cmpxchg( +- volatile void *ptr, unsigned long old, unsigned long new, int size) ++static always_inline bool __int_cmpxchg(volatile void *ptr, unsigned long *old, ++ unsigned long new, int size, ++ bool timeout, unsigned int max_try) + { + prefetchw((const void *)ptr); + + switch (size) { + case 1: +- return __cmpxchg_case_1(ptr, old, new); ++ return __cmpxchg_case_1(ptr, old, new, timeout, max_try); + case 2: +- return __cmpxchg_case_2(ptr, old, new); ++ return __cmpxchg_case_2(ptr, old, new, timeout, max_try); + case 4: +- return __cmpxchg_case_4(ptr, old, new); ++ return __cmpxchg_case_4(ptr, old, new, timeout, max_try); + default: + return __bad_cmpxchg(ptr, size); + } +@@ -98,6 +107,17 @@ static always_inline unsigned long __cmpxchg( + ASSERT_UNREACHABLE(); + } + ++static always_inline unsigned long __cmpxchg(volatile void *ptr, ++ unsigned long old, ++ unsigned long new, ++ int size) ++{ ++ if (!__int_cmpxchg(ptr, &old, new, size, false, 0)) ++ ASSERT_UNREACHABLE(); ++ ++ return old; ++} ++ + static always_inline unsigned long __cmpxchg_mb(volatile void *ptr, + unsigned long old, + unsigned long new, int size) +@@ -111,6 +131,25 @@ static always_inline unsigned long __cmpxchg_mb(volatile void *ptr, + return ret; + } + ++/* ++ * The helper may fail to update the memory if the action takes too long. ++ * ++ * @old: On call the value pointed contains the expected old value. It will be ++ * updated to the actual old value. ++ * @max_try: Maximum number of iterations ++ * ++ * The helper will return true when the update has succeeded (i.e no ++ * timeout) and false if the update has failed. ++ */ ++static always_inline bool __cmpxchg_mb_timeout(volatile void *ptr, ++ unsigned long *old, ++ unsigned long new, ++ int size, ++ unsigned int max_try) ++{ ++ return __int_cmpxchg(ptr, old, new, size, true, max_try); ++} ++ + #define cmpxchg(ptr,o,n) \ + ((__typeof__(*(ptr)))__cmpxchg_mb((ptr), \ + (unsigned long)(o), \ +diff --git a/xen/include/asm-arm/arm64/cmpxchg.h b/xen/include/asm-arm/arm64/cmpxchg.h +index 393fbca6a5..5bc2e1f786 100644 +--- a/xen/include/asm-arm/arm64/cmpxchg.h ++++ b/xen/include/asm-arm/arm64/cmpxchg.h +@@ -64,11 +64,14 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size + extern unsigned long __bad_cmpxchg(volatile void *ptr, int size); + + #define __CMPXCHG_CASE(w, sz, name) \ +-static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ +- unsigned long old, \ +- unsigned long new) \ ++static inline bool __cmpxchg_case_##name(volatile void *ptr, \ ++ unsigned long *old, \ ++ unsigned long new, \ ++ bool timeout, \ ++ unsigned int max_try) \ + { \ +- unsigned long res, oldval; \ ++ unsigned long oldval; \ ++ unsigned long res; \ + \ + do { \ + asm volatile("// __cmpxchg_case_" #name "\n" \ +@@ -80,11 +83,16 @@ static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ + "1:\n" \ + : "=&r" (res), "=&r" (oldval), \ + "+Q" (*(unsigned long *)ptr) \ +- : "Ir" (old), "r" (new) \ ++ : "Ir" (*old), "r" (new) \ + : "cc"); \ +- } while (res); \ + \ +- return oldval; \ ++ if (!res) \ ++ break; \ ++ } while (!timeout || ((--max_try) > 0)); \ ++ \ ++ *old = oldval; \ ++ \ ++ return !res; \ + } + + __CMPXCHG_CASE(w, b, 1) +@@ -92,18 +100,19 @@ __CMPXCHG_CASE(w, h, 2) + __CMPXCHG_CASE(w, , 4) + __CMPXCHG_CASE( , , 8) + +-static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, +- unsigned long new, int size) ++static always_inline bool __int_cmpxchg(volatile void *ptr, unsigned long *old, ++ unsigned long new, int size, ++ bool timeout, unsigned int max_try) + { + switch (size) { + case 1: +- return __cmpxchg_case_1(ptr, old, new); ++ return __cmpxchg_case_1(ptr, old, new, timeout, max_try); + case 2: +- return __cmpxchg_case_2(ptr, old, new); ++ return __cmpxchg_case_2(ptr, old, new, timeout, max_try); + case 4: +- return __cmpxchg_case_4(ptr, old, new); ++ return __cmpxchg_case_4(ptr, old, new, timeout, max_try); + case 8: +- return __cmpxchg_case_8(ptr, old, new); ++ return __cmpxchg_case_8(ptr, old, new, timeout, max_try); + default: + return __bad_cmpxchg(ptr, size); + } +@@ -111,8 +120,20 @@ static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, + ASSERT_UNREACHABLE(); + } + +-static inline unsigned long __cmpxchg_mb(volatile void *ptr, unsigned long old, +- unsigned long new, int size) ++static always_inline unsigned long __cmpxchg(volatile void *ptr, ++ unsigned long old, ++ unsigned long new, ++ int size) ++{ ++ if (!__int_cmpxchg(ptr, &old, new, size, false, 0)) ++ ASSERT_UNREACHABLE(); ++ ++ return old; ++} ++ ++static always_inline unsigned long __cmpxchg_mb(volatile void *ptr, ++ unsigned long old, ++ unsigned long new, int size) + { + unsigned long ret; + +@@ -123,6 +144,25 @@ static inline unsigned long __cmpxchg_mb(volatile void *ptr, unsigned long old, + return ret; + } + ++/* ++ * The helper may fail to update the memory if the action takes too long. ++ * ++ * @old: On call the value pointed contains the expected old value. It will be ++ * updated to the actual old value. ++ * @max_try: Maximum number of iterations ++ * ++ * The helper will return true when the update has succeeded (i.e no ++ * timeout) and false if the update has failed. ++ */ ++static always_inline bool __cmpxchg_mb_timeout(volatile void *ptr, ++ unsigned long *old, ++ unsigned long new, ++ int size, ++ unsigned int max_try) ++{ ++ return __int_cmpxchg(ptr, old, new, size, true, max_try); ++} ++ + #define cmpxchg(ptr, o, n) \ + ({ \ + __typeof__(*(ptr)) __ret; \ +-- +2.17.1 + diff --git a/xsa295-4.11-12.patch b/xsa295-4.11-12.patch new file mode 100644 index 0000000..011dd2e --- /dev/null +++ b/xsa295-4.11-12.patch @@ -0,0 +1,30 @@ +From fc01557fe8d60cc9a29d8f29568f940a7873d063 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Tue, 9 Oct 2018 17:33:18 +0800 +Subject: [PATCH v2 4.11 12/20] xen/xsm: remove unnecessary #define + +this #define is unnecessary since XSM_INLINE is redefined in +xsm/dummy.h, it's a risk of build breakage, so remove it. + +Signed-off-by: Xin Li +Reviewed-by: Jan Beulich +Acked-by: Daniel De Graaf +--- + xen/xsm/dummy.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/xen/xsm/dummy.c b/xen/xsm/dummy.c +index 6e751199ee..f371a841c1 100644 +--- a/xen/xsm/dummy.c ++++ b/xen/xsm/dummy.c +@@ -11,7 +11,6 @@ + */ + + #define XSM_NO_WRAPPERS +-#define XSM_INLINE /* */ + #include + + struct xsm_operations dummy_xsm_ops; +-- +2.17.1 + diff --git a/xsa295-4.11-13.patch b/xsa295-4.11-13.patch new file mode 100644 index 0000000..afd1de7 --- /dev/null +++ b/xsa295-4.11-13.patch @@ -0,0 +1,138 @@ +From 046280b23fa40176006b9ab300a46452e166e6ca Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Tue, 9 Oct 2018 17:33:19 +0800 +Subject: [PATCH v2 4.11 13/20] xen/xsm: Introduce new boot parameter xsm + +Introduce new boot parameter xsm to choose which xsm module is enabled, +and set default to dummy. And add new option in Kconfig to choose the +default XSM implementation. + +Signed-off-by: Xin Li +Acked-by: Daniel De Graaf +Acked-by: Andrew Cooper +--- + docs/misc/xen-command-line.markdown | 13 +++++++++ + xen/common/Kconfig | 13 ++++++++- + xen/xsm/xsm_core.c | 44 ++++++++++++++++++++++++++++- + 3 files changed, 68 insertions(+), 2 deletions(-) + +diff --git a/docs/misc/xen-command-line.markdown b/docs/misc/xen-command-line.markdown +index 8108bbf117..1025fb8a6b 100644 +--- a/docs/misc/xen-command-line.markdown ++++ b/docs/misc/xen-command-line.markdown +@@ -865,6 +865,19 @@ hardware domain is architecture dependent. + Note that specifying zero as domU value means zero, while for dom0 it means + to use the default. + ++### xsm ++> `= dummy | flask` ++ ++> Default: `dummy` ++ ++Specify which XSM module should be enabled. This option is only available if ++the hypervisor was compiled with XSM support. ++ ++* `dummy`: this is the default choice. Basic restriction for common deployment ++ (the dummy module) will be applied. It's also used when XSM is compiled out. ++* `flask`: this is the policy based access control. To choose this, the ++ separated option in kconfig must also be enabled. ++ + ### flask + > `= permissive | enforcing | late | disabled` + +diff --git a/xen/common/Kconfig b/xen/common/Kconfig +index 9043dce937..448c2888fe 100644 +--- a/xen/common/Kconfig ++++ b/xen/common/Kconfig +@@ -105,7 +105,7 @@ config XSM + + config FLASK + def_bool y +- prompt "FLux Advanced Security Kernel support" if EXPERT = "y" ++ prompt "FLux Advanced Security Kernel support" + depends on XSM + ---help--- + Enables FLASK (FLux Advanced Security Kernel) as the access control +@@ -143,6 +143,17 @@ config XSM_POLICY + + If unsure, say Y. + ++choice ++ prompt "Default XSM implementation" ++ depends on XSM ++ default XSM_FLASK_DEFAULT if XSM_FLASK ++ default XSM_DUMMY_DEFAULT ++ config XSM_DUMMY_DEFAULT ++ bool "Match non-XSM behavior" ++ config XSM_FLASK_DEFAULT ++ bool "FLux Advanced Security Kernel" if XSM_FLASK ++endchoice ++ + config LATE_HWDOM + bool "Dedicated hardware domain" + default n +diff --git a/xen/xsm/xsm_core.c b/xen/xsm/xsm_core.c +index 949dfcff75..e2f17e4b41 100644 +--- a/xen/xsm/xsm_core.c ++++ b/xen/xsm/xsm_core.c +@@ -31,6 +31,35 @@ + + struct xsm_operations *xsm_ops; + ++enum xsm_bootparam { ++ XSM_BOOTPARAM_DUMMY, ++ XSM_BOOTPARAM_FLASK, ++}; ++ ++static enum xsm_bootparam __initdata xsm_bootparam = ++#ifdef CONFIG_XSM_FLASK_DEFAULT ++ XSM_BOOTPARAM_FLASK; ++#else ++ XSM_BOOTPARAM_DUMMY; ++#endif ++ ++static int __init parse_xsm_param(const char *s) ++{ ++ int rc = 0; ++ ++ if ( !strcmp(s, "dummy") ) ++ xsm_bootparam = XSM_BOOTPARAM_DUMMY; ++#ifdef CONFIG_XSM_FLASK ++ else if ( !strcmp(s, "flask") ) ++ xsm_bootparam = XSM_BOOTPARAM_FLASK; ++#endif ++ else ++ rc = -EINVAL; ++ ++ return rc; ++} ++custom_param("xsm", parse_xsm_param); ++ + static inline int verify(struct xsm_operations *ops) + { + /* verify the security_operations structure exists */ +@@ -57,7 +86,20 @@ static int __init xsm_core_init(const void *policy_buffer, size_t policy_size) + } + + xsm_ops = &dummy_xsm_ops; +- flask_init(policy_buffer, policy_size); ++ ++ switch ( xsm_bootparam ) ++ { ++ case XSM_BOOTPARAM_DUMMY: ++ break; ++ ++ case XSM_BOOTPARAM_FLASK: ++ flask_init(policy_buffer, policy_size); ++ break; ++ ++ default: ++ ASSERT_UNREACHABLE(); ++ break; ++ } + + return 0; + } +-- +2.17.1 + diff --git a/xsa295-4.11-14.patch b/xsa295-4.11-14.patch new file mode 100644 index 0000000..31744e9 --- /dev/null +++ b/xsa295-4.11-14.patch @@ -0,0 +1,280 @@ +From 0b4a5f09333267c6ee57ff1ee99cb9e7667b55df Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Tue, 9 Oct 2018 17:33:20 +0800 +Subject: [PATCH v2 4.11 14/20] xen/xsm: Add new SILO mode for XSM + +When SILO is enabled, there would be no page-sharing or event notifications +between unprivileged VMs (no grant tables or event channels). + +Signed-off-by: Xin Li +Acked-by: Daniel De Graaf +Acked-by: Andrew Cooper +--- + docs/misc/xen-command-line.markdown | 5 +- + xen/common/Kconfig | 15 ++++ + xen/include/xsm/dummy.h | 3 +- + xen/include/xsm/xsm.h | 6 ++ + xen/xsm/Makefile | 1 + + xen/xsm/silo.c | 108 ++++++++++++++++++++++++++++ + xen/xsm/xsm_core.c | 11 +++ + 7 files changed, 147 insertions(+), 2 deletions(-) + create mode 100644 xen/xsm/silo.c + +diff --git a/docs/misc/xen-command-line.markdown b/docs/misc/xen-command-line.markdown +index 1025fb8a6b..c63a07d29b 100644 +--- a/docs/misc/xen-command-line.markdown ++++ b/docs/misc/xen-command-line.markdown +@@ -866,7 +866,7 @@ Note that specifying zero as domU value means zero, while for dom0 it means + to use the default. + + ### xsm +-> `= dummy | flask` ++> `= dummy | flask | silo` + + > Default: `dummy` + +@@ -877,6 +877,9 @@ the hypervisor was compiled with XSM support. + (the dummy module) will be applied. It's also used when XSM is compiled out. + * `flask`: this is the policy based access control. To choose this, the + separated option in kconfig must also be enabled. ++* `silo`: this will deny any unmediated communication channels between ++ unprivileged VMs. To choose this, the separated option in kconfig must also ++ be enabled. + + ### flask + > `= permissive | enforcing | late | disabled` +diff --git a/xen/common/Kconfig b/xen/common/Kconfig +index 448c2888fe..512f6446a3 100644 +--- a/xen/common/Kconfig ++++ b/xen/common/Kconfig +@@ -143,15 +143,30 @@ config XSM_POLICY + + If unsure, say Y. + ++config XSM_SILO ++ def_bool y ++ prompt "SILO support" ++ depends on XSM ++ ---help--- ++ Enables SILO as the access control mechanism used by the XSM framework. ++ This is not the default module, add boot parameter xsm=silo to choose ++ it. This will deny any unmediated communication channels (grant tables ++ and event channels) between unprivileged VMs. ++ ++ If unsure, say Y. ++ + choice + prompt "Default XSM implementation" + depends on XSM + default XSM_FLASK_DEFAULT if XSM_FLASK ++ default XSM_SILO_DEFAULT if XSM_SILO + default XSM_DUMMY_DEFAULT + config XSM_DUMMY_DEFAULT + bool "Match non-XSM behavior" + config XSM_FLASK_DEFAULT + bool "FLux Advanced Security Kernel" if XSM_FLASK ++ config XSM_SILO_DEFAULT ++ bool "SILO" if XSM_SILO + endchoice + + config LATE_HWDOM +diff --git a/xen/include/xsm/dummy.h b/xen/include/xsm/dummy.h +index ff6b2dbf39..a507fa43f5 100644 +--- a/xen/include/xsm/dummy.h ++++ b/xen/include/xsm/dummy.h +@@ -48,7 +48,8 @@ void __xsm_action_mismatch_detected(void); + * There is no xsm_default_t argument available, so the value from the assertion + * is used to initialize the variable. + */ +-#define XSM_INLINE /* */ ++#define XSM_INLINE __maybe_unused ++ + #define XSM_DEFAULT_ARG /* */ + #define XSM_DEFAULT_VOID void + #define XSM_ASSERT_ACTION(def) xsm_default_t action = def; (void)action +diff --git a/xen/include/xsm/xsm.h b/xen/include/xsm/xsm.h +index f0c6fc7e29..b16a1b5b18 100644 +--- a/xen/include/xsm/xsm.h ++++ b/xen/include/xsm/xsm.h +@@ -733,6 +733,12 @@ extern const unsigned char xsm_init_policy[]; + extern const unsigned int xsm_init_policy_size; + #endif + ++#ifdef CONFIG_XSM_SILO ++extern void silo_init(void); ++#else ++static inline void silo_init(void) {} ++#endif ++ + #else /* CONFIG_XSM */ + + #include +diff --git a/xen/xsm/Makefile b/xen/xsm/Makefile +index 3252c46e64..50e21303b3 100644 +--- a/xen/xsm/Makefile ++++ b/xen/xsm/Makefile +@@ -1,5 +1,6 @@ + obj-y += xsm_core.o + obj-$(CONFIG_XSM) += xsm_policy.o + obj-$(CONFIG_XSM) += dummy.o ++obj-$(CONFIG_XSM_SILO) += silo.o + + subdir-$(CONFIG_FLASK) += flask +diff --git a/xen/xsm/silo.c b/xen/xsm/silo.c +new file mode 100644 +index 0000000000..4850756a3d +--- /dev/null ++++ b/xen/xsm/silo.c +@@ -0,0 +1,108 @@ ++/****************************************************************************** ++ * xsm/silo.c ++ * ++ * SILO module for XSM (Xen Security Modules) ++ * ++ * Copyright (c) 2018 Citrix Systems Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; If not, see . ++ */ ++#define XSM_NO_WRAPPERS ++#include ++ ++/* ++ * Check if inter-domain communication is allowed. ++ * Return true when pass check. ++ */ ++static bool silo_mode_dom_check(const struct domain *ldom, ++ const struct domain *rdom) ++{ ++ const struct domain *currd = current->domain; ++ ++ return (is_control_domain(currd) || is_control_domain(ldom) || ++ is_control_domain(rdom) || ldom == rdom); ++} ++ ++static int silo_evtchn_unbound(struct domain *d1, struct evtchn *chn, ++ domid_t id2) ++{ ++ int rc = -EPERM; ++ struct domain *d2 = rcu_lock_domain_by_any_id(id2); ++ ++ if ( d2 == NULL ) ++ rc = -ESRCH; ++ else ++ { ++ if ( silo_mode_dom_check(d1, d2) ) ++ rc = xsm_evtchn_unbound(d1, chn, id2); ++ rcu_unlock_domain(d2); ++ } ++ ++ return rc; ++} ++ ++static int silo_evtchn_interdomain(struct domain *d1, struct evtchn *chan1, ++ struct domain *d2, struct evtchn *chan2) ++{ ++ if ( silo_mode_dom_check(d1, d2) ) ++ return xsm_evtchn_interdomain(d1, chan1, d2, chan2); ++ return -EPERM; ++} ++ ++static int silo_grant_mapref(struct domain *d1, struct domain *d2, ++ uint32_t flags) ++{ ++ if ( silo_mode_dom_check(d1, d2) ) ++ return xsm_grant_mapref(d1, d2, flags); ++ return -EPERM; ++} ++ ++static int silo_grant_transfer(struct domain *d1, struct domain *d2) ++{ ++ if ( silo_mode_dom_check(d1, d2) ) ++ return xsm_grant_transfer(d1, d2); ++ return -EPERM; ++} ++ ++static int silo_grant_copy(struct domain *d1, struct domain *d2) ++{ ++ if ( silo_mode_dom_check(d1, d2) ) ++ return xsm_grant_copy(d1, d2); ++ return -EPERM; ++} ++ ++static struct xsm_operations silo_xsm_ops = { ++ .evtchn_unbound = silo_evtchn_unbound, ++ .evtchn_interdomain = silo_evtchn_interdomain, ++ .grant_mapref = silo_grant_mapref, ++ .grant_transfer = silo_grant_transfer, ++ .grant_copy = silo_grant_copy, ++}; ++ ++void __init silo_init(void) ++{ ++ printk("Initialising XSM SILO mode\n"); ++ ++ if ( register_xsm(&silo_xsm_ops) ) ++ panic("SILO: Unable to register with XSM\n"); ++} ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-file-style: "BSD" ++ * c-basic-offset: 4 ++ * tab-width: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +diff --git a/xen/xsm/xsm_core.c b/xen/xsm/xsm_core.c +index e2f17e4b41..7b862ea79d 100644 +--- a/xen/xsm/xsm_core.c ++++ b/xen/xsm/xsm_core.c +@@ -34,11 +34,14 @@ struct xsm_operations *xsm_ops; + enum xsm_bootparam { + XSM_BOOTPARAM_DUMMY, + XSM_BOOTPARAM_FLASK, ++ XSM_BOOTPARAM_SILO, + }; + + static enum xsm_bootparam __initdata xsm_bootparam = + #ifdef CONFIG_XSM_FLASK_DEFAULT + XSM_BOOTPARAM_FLASK; ++#elif CONFIG_XSM_SILO_DEFAULT ++ XSM_BOOTPARAM_SILO; + #else + XSM_BOOTPARAM_DUMMY; + #endif +@@ -52,6 +55,10 @@ static int __init parse_xsm_param(const char *s) + #ifdef CONFIG_XSM_FLASK + else if ( !strcmp(s, "flask") ) + xsm_bootparam = XSM_BOOTPARAM_FLASK; ++#endif ++#ifdef CONFIG_XSM_SILO ++ else if ( !strcmp(s, "silo") ) ++ xsm_bootparam = XSM_BOOTPARAM_SILO; + #endif + else + rc = -EINVAL; +@@ -96,6 +103,10 @@ static int __init xsm_core_init(const void *policy_buffer, size_t policy_size) + flask_init(policy_buffer, policy_size); + break; + ++ case XSM_BOOTPARAM_SILO: ++ silo_init(); ++ break; ++ + default: + ASSERT_UNREACHABLE(); + break; +-- +2.17.1 + diff --git a/xsa295-4.11-15.patch b/xsa295-4.11-15.patch new file mode 100644 index 0000000..f4c1678 --- /dev/null +++ b/xsa295-4.11-15.patch @@ -0,0 +1,112 @@ +From 7f77f648cec9a30cfdca4222686dc999ff113789 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:25 +0100 +Subject: [PATCH v2 4.11 15/20] xen/arm: Turn on SILO mode by default on Arm + +On Arm, exclusive load-store atomics should only be used between trusted +thread. As not all the guests are trusted, it may be possible to DoS Xen +when updating shared memory with guest atomically. + +Recent patches introduced new helpers to update shared memory with guest +atomically. Those helpers relies on a memory region to be be shared with +Xen and a single guest. + +At the moment, nothing prevent a guest sharing a page with Xen and as +well with another guest (e.g via grant table). + +For the scope of the XSA, the quickest way is to deny communications +between unprivileged guest. So this patch is enabling and using SILO +mode by default on Arm. + +Users wanted finer graine policy could wrote their own Flask policy. + +This is part of XSA-295. + +Signed-off-by: Julien Grall +Acked-by: Jan Beulich +--- + xen/arch/arm/setup.c | 8 ++++++-- + xen/common/Kconfig | 3 ++- + xen/include/xsm/xsm.h | 5 +++++ + xen/xsm/xsm_core.c | 2 +- + 4 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c +index 1d6f6bf37e..ff949f545a 100644 +--- a/xen/arch/arm/setup.c ++++ b/xen/arch/arm/setup.c +@@ -37,6 +37,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -787,8 +788,11 @@ void __init start_xen(unsigned long boot_phys_offset, + + tasklet_subsys_init(); + +- +- xsm_dt_init(); ++ if ( xsm_dt_init() != 1 ) ++ warning_add("WARNING: SILO mode is not enabled.\n" ++ "It has implications on the security of the system,\n" ++ "unless the communications have been forbidden between\n" ++ "untrusted domains.\n"); + + init_maintenance_interrupt(); + init_timer_interrupt(); +diff --git a/xen/common/Kconfig b/xen/common/Kconfig +index 512f6446a3..e4af3f13eb 100644 +--- a/xen/common/Kconfig ++++ b/xen/common/Kconfig +@@ -93,7 +93,7 @@ config XENOPROF + + config XSM + bool "Xen Security Modules support" +- default n ++ default ARM + ---help--- + Enables the security framework known as Xen Security Modules which + allows administrators fine-grained control over a Xen domain and +@@ -158,6 +158,7 @@ config XSM_SILO + choice + prompt "Default XSM implementation" + depends on XSM ++ default XSM_SILO_DEFAULT if XSM_SILO && ARM + default XSM_FLASK_DEFAULT if XSM_FLASK + default XSM_SILO_DEFAULT if XSM_SILO + default XSM_DUMMY_DEFAULT +diff --git a/xen/include/xsm/xsm.h b/xen/include/xsm/xsm.h +index b16a1b5b18..0c803531eb 100644 +--- a/xen/include/xsm/xsm.h ++++ b/xen/include/xsm/xsm.h +@@ -710,6 +710,11 @@ extern int xsm_multiboot_policy_init(unsigned long *module_map, + #endif + + #ifdef CONFIG_HAS_DEVICE_TREE ++/* ++ * Initialize XSM ++ * ++ * On success, return 1 if using SILO mode else 0. ++ */ + extern int xsm_dt_init(void); + extern int xsm_dt_policy_init(void **policy_buffer, size_t *policy_size); + extern bool has_xsm_magic(paddr_t); +diff --git a/xen/xsm/xsm_core.c b/xen/xsm/xsm_core.c +index 7b862ea79d..1179cdf610 100644 +--- a/xen/xsm/xsm_core.c ++++ b/xen/xsm/xsm_core.c +@@ -167,7 +167,7 @@ int __init xsm_dt_init(void) + + xfree(policy_buffer); + +- return ret; ++ return ret ?: (xsm_bootparam == XSM_BOOTPARAM_SILO); + } + + /** +-- +2.17.1 + diff --git a/xsa295-4.11-16.patch b/xsa295-4.11-16.patch new file mode 100644 index 0000000..8acd963 --- /dev/null +++ b/xsa295-4.11-16.patch @@ -0,0 +1,277 @@ +From 63966ce61c86b68592a87353176210ab42d9f8fd Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:26 +0100 +Subject: [PATCH v2 4.11 16/20] xen/bitops: Provide helpers to safely modify + guest memory atomically + +On Arm, exclusive load-store atomics should only be used between trusted +thread. As not all the guests are trusted, it may be possible to DoS Xen +when updating shared memory with guest atomically. + +This patch adds a new set of helper that will update the guest memory +safely. For x86, it is already possible to use the current helpers +safely. So just wrap them. + +For Arm, we will first attempt to update the guest memory with the loop +bounded by a maximum number of iterations. If it fails, we will pause the +domain and try again. + +Note that this heuristics assumes that a page can only be shared between +Xen and one domain. Not Xen and multiple domain. + +The maximum number of iterations is based on how many times a simple +load-store atomic operation can be executed in 1uS. The maximum value is +per-CPU to cater big.LITTLE and calculated when the CPU is booting. The +heuristic was randomly chosen and can be modified if impact too much +good-behaving guest. + +Note, while test_bit does not requires to use atomic operation, a +wrapper for test_bit was added for completeness. In this case, the +domain stays constified to avoid major rework in the caller for the +time-being. + +This is part of XSA-295. + +Signed-of-by: Julien Grall +Acked-by: Jan Beulich +Reviewed-by: Stefano Stabellini +--- + xen/arch/arm/Makefile | 1 + + xen/arch/arm/guest_atomics.c | 91 +++++++++++++++++++++++++++++ + xen/include/asm-arm/guest_atomics.h | 76 ++++++++++++++++++++++++ + xen/include/asm-x86/guest_atomics.h | 30 ++++++++++ + 4 files changed, 198 insertions(+) + create mode 100644 xen/arch/arm/guest_atomics.c + create mode 100644 xen/include/asm-arm/guest_atomics.h + create mode 100644 xen/include/asm-x86/guest_atomics.h + +diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile +index a9533b107e..3559e3ac46 100644 +--- a/xen/arch/arm/Makefile ++++ b/xen/arch/arm/Makefile +@@ -21,6 +21,7 @@ obj-$(CONFIG_HAS_GICV3) += gic-v3.o + obj-$(CONFIG_HAS_ITS) += gic-v3-its.o + obj-$(CONFIG_HAS_ITS) += gic-v3-lpi.o + obj-y += guestcopy.o ++obj-y += guest_atomics.o + obj-y += guest_walk.o + obj-y += hvm.o + obj-y += io.o +diff --git a/xen/arch/arm/guest_atomics.c b/xen/arch/arm/guest_atomics.c +new file mode 100644 +index 0000000000..1b78a062f0 +--- /dev/null ++++ b/xen/arch/arm/guest_atomics.c +@@ -0,0 +1,91 @@ ++/* ++ * arch/arm/guest_atomics.c ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; If not, see . ++ */ ++#include ++ ++#include ++ ++DEFINE_PER_CPU_READ_MOSTLY(unsigned int, guest_safe_atomic_max); ++ ++/* ++ * Heuristic to find a safe upper-limit for load-store exclusive ++ * operations on memory shared with guest. ++ * ++ * At the moment, we calculate the number of iterations of a simple ++ * load-store atomic loop in 1uS. ++ */ ++static void calibrate_safe_atomic(void) ++{ ++ s_time_t deadline = NOW() + MICROSECS(1); ++ unsigned int counter = 0; ++ unsigned long mem = 0; ++ ++ do ++ { ++ unsigned long res, tmp; ++ ++#ifdef CONFIG_ARM_32 ++ asm volatile (" ldrex %2, %1\n" ++ " add %2, %2, #1\n" ++ " strex %0, %2, %1\n" ++ : "=&r" (res), "+Q" (mem), "=&r" (tmp)); ++#else ++ asm volatile (" ldxr %w2, %1\n" ++ " add %w2, %w2, #1\n" ++ " stxr %w0, %w2, %1\n" ++ : "=&r" (res), "+Q" (mem), "=&r" (tmp)); ++#endif ++ counter++; ++ } while (NOW() < deadline); ++ ++ this_cpu(guest_safe_atomic_max) = counter; ++ ++ printk(XENLOG_DEBUG ++ "CPU%u: Guest atomics will try %u times before pausing the domain\n", ++ smp_processor_id(), counter); ++} ++ ++static int cpu_guest_safe_atomic_callback(struct notifier_block *nfb, ++ unsigned long action, ++ void *hcpu) ++{ ++ if ( action == CPU_STARTING ) ++ calibrate_safe_atomic(); ++ ++ return NOTIFY_DONE; ++} ++ ++static struct notifier_block cpu_guest_safe_atomic_nfb = { ++ .notifier_call = cpu_guest_safe_atomic_callback, ++}; ++ ++static int __init guest_safe_atomic_init(void) ++{ ++ register_cpu_notifier(&cpu_guest_safe_atomic_nfb); ++ ++ calibrate_safe_atomic(); ++ ++ return 0; ++} ++presmp_initcall(guest_safe_atomic_init); ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-file-style: "BSD" ++ * c-basic-offset: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +diff --git a/xen/include/asm-arm/guest_atomics.h b/xen/include/asm-arm/guest_atomics.h +new file mode 100644 +index 0000000000..4f127fda41 +--- /dev/null ++++ b/xen/include/asm-arm/guest_atomics.h +@@ -0,0 +1,76 @@ ++#ifndef _ARM_GUEST_ATOMICS_H ++#define _ARM_GUEST_ATOMICS_H ++ ++#include ++#include ++ ++/* ++ * The guest atomics helpers shares the same logic. We first try to use ++ * the *_timeout version of the operation. If it didn't timeout, then we ++ * successfully updated the memory. Nothing else to do. ++ * ++ * If it did timeout, then it means we didn't manage to update the ++ * memory. This is possibly because the guest is misbehaving (i.e tight ++ * store loop) but can also happen for other reasons (i.e nested Xen). ++ * In that case pause the domain and retry the operation, this time ++ * without a timeout. ++ * ++ * Note, those helpers rely on other part of the code to prevent sharing ++ * a page between Xen and multiple domain. ++ */ ++ ++DECLARE_PER_CPU(unsigned int, guest_safe_atomic_max); ++ ++#define guest_bitop(name) \ ++static inline void guest_##name(struct domain *d, int nr, volatile void *p) \ ++{ \ ++ if ( name##_timeout(nr, p, this_cpu(guest_safe_atomic_max)) ) \ ++ return; \ ++ \ ++ domain_pause_nosync(d); \ ++ name(nr, p); \ ++ domain_unpause(d); \ ++} ++ ++#define guest_testop(name) \ ++static inline int guest_##name(struct domain *d, int nr, volatile void *p) \ ++{ \ ++ bool succeed; \ ++ int oldbit; \ ++ \ ++ succeed = name##_timeout(nr, p, &oldbit, \ ++ this_cpu(guest_safe_atomic_max)); \ ++ if ( succeed ) \ ++ return oldbit; \ ++ \ ++ domain_pause_nosync(d); \ ++ oldbit = name(nr, p); \ ++ domain_unpause(d); \ ++ \ ++ return oldbit; \ ++} ++ ++guest_bitop(set_bit) ++guest_bitop(clear_bit) ++guest_bitop(change_bit) ++ ++#undef guest_bitop ++ ++/* test_bit does not use load-store atomic operations */ ++#define guest_test_bit(d, nr, p) ((void)(d), test_bit(nr, p)) ++ ++guest_testop(test_and_set_bit) ++guest_testop(test_and_clear_bit) ++guest_testop(test_and_change_bit) ++ ++#undef guest_testop ++ ++#endif /* _ARM_GUEST_ATOMICS_H */ ++/* ++ * Local variables: ++ * mode: C ++ * c-file-style: "BSD" ++ * c-basic-offset: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +diff --git a/xen/include/asm-x86/guest_atomics.h b/xen/include/asm-x86/guest_atomics.h +new file mode 100644 +index 0000000000..0c71d2d278 +--- /dev/null ++++ b/xen/include/asm-x86/guest_atomics.h +@@ -0,0 +1,30 @@ ++#ifndef _X86_GUEST_ATOMICS_H ++#define _X86_GUEST_ATOMICS_H ++ ++#include ++ ++/* ++ * It is safe to use the atomics helpers on x86 on memory shared with ++ * the guests. ++ */ ++#define guest_set_bit(d, nr, p) ((void)(d), set_bit(nr, p)) ++#define guest_clear_bit(d, nr, p) ((void)(d), clear_bit(nr, p)) ++#define guest_change_bit(d, nr, p) ((void)(d), change_bit(nr, p)) ++#define guest_test_bit(d, nr, p) ((void)(d), test_bit(nr, p)) ++ ++#define guest_test_and_set_bit(d, nr, p) \ ++ ((void)(d), test_and_set_bit(nr, p)) ++#define guest_test_and_clear_bit(d, nr, p) \ ++ ((void)(d), test_and_clear_bit(nr, p)) ++#define guest_test_and_change_bit(d, nr, p) \ ++ ((void)(d), test_and_change_bit(nr, p)) ++ ++#endif /* _X86_GUEST_ATOMICS_H */ ++/* ++ * Local variables: ++ * mode: C ++ * c-file-style: "BSD" ++ * c-basic-offset: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +-- +2.17.1 + diff --git a/xsa295-4.11-17.patch b/xsa295-4.11-17.patch new file mode 100644 index 0000000..f3db6ea --- /dev/null +++ b/xsa295-4.11-17.patch @@ -0,0 +1,93 @@ +From d410b20098cfceb17eff3c12031e528542569197 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:27 +0100 +Subject: [PATCH v2 4.11 17/20] xen/cmpxchg: Provide helper to safely modify + guest memory atomically + +On Arm, exclusive load-store atomics should only be used between trusted +thread. As not all the guests are trusted, it may be possible to DoS Xen +when updating shared memory with guest atomically. + +This patch adds a new helper that will update the guest memory safely. +For x86, it is already possible to use the current helper safely. So +just wrap it. + +For Arm, we will first attempt to update the guest memory with the +loop bounded by a maximum number of iterations. If it fails, we will +pause the domain and try again. + +Note that this heuristics assumes that a page can only +be shared between Xen and one domain. Not Xen and multiple domain. + +The maximum number of iterations is based on how many times atomic_inc() +can be executed in 1uS. The maximum value is per-CPU to cater big.LITTLE +and calculated when the CPU is booting. + +The maximum number of iterations is based on how many times a simple +load-store atomic operation can be executed in 1uS. The maximum +value is per-CPU to cater big.LITTLE and calculated when the CPU is +booting. The heuristic was randomly chosen and can be modified if +impact too much good-behaving guest. + +This is part of XSA-295. + +Signed-of-by: Julien Grall +Reviewed-by: Stefano Stabellini +Acked-by: Jan Beulich +--- + xen/include/asm-arm/guest_atomics.h | 25 +++++++++++++++++++++++++ + xen/include/asm-x86/guest_atomics.h | 2 ++ + 2 files changed, 27 insertions(+) + +diff --git a/xen/include/asm-arm/guest_atomics.h b/xen/include/asm-arm/guest_atomics.h +index 4f127fda41..61925d313d 100644 +--- a/xen/include/asm-arm/guest_atomics.h ++++ b/xen/include/asm-arm/guest_atomics.h +@@ -65,6 +65,31 @@ guest_testop(test_and_change_bit) + + #undef guest_testop + ++static inline unsigned long __guest_cmpxchg(struct domain *d, ++ volatile void *ptr, ++ unsigned long old, ++ unsigned long new, ++ unsigned int size) ++{ ++ unsigned long oldval = old; ++ ++ if ( __cmpxchg_mb_timeout(ptr, &oldval, new, size, ++ this_cpu(guest_safe_atomic_max)) ) ++ return oldval; ++ ++ domain_pause_nosync(d); ++ oldval = __cmpxchg_mb(ptr, old, new, size); ++ domain_unpause(d); ++ ++ return oldval; ++} ++ ++#define guest_cmpxchg(d, ptr, o, n) \ ++ ((__typeof__(*(ptr)))__guest_cmpxchg(d, ptr, \ ++ (unsigned long)(o),\ ++ (unsigned long)(n),\ ++ sizeof (*(ptr)))) ++ + #endif /* _ARM_GUEST_ATOMICS_H */ + /* + * Local variables: +diff --git a/xen/include/asm-x86/guest_atomics.h b/xen/include/asm-x86/guest_atomics.h +index 0c71d2d278..029417c8ff 100644 +--- a/xen/include/asm-x86/guest_atomics.h ++++ b/xen/include/asm-x86/guest_atomics.h +@@ -19,6 +19,8 @@ + #define guest_test_and_change_bit(d, nr, p) \ + ((void)(d), test_and_change_bit(nr, p)) + ++#define guest_cmpxchg(d, ptr, o, n) ((void)(d), cmpxchg(ptr, o, n)) ++ + #endif /* _X86_GUEST_ATOMICS_H */ + /* + * Local variables: +-- +2.17.1 + diff --git a/xsa295-4.11-18.patch b/xsa295-4.11-18.patch new file mode 100644 index 0000000..4babc55 --- /dev/null +++ b/xsa295-4.11-18.patch @@ -0,0 +1,577 @@ +From 0689672212e95d65d2dd574c1fb620d3b88073cc Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:28 +0100 +Subject: [PATCH v2 4.11 18/20] xen: Use guest atomics helpers when modifying + atomically guest memory + +On Arm, exclusive load-store atomics should only be used between trusted +thread. As not all the guests are trusted, it may be possible to DoS Xen +when updating shared memory with guest atomically. + +This patch replaces all the atomics operations on shared memory with +a guest by the new guest atomics helpers. The x86 code was not audited +to know where guest atomics helpers could be used. I will leave that +to the x86 folks. + +Note that some rework was required in order to plumb use the new guest +atomics in event channel and grant-table. + +Because guest_test_bit is ignoring the parameter "d" for now, it +means there a lot of places do not need to drop the const. We may want +to revisit this in the future if the parameter "d" becomes necessary. + +This is part of XSA-295. + +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +--- + xen/arch/arm/domain.c | 3 +- + xen/arch/arm/mm.c | 6 ++-- + xen/common/event_2l.c | 26 ++++++++------ + xen/common/event_fifo.c | 44 ++++++++++++----------- + xen/common/grant_table.c | 59 +++++++++++++++++-------------- + xen/include/asm-arm/grant_table.h | 2 +- + xen/include/asm-x86/grant_table.h | 3 +- + 7 files changed, 79 insertions(+), 64 deletions(-) + +diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c +index ec0f042bf7..112a146fd9 100644 +--- a/xen/arch/arm/domain.c ++++ b/xen/arch/arm/domain.c +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -961,7 +962,7 @@ void arch_dump_vcpu_info(struct vcpu *v) + + void vcpu_mark_events_pending(struct vcpu *v) + { +- int already_pending = test_and_set_bit( ++ bool already_pending = guest_test_and_set_bit(v->domain, + 0, (unsigned long *)&vcpu_info(v, evtchn_upcall_pending)); + + if ( already_pending ) +diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c +index a6de77c28c..6b7e6b675b 100644 +--- a/xen/arch/arm/mm.c ++++ b/xen/arch/arm/mm.c +@@ -40,6 +40,8 @@ + #include + #include + #include ++ ++#include + #include + + struct domain *dom_xen, *dom_io, *dom_cow; +@@ -1395,7 +1397,7 @@ void put_page_type(struct page_info *page) + return; + } + +-void gnttab_clear_flag(unsigned long nr, uint16_t *addr) ++void gnttab_clear_flag(struct domain *d, unsigned long nr, uint16_t *addr) + { + /* + * Note that this cannot be clear_bit(), as the access must be +@@ -1405,7 +1407,7 @@ void gnttab_clear_flag(unsigned long nr, uint16_t *addr) + + do { + old = *addr; +- } while (cmpxchg(addr, old, old & mask) != old); ++ } while (guest_cmpxchg(d, addr, old, old & mask) != old); + } + + void gnttab_mark_dirty(struct domain *d, mfn_t mfn) +diff --git a/xen/common/event_2l.c b/xen/common/event_2l.c +index 8ca90899ab..e1dbb860f4 100644 +--- a/xen/common/event_2l.c ++++ b/xen/common/event_2l.c +@@ -13,6 +13,8 @@ + #include + #include + ++#include ++ + static void evtchn_2l_set_pending(struct vcpu *v, struct evtchn *evtchn) + { + struct domain *d = v->domain; +@@ -25,12 +27,12 @@ static void evtchn_2l_set_pending(struct vcpu *v, struct evtchn *evtchn) + * others may require explicit memory barriers. + */ + +- if ( test_and_set_bit(port, &shared_info(d, evtchn_pending)) ) ++ if ( guest_test_and_set_bit(d, port, &shared_info(d, evtchn_pending)) ) + return; + +- if ( !test_bit (port, &shared_info(d, evtchn_mask)) && +- !test_and_set_bit(port / BITS_PER_EVTCHN_WORD(d), +- &vcpu_info(v, evtchn_pending_sel)) ) ++ if ( !guest_test_bit(d, port, &shared_info(d, evtchn_mask)) && ++ !guest_test_and_set_bit(d, port / BITS_PER_EVTCHN_WORD(d), ++ &vcpu_info(v, evtchn_pending_sel)) ) + { + vcpu_mark_events_pending(v); + } +@@ -40,7 +42,7 @@ static void evtchn_2l_set_pending(struct vcpu *v, struct evtchn *evtchn) + + static void evtchn_2l_clear_pending(struct domain *d, struct evtchn *evtchn) + { +- clear_bit(evtchn->port, &shared_info(d, evtchn_pending)); ++ guest_clear_bit(d, evtchn->port, &shared_info(d, evtchn_pending)); + } + + static void evtchn_2l_unmask(struct domain *d, struct evtchn *evtchn) +@@ -52,10 +54,10 @@ static void evtchn_2l_unmask(struct domain *d, struct evtchn *evtchn) + * These operations must happen in strict order. Based on + * evtchn_2l_set_pending() above. + */ +- if ( test_and_clear_bit(port, &shared_info(d, evtchn_mask)) && +- test_bit (port, &shared_info(d, evtchn_pending)) && +- !test_and_set_bit (port / BITS_PER_EVTCHN_WORD(d), +- &vcpu_info(v, evtchn_pending_sel)) ) ++ if ( guest_test_and_clear_bit(d, port, &shared_info(d, evtchn_mask)) && ++ guest_test_bit(d, port, &shared_info(d, evtchn_pending)) && ++ !guest_test_and_set_bit(d, port / BITS_PER_EVTCHN_WORD(d), ++ &vcpu_info(v, evtchn_pending_sel)) ) + { + vcpu_mark_events_pending(v); + } +@@ -66,7 +68,8 @@ static bool evtchn_2l_is_pending(const struct domain *d, evtchn_port_t port) + unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); + + ASSERT(port < max_ports); +- return port < max_ports && test_bit(port, &shared_info(d, evtchn_pending)); ++ return (port < max_ports && ++ guest_test_bit(d, port, &shared_info(d, evtchn_pending))); + } + + static bool evtchn_2l_is_masked(const struct domain *d, evtchn_port_t port) +@@ -74,7 +77,8 @@ static bool evtchn_2l_is_masked(const struct domain *d, evtchn_port_t port) + unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); + + ASSERT(port < max_ports); +- return port >= max_ports || test_bit(port, &shared_info(d, evtchn_mask)); ++ return (port >= max_ports || ++ guest_test_bit(d, port, &shared_info(d, evtchn_mask))); + } + + static void evtchn_2l_print_state(struct domain *d, +diff --git a/xen/common/event_fifo.c b/xen/common/event_fifo.c +index c49f446754..3f4c835518 100644 +--- a/xen/common/event_fifo.c ++++ b/xen/common/event_fifo.c +@@ -17,6 +17,8 @@ + #include + #include + ++#include ++ + #include + + static inline event_word_t *evtchn_fifo_word_from_port(const struct domain *d, +@@ -50,7 +52,7 @@ static void evtchn_fifo_init(struct domain *d, struct evtchn *evtchn) + * on the wrong VCPU or with an unexpected priority. + */ + word = evtchn_fifo_word_from_port(d, evtchn->port); +- if ( word && test_bit(EVTCHN_FIFO_LINKED, word) ) ++ if ( word && guest_test_bit(d, EVTCHN_FIFO_LINKED, word) ) + gdprintk(XENLOG_WARNING, "domain %d, port %d already on a queue\n", + d->domain_id, evtchn->port); + } +@@ -115,7 +117,7 @@ static int try_set_link(event_word_t *word, event_word_t *w, uint32_t link) + * We block unmasking by the guest by marking the tail word as BUSY, + * therefore, the cmpxchg() may fail at most 4 times. + */ +-static bool_t evtchn_fifo_set_link(const struct domain *d, event_word_t *word, ++static bool_t evtchn_fifo_set_link(struct domain *d, event_word_t *word, + uint32_t link) + { + event_word_t w; +@@ -129,7 +131,7 @@ static bool_t evtchn_fifo_set_link(const struct domain *d, event_word_t *word, + return ret; + + /* Lock the word to prevent guest unmasking. */ +- set_bit(EVTCHN_FIFO_BUSY, word); ++ guest_set_bit(d, EVTCHN_FIFO_BUSY, word); + + w = read_atomic(word); + +@@ -139,13 +141,13 @@ static bool_t evtchn_fifo_set_link(const struct domain *d, event_word_t *word, + if ( ret >= 0 ) + { + if ( ret == 0 ) +- clear_bit(EVTCHN_FIFO_BUSY, word); ++ guest_clear_bit(d, EVTCHN_FIFO_BUSY, word); + return ret; + } + } + gdprintk(XENLOG_WARNING, "domain %d, port %d not linked\n", + d->domain_id, link); +- clear_bit(EVTCHN_FIFO_BUSY, word); ++ guest_clear_bit(d, EVTCHN_FIFO_BUSY, word); + return 1; + } + +@@ -170,13 +172,13 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) + return; + } + +- was_pending = test_and_set_bit(EVTCHN_FIFO_PENDING, word); ++ was_pending = guest_test_and_set_bit(d, EVTCHN_FIFO_PENDING, word); + + /* + * Link the event if it unmasked and not already linked. + */ +- if ( !test_bit(EVTCHN_FIFO_MASKED, word) +- && !test_bit(EVTCHN_FIFO_LINKED, word) ) ++ if ( !guest_test_bit(d, EVTCHN_FIFO_MASKED, word) && ++ !guest_test_bit(d, EVTCHN_FIFO_LINKED, word) ) + { + struct evtchn_fifo_queue *q, *old_q; + event_word_t *tail_word; +@@ -205,7 +207,7 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) + if ( !old_q ) + goto done; + +- if ( test_and_set_bit(EVTCHN_FIFO_LINKED, word) ) ++ if ( guest_test_and_set_bit(d, EVTCHN_FIFO_LINKED, word) ) + { + spin_unlock_irqrestore(&old_q->lock, flags); + goto done; +@@ -251,8 +253,8 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) + spin_unlock_irqrestore(&q->lock, flags); + + if ( !linked +- && !test_and_set_bit(q->priority, +- &v->evtchn_fifo->control_block->ready) ) ++ && !guest_test_and_set_bit(d, q->priority, ++ &v->evtchn_fifo->control_block->ready) ) + vcpu_mark_events_pending(v); + } + done: +@@ -274,7 +276,7 @@ static void evtchn_fifo_clear_pending(struct domain *d, struct evtchn *evtchn) + * No need to unlink as the guest will unlink and ignore + * non-pending events. + */ +- clear_bit(EVTCHN_FIFO_PENDING, word); ++ guest_clear_bit(d, EVTCHN_FIFO_PENDING, word); + } + + static void evtchn_fifo_unmask(struct domain *d, struct evtchn *evtchn) +@@ -286,10 +288,10 @@ static void evtchn_fifo_unmask(struct domain *d, struct evtchn *evtchn) + if ( unlikely(!word) ) + return; + +- clear_bit(EVTCHN_FIFO_MASKED, word); ++ guest_clear_bit(d, EVTCHN_FIFO_MASKED, word); + + /* Relink if pending. */ +- if ( test_bit(EVTCHN_FIFO_PENDING, word) ) ++ if ( guest_test_bit(d, EVTCHN_FIFO_PENDING, word) ) + evtchn_fifo_set_pending(v, evtchn); + } + +@@ -297,21 +299,21 @@ static bool evtchn_fifo_is_pending(const struct domain *d, evtchn_port_t port) + { + const event_word_t *word = evtchn_fifo_word_from_port(d, port); + +- return word && test_bit(EVTCHN_FIFO_PENDING, word); ++ return word && guest_test_bit(d, EVTCHN_FIFO_PENDING, word); + } + + static bool_t evtchn_fifo_is_masked(const struct domain *d, evtchn_port_t port) + { + const event_word_t *word = evtchn_fifo_word_from_port(d, port); + +- return !word || test_bit(EVTCHN_FIFO_MASKED, word); ++ return !word || guest_test_bit(d, EVTCHN_FIFO_MASKED, word); + } + + static bool_t evtchn_fifo_is_busy(const struct domain *d, evtchn_port_t port) + { + const event_word_t *word = evtchn_fifo_word_from_port(d, port); + +- return word && test_bit(EVTCHN_FIFO_LINKED, word); ++ return word && guest_test_bit(d, EVTCHN_FIFO_LINKED, word); + } + + static int evtchn_fifo_set_priority(struct domain *d, struct evtchn *evtchn, +@@ -338,11 +340,11 @@ static void evtchn_fifo_print_state(struct domain *d, + word = evtchn_fifo_word_from_port(d, evtchn->port); + if ( !word ) + printk("? "); +- else if ( test_bit(EVTCHN_FIFO_LINKED, word) ) +- printk("%c %-4u", test_bit(EVTCHN_FIFO_BUSY, word) ? 'B' : ' ', ++ else if ( guest_test_bit(d, EVTCHN_FIFO_LINKED, word) ) ++ printk("%c %-4u", guest_test_bit(d, EVTCHN_FIFO_BUSY, word) ? 'B' : ' ', + *word & EVTCHN_FIFO_LINK_MASK); + else +- printk("%c - ", test_bit(EVTCHN_FIFO_BUSY, word) ? 'B' : ' '); ++ printk("%c - ", guest_test_bit(d, EVTCHN_FIFO_BUSY, word) ? 'B' : ' '); + } + + static const struct evtchn_port_ops evtchn_port_ops_fifo = +@@ -494,7 +496,7 @@ static void setup_ports(struct domain *d) + + evtchn = evtchn_from_port(d, port); + +- if ( test_bit(port, &shared_info(d, evtchn_pending)) ) ++ if ( guest_test_bit(d, port, &shared_info(d, evtchn_pending)) ) + evtchn->pending = 1; + + evtchn_fifo_set_priority(d, evtchn, EVTCHN_FIFO_PRIORITY_DEFAULT); +diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c +index c3a806fe47..221563a232 100644 +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -39,6 +39,7 @@ + #include + #include + #include ++#include + + /* Per-domain grant information. */ + struct grant_table { +@@ -653,6 +654,7 @@ static unsigned int nr_grant_entries(struct grant_table *gt) + } + + static int _set_status_v1(const grant_entry_header_t *shah, ++ struct domain *rd, + struct active_grant_entry *act, + int readonly, + int mapflag, +@@ -708,8 +710,8 @@ static int _set_status_v1(const grant_entry_header_t *shah, + "Attempt to write-pin a r/o grant entry\n"); + } + +- prev_scombo.word = cmpxchg((u32 *)shah, +- scombo.word, new_scombo.word); ++ prev_scombo.word = guest_cmpxchg(rd, (u32 *)shah, ++ scombo.word, new_scombo.word); + if ( likely(prev_scombo.word == scombo.word) ) + break; + +@@ -726,6 +728,7 @@ done: + + static int _set_status_v2(const grant_entry_header_t *shah, + grant_status_t *status, ++ struct domain *rd, + struct active_grant_entry *act, + int readonly, + int mapflag, +@@ -788,8 +791,8 @@ static int _set_status_v2(const grant_entry_header_t *shah, + (id != ldomid) || + (!readonly && (flags & GTF_readonly)) ) + { +- gnttab_clear_flag(_GTF_writing, status); +- gnttab_clear_flag(_GTF_reading, status); ++ gnttab_clear_flag(rd, _GTF_writing, status); ++ gnttab_clear_flag(rd, _GTF_reading, status); + PIN_FAIL(done, GNTST_general_error, + "Unstable flags (%x) or dom (%d); expected d%d (r/w: %d)\n", + flags, id, ldomid, !readonly); +@@ -799,7 +802,7 @@ static int _set_status_v2(const grant_entry_header_t *shah, + { + if ( unlikely(flags & GTF_readonly) ) + { +- gnttab_clear_flag(_GTF_writing, status); ++ gnttab_clear_flag(rd, _GTF_writing, status); + PIN_FAIL(done, GNTST_general_error, + "Unstable grant readonly flag\n"); + } +@@ -812,6 +815,7 @@ done: + + static int _set_status(const grant_entry_header_t *shah, + grant_status_t *status, ++ struct domain *rd, + unsigned rgt_version, + struct active_grant_entry *act, + int readonly, +@@ -820,9 +824,9 @@ static int _set_status(const grant_entry_header_t *shah, + { + + if ( rgt_version == 1 ) +- return _set_status_v1(shah, act, readonly, mapflag, ldomid); ++ return _set_status_v1(shah, rd, act, readonly, mapflag, ldomid); + else +- return _set_status_v2(shah, status, act, readonly, mapflag, ldomid); ++ return _set_status_v2(shah, status, rd, act, readonly, mapflag, ldomid); + } + + static struct active_grant_entry *grant_map_exists(const struct domain *ld, +@@ -994,7 +998,7 @@ map_grant_ref( + (!(op->flags & GNTMAP_readonly) && + !(act->pin & (GNTPIN_hstw_mask|GNTPIN_devw_mask))) ) + { +- if ( (rc = _set_status(shah, status, rgt->gt_version, act, ++ if ( (rc = _set_status(shah, status, rd, rgt->gt_version, act, + op->flags & GNTMAP_readonly, 1, + ld->domain_id) != GNTST_okay) ) + goto act_release_out; +@@ -1218,10 +1222,10 @@ map_grant_ref( + unlock_out_clear: + if ( !(op->flags & GNTMAP_readonly) && + !(act->pin & (GNTPIN_hstw_mask|GNTPIN_devw_mask)) ) +- gnttab_clear_flag(_GTF_writing, status); ++ gnttab_clear_flag(rd, _GTF_writing, status); + + if ( !act->pin ) +- gnttab_clear_flag(_GTF_reading, status); ++ gnttab_clear_flag(rd, _GTF_reading, status); + + act_release_out: + active_entry_release(act); +@@ -1505,10 +1509,10 @@ unmap_common_complete(struct gnttab_unmap_common *op) + + if ( ((act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)) == 0) && + !(op->done & GNTMAP_readonly) ) +- gnttab_clear_flag(_GTF_writing, status); ++ gnttab_clear_flag(rd, _GTF_writing, status); + + if ( act->pin == 0 ) +- gnttab_clear_flag(_GTF_reading, status); ++ gnttab_clear_flag(rd, _GTF_reading, status); + + active_entry_release(act); + grant_read_unlock(rgt); +@@ -2073,8 +2077,8 @@ gnttab_prepare_for_transfer( + new_scombo = scombo; + new_scombo.shorts.flags |= GTF_transfer_committed; + +- prev_scombo.word = cmpxchg((u32 *)&sha->flags, +- scombo.word, new_scombo.word); ++ prev_scombo.word = guest_cmpxchg(rd, (u32 *)&sha->flags, ++ scombo.word, new_scombo.word); + if ( likely(prev_scombo.word == scombo.word) ) + break; + +@@ -2359,11 +2363,11 @@ release_grant_for_copy( + + act->pin -= GNTPIN_hstw_inc; + if ( !(act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)) ) +- gnttab_clear_flag(_GTF_writing, status); ++ gnttab_clear_flag(rd, _GTF_writing, status); + } + + if ( !act->pin ) +- gnttab_clear_flag(_GTF_reading, status); ++ gnttab_clear_flag(rd, _GTF_reading, status); + + active_entry_release(act); + grant_read_unlock(rgt); +@@ -2385,14 +2389,15 @@ release_grant_for_copy( + under the domain's grant table lock. */ + /* Only safe on transitive grants. Even then, note that we don't + attempt to drop any pin on the referent grant. */ +-static void fixup_status_for_copy_pin(const struct active_grant_entry *act, ++static void fixup_status_for_copy_pin(struct domain *rd, ++ const struct active_grant_entry *act, + uint16_t *status) + { + if ( !(act->pin & (GNTPIN_hstw_mask | GNTPIN_devw_mask)) ) +- gnttab_clear_flag(_GTF_writing, status); ++ gnttab_clear_flag(rd, _GTF_writing, status); + + if ( !act->pin ) +- gnttab_clear_flag(_GTF_reading, status); ++ gnttab_clear_flag(rd, _GTF_reading, status); + } + + /* Grab a frame number from a grant entry and update the flags and pin +@@ -2452,7 +2457,7 @@ acquire_grant_for_copy( + { + if ( (!old_pin || (!readonly && + !(old_pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)))) && +- (rc = _set_status_v2(shah, status, act, readonly, 0, ++ (rc = _set_status_v2(shah, status, rd, act, readonly, 0, + ldom)) != GNTST_okay ) + goto unlock_out; + +@@ -2501,7 +2506,7 @@ acquire_grant_for_copy( + + if ( rc != GNTST_okay ) + { +- fixup_status_for_copy_pin(act, status); ++ fixup_status_for_copy_pin(rd, act, status); + rcu_unlock_domain(td); + active_entry_release(act); + grant_read_unlock(rgt); +@@ -2524,7 +2529,7 @@ acquire_grant_for_copy( + !act->is_sub_page)) ) + { + release_grant_for_copy(td, trans_gref, readonly); +- fixup_status_for_copy_pin(act, status); ++ fixup_status_for_copy_pin(rd, act, status); + rcu_unlock_domain(td); + active_entry_release(act); + grant_read_unlock(rgt); +@@ -2553,7 +2558,7 @@ acquire_grant_for_copy( + else if ( !old_pin || + (!readonly && !(old_pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask))) ) + { +- if ( (rc = _set_status(shah, status, rgt->gt_version, act, ++ if ( (rc = _set_status(shah, status, rd, rgt->gt_version, act, + readonly, 0, ldom)) != GNTST_okay ) + goto unlock_out; + +@@ -2641,10 +2646,10 @@ acquire_grant_for_copy( + unlock_out_clear: + if ( !(readonly) && + !(act->pin & (GNTPIN_hstw_mask | GNTPIN_devw_mask)) ) +- gnttab_clear_flag(_GTF_writing, status); ++ gnttab_clear_flag(rd, _GTF_writing, status); + + if ( !act->pin ) +- gnttab_clear_flag(_GTF_reading, status); ++ gnttab_clear_flag(rd, _GTF_reading, status); + + unlock_out: + active_entry_release(act); +@@ -3712,11 +3717,11 @@ gnttab_release_mappings( + } + + if ( (act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)) == 0 ) +- gnttab_clear_flag(_GTF_writing, status); ++ gnttab_clear_flag(rd, _GTF_writing, status); + } + + if ( act->pin == 0 ) +- gnttab_clear_flag(_GTF_reading, status); ++ gnttab_clear_flag(rd, _GTF_reading, status); + + active_entry_release(act); + grant_read_unlock(rgt); +diff --git a/xen/include/asm-arm/grant_table.h b/xen/include/asm-arm/grant_table.h +index 24958e4670..8ae8ba47a1 100644 +--- a/xen/include/asm-arm/grant_table.h ++++ b/xen/include/asm-arm/grant_table.h +@@ -14,7 +14,7 @@ struct grant_table_arch { + gfn_t *status_gfn; + }; + +-void gnttab_clear_flag(unsigned long nr, uint16_t *addr); ++void gnttab_clear_flag(struct domain *d, unsigned long nr, uint16_t *addr); + int create_grant_host_mapping(unsigned long gpaddr, mfn_t mfn, + unsigned int flags, unsigned int cache_flags); + #define gnttab_host_mapping_get_page_type(ro, ld, rd) (0) +diff --git a/xen/include/asm-x86/grant_table.h b/xen/include/asm-x86/grant_table.h +index e42030936b..2a54fc8e1d 100644 +--- a/xen/include/asm-x86/grant_table.h ++++ b/xen/include/asm-x86/grant_table.h +@@ -82,7 +82,8 @@ static inline unsigned int gnttab_dom0_max(void) + + #define gnttab_mark_dirty(d, f) paging_mark_dirty((d), f) + +-static inline void gnttab_clear_flag(unsigned int nr, uint16_t *st) ++static inline void gnttab_clear_flag(struct domain *d, unsigned int nr, ++ uint16_t *st) + { + /* + * Note that this cannot be clear_bit(), as the access must be +-- +2.17.1 + diff --git a/xsa295-4.11-19.patch b/xsa295-4.11-19.patch new file mode 100644 index 0000000..69e18d4 --- /dev/null +++ b/xsa295-4.11-19.patch @@ -0,0 +1,84 @@ +From 2430cd0dfc21d595c2f5d9b98f41531ee0ca8f53 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:29 +0100 +Subject: [PATCH v2 4.11 19/20] xen/arm: Add performance counters in guest + atomic helpers + +Add performance counters in guest atomic helpers to be able to detect +whether a guest is often paused during the operations. + +This is part of XSA-295. + +Signed-off-by: Julien Grall +Acked-by: Stefano Stabellini +--- + xen/include/asm-arm/guest_atomics.h | 12 ++++++++++++ + xen/include/asm-arm/perfc_defn.h | 3 +++ + 2 files changed, 15 insertions(+) + +diff --git a/xen/include/asm-arm/guest_atomics.h b/xen/include/asm-arm/guest_atomics.h +index 61925d313d..698508bf87 100644 +--- a/xen/include/asm-arm/guest_atomics.h ++++ b/xen/include/asm-arm/guest_atomics.h +@@ -24,9 +24,13 @@ DECLARE_PER_CPU(unsigned int, guest_safe_atomic_max); + #define guest_bitop(name) \ + static inline void guest_##name(struct domain *d, int nr, volatile void *p) \ + { \ ++ perfc_incr(atomics_guest); \ ++ \ + if ( name##_timeout(nr, p, this_cpu(guest_safe_atomic_max)) ) \ + return; \ + \ ++ perfc_incr(atomics_guest_paused); \ ++ \ + domain_pause_nosync(d); \ + name(nr, p); \ + domain_unpause(d); \ +@@ -38,11 +42,15 @@ static inline int guest_##name(struct domain *d, int nr, volatile void *p) \ + bool succeed; \ + int oldbit; \ + \ ++ perfc_incr(atomics_guest); \ ++ \ + succeed = name##_timeout(nr, p, &oldbit, \ + this_cpu(guest_safe_atomic_max)); \ + if ( succeed ) \ + return oldbit; \ + \ ++ perfc_incr(atomics_guest_paused); \ ++ \ + domain_pause_nosync(d); \ + oldbit = name(nr, p); \ + domain_unpause(d); \ +@@ -73,10 +81,14 @@ static inline unsigned long __guest_cmpxchg(struct domain *d, + { + unsigned long oldval = old; + ++ perfc_incr(atomics_guest); ++ + if ( __cmpxchg_mb_timeout(ptr, &oldval, new, size, + this_cpu(guest_safe_atomic_max)) ) + return oldval; + ++ perfc_incr(atomics_guest_paused); ++ + domain_pause_nosync(d); + oldval = __cmpxchg_mb(ptr, old, new, size); + domain_unpause(d); +diff --git a/xen/include/asm-arm/perfc_defn.h b/xen/include/asm-arm/perfc_defn.h +index 8922e9525a..6a83185163 100644 +--- a/xen/include/asm-arm/perfc_defn.h ++++ b/xen/include/asm-arm/perfc_defn.h +@@ -73,6 +73,9 @@ PERFCOUNTER(phys_timer_irqs, "Physical timer interrupts") + PERFCOUNTER(virt_timer_irqs, "Virtual timer interrupts") + PERFCOUNTER(maintenance_irqs, "Maintenance interrupts") + ++PERFCOUNTER(atomics_guest, "atomics: guest access") ++PERFCOUNTER(atomics_guest_paused, "atomics: guest paused") ++ + /*#endif*/ /* __XEN_PERFC_DEFN_H__ */ + + /* +-- +2.17.1 + diff --git a/xsa295-4.11-20.patch b/xsa295-4.11-20.patch new file mode 100644 index 0000000..66f83d9 --- /dev/null +++ b/xsa295-4.11-20.patch @@ -0,0 +1,199 @@ +From 9294c354e1830be750177ed4e5f3f693aba08de3 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:30 +0100 +Subject: [PATCH v2 4.11 20/20] xen/arm: grant-table: Protect gnttab_clear_flag + against guest misbehavior + +The function gnttab_clear_flag is used to clear the access flags. On +Arm, it is implemented using a loop and guest_cmpxchg. + +It is possible that guest_cmpxchg will always return a different value +than old. This can happen if the guest updated the memory before Xen has +time to do the exchange. Because of that, there are no way for to +promise the loop will end. + +It is possible to make the current code safe by re-using the same +principle as applied on the guest atomic helper. However this patch +takes a different approach that should lead to more efficient code in +the default case. + +A new helper is introduced to clear a set of bits on a 16-bits word. +This should avoid a an extra loop to check cmpxchg succeeded. + +Note that a mask is used instead of a bit, so the helper can be re-used +later on for clearing multiple flags at the same time. + +This is part of XSA-295. + +Reported-by: Andrew Cooper +Signed-off-by: Julien Grall +Signed-off-by: Stefano Stabellini +Reviewed-by: Stefano Stabellini +--- + xen/arch/arm/arm32/lib/bitops.c | 35 +++++++++++++++++++++++++++++ + xen/arch/arm/arm64/lib/bitops.c | 33 +++++++++++++++++++++++++++ + xen/arch/arm/mm.c | 10 +-------- + xen/include/asm-arm/bitops.h | 4 ++++ + xen/include/asm-arm/guest_atomics.h | 13 +++++++++++ + 5 files changed, 86 insertions(+), 9 deletions(-) + +diff --git a/xen/arch/arm/arm32/lib/bitops.c b/xen/arch/arm/arm32/lib/bitops.c +index 08750314fc..3dca769bf0 100644 +--- a/xen/arch/arm/arm32/lib/bitops.c ++++ b/xen/arch/arm/arm32/lib/bitops.c +@@ -126,6 +126,41 @@ testop(test_and_change_bit, eor) + testop(test_and_clear_bit, bic) + testop(test_and_set_bit, orr) + ++static always_inline bool int_clear_mask16(uint16_t mask, volatile uint16_t *p, ++ bool timeout, unsigned int max_try) ++{ ++ unsigned long res, tmp; ++ ++ prefetchw((const uint16_t *)p); ++ ++ do ++ { ++ asm volatile ("// int_clear_mask16\n" ++ " ldrexh %2, %1\n" ++ " bic %2, %2, %3\n" ++ " strexh %0, %2, %1\n" ++ : "=&r" (res), "+Qo" (*p), "=&r" (tmp) ++ : "r" (mask)); ++ ++ if ( !res ) ++ break; ++ } while ( !timeout || ((--max_try) > 0) ); ++ ++ return !res; ++} ++ ++void clear_mask16(uint16_t mask, volatile void *p) ++{ ++ if ( !int_clear_mask16(mask, p, false, 0) ) ++ ASSERT_UNREACHABLE(); ++} ++ ++bool clear_mask16_timeout(uint16_t mask, volatile void *p, ++ unsigned int max_try) ++{ ++ return int_clear_mask16(mask, p, true, max_try); ++} ++ + /* + * Local variables: + * mode: C +diff --git a/xen/arch/arm/arm64/lib/bitops.c b/xen/arch/arm/arm64/lib/bitops.c +index 78bf4ed8c5..27688e5418 100644 +--- a/xen/arch/arm/arm64/lib/bitops.c ++++ b/xen/arch/arm/arm64/lib/bitops.c +@@ -118,6 +118,39 @@ testop(test_and_change_bit, eor) + testop(test_and_clear_bit, bic) + testop(test_and_set_bit, orr) + ++static always_inline bool int_clear_mask16(uint16_t mask, volatile uint16_t *p, ++ bool timeout, unsigned int max_try) ++{ ++ unsigned long res, tmp; ++ ++ do ++ { ++ asm volatile ("// int_clear_mask16\n" ++ " ldxrh %w2, %1\n" ++ " bic %w2, %w2, %w3\n" ++ " stxrh %w0, %w2, %1\n" ++ : "=&r" (res), "+Q" (*p), "=&r" (tmp) ++ : "r" (mask)); ++ ++ if ( !res ) ++ break; ++ } while ( !timeout || ((--max_try) > 0) ); ++ ++ return !res; ++} ++ ++void clear_mask16(uint16_t mask, volatile void *p) ++{ ++ if ( !int_clear_mask16(mask, p, false, 0) ) ++ ASSERT_UNREACHABLE(); ++} ++ ++bool clear_mask16_timeout(uint16_t mask, volatile void *p, ++ unsigned int max_try) ++{ ++ return int_clear_mask16(mask, p, true, max_try); ++} ++ + /* + * Local variables: + * mode: C +diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c +index 6b7e6b675b..2b4d5ed091 100644 +--- a/xen/arch/arm/mm.c ++++ b/xen/arch/arm/mm.c +@@ -1399,15 +1399,7 @@ void put_page_type(struct page_info *page) + + void gnttab_clear_flag(struct domain *d, unsigned long nr, uint16_t *addr) + { +- /* +- * Note that this cannot be clear_bit(), as the access must be +- * confined to the specified 2 bytes. +- */ +- uint16_t mask = ~(1 << nr), old; +- +- do { +- old = *addr; +- } while (guest_cmpxchg(d, addr, old, old & mask) != old); ++ guest_clear_mask16(d, BIT(nr), addr); + } + + void gnttab_mark_dirty(struct domain *d, mfn_t mfn) +diff --git a/xen/include/asm-arm/bitops.h b/xen/include/asm-arm/bitops.h +index f6782b33be..f989bc726c 100644 +--- a/xen/include/asm-arm/bitops.h ++++ b/xen/include/asm-arm/bitops.h +@@ -53,6 +53,8 @@ int test_and_set_bit(int nr, volatile void *p); + int test_and_clear_bit(int nr, volatile void *p); + int test_and_change_bit(int nr, volatile void *p); + ++void clear_mask16(uint16_t mask, volatile void *p); ++ + /* + * The helpers below may fail to update the memory if the action takes + * too long. +@@ -71,6 +73,8 @@ bool test_and_clear_bit_timeout(int nr, volatile void *p, + int *oldbit, unsigned int max_try); + bool test_and_change_bit_timeout(int nr, volatile void *p, + int *oldbit, unsigned int max_try); ++bool clear_mask16_timeout(uint16_t mask, volatile void *p, ++ unsigned int max_try); + + /** + * __test_and_set_bit - Set a bit and return its old value +diff --git a/xen/include/asm-arm/guest_atomics.h b/xen/include/asm-arm/guest_atomics.h +index 698508bf87..af27cc627b 100644 +--- a/xen/include/asm-arm/guest_atomics.h ++++ b/xen/include/asm-arm/guest_atomics.h +@@ -73,6 +73,19 @@ guest_testop(test_and_change_bit) + + #undef guest_testop + ++static inline void guest_clear_mask16(struct domain *d, uint16_t mask, ++ volatile uint16_t *p) ++{ ++ perfc_incr(atomics_guest); ++ ++ if ( clear_mask16_timeout(mask, p, this_cpu(guest_safe_atomic_max)) ) ++ return; ++ ++ domain_pause_nosync(d); ++ clear_mask16(mask, p); ++ domain_unpause(d); ++} ++ + static inline unsigned long __guest_cmpxchg(struct domain *d, + volatile void *ptr, + unsigned long old, +-- +2.17.1 + From c362e0407d553152553cf6ee8f0b002d15a91f9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 18 Jun 2019 15:49:10 +0200 Subject: [PATCH 009/194] Fix build with python3.8 --- xen.spec | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/xen.spec b/xen.spec index b281887..9a319df 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.12.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 @@ -372,7 +372,7 @@ mkdir -p dist/install%{_libdir}/ocaml/stublibs %define seabiosloc /usr/share/seabios/bios.bin %endif #export XEN_VENDORVERSION="-%{release}" -export EXTRA_CFLAGS_XEN_TOOLS="$RPM_OPT_FLAGS" +export EXTRA_CFLAGS_XEN_TOOLS="$RPM_OPT_FLAGS -Wno-declaration-after-statement" export EXTRA_CFLAGS_QEMU_TRADITIONAL="$RPM_OPT_FLAGS" export EXTRA_CFLAGS_QEMU_XEN="$RPM_OPT_FLAGS" export PYTHON="/usr/bin/python3" @@ -896,6 +896,9 @@ fi %endif %changelog +* Tue Jun 18 2019 Zbigniew Jędrzejewski-Szmek - 4.12.0-4 +- Fix build with python3.8 (#1704807) + * Sat Jun 01 2019 Michael Young - 4.12.0-3 - fix HVM DomU boot on some chipsets - fix expected FTBFS with Python 3.8 (#1704807) From 7829dfecf9774258a7fd8144c2489ba67b0f748c Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 18 Jun 2019 20:21:13 +0100 Subject: [PATCH 010/194] Unlimited Arm Atomics Operations [XSA-295] (#1720760) --- xen.spec | 41 ++- xsa295-4.12-01.patch | 99 ++++++++ xsa295-4.12-02.patch | 90 +++++++ xsa295-4.12-03.patch | 68 +++++ xsa295-4.12-04.patch | 171 +++++++++++++ xsa295-4.12-05.patch | 202 +++++++++++++++ xsa295-4.12-06.patch | 442 +++++++++++++++++++++++++++++++++ xsa295-4.12-07.patch | 83 +++++++ xsa295-4.12-08.patch | 145 +++++++++++ xsa295-4.12-09.patch | 135 ++++++++++ xsa295-4.12-10.patch | 255 +++++++++++++++++++ xsa295-4.12-11.patch | 258 +++++++++++++++++++ xsa295-4.12-12.patch | 112 +++++++++ xsa295-4.12-13.patch | 277 +++++++++++++++++++++ xsa295-4.12-14.patch | 93 +++++++ xsa295-4.12-15.patch | 577 +++++++++++++++++++++++++++++++++++++++++++ xsa295-4.12-16.patch | 84 +++++++ xsa295-4.12-17.patch | 199 +++++++++++++++ 18 files changed, 3329 insertions(+), 2 deletions(-) create mode 100644 xsa295-4.12-01.patch create mode 100644 xsa295-4.12-02.patch create mode 100644 xsa295-4.12-03.patch create mode 100644 xsa295-4.12-04.patch create mode 100644 xsa295-4.12-05.patch create mode 100644 xsa295-4.12-06.patch create mode 100644 xsa295-4.12-07.patch create mode 100644 xsa295-4.12-08.patch create mode 100644 xsa295-4.12-09.patch create mode 100644 xsa295-4.12-10.patch create mode 100644 xsa295-4.12-11.patch create mode 100644 xsa295-4.12-12.patch create mode 100644 xsa295-4.12-13.patch create mode 100644 xsa295-4.12-14.patch create mode 100644 xsa295-4.12-15.patch create mode 100644 xsa295-4.12-16.patch create mode 100644 xsa295-4.12-17.patch diff --git a/xen.spec b/xen.spec index 9a319df..f79bf2d 100644 --- a/xen.spec +++ b/xen.spec @@ -124,6 +124,23 @@ Patch51: xsa297-4.12-6.patch Patch52: xsa297-4.12-7.patch Patch53: xen.git-8457c15b981ba04c0709e6f25af3b76beb34cafa.patch Patch54: xen.python38.patch +Patch55: xsa295-4.12-01.patch +Patch56: xsa295-4.12-02.patch +Patch57: xsa295-4.12-03.patch +Patch58: xsa295-4.12-04.patch +Patch59: xsa295-4.12-05.patch +Patch60: xsa295-4.12-06.patch +Patch61: xsa295-4.12-07.patch +Patch62: xsa295-4.12-08.patch +Patch63: xsa295-4.12-09.patch +Patch64: xsa295-4.12-10.patch +Patch65: xsa295-4.12-11.patch +Patch66: xsa295-4.12-12.patch +Patch67: xsa295-4.12-13.patch +Patch68: xsa295-4.12-14.patch +Patch69: xsa295-4.12-15.patch +Patch70: xsa295-4.12-16.patch +Patch71: xsa295-4.12-17.patch %if %build_qemutrad @@ -332,6 +349,23 @@ 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 +%patch69 -p1 +%patch70 -p1 +%patch71 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -372,7 +406,7 @@ mkdir -p dist/install%{_libdir}/ocaml/stublibs %define seabiosloc /usr/share/seabios/bios.bin %endif #export XEN_VENDORVERSION="-%{release}" -export EXTRA_CFLAGS_XEN_TOOLS="$RPM_OPT_FLAGS -Wno-declaration-after-statement" +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" export PYTHON="/usr/bin/python3" @@ -896,7 +930,10 @@ fi %endif %changelog -* Tue Jun 18 2019 Zbigniew Jędrzejewski-Szmek - 4.12.0-4 +* Tue Jun 18 2019 Michael Young - 4.12.0-4 +- Unlimited Arm Atomics Operations [XSA-295] (#1720760) + +* Tue Jun 18 2019 Zbigniew Jędrzejewski-Szmek - Fix build with python3.8 (#1704807) * Sat Jun 01 2019 Michael Young - 4.12.0-3 diff --git a/xsa295-4.12-01.patch b/xsa295-4.12-01.patch new file mode 100644 index 0000000..367a821 --- /dev/null +++ b/xsa295-4.12-01.patch @@ -0,0 +1,99 @@ +From 00617d5cc62d2bf4d02493aeafad14cca4e09a66 Mon Sep 17 00:00:00 2001 +From: Jan Beulich +Date: Tue, 12 Mar 2019 14:40:24 +0100 +Subject: [PATCH v2 4.12 01/17] events: drop arch_evtchn_inject() + +Have the only user call vcpu_mark_events_pending() instead, at the same +time arranging for correct ordering of the writes (evtchn_pending_sel +should be written before evtchn_upcall_pending). + +Signed-off-by: Jan Beulich +Reviewed-by: Andrew Cooper +Reviewed-by: Julien Grall +--- + xen/arch/arm/vgic.c | 5 ----- + xen/arch/arm/vgic/vgic.c | 5 ----- + xen/arch/x86/irq.c | 6 ------ + xen/common/domain.c | 3 +-- + xen/include/xen/event.h | 3 --- + 5 files changed, 1 insertion(+), 21 deletions(-) + +diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c +index f2608b042d..19d8c9b876 100644 +--- a/xen/arch/arm/vgic.c ++++ b/xen/arch/arm/vgic.c +@@ -597,11 +597,6 @@ out: + return; + } + +-void arch_evtchn_inject(struct vcpu *v) +-{ +- vgic_inject_irq(v->domain, v, v->domain->arch.evtchn_irq, true); +-} +- + bool vgic_evtchn_irq_pending(struct vcpu *v) + { + struct pending_irq *p; +diff --git a/xen/arch/arm/vgic/vgic.c b/xen/arch/arm/vgic/vgic.c +index e2844dcc20..f0f2ea5021 100644 +--- a/xen/arch/arm/vgic/vgic.c ++++ b/xen/arch/arm/vgic/vgic.c +@@ -692,11 +692,6 @@ void vgic_kick_vcpus(struct domain *d) + } + } + +-void arch_evtchn_inject(struct vcpu *v) +-{ +- vgic_inject_irq(v->domain, v, v->domain->arch.evtchn_irq, true); +-} +- + bool vgic_evtchn_irq_pending(struct vcpu *v) + { + struct vgic_irq *irq; +diff --git a/xen/arch/x86/irq.c b/xen/arch/x86/irq.c +index 23b4f423e6..8511953977 100644 +--- a/xen/arch/x86/irq.c ++++ b/xen/arch/x86/irq.c +@@ -2724,9 +2724,3 @@ int allocate_and_map_msi_pirq(struct domain *d, int index, int *pirq_p, + + return ret; + } +- +-void arch_evtchn_inject(struct vcpu *v) +-{ +- if ( is_hvm_vcpu(v) ) +- hvm_assert_evtchn_irq(v); +-} +diff --git a/xen/common/domain.c b/xen/common/domain.c +index 32bca8dbf2..0542338862 100644 +--- a/xen/common/domain.c ++++ b/xen/common/domain.c +@@ -1306,10 +1306,9 @@ int map_vcpu_info(struct vcpu *v, unsigned long gfn, unsigned offset) + * Mark everything as being pending just to make sure nothing gets + * lost. The domain will get a spurious event, but it can cope. + */ +- vcpu_info(v, evtchn_upcall_pending) = 1; + for ( i = 0; i < BITS_PER_EVTCHN_WORD(d); i++ ) + set_bit(i, &vcpu_info(v, evtchn_pending_sel)); +- arch_evtchn_inject(v); ++ vcpu_mark_events_pending(v); + + return 0; + } +diff --git a/xen/include/xen/event.h b/xen/include/xen/event.h +index e91097d77e..a7798f6765 100644 +--- a/xen/include/xen/event.h ++++ b/xen/include/xen/event.h +@@ -91,9 +91,6 @@ int guest_enabled_event(struct vcpu *v, uint32_t virq); + /* Notify remote end of a Xen-attached event channel.*/ + void notify_via_xen_event_channel(struct domain *ld, int lport); + +-/* Inject an event channel notification into the guest */ +-void arch_evtchn_inject(struct vcpu *v); +- + /* + * Internal event channel object storage. + * +-- +2.17.1 + diff --git a/xsa295-4.12-02.patch b/xsa295-4.12-02.patch new file mode 100644 index 0000000..b9b24bb --- /dev/null +++ b/xsa295-4.12-02.patch @@ -0,0 +1,90 @@ +From 8908fe0d3f42a4764d29e6aa657749d7a1b4c8ca Mon Sep 17 00:00:00 2001 +From: Jan Beulich +Date: Tue, 12 Mar 2019 14:40:56 +0100 +Subject: [PATCH v2 4.12 02/17] common: avoid atomic read-modify-write accesses + in map_vcpu_info() + +There's no need to set the evtchn_pending_sel bits one by one. Simply +write full words with all ones. + +For Arm this requires extending write_atomic() to also handle 64-bit +values; for symmetry read_atomic() gets adjusted as well. + +Signed-off-by: Jan Beulich +Reviewed-by: Andrew Cooper +Reviewed-by: Julien Grall +--- + xen/common/domain.c | 9 ++++++--- + xen/include/asm-arm/atomic.h | 15 +++++++++++++++ + 2 files changed, 21 insertions(+), 3 deletions(-) + +diff --git a/xen/common/domain.c b/xen/common/domain.c +index 0542338862..3b18f11f12 100644 +--- a/xen/common/domain.c ++++ b/xen/common/domain.c +@@ -1253,7 +1253,6 @@ int map_vcpu_info(struct vcpu *v, unsigned long gfn, unsigned offset) + void *mapping; + vcpu_info_t *new_info; + struct page_info *page; +- int i; + + if ( offset > (PAGE_SIZE - sizeof(vcpu_info_t)) ) + return -EINVAL; +@@ -1306,8 +1305,12 @@ int map_vcpu_info(struct vcpu *v, unsigned long gfn, unsigned offset) + * Mark everything as being pending just to make sure nothing gets + * lost. The domain will get a spurious event, but it can cope. + */ +- for ( i = 0; i < BITS_PER_EVTCHN_WORD(d); i++ ) +- set_bit(i, &vcpu_info(v, evtchn_pending_sel)); ++#ifdef CONFIG_COMPAT ++ if ( !has_32bit_shinfo(d) ) ++ write_atomic(&new_info->native.evtchn_pending_sel, ~0); ++ else ++#endif ++ write_atomic(&vcpu_info(v, evtchn_pending_sel), ~0); + vcpu_mark_events_pending(v); + + return 0; +diff --git a/xen/include/asm-arm/atomic.h b/xen/include/asm-arm/atomic.h +index afb3eeea5b..7b4c987fa7 100644 +--- a/xen/include/asm-arm/atomic.h ++++ b/xen/include/asm-arm/atomic.h +@@ -55,6 +55,19 @@ build_atomic_write(write_int_atomic, "", WORD, int, "r") + #if defined (CONFIG_ARM_64) + build_atomic_read(read_u64_atomic, "", "", uint64_t, "=r") + build_atomic_write(write_u64_atomic, "", "", uint64_t, "r") ++#elif defined (CONFIG_ARM_32) ++static inline uint64_t read_u64_atomic(const volatile uint64_t *addr) ++{ ++ uint64_t val; ++ ++ asm volatile ( "ldrd %0,%H0,%1" : "=r" (val) : "m" (*addr) ); ++ ++ return val; ++} ++static inline void write_u64_atomic(volatile uint64_t *addr, uint64_t val) ++{ ++ asm volatile ( "strd %1,%H1,%0" : "=m" (*addr) : "r" (val) ); ++} + #endif + + build_add_sized(add_u8_sized, "b", BYTE, uint8_t, "ri") +@@ -69,6 +82,7 @@ void __bad_atomic_size(void); + case 1: __x = (typeof(*p))read_u8_atomic((uint8_t *)p); break; \ + case 2: __x = (typeof(*p))read_u16_atomic((uint16_t *)p); break; \ + case 4: __x = (typeof(*p))read_u32_atomic((uint32_t *)p); break; \ ++ case 8: __x = (typeof(*p))read_u64_atomic((uint64_t *)p); break; \ + default: __x = 0; __bad_atomic_size(); break; \ + } \ + __x; \ +@@ -80,6 +94,7 @@ void __bad_atomic_size(void); + case 1: write_u8_atomic((uint8_t *)p, (uint8_t)__x); break; \ + case 2: write_u16_atomic((uint16_t *)p, (uint16_t)__x); break; \ + case 4: write_u32_atomic((uint32_t *)p, (uint32_t)__x); break; \ ++ case 8: write_u64_atomic((uint64_t *)p, (uint64_t)__x); break; \ + default: __bad_atomic_size(); break; \ + } \ + __x; \ +-- +2.17.1 + diff --git a/xsa295-4.12-03.patch b/xsa295-4.12-03.patch new file mode 100644 index 0000000..9dacd40 --- /dev/null +++ b/xsa295-4.12-03.patch @@ -0,0 +1,68 @@ +From bbe4ec0187f66c8afc0a4c55bb94e7ccd4ce68a5 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:16 +0100 +Subject: [PATCH v2 4.12 03/17] xen/arm: Add an isb() before reading CNTPCT_EL0 + to prevent re-ordering + +Per D8.2.1 in ARM DDI 0487C.a, "a read to CNTPCT_EL0 can occur +speculatively and out of order relative to other instructions executed +on the same PE." + +Add an instruction barrier to get accurate number of cycles when +requested in get_cycles(). For the other users of CNPCT_EL0, replace by +a call to get_cycles(). + +This is part of XSA-295. + +Signed-off-by: Julien Grall +Acked-by: Stefano Stabellini +--- + xen/arch/arm/time.c | 4 ++-- + xen/include/asm-arm/time.h | 2 ++ + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/xen/arch/arm/time.c b/xen/arch/arm/time.c +index bbccee742e..739bcf186c 100644 +--- a/xen/arch/arm/time.c ++++ b/xen/arch/arm/time.c +@@ -151,7 +151,7 @@ void __init preinit_xen_time(void) + if ( res ) + panic("Timer: Cannot initialize platform timer\n"); + +- boot_count = READ_SYSREG64(CNTPCT_EL0); ++ boot_count = get_cycles(); + } + + static void __init init_dt_xen_time(void) +@@ -192,7 +192,7 @@ int __init init_xen_time(void) + /* Return number of nanoseconds since boot */ + s_time_t get_s_time(void) + { +- uint64_t ticks = READ_SYSREG64(CNTPCT_EL0) - boot_count; ++ uint64_t ticks = get_cycles() - boot_count; + return ticks_to_ns(ticks); + } + +diff --git a/xen/include/asm-arm/time.h b/xen/include/asm-arm/time.h +index 9a7071a546..9cb6f9b0b4 100644 +--- a/xen/include/asm-arm/time.h ++++ b/xen/include/asm-arm/time.h +@@ -2,6 +2,7 @@ + #define __ARM_TIME_H__ + + #include ++#include + + #define DT_MATCH_TIMER \ + DT_MATCH_COMPATIBLE("arm,armv7-timer"), \ +@@ -11,6 +12,7 @@ typedef uint64_t cycles_t; + + static inline cycles_t get_cycles (void) + { ++ isb(); + return READ_SYSREG64(CNTPCT_EL0); + } + +-- +2.17.1 + diff --git a/xsa295-4.12-04.patch b/xsa295-4.12-04.patch new file mode 100644 index 0000000..9ac0027 --- /dev/null +++ b/xsa295-4.12-04.patch @@ -0,0 +1,171 @@ +From d0020117111a77eeee7db6ef52ff87347c8c54b7 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:17 +0100 +Subject: [PATCH v2 4.12 04/17] xen/grant_table: Rework the prototype of + _set_status* for lisibility + +It is not clear from the parameters name whether domid and gt_version +correspond to the local or remote domain. A follow-up patch will make +them more confusing. + +So rename domid (resp. gt_version) to ldomid (resp. rgt_version). At +the same time re-order the parameters to hopefully make it more +readable. + +This is part of XSA-295. + +Suggested-by: Jan Beulich +Signed-off-by: Julien Grall +Reviewed-by: Jan Beulich +Acked-by: Stefano Stabellini +--- + xen/common/grant_table.c | 57 ++++++++++++++++++++-------------------- + 1 file changed, 28 insertions(+), 29 deletions(-) + +diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c +index 80728ea57d..1b82d534a3 100644 +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -645,11 +645,11 @@ static unsigned int nr_grant_entries(struct grant_table *gt) + return 0; + } + +-static int _set_status_v1(domid_t domid, ++static int _set_status_v1(const grant_entry_header_t *shah, ++ struct active_grant_entry *act, + int readonly, + int mapflag, +- grant_entry_header_t *shah, +- struct active_grant_entry *act) ++ domid_t ldomid) + { + int rc = GNTST_okay; + union grant_combo scombo, prev_scombo, new_scombo; +@@ -684,11 +684,11 @@ static int _set_status_v1(domid_t domid, + if ( !act->pin && + (((scombo.shorts.flags & mask) != + GTF_permit_access) || +- (scombo.shorts.domid != domid)) ) ++ (scombo.shorts.domid != ldomid)) ) + PIN_FAIL(done, GNTST_general_error, + "Bad flags (%x) or dom (%d); expected d%d\n", + scombo.shorts.flags, scombo.shorts.domid, +- domid); ++ ldomid); + + new_scombo = scombo; + new_scombo.shorts.flags |= GTF_reading; +@@ -717,12 +717,12 @@ done: + return rc; + } + +-static int _set_status_v2(domid_t domid, ++static int _set_status_v2(const grant_entry_header_t *shah, ++ grant_status_t *status, ++ struct active_grant_entry *act, + int readonly, + int mapflag, +- grant_entry_header_t *shah, +- struct active_grant_entry *act, +- grant_status_t *status) ++ domid_t ldomid) + { + int rc = GNTST_okay; + union grant_combo scombo; +@@ -748,10 +748,10 @@ static int _set_status_v2(domid_t domid, + if ( !act->pin && + ( (((flags & mask) != GTF_permit_access) && + ((flags & mask) != GTF_transitive)) || +- (id != domid)) ) ++ (id != ldomid)) ) + PIN_FAIL(done, GNTST_general_error, + "Bad flags (%x) or dom (%d); expected d%d, flags %x\n", +- flags, id, domid, mask); ++ flags, id, ldomid, mask); + + if ( readonly ) + { +@@ -778,14 +778,14 @@ static int _set_status_v2(domid_t domid, + { + if ( (((flags & mask) != GTF_permit_access) && + ((flags & mask) != GTF_transitive)) || +- (id != domid) || ++ (id != ldomid) || + (!readonly && (flags & GTF_readonly)) ) + { + gnttab_clear_flag(_GTF_writing, status); + gnttab_clear_flag(_GTF_reading, status); + PIN_FAIL(done, GNTST_general_error, + "Unstable flags (%x) or dom (%d); expected d%d (r/w: %d)\n", +- flags, id, domid, !readonly); ++ flags, id, ldomid, !readonly); + } + } + else +@@ -803,19 +803,19 @@ done: + } + + +-static int _set_status(unsigned gt_version, +- domid_t domid, ++static int _set_status(const grant_entry_header_t *shah, ++ grant_status_t *status, ++ unsigned rgt_version, ++ struct active_grant_entry *act, + int readonly, + int mapflag, +- grant_entry_header_t *shah, +- struct active_grant_entry *act, +- grant_status_t *status) ++ domid_t ldomid) + { + +- if ( gt_version == 1 ) +- return _set_status_v1(domid, readonly, mapflag, shah, act); ++ if ( rgt_version == 1 ) ++ return _set_status_v1(shah, act, readonly, mapflag, ldomid); + else +- return _set_status_v2(domid, readonly, mapflag, shah, act, status); ++ return _set_status_v2(shah, status, act, readonly, mapflag, ldomid); + } + + static struct active_grant_entry *grant_map_exists(const struct domain *ld, +@@ -980,9 +980,9 @@ map_grant_ref( + (!(op->flags & GNTMAP_readonly) && + !(act->pin & (GNTPIN_hstw_mask|GNTPIN_devw_mask))) ) + { +- if ( (rc = _set_status(rgt->gt_version, ld->domain_id, +- op->flags & GNTMAP_readonly, +- 1, shah, act, status) ) != GNTST_okay ) ++ if ( (rc = _set_status(shah, status, rgt->gt_version, act, ++ op->flags & GNTMAP_readonly, 1, ++ ld->domain_id) != GNTST_okay) ) + goto act_release_out; + + if ( !act->pin ) +@@ -2434,8 +2434,8 @@ acquire_grant_for_copy( + { + if ( (!old_pin || (!readonly && + !(old_pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)))) && +- (rc = _set_status_v2(ldom, readonly, 0, shah, act, +- status)) != GNTST_okay ) ++ (rc = _set_status_v2(shah, status, act, readonly, 0, ++ ldom)) != GNTST_okay ) + goto unlock_out; + + if ( !allow_transitive ) +@@ -2535,9 +2535,8 @@ acquire_grant_for_copy( + else if ( !old_pin || + (!readonly && !(old_pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask))) ) + { +- if ( (rc = _set_status(rgt->gt_version, ldom, +- readonly, 0, shah, act, +- status) ) != GNTST_okay ) ++ if ( (rc = _set_status(shah, status, rgt->gt_version, act, ++ readonly, 0, ldom)) != GNTST_okay ) + goto unlock_out; + + td = rd; +-- +2.17.1 + diff --git a/xsa295-4.12-05.patch b/xsa295-4.12-05.patch new file mode 100644 index 0000000..3054822 --- /dev/null +++ b/xsa295-4.12-05.patch @@ -0,0 +1,202 @@ +From e40140db5da810c4ead632ae5d346558f2b93695 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:18 +0100 +Subject: [PATCH v2 4.12 05/17] xen/arm64: bitops: Rewrite bitop helpers in C + +This is part of XSA-295. + +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +Signed-off-by: Stefano Stabellini +--- + xen/arch/arm/README.LinuxPrimitives | 1 - + xen/arch/arm/arm64/lib/bitops.S | 67 --------------------- + xen/arch/arm/arm64/lib/bitops.c | 90 +++++++++++++++++++++++++++++ + 3 files changed, 90 insertions(+), 68 deletions(-) + delete mode 100644 xen/arch/arm/arm64/lib/bitops.S + create mode 100644 xen/arch/arm/arm64/lib/bitops.c + +diff --git a/xen/arch/arm/README.LinuxPrimitives b/xen/arch/arm/README.LinuxPrimitives +index 028e8721f9..891667a5da 100644 +--- a/xen/arch/arm/README.LinuxPrimitives ++++ b/xen/arch/arm/README.LinuxPrimitives +@@ -8,7 +8,6 @@ arm64: + + bitops: last sync @ v3.16-rc6 (last commit: 8715466b6027) + +-linux/arch/arm64/lib/bitops.S xen/arch/arm/arm64/lib/bitops.S + linux/arch/arm64/include/asm/bitops.h xen/include/asm-arm/arm64/bitops.h + + --------------------------------------------------------------------- +diff --git a/xen/arch/arm/arm64/lib/bitops.S b/xen/arch/arm/arm64/lib/bitops.S +deleted file mode 100644 +index 6471dd1875..0000000000 +--- a/xen/arch/arm/arm64/lib/bitops.S ++++ /dev/null +@@ -1,67 +0,0 @@ +-/* +- * Based on linux/arch/arm64/lib/bitops.h which in turn is +- * Based on arch/arm/lib/bitops.h +- * +- * Copyright (C) 2013 ARM Ltd. +- * +- * 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 distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program. If not, see . +- */ +- +-/* +- * x0: bits 4:0 bit offset +- * bits 31:5 word offset +- * x1: address +- */ +- .macro bitop, name, instr +-ENTRY( \name ) +- and w3, w0, #31 // Get bit offset +- eor w0, w0, w3 // Clear low bits +- mov x2, #1 +- add x1, x1, x0, lsr #3 // Get word offset +- lsl x3, x2, x3 // Create mask +-1: ldxr w2, [x1] +- \instr w2, w2, w3 +- stxr w0, w2, [x1] +- cbnz w0, 1b +- ret +-ENDPROC(\name ) +- .endm +- +- .macro testop, name, instr +-ENTRY( \name ) +- and w3, w0, #31 // Get bit offset +- eor w0, w0, w3 // Clear low bits +- mov x2, #1 +- add x1, x1, x0, lsr #3 // Get word offset +- lsl x4, x2, x3 // Create mask +-1: ldxr w2, [x1] +- lsr w0, w2, w3 // Save old value of bit +- \instr w2, w2, w4 // toggle bit +- stlxr w5, w2, [x1] +- cbnz w5, 1b +- dmb ish +- and w0, w0, #1 +-3: ret +-ENDPROC(\name ) +- .endm +- +-/* +- * Atomic bit operations. +- */ +- bitop change_bit, eor +- bitop clear_bit, bic +- bitop set_bit, orr +- +- testop test_and_change_bit, eor +- testop test_and_clear_bit, bic +- testop test_and_set_bit, orr +diff --git a/xen/arch/arm/arm64/lib/bitops.c b/xen/arch/arm/arm64/lib/bitops.c +new file mode 100644 +index 0000000000..b1c681c642 +--- /dev/null ++++ b/xen/arch/arm/arm64/lib/bitops.c +@@ -0,0 +1,90 @@ ++/* ++ * Copyright (C) 2018 ARM Ltd. ++ * ++ * 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 distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include ++#include ++ ++/* ++ * The atomic bit operations pass the number of bit in a signed number ++ * (not sure why). This has the drawback to increase the complexity of ++ * the resulting assembly. ++ * ++ * To generate simpler code, the number of bit (nr) will be cast to ++ * unsigned int. ++ * ++ * XXX: Rework the interface to use unsigned int. ++ */ ++ ++#define bitop(name, instr) \ ++void name(int nr, volatile void *p) \ ++{ \ ++ volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ ++ const uint32_t mask = BIT_MASK((unsigned int)nr); \ ++ unsigned long res, tmp; \ ++ \ ++ do \ ++ { \ ++ asm volatile ("// " __stringify(name) "\n" \ ++ " ldxr %w2, %1\n" \ ++ " " __stringify(instr) " %w2, %w2, %w3\n" \ ++ " stxr %w0, %w2, %1\n" \ ++ : "=&r" (res), "+Q" (*ptr), "=&r" (tmp) \ ++ : "r" (mask)); \ ++ } while ( res ); \ ++} \ ++ ++#define testop(name, instr) \ ++int name(int nr, volatile void *p) \ ++{ \ ++ volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ ++ unsigned int bit = (unsigned int)nr % BITS_PER_WORD; \ ++ const uint32_t mask = BIT_MASK(bit); \ ++ unsigned long res, tmp; \ ++ unsigned long oldbit; \ ++ \ ++ do \ ++ { \ ++ asm volatile ("// " __stringify(name) "\n" \ ++ " ldxr %w3, %2\n" \ ++ " lsr %w1, %w3, %w5 // Save old value of bit\n" \ ++ " " __stringify(instr) " %w3, %w3, %w4 // Toggle bit\n" \ ++ " stlxr %w0, %w3, %2\n" \ ++ : "=&r" (res), "=&r" (oldbit), "+Q" (*ptr), "=&r" (tmp) \ ++ : "r" (mask), "r" (bit) \ ++ : "memory"); \ ++ } while ( res ); \ ++ \ ++ dmb(ish); \ ++ \ ++ return oldbit & 1; \ ++} ++ ++bitop(change_bit, eor) ++bitop(clear_bit, bic) ++bitop(set_bit, orr) ++ ++testop(test_and_change_bit, eor) ++testop(test_and_clear_bit, bic) ++testop(test_and_set_bit, orr) ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-file-style: "BSD" ++ * c-basic-offset: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +-- +2.17.1 + diff --git a/xsa295-4.12-06.patch b/xsa295-4.12-06.patch new file mode 100644 index 0000000..7fb835f --- /dev/null +++ b/xsa295-4.12-06.patch @@ -0,0 +1,442 @@ +From b919e89a3cc3295ee56b1392d42ba7eea898f9d4 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:19 +0100 +Subject: [PATCH v2 4.12 06/17] xen/arm32: bitops: Rewrite bitop helpers in C + +This is part of XSA-295. + +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +Signed-off-by: Stefano Stabellini +--- + xen/arch/arm/README.LinuxPrimitives | 14 +--- + xen/arch/arm/arm32/lib/Makefile | 5 +- + xen/arch/arm/arm32/lib/bitops.c | 98 +++++++++++++++++++++++ + xen/arch/arm/arm32/lib/bitops.h | 104 ------------------------- + xen/arch/arm/arm32/lib/changebit.S | 14 ---- + xen/arch/arm/arm32/lib/clearbit.S | 14 ---- + xen/arch/arm/arm32/lib/setbit.S | 15 ---- + xen/arch/arm/arm32/lib/testchangebit.S | 15 ---- + xen/arch/arm/arm32/lib/testclearbit.S | 15 ---- + xen/arch/arm/arm32/lib/testsetbit.S | 15 ---- + xen/include/asm-arm/arm32/bitops.h | 19 ++--- + 11 files changed, 108 insertions(+), 220 deletions(-) + create mode 100644 xen/arch/arm/arm32/lib/bitops.c + delete mode 100644 xen/arch/arm/arm32/lib/bitops.h + delete mode 100644 xen/arch/arm/arm32/lib/changebit.S + delete mode 100644 xen/arch/arm/arm32/lib/clearbit.S + delete mode 100644 xen/arch/arm/arm32/lib/setbit.S + delete mode 100644 xen/arch/arm/arm32/lib/testchangebit.S + delete mode 100644 xen/arch/arm/arm32/lib/testclearbit.S + delete mode 100644 xen/arch/arm/arm32/lib/testsetbit.S + +diff --git a/xen/arch/arm/README.LinuxPrimitives b/xen/arch/arm/README.LinuxPrimitives +index 891667a5da..664a9f89ed 100644 +--- a/xen/arch/arm/README.LinuxPrimitives ++++ b/xen/arch/arm/README.LinuxPrimitives +@@ -68,19 +68,9 @@ arm32 + + bitops: last sync @ v3.16-rc6 (last commit: c32ffce0f66e) + +-linux/arch/arm/lib/bitops.h xen/arch/arm/arm32/lib/bitops.h +-linux/arch/arm/lib/changebit.S xen/arch/arm/arm32/lib/changebit.S +-linux/arch/arm/lib/clearbit.S xen/arch/arm/arm32/lib/clearbit.S + linux/arch/arm/lib/findbit.S xen/arch/arm/arm32/lib/findbit.S +-linux/arch/arm/lib/setbit.S xen/arch/arm/arm32/lib/setbit.S +-linux/arch/arm/lib/testchangebit.S xen/arch/arm/arm32/lib/testchangebit.S +-linux/arch/arm/lib/testclearbit.S xen/arch/arm/arm32/lib/testclearbit.S +-linux/arch/arm/lib/testsetbit.S xen/arch/arm/arm32/lib/testsetbit.S +- +-for i in bitops.h changebit.S clearbit.S findbit.S setbit.S testchangebit.S \ +- testclearbit.S testsetbit.S; do +- diff -u ../linux/arch/arm/lib/$i xen/arch/arm/arm32/lib/$i; +-done ++ ++diff -u ../linux/arch/arm/lib/findbit.S xen/arch/arm/arm32/lib/findbit.S + + --------------------------------------------------------------------- + +diff --git a/xen/arch/arm/arm32/lib/Makefile b/xen/arch/arm/arm32/lib/Makefile +index e9fbc595b9..b1457c89dc 100644 +--- a/xen/arch/arm/arm32/lib/Makefile ++++ b/xen/arch/arm/arm32/lib/Makefile +@@ -1,6 +1,5 @@ + obj-y += memcpy.o memmove.o memset.o memchr.o memzero.o +-obj-y += findbit.o setbit.o +-obj-y += setbit.o clearbit.o changebit.o +-obj-y += testsetbit.o testclearbit.o testchangebit.o ++obj-y += findbit.o ++obj-y += bitops.o + obj-y += strchr.o strrchr.o + obj-y += lib1funcs.o lshrdi3.o div64.o +diff --git a/xen/arch/arm/arm32/lib/bitops.c b/xen/arch/arm/arm32/lib/bitops.c +new file mode 100644 +index 0000000000..c69bb53037 +--- /dev/null ++++ b/xen/arch/arm/arm32/lib/bitops.c +@@ -0,0 +1,98 @@ ++/* ++ * Copyright (C) 2018 ARM Ltd. ++ * ++ * 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 distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include ++#include ++#include ++ ++/* ++ * The atomic bit operations pass the number of bit in a signed number ++ * (not sure why). This has the drawback to increase the complexity of ++ * the resulting assembly. ++ * ++ * To generate simpler code, the number of bit (nr) will be cast to ++ * unsigned int. ++ * ++ * XXX: Rework the interface to use unsigned int. ++ */ ++ ++#define bitop(name, instr) \ ++void name(int nr, volatile void *p) \ ++{ \ ++ volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ ++ const uint32_t mask = BIT_MASK((unsigned int)nr); \ ++ unsigned long res, tmp; \ ++ \ ++ ASSERT(((vaddr_t)p & 0x3) == 0); \ ++ prefetchw((const void *)ptr); \ ++ \ ++ do \ ++ { \ ++ asm volatile ("// " __stringify(name) "\n" \ ++ " ldrex %2, %1\n" \ ++ " " __stringify(instr) " %2, %2, %3\n" \ ++ " strex %0, %2, %1\n" \ ++ : "=&r" (res), "+Qo" (*ptr), "=&r" (tmp) \ ++ : "r" (mask)); \ ++ } while ( res ); \ ++} ++ ++#define testop(name, instr) \ ++int name(int nr, volatile void *p) \ ++{ \ ++ volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ ++ unsigned int bit = (unsigned int)nr % BITS_PER_WORD; \ ++ const uint32_t mask = BIT_MASK(bit); \ ++ unsigned long res, tmp; \ ++ int oldbit; \ ++ \ ++ ASSERT(((vaddr_t)p & 0x3) == 0); \ ++ smp_mb(); \ ++ \ ++ prefetchw((const void *)ptr); \ ++ \ ++ do \ ++ { \ ++ asm volatile ("// " __stringify(name) "\n" \ ++ " ldrex %3, %2\n" \ ++ " lsr %1, %3, %5 // Save old value of bit\n" \ ++ " " __stringify(instr) " %3, %3, %4 // Toggle bit\n" \ ++ " strex %0, %3, %2\n" \ ++ : "=&r" (res), "=&r" (oldbit), "+Qo" (*ptr), "=&r" (tmp) \ ++ : "r" (mask), "r" (bit)); \ ++ } while ( res ); \ ++ \ ++ smp_mb(); \ ++ \ ++ return oldbit & 1; \ ++} \ ++ ++bitop(change_bit, eor) ++bitop(clear_bit, bic) ++bitop(set_bit, orr) ++ ++testop(test_and_change_bit, eor) ++testop(test_and_clear_bit, bic) ++testop(test_and_set_bit, orr) ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-file-style: "BSD" ++ * c-basic-offset: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +diff --git a/xen/arch/arm/arm32/lib/bitops.h b/xen/arch/arm/arm32/lib/bitops.h +deleted file mode 100644 +index d5e13476f4..0000000000 +--- a/xen/arch/arm/arm32/lib/bitops.h ++++ /dev/null +@@ -1,104 +0,0 @@ +- +-#if __LINUX_ARM_ARCH__ >= 6 +- .macro bitop, name, instr +-ENTRY( \name ) +-UNWIND( .fnstart ) +- ands ip, r1, #3 +- strneb r1, [ip] @ assert word-aligned +- mov r2, #1 +- and r3, r0, #31 @ Get bit offset +- mov r0, r0, lsr #5 +- add r1, r1, r0, lsl #2 @ Get word offset +-#if __LINUX_ARM_ARCH__ >= 7 && defined(CONFIG_SMP) +- .arch_extension mp +- ALT_SMP(W(pldw) [r1]) +- ALT_UP(W(nop)) +-#endif +- mov r3, r2, lsl r3 +-1: ldrex r2, [r1] +- \instr r2, r2, r3 +- strex r0, r2, [r1] +- cmp r0, #0 +- bne 1b +- bx lr +-UNWIND( .fnend ) +-ENDPROC(\name ) +- .endm +- +- .macro testop, name, instr, store +-ENTRY( \name ) +-UNWIND( .fnstart ) +- ands ip, r1, #3 +- strneb r1, [ip] @ assert word-aligned +- mov r2, #1 +- and r3, r0, #31 @ Get bit offset +- mov r0, r0, lsr #5 +- add r1, r1, r0, lsl #2 @ Get word offset +- mov r3, r2, lsl r3 @ create mask +- smp_dmb +-#if __LINUX_ARM_ARCH__ >= 7 && defined(CONFIG_SMP) +- .arch_extension mp +- ALT_SMP(W(pldw) [r1]) +- ALT_UP(W(nop)) +-#endif +-1: ldrex r2, [r1] +- ands r0, r2, r3 @ save old value of bit +- \instr r2, r2, r3 @ toggle bit +- strex ip, r2, [r1] +- cmp ip, #0 +- bne 1b +- smp_dmb +- cmp r0, #0 +- movne r0, #1 +-2: bx lr +-UNWIND( .fnend ) +-ENDPROC(\name ) +- .endm +-#else +- .macro bitop, name, instr +-ENTRY( \name ) +-UNWIND( .fnstart ) +- ands ip, r1, #3 +- strneb r1, [ip] @ assert word-aligned +- and r2, r0, #31 +- mov r0, r0, lsr #5 +- mov r3, #1 +- mov r3, r3, lsl r2 +- save_and_disable_irqs ip +- ldr r2, [r1, r0, lsl #2] +- \instr r2, r2, r3 +- str r2, [r1, r0, lsl #2] +- restore_irqs ip +- mov pc, lr +-UNWIND( .fnend ) +-ENDPROC(\name ) +- .endm +- +-/** +- * testop - implement a test_and_xxx_bit operation. +- * @instr: operational instruction +- * @store: store instruction +- * +- * Note: we can trivially conditionalise the store instruction +- * to avoid dirtying the data cache. +- */ +- .macro testop, name, instr, store +-ENTRY( \name ) +-UNWIND( .fnstart ) +- ands ip, r1, #3 +- strneb r1, [ip] @ assert word-aligned +- and r3, r0, #31 +- mov r0, r0, lsr #5 +- save_and_disable_irqs ip +- ldr r2, [r1, r0, lsl #2]! +- mov r0, #1 +- tst r2, r0, lsl r3 +- \instr r2, r2, r0, lsl r3 +- \store r2, [r1] +- moveq r0, #0 +- restore_irqs ip +- mov pc, lr +-UNWIND( .fnend ) +-ENDPROC(\name ) +- .endm +-#endif +diff --git a/xen/arch/arm/arm32/lib/changebit.S b/xen/arch/arm/arm32/lib/changebit.S +deleted file mode 100644 +index 6e4ae7594a..0000000000 +--- a/xen/arch/arm/arm32/lib/changebit.S ++++ /dev/null +@@ -1,14 +0,0 @@ +-/* +- * linux/arch/arm/lib/changebit.S +- * +- * Copyright (C) 1995-1996 Russell King +- * +- * 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. +- */ +-#include "assembler.h" +-#include "bitops.h" +- .text +- +-bitop _change_bit, eor +diff --git a/xen/arch/arm/arm32/lib/clearbit.S b/xen/arch/arm/arm32/lib/clearbit.S +deleted file mode 100644 +index fda553f246..0000000000 +--- a/xen/arch/arm/arm32/lib/clearbit.S ++++ /dev/null +@@ -1,14 +0,0 @@ +-/* +- * linux/arch/arm/lib/clearbit.S +- * +- * Copyright (C) 1995-1996 Russell King +- * +- * 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. +- */ +-#include "assembler.h" +-#include "bitops.h" +- .text +- +-bitop _clear_bit, bic +diff --git a/xen/arch/arm/arm32/lib/setbit.S b/xen/arch/arm/arm32/lib/setbit.S +deleted file mode 100644 +index d52f0ab65c..0000000000 +--- a/xen/arch/arm/arm32/lib/setbit.S ++++ /dev/null +@@ -1,15 +0,0 @@ +-/* +- * linux/arch/arm/lib/setbit.S +- * +- * Copyright (C) 1995-1996 Russell King +- * +- * 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. +- */ +- +-#include "assembler.h" +-#include "bitops.h" +- .text +- +-bitop _set_bit, orr +diff --git a/xen/arch/arm/arm32/lib/testchangebit.S b/xen/arch/arm/arm32/lib/testchangebit.S +deleted file mode 100644 +index d83b04c057..0000000000 +--- a/xen/arch/arm/arm32/lib/testchangebit.S ++++ /dev/null +@@ -1,15 +0,0 @@ +-/* +- * linux/arch/arm/lib/testchangebit.S +- * +- * Copyright (C) 1995-1996 Russell King +- * +- * 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. +- */ +- +-#include "assembler.h" +-#include "bitops.h" +- .text +- +-testop _test_and_change_bit, eor, str +diff --git a/xen/arch/arm/arm32/lib/testclearbit.S b/xen/arch/arm/arm32/lib/testclearbit.S +deleted file mode 100644 +index 6f5b7b92d1..0000000000 +--- a/xen/arch/arm/arm32/lib/testclearbit.S ++++ /dev/null +@@ -1,15 +0,0 @@ +-/* +- * linux/arch/arm/lib/testclearbit.S +- * +- * Copyright (C) 1995-1996 Russell King +- * +- * 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. +- */ +- +-#include "assembler.h" +-#include "bitops.h" +- .text +- +-testop _test_and_clear_bit, bicne, strne +diff --git a/xen/arch/arm/arm32/lib/testsetbit.S b/xen/arch/arm/arm32/lib/testsetbit.S +deleted file mode 100644 +index 30425a842a..0000000000 +--- a/xen/arch/arm/arm32/lib/testsetbit.S ++++ /dev/null +@@ -1,15 +0,0 @@ +-/* +- * linux/arch/arm/lib/testsetbit.S +- * +- * Copyright (C) 1995-1996 Russell King +- * +- * 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. +- */ +- +-#include "assembler.h" +-#include "bitops.h" +- .text +- +-testop _test_and_set_bit, orreq, streq +diff --git a/xen/include/asm-arm/arm32/bitops.h b/xen/include/asm-arm/arm32/bitops.h +index 8be3564540..67c4c3f55c 100644 +--- a/xen/include/asm-arm/arm32/bitops.h ++++ b/xen/include/asm-arm/arm32/bitops.h +@@ -1,19 +1,12 @@ + #ifndef _ARM_ARM32_BITOPS_H + #define _ARM_ARM32_BITOPS_H + +-extern void _set_bit(int nr, volatile void * p); +-extern void _clear_bit(int nr, volatile void * p); +-extern void _change_bit(int nr, volatile void * p); +-extern int _test_and_set_bit(int nr, volatile void * p); +-extern int _test_and_clear_bit(int nr, volatile void * p); +-extern int _test_and_change_bit(int nr, volatile void * p); +- +-#define set_bit(n,p) _set_bit(n,p) +-#define clear_bit(n,p) _clear_bit(n,p) +-#define change_bit(n,p) _change_bit(n,p) +-#define test_and_set_bit(n,p) _test_and_set_bit(n,p) +-#define test_and_clear_bit(n,p) _test_and_clear_bit(n,p) +-#define test_and_change_bit(n,p) _test_and_change_bit(n,p) ++extern void set_bit(int nr, volatile void * p); ++extern void clear_bit(int nr, volatile void * p); ++extern void change_bit(int nr, volatile void * p); ++extern int test_and_set_bit(int nr, volatile void * p); ++extern int test_and_clear_bit(int nr, volatile void * p); ++extern int test_and_change_bit(int nr, volatile void * p); + + #define flsl fls + +-- +2.17.1 + diff --git a/xsa295-4.12-07.patch b/xsa295-4.12-07.patch new file mode 100644 index 0000000..017a0c7 --- /dev/null +++ b/xsa295-4.12-07.patch @@ -0,0 +1,83 @@ +From 244df788936cb47ee5479617ebf1d1e8b656266b Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:20 +0100 +Subject: [PATCH v2 4.12 07/17] xen/arm: bitops: Consolidate prototypes in one + place + +The prototype are the same between arm32 and arm64. Consolidate them in +asm-arm/bitops.h. + +This change will help the introductions of new helpers in a follow-up +patch. + +This is part of XSA-295. + +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +--- + xen/include/asm-arm/arm32/bitops.h | 7 ------- + xen/include/asm-arm/arm64/bitops.h | 10 ---------- + xen/include/asm-arm/bitops.h | 8 ++++++++ + 3 files changed, 8 insertions(+), 17 deletions(-) + +diff --git a/xen/include/asm-arm/arm32/bitops.h b/xen/include/asm-arm/arm32/bitops.h +index 67c4c3f55c..57938a5874 100644 +--- a/xen/include/asm-arm/arm32/bitops.h ++++ b/xen/include/asm-arm/arm32/bitops.h +@@ -1,13 +1,6 @@ + #ifndef _ARM_ARM32_BITOPS_H + #define _ARM_ARM32_BITOPS_H + +-extern void set_bit(int nr, volatile void * p); +-extern void clear_bit(int nr, volatile void * p); +-extern void change_bit(int nr, volatile void * p); +-extern int test_and_set_bit(int nr, volatile void * p); +-extern int test_and_clear_bit(int nr, volatile void * p); +-extern int test_and_change_bit(int nr, volatile void * p); +- + #define flsl fls + + /* +diff --git a/xen/include/asm-arm/arm64/bitops.h b/xen/include/asm-arm/arm64/bitops.h +index 6bf1922680..6cc224ad13 100644 +--- a/xen/include/asm-arm/arm64/bitops.h ++++ b/xen/include/asm-arm/arm64/bitops.h +@@ -1,16 +1,6 @@ + #ifndef _ARM_ARM64_BITOPS_H + #define _ARM_ARM64_BITOPS_H + +-/* +- * Little endian assembly atomic bitops. +- */ +-extern void set_bit(int nr, volatile void *p); +-extern void clear_bit(int nr, volatile void *p); +-extern void change_bit(int nr, volatile void *p); +-extern int test_and_set_bit(int nr, volatile void *p); +-extern int test_and_clear_bit(int nr, volatile void *p); +-extern int test_and_change_bit(int nr, volatile void *p); +- + /* Based on linux/include/asm-generic/bitops/builtin-__ffs.h */ + /** + * __ffs - find first bit in word. +diff --git a/xen/include/asm-arm/bitops.h b/xen/include/asm-arm/bitops.h +index 1cbfb9edb2..c69b08adf6 100644 +--- a/xen/include/asm-arm/bitops.h ++++ b/xen/include/asm-arm/bitops.h +@@ -38,6 +38,14 @@ + # error "unknown ARM variant" + #endif + ++/* Atomics bitops */ ++void set_bit(int nr, volatile void *p); ++void clear_bit(int nr, volatile void *p); ++void change_bit(int nr, volatile void *p); ++int test_and_set_bit(int nr, volatile void *p); ++int test_and_clear_bit(int nr, volatile void *p); ++int test_and_change_bit(int nr, volatile void *p); ++ + /** + * __test_and_set_bit - Set a bit and return its old value + * @nr: Bit to set +-- +2.17.1 + diff --git a/xsa295-4.12-08.patch b/xsa295-4.12-08.patch new file mode 100644 index 0000000..b5b2327 --- /dev/null +++ b/xsa295-4.12-08.patch @@ -0,0 +1,145 @@ +From 25f650e4dbf25f1aff1a4c9d509ca7f0ed8da104 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Wed, 22 May 2019 13:37:53 -0700 +Subject: [PATCH v2 4.12 08/17] xen/arm64: cmpxchg: Simplify the cmpxchg + implementation + +The only difference between each case of the cmpxchg is the size of +used. Rather than duplicating the code, provide a macro to generate each +cases. + +This makes the code easier to read and modify. + +This is part of XSA-295. + +Signed-off-by; Julien Grall +Reviewed-by: Stefano Stabellini +Signed-off-by: Stefano Stabellini +--- + xen/include/asm-arm/arm64/cmpxchg.h | 98 +++++++++++------------------ + 1 file changed, 36 insertions(+), 62 deletions(-) + +diff --git a/xen/include/asm-arm/arm64/cmpxchg.h b/xen/include/asm-arm/arm64/cmpxchg.h +index ae42b2f5ff..393fbca6a5 100644 +--- a/xen/include/asm-arm/arm64/cmpxchg.h ++++ b/xen/include/asm-arm/arm64/cmpxchg.h +@@ -61,80 +61,54 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size + __ret; \ + }) + +-extern void __bad_cmpxchg(volatile void *ptr, int size); ++extern unsigned long __bad_cmpxchg(volatile void *ptr, int size); ++ ++#define __CMPXCHG_CASE(w, sz, name) \ ++static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ ++ unsigned long old, \ ++ unsigned long new) \ ++{ \ ++ unsigned long res, oldval; \ ++ \ ++ do { \ ++ asm volatile("// __cmpxchg_case_" #name "\n" \ ++ " ldxr" #sz " %" #w "1, %2\n" \ ++ " mov %w0, #0\n" \ ++ " cmp %" #w "1, %" #w "3\n" \ ++ " b.ne 1f\n" \ ++ " stxr" #sz " %w0, %" #w "4, %2\n" \ ++ "1:\n" \ ++ : "=&r" (res), "=&r" (oldval), \ ++ "+Q" (*(unsigned long *)ptr) \ ++ : "Ir" (old), "r" (new) \ ++ : "cc"); \ ++ } while (res); \ ++ \ ++ return oldval; \ ++} ++ ++__CMPXCHG_CASE(w, b, 1) ++__CMPXCHG_CASE(w, h, 2) ++__CMPXCHG_CASE(w, , 4) ++__CMPXCHG_CASE( , , 8) + + static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, + unsigned long new, int size) + { +- unsigned long oldval = 0, res; +- + switch (size) { + case 1: +- do { +- asm volatile("// __cmpxchg1\n" +- " ldxrb %w1, %2\n" +- " mov %w0, #0\n" +- " cmp %w1, %w3\n" +- " b.ne 1f\n" +- " stxrb %w0, %w4, %2\n" +- "1:\n" +- : "=&r" (res), "=&r" (oldval), "+Q" (*(u8 *)ptr) +- : "Ir" (old), "r" (new) +- : "cc"); +- } while (res); +- break; +- ++ return __cmpxchg_case_1(ptr, old, new); + case 2: +- do { +- asm volatile("// __cmpxchg2\n" +- " ldxrh %w1, %2\n" +- " mov %w0, #0\n" +- " cmp %w1, %w3\n" +- " b.ne 1f\n" +- " stxrh %w0, %w4, %2\n" +- "1:\n" +- : "=&r" (res), "=&r" (oldval), "+Q" (*(u16 *)ptr) +- : "Ir" (old), "r" (new) +- : "cc"); +- } while (res); +- break; +- ++ return __cmpxchg_case_2(ptr, old, new); + case 4: +- do { +- asm volatile("// __cmpxchg4\n" +- " ldxr %w1, %2\n" +- " mov %w0, #0\n" +- " cmp %w1, %w3\n" +- " b.ne 1f\n" +- " stxr %w0, %w4, %2\n" +- "1:\n" +- : "=&r" (res), "=&r" (oldval), "+Q" (*(u32 *)ptr) +- : "Ir" (old), "r" (new) +- : "cc"); +- } while (res); +- break; +- ++ return __cmpxchg_case_4(ptr, old, new); + case 8: +- do { +- asm volatile("// __cmpxchg8\n" +- " ldxr %1, %2\n" +- " mov %w0, #0\n" +- " cmp %1, %3\n" +- " b.ne 1f\n" +- " stxr %w0, %4, %2\n" +- "1:\n" +- : "=&r" (res), "=&r" (oldval), "+Q" (*(u64 *)ptr) +- : "Ir" (old), "r" (new) +- : "cc"); +- } while (res); +- break; +- ++ return __cmpxchg_case_8(ptr, old, new); + default: +- __bad_cmpxchg(ptr, size); +- oldval = 0; ++ return __bad_cmpxchg(ptr, size); + } + +- return oldval; ++ ASSERT_UNREACHABLE(); + } + + static inline unsigned long __cmpxchg_mb(volatile void *ptr, unsigned long old, +-- +2.17.1 + diff --git a/xsa295-4.12-09.patch b/xsa295-4.12-09.patch new file mode 100644 index 0000000..1e2b9d1 --- /dev/null +++ b/xsa295-4.12-09.patch @@ -0,0 +1,135 @@ +From f33610438bb6586eb665922d9f32bb2889220b2b Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:22 +0100 +Subject: [PATCH v2 4.12 09/17] xen/arm32: cmpxchg: Simplify the cmpxchg + implementation + +The only difference between each case of the cmpxchg is the size of +used. Rather than duplicating the code, provide a macro to generate each +cases. + +This makes the code easier to read and modify. + +While doing the rework, the case for 64-bit cmpxchg is removed. This is +unused today (already commented) and it would not be possible to use +it directly. + +This is part of XSA-295. + +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +--- + xen/include/asm-arm/arm32/cmpxchg.h | 84 +++++++++++------------------ + 1 file changed, 31 insertions(+), 53 deletions(-) + +diff --git a/xen/include/asm-arm/arm32/cmpxchg.h b/xen/include/asm-arm/arm32/cmpxchg.h +index 03e0bed3a6..471a9e3a3f 100644 +--- a/xen/include/asm-arm/arm32/cmpxchg.h ++++ b/xen/include/asm-arm/arm32/cmpxchg.h +@@ -52,72 +52,50 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size + * indicated by comparing RETURN with OLD. + */ + +-extern void __bad_cmpxchg(volatile void *ptr, int size); ++extern unsigned long __bad_cmpxchg(volatile void *ptr, int size); ++ ++#define __CMPXCHG_CASE(sz, name) \ ++static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ ++ unsigned long old, \ ++ unsigned long new) \ ++{ \ ++ unsigned long oldval, res; \ ++ \ ++ do { \ ++ asm volatile("@ __cmpxchg_case_" #name "\n" \ ++ " ldrex" #sz " %1, [%2]\n" \ ++ " mov %0, #0\n" \ ++ " teq %1, %3\n" \ ++ " strex" #sz "eq %0, %4, [%2]\n" \ ++ : "=&r" (res), "=&r" (oldval) \ ++ : "r" (ptr), "Ir" (old), "r" (new) \ ++ : "memory", "cc"); \ ++ } while (res); \ ++ \ ++ return oldval; \ ++} ++ ++__CMPXCHG_CASE(b, 1) ++__CMPXCHG_CASE(h, 2) ++__CMPXCHG_CASE( , 4) + + static always_inline unsigned long __cmpxchg( + volatile void *ptr, unsigned long old, unsigned long new, int size) + { +- unsigned long oldval, res; +- + prefetchw((const void *)ptr); + + switch (size) { + case 1: +- do { +- asm volatile("@ __cmpxchg1\n" +- " ldrexb %1, [%2]\n" +- " mov %0, #0\n" +- " teq %1, %3\n" +- " strexbeq %0, %4, [%2]\n" +- : "=&r" (res), "=&r" (oldval) +- : "r" (ptr), "Ir" (old), "r" (new) +- : "memory", "cc"); +- } while (res); +- break; ++ return __cmpxchg_case_1(ptr, old, new); + case 2: +- do { +- asm volatile("@ __cmpxchg2\n" +- " ldrexh %1, [%2]\n" +- " mov %0, #0\n" +- " teq %1, %3\n" +- " strexheq %0, %4, [%2]\n" +- : "=&r" (res), "=&r" (oldval) +- : "r" (ptr), "Ir" (old), "r" (new) +- : "memory", "cc"); +- } while (res); +- break; ++ return __cmpxchg_case_2(ptr, old, new); + case 4: +- do { +- asm volatile("@ __cmpxchg4\n" +- " ldrex %1, [%2]\n" +- " mov %0, #0\n" +- " teq %1, %3\n" +- " strexeq %0, %4, [%2]\n" +- : "=&r" (res), "=&r" (oldval) +- : "r" (ptr), "Ir" (old), "r" (new) +- : "memory", "cc"); +- } while (res); +- break; +-#if 0 +- case 8: +- do { +- asm volatile("@ __cmpxchg8\n" +- " ldrexd %1, [%2]\n" +- " mov %0, #0\n" +- " teq %1, %3\n" +- " strexdeq %0, %4, [%2]\n" +- : "=&r" (res), "=&r" (oldval) +- : "r" (ptr), "Ir" (old), "r" (new) +- : "memory", "cc"); +- } while (res); +- break; +-#endif ++ return __cmpxchg_case_4(ptr, old, new); + default: +- __bad_cmpxchg(ptr, size); +- oldval = 0; ++ return __bad_cmpxchg(ptr, size); + } + +- return oldval; ++ ASSERT_UNREACHABLE(); + } + + static always_inline unsigned long __cmpxchg_mb(volatile void *ptr, +-- +2.17.1 + diff --git a/xsa295-4.12-10.patch b/xsa295-4.12-10.patch new file mode 100644 index 0000000..9c2d235 --- /dev/null +++ b/xsa295-4.12-10.patch @@ -0,0 +1,255 @@ +From 7cd0aeaa3ef96180ed251d66a4aff8ffad8c3dfe Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:23 +0100 +Subject: [PATCH v2 4.12 10/17] xen/arm: bitops: Implement a new set of helpers + that can timeout + +Exclusive load-store atomics should only be used between trusted +threads. As not all the guests are trusted, it may be possible to DoS +Xen when updating shared memory with guest atomically. + +To prevent the infinite loop, we introduce a new set of helpers that can +timeout. The timeout is based on the maximum number of iterations. + +They will be used in follow-up patch to make atomic operations +on shared memory safe. + +This is part of XSA-295. + +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +--- + xen/arch/arm/arm32/lib/bitops.c | 52 ++++++++++++++++++++++++++++----- + xen/arch/arm/arm64/lib/bitops.c | 52 ++++++++++++++++++++++++++++----- + xen/include/asm-arm/bitops.h | 28 +++++++++++++++++- + 3 files changed, 117 insertions(+), 15 deletions(-) + +diff --git a/xen/arch/arm/arm32/lib/bitops.c b/xen/arch/arm/arm32/lib/bitops.c +index c69bb53037..08750314fc 100644 +--- a/xen/arch/arm/arm32/lib/bitops.c ++++ b/xen/arch/arm/arm32/lib/bitops.c +@@ -30,7 +30,8 @@ + */ + + #define bitop(name, instr) \ +-void name(int nr, volatile void *p) \ ++static always_inline bool int_##name(int nr, volatile void *p, bool timeout,\ ++ unsigned int max_try) \ + { \ + volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ + const uint32_t mask = BIT_MASK((unsigned int)nr); \ +@@ -47,17 +48,33 @@ void name(int nr, volatile void *p) \ + " strex %0, %2, %1\n" \ + : "=&r" (res), "+Qo" (*ptr), "=&r" (tmp) \ + : "r" (mask)); \ +- } while ( res ); \ ++ \ ++ if ( !res ) \ ++ break; \ ++ } while ( !timeout || ((--max_try) > 0) ); \ ++ \ ++ return !res; \ ++} \ ++ \ ++void name(int nr, volatile void *p) \ ++{ \ ++ if ( !int_##name(nr, p, false, 0) ) \ ++ ASSERT_UNREACHABLE(); \ ++} \ ++ \ ++bool name##_timeout(int nr, volatile void *p, unsigned int max_try) \ ++{ \ ++ return int_##name(nr, p, true, max_try); \ + } + + #define testop(name, instr) \ +-int name(int nr, volatile void *p) \ ++static always_inline bool int_##name(int nr, volatile void *p, int *oldbit, \ ++ bool timeout, unsigned int max_try) \ + { \ + volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ + unsigned int bit = (unsigned int)nr % BITS_PER_WORD; \ + const uint32_t mask = BIT_MASK(bit); \ + unsigned long res, tmp; \ +- int oldbit; \ + \ + ASSERT(((vaddr_t)p & 0x3) == 0); \ + smp_mb(); \ +@@ -71,14 +88,35 @@ int name(int nr, volatile void *p) \ + " lsr %1, %3, %5 // Save old value of bit\n" \ + " " __stringify(instr) " %3, %3, %4 // Toggle bit\n" \ + " strex %0, %3, %2\n" \ +- : "=&r" (res), "=&r" (oldbit), "+Qo" (*ptr), "=&r" (tmp) \ ++ : "=&r" (res), "=&r" (*oldbit), "+Qo" (*ptr), "=&r" (tmp) \ + : "r" (mask), "r" (bit)); \ +- } while ( res ); \ ++ \ ++ if ( !res ) \ ++ break; \ ++ } while ( !timeout || ((--max_try) > 0) ); \ + \ + smp_mb(); \ + \ +- return oldbit & 1; \ ++ *oldbit &= 1; \ ++ \ ++ return !res; \ ++} \ ++ \ ++int name(int nr, volatile void *p) \ ++{ \ ++ int oldbit; \ ++ \ ++ if ( !int_##name(nr, p, &oldbit, false, 0) ) \ ++ ASSERT_UNREACHABLE(); \ ++ \ ++ return oldbit; \ + } \ ++ \ ++bool name##_timeout(int nr, volatile void *p, \ ++ int *oldbit, unsigned int max_try) \ ++{ \ ++ return int_##name(nr, p, oldbit, true, max_try); \ ++} + + bitop(change_bit, eor) + bitop(clear_bit, bic) +diff --git a/xen/arch/arm/arm64/lib/bitops.c b/xen/arch/arm/arm64/lib/bitops.c +index b1c681c642..78bf4ed8c5 100644 +--- a/xen/arch/arm/arm64/lib/bitops.c ++++ b/xen/arch/arm/arm64/lib/bitops.c +@@ -29,7 +29,8 @@ + */ + + #define bitop(name, instr) \ +-void name(int nr, volatile void *p) \ ++static always_inline bool int_##name(int nr, volatile void *p, bool timeout,\ ++ unsigned int max_try) \ + { \ + volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ + const uint32_t mask = BIT_MASK((unsigned int)nr); \ +@@ -43,17 +44,33 @@ void name(int nr, volatile void *p) \ + " stxr %w0, %w2, %1\n" \ + : "=&r" (res), "+Q" (*ptr), "=&r" (tmp) \ + : "r" (mask)); \ +- } while ( res ); \ ++ \ ++ if ( !res ) \ ++ break; \ ++ } while ( !timeout || ((--max_try) > 0) ); \ ++ \ ++ return !res; \ + } \ ++ \ ++void name(int nr, volatile void *p) \ ++{ \ ++ if ( !int_##name(nr, p, false, 0) ) \ ++ ASSERT_UNREACHABLE(); \ ++} \ ++ \ ++bool name##_timeout(int nr, volatile void *p, unsigned int max_try) \ ++{ \ ++ return int_##name(nr, p, true, max_try); \ ++} + + #define testop(name, instr) \ +-int name(int nr, volatile void *p) \ ++static always_inline bool int_##name(int nr, volatile void *p, int *oldbit, \ ++ bool timeout, unsigned int max_try) \ + { \ + volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ + unsigned int bit = (unsigned int)nr % BITS_PER_WORD; \ + const uint32_t mask = BIT_MASK(bit); \ + unsigned long res, tmp; \ +- unsigned long oldbit; \ + \ + do \ + { \ +@@ -62,14 +79,35 @@ int name(int nr, volatile void *p) \ + " lsr %w1, %w3, %w5 // Save old value of bit\n" \ + " " __stringify(instr) " %w3, %w3, %w4 // Toggle bit\n" \ + " stlxr %w0, %w3, %2\n" \ +- : "=&r" (res), "=&r" (oldbit), "+Q" (*ptr), "=&r" (tmp) \ ++ : "=&r" (res), "=&r" (*oldbit), "+Q" (*ptr), "=&r" (tmp) \ + : "r" (mask), "r" (bit) \ + : "memory"); \ +- } while ( res ); \ ++ \ ++ if ( !res ) \ ++ break; \ ++ } while ( !timeout || ((--max_try) > 0) ); \ + \ + dmb(ish); \ + \ +- return oldbit & 1; \ ++ *oldbit &= 1; \ ++ \ ++ return !res; \ ++} \ ++ \ ++int name(int nr, volatile void *p) \ ++{ \ ++ int oldbit; \ ++ \ ++ if ( !int_##name(nr, p, &oldbit, false, 0) ) \ ++ ASSERT_UNREACHABLE(); \ ++ \ ++ return oldbit; \ ++} \ ++ \ ++bool name##_timeout(int nr, volatile void *p, \ ++ int *oldbit, unsigned int max_try) \ ++{ \ ++ return int_##name(nr, p, oldbit, true, max_try); \ + } + + bitop(change_bit, eor) +diff --git a/xen/include/asm-arm/bitops.h b/xen/include/asm-arm/bitops.h +index c69b08adf6..f6782b33be 100644 +--- a/xen/include/asm-arm/bitops.h ++++ b/xen/include/asm-arm/bitops.h +@@ -38,7 +38,14 @@ + # error "unknown ARM variant" + #endif + +-/* Atomics bitops */ ++/* ++ * Atomic bitops ++ * ++ * The helpers below *should* only be used on memory shared between ++ * trusted threads or we know the memory cannot be accessed by another ++ * thread. ++ */ ++ + void set_bit(int nr, volatile void *p); + void clear_bit(int nr, volatile void *p); + void change_bit(int nr, volatile void *p); +@@ -46,6 +53,25 @@ int test_and_set_bit(int nr, volatile void *p); + int test_and_clear_bit(int nr, volatile void *p); + int test_and_change_bit(int nr, volatile void *p); + ++/* ++ * The helpers below may fail to update the memory if the action takes ++ * too long. ++ * ++ * @max_try: Maximum number of iterations ++ * ++ * The helpers will return true when the update has succeeded (i.e no ++ * timeout) and false if the update has failed. ++ */ ++bool set_bit_timeout(int nr, volatile void *p, unsigned int max_try); ++bool clear_bit_timeout(int nr, volatile void *p, unsigned int max_try); ++bool change_bit_timeout(int nr, volatile void *p, unsigned int max_try); ++bool test_and_set_bit_timeout(int nr, volatile void *p, ++ int *oldbit, unsigned int max_try); ++bool test_and_clear_bit_timeout(int nr, volatile void *p, ++ int *oldbit, unsigned int max_try); ++bool test_and_change_bit_timeout(int nr, volatile void *p, ++ int *oldbit, unsigned int max_try); ++ + /** + * __test_and_set_bit - Set a bit and return its old value + * @nr: Bit to set +-- +2.17.1 + diff --git a/xsa295-4.12-11.patch b/xsa295-4.12-11.patch new file mode 100644 index 0000000..b915abc --- /dev/null +++ b/xsa295-4.12-11.patch @@ -0,0 +1,258 @@ +From 9a6ac9945d1d742185189a7d751d3ad10c8bed3f Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Wed, 22 May 2019 13:39:17 -0700 +Subject: [PATCH v2 4.12 11/17] xen/arm: cmpxchg: Provide a new helper that can + timeout + +Exclusive load-store atomics should only be used between trusted +threads. As not all the guests are trusted, it may be possible to DoS +Xen when updating shared memory with guest atomically. + +To prevent the infinite loop, we introduce a new helper that can timeout. +The timeout is based on the maximum number of iterations. + +It will be used in follow-up patch to make atomic operations on shared +memory safe. + +This is part of XSA-295. + +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +Signed-off-by: Stefano Stabellini +--- + xen/include/asm-arm/arm32/cmpxchg.h | 63 +++++++++++++++++++++----- + xen/include/asm-arm/arm64/cmpxchg.h | 70 ++++++++++++++++++++++------- + 2 files changed, 106 insertions(+), 27 deletions(-) + +diff --git a/xen/include/asm-arm/arm32/cmpxchg.h b/xen/include/asm-arm/arm32/cmpxchg.h +index 471a9e3a3f..49ca2a0d7a 100644 +--- a/xen/include/asm-arm/arm32/cmpxchg.h ++++ b/xen/include/asm-arm/arm32/cmpxchg.h +@@ -55,11 +55,14 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size + extern unsigned long __bad_cmpxchg(volatile void *ptr, int size); + + #define __CMPXCHG_CASE(sz, name) \ +-static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ +- unsigned long old, \ +- unsigned long new) \ ++static inline bool __cmpxchg_case_##name(volatile void *ptr, \ ++ unsigned long *old, \ ++ unsigned long new, \ ++ bool timeout, \ ++ unsigned int max_try) \ + { \ +- unsigned long oldval, res; \ ++ unsigned long oldval; \ ++ unsigned long res; \ + \ + do { \ + asm volatile("@ __cmpxchg_case_" #name "\n" \ +@@ -68,29 +71,35 @@ static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ + " teq %1, %3\n" \ + " strex" #sz "eq %0, %4, [%2]\n" \ + : "=&r" (res), "=&r" (oldval) \ +- : "r" (ptr), "Ir" (old), "r" (new) \ ++ : "r" (ptr), "Ir" (*old), "r" (new) \ + : "memory", "cc"); \ +- } while (res); \ + \ +- return oldval; \ ++ if (!res) \ ++ break; \ ++ } while (!timeout || ((--max_try) > 0)); \ ++ \ ++ *old = oldval; \ ++ \ ++ return !res; \ + } + + __CMPXCHG_CASE(b, 1) + __CMPXCHG_CASE(h, 2) + __CMPXCHG_CASE( , 4) + +-static always_inline unsigned long __cmpxchg( +- volatile void *ptr, unsigned long old, unsigned long new, int size) ++static always_inline bool __int_cmpxchg(volatile void *ptr, unsigned long *old, ++ unsigned long new, int size, ++ bool timeout, unsigned int max_try) + { + prefetchw((const void *)ptr); + + switch (size) { + case 1: +- return __cmpxchg_case_1(ptr, old, new); ++ return __cmpxchg_case_1(ptr, old, new, timeout, max_try); + case 2: +- return __cmpxchg_case_2(ptr, old, new); ++ return __cmpxchg_case_2(ptr, old, new, timeout, max_try); + case 4: +- return __cmpxchg_case_4(ptr, old, new); ++ return __cmpxchg_case_4(ptr, old, new, timeout, max_try); + default: + return __bad_cmpxchg(ptr, size); + } +@@ -98,6 +107,17 @@ static always_inline unsigned long __cmpxchg( + ASSERT_UNREACHABLE(); + } + ++static always_inline unsigned long __cmpxchg(volatile void *ptr, ++ unsigned long old, ++ unsigned long new, ++ int size) ++{ ++ if (!__int_cmpxchg(ptr, &old, new, size, false, 0)) ++ ASSERT_UNREACHABLE(); ++ ++ return old; ++} ++ + static always_inline unsigned long __cmpxchg_mb(volatile void *ptr, + unsigned long old, + unsigned long new, int size) +@@ -111,6 +131,25 @@ static always_inline unsigned long __cmpxchg_mb(volatile void *ptr, + return ret; + } + ++/* ++ * The helper may fail to update the memory if the action takes too long. ++ * ++ * @old: On call the value pointed contains the expected old value. It will be ++ * updated to the actual old value. ++ * @max_try: Maximum number of iterations ++ * ++ * The helper will return true when the update has succeeded (i.e no ++ * timeout) and false if the update has failed. ++ */ ++static always_inline bool __cmpxchg_mb_timeout(volatile void *ptr, ++ unsigned long *old, ++ unsigned long new, ++ int size, ++ unsigned int max_try) ++{ ++ return __int_cmpxchg(ptr, old, new, size, true, max_try); ++} ++ + #define cmpxchg(ptr,o,n) \ + ((__typeof__(*(ptr)))__cmpxchg_mb((ptr), \ + (unsigned long)(o), \ +diff --git a/xen/include/asm-arm/arm64/cmpxchg.h b/xen/include/asm-arm/arm64/cmpxchg.h +index 393fbca6a5..5bc2e1f786 100644 +--- a/xen/include/asm-arm/arm64/cmpxchg.h ++++ b/xen/include/asm-arm/arm64/cmpxchg.h +@@ -64,11 +64,14 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size + extern unsigned long __bad_cmpxchg(volatile void *ptr, int size); + + #define __CMPXCHG_CASE(w, sz, name) \ +-static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ +- unsigned long old, \ +- unsigned long new) \ ++static inline bool __cmpxchg_case_##name(volatile void *ptr, \ ++ unsigned long *old, \ ++ unsigned long new, \ ++ bool timeout, \ ++ unsigned int max_try) \ + { \ +- unsigned long res, oldval; \ ++ unsigned long oldval; \ ++ unsigned long res; \ + \ + do { \ + asm volatile("// __cmpxchg_case_" #name "\n" \ +@@ -80,11 +83,16 @@ static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ + "1:\n" \ + : "=&r" (res), "=&r" (oldval), \ + "+Q" (*(unsigned long *)ptr) \ +- : "Ir" (old), "r" (new) \ ++ : "Ir" (*old), "r" (new) \ + : "cc"); \ +- } while (res); \ + \ +- return oldval; \ ++ if (!res) \ ++ break; \ ++ } while (!timeout || ((--max_try) > 0)); \ ++ \ ++ *old = oldval; \ ++ \ ++ return !res; \ + } + + __CMPXCHG_CASE(w, b, 1) +@@ -92,18 +100,19 @@ __CMPXCHG_CASE(w, h, 2) + __CMPXCHG_CASE(w, , 4) + __CMPXCHG_CASE( , , 8) + +-static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, +- unsigned long new, int size) ++static always_inline bool __int_cmpxchg(volatile void *ptr, unsigned long *old, ++ unsigned long new, int size, ++ bool timeout, unsigned int max_try) + { + switch (size) { + case 1: +- return __cmpxchg_case_1(ptr, old, new); ++ return __cmpxchg_case_1(ptr, old, new, timeout, max_try); + case 2: +- return __cmpxchg_case_2(ptr, old, new); ++ return __cmpxchg_case_2(ptr, old, new, timeout, max_try); + case 4: +- return __cmpxchg_case_4(ptr, old, new); ++ return __cmpxchg_case_4(ptr, old, new, timeout, max_try); + case 8: +- return __cmpxchg_case_8(ptr, old, new); ++ return __cmpxchg_case_8(ptr, old, new, timeout, max_try); + default: + return __bad_cmpxchg(ptr, size); + } +@@ -111,8 +120,20 @@ static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, + ASSERT_UNREACHABLE(); + } + +-static inline unsigned long __cmpxchg_mb(volatile void *ptr, unsigned long old, +- unsigned long new, int size) ++static always_inline unsigned long __cmpxchg(volatile void *ptr, ++ unsigned long old, ++ unsigned long new, ++ int size) ++{ ++ if (!__int_cmpxchg(ptr, &old, new, size, false, 0)) ++ ASSERT_UNREACHABLE(); ++ ++ return old; ++} ++ ++static always_inline unsigned long __cmpxchg_mb(volatile void *ptr, ++ unsigned long old, ++ unsigned long new, int size) + { + unsigned long ret; + +@@ -123,6 +144,25 @@ static inline unsigned long __cmpxchg_mb(volatile void *ptr, unsigned long old, + return ret; + } + ++/* ++ * The helper may fail to update the memory if the action takes too long. ++ * ++ * @old: On call the value pointed contains the expected old value. It will be ++ * updated to the actual old value. ++ * @max_try: Maximum number of iterations ++ * ++ * The helper will return true when the update has succeeded (i.e no ++ * timeout) and false if the update has failed. ++ */ ++static always_inline bool __cmpxchg_mb_timeout(volatile void *ptr, ++ unsigned long *old, ++ unsigned long new, ++ int size, ++ unsigned int max_try) ++{ ++ return __int_cmpxchg(ptr, old, new, size, true, max_try); ++} ++ + #define cmpxchg(ptr, o, n) \ + ({ \ + __typeof__(*(ptr)) __ret; \ +-- +2.17.1 + diff --git a/xsa295-4.12-12.patch b/xsa295-4.12-12.patch new file mode 100644 index 0000000..3ff0716 --- /dev/null +++ b/xsa295-4.12-12.patch @@ -0,0 +1,112 @@ +From 07c47ec0a6aa0c76e6822127ea5ec5122c9282a0 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:25 +0100 +Subject: [PATCH v2 4.12 12/17] xen/arm: Turn on SILO mode by default on Arm + +On Arm, exclusive load-store atomics should only be used between trusted +thread. As not all the guests are trusted, it may be possible to DoS Xen +when updating shared memory with guest atomically. + +Recent patches introduced new helpers to update shared memory with guest +atomically. Those helpers relies on a memory region to be be shared with +Xen and a single guest. + +At the moment, nothing prevent a guest sharing a page with Xen and as +well with another guest (e.g via grant table). + +For the scope of the XSA, the quickest way is to deny communications +between unprivileged guest. So this patch is enabling and using SILO +mode by default on Arm. + +Users wanted finer graine policy could wrote their own Flask policy. + +This is part of XSA-295. + +Signed-off-by: Julien Grall +Acked-by: Jan Beulich +--- + xen/arch/arm/setup.c | 8 ++++++-- + xen/common/Kconfig | 3 ++- + xen/include/xsm/xsm.h | 5 +++++ + xen/xsm/xsm_core.c | 2 +- + 4 files changed, 14 insertions(+), 4 deletions(-) + +diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c +index 444857a967..a2aedc43e2 100644 +--- a/xen/arch/arm/setup.c ++++ b/xen/arch/arm/setup.c +@@ -39,6 +39,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -834,8 +835,11 @@ void __init start_xen(unsigned long boot_phys_offset, + + tasklet_subsys_init(); + +- +- xsm_dt_init(); ++ if ( xsm_dt_init() != 1 ) ++ warning_add("WARNING: SILO mode is not enabled.\n" ++ "It has implications on the security of the system,\n" ++ "unless the communications have been forbidden between\n" ++ "untrusted domains.\n"); + + init_maintenance_interrupt(); + init_timer_interrupt(); +diff --git a/xen/common/Kconfig b/xen/common/Kconfig +index 04384628bb..e9b1006852 100644 +--- a/xen/common/Kconfig ++++ b/xen/common/Kconfig +@@ -106,7 +106,7 @@ config XENOPROF + + config XSM + bool "Xen Security Modules support" +- default n ++ default ARM + ---help--- + Enables the security framework known as Xen Security Modules which + allows administrators fine-grained control over a Xen domain and +@@ -171,6 +171,7 @@ config XSM_SILO + choice + prompt "Default XSM implementation" + depends on XSM ++ default XSM_SILO_DEFAULT if XSM_SILO && ARM + default XSM_FLASK_DEFAULT if XSM_FLASK + default XSM_SILO_DEFAULT if XSM_SILO + default XSM_DUMMY_DEFAULT +diff --git a/xen/include/xsm/xsm.h b/xen/include/xsm/xsm.h +index 8a78d8abd3..fc9d6b5bf0 100644 +--- a/xen/include/xsm/xsm.h ++++ b/xen/include/xsm/xsm.h +@@ -741,6 +741,11 @@ extern int xsm_multiboot_policy_init(unsigned long *module_map, + #endif + + #ifdef CONFIG_HAS_DEVICE_TREE ++/* ++ * Initialize XSM ++ * ++ * On success, return 1 if using SILO mode else 0. ++ */ + extern int xsm_dt_init(void); + extern int xsm_dt_policy_init(void **policy_buffer, size_t *policy_size); + extern bool has_xsm_magic(paddr_t); +diff --git a/xen/xsm/xsm_core.c b/xen/xsm/xsm_core.c +index 201c354390..a319df253d 100644 +--- a/xen/xsm/xsm_core.c ++++ b/xen/xsm/xsm_core.c +@@ -167,7 +167,7 @@ int __init xsm_dt_init(void) + + xfree(policy_buffer); + +- return ret; ++ return ret ?: (xsm_bootparam == XSM_BOOTPARAM_SILO); + } + + /** +-- +2.17.1 + diff --git a/xsa295-4.12-13.patch b/xsa295-4.12-13.patch new file mode 100644 index 0000000..68f5ae0 --- /dev/null +++ b/xsa295-4.12-13.patch @@ -0,0 +1,277 @@ +From 0566038dc84d91b962e5338a9c12e29c2d7d2e9b Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:26 +0100 +Subject: [PATCH v2 4.12 13/17] xen/bitops: Provide helpers to safely modify + guest memory atomically + +On Arm, exclusive load-store atomics should only be used between trusted +thread. As not all the guests are trusted, it may be possible to DoS Xen +when updating shared memory with guest atomically. + +This patch adds a new set of helper that will update the guest memory +safely. For x86, it is already possible to use the current helpers +safely. So just wrap them. + +For Arm, we will first attempt to update the guest memory with the loop +bounded by a maximum number of iterations. If it fails, we will pause the +domain and try again. + +Note that this heuristics assumes that a page can only be shared between +Xen and one domain. Not Xen and multiple domain. + +The maximum number of iterations is based on how many times a simple +load-store atomic operation can be executed in 1uS. The maximum value is +per-CPU to cater big.LITTLE and calculated when the CPU is booting. The +heuristic was randomly chosen and can be modified if impact too much +good-behaving guest. + +Note, while test_bit does not requires to use atomic operation, a +wrapper for test_bit was added for completeness. In this case, the +domain stays constified to avoid major rework in the caller for the +time-being. + +This is part of XSA-295. + +Signed-of-by: Julien Grall +Acked-by: Jan Beulich +Reviewed-by: Stefano Stabellini +--- + xen/arch/arm/Makefile | 1 + + xen/arch/arm/guest_atomics.c | 91 +++++++++++++++++++++++++++++ + xen/include/asm-arm/guest_atomics.h | 76 ++++++++++++++++++++++++ + xen/include/asm-x86/guest_atomics.h | 30 ++++++++++ + 4 files changed, 198 insertions(+) + create mode 100644 xen/arch/arm/guest_atomics.c + create mode 100644 xen/include/asm-arm/guest_atomics.h + create mode 100644 xen/include/asm-x86/guest_atomics.h + +diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile +index cb902cb6fe..872a155b60 100644 +--- a/xen/arch/arm/Makefile ++++ b/xen/arch/arm/Makefile +@@ -22,6 +22,7 @@ obj-$(CONFIG_GICV3) += gic-v3.o + obj-$(CONFIG_HAS_ITS) += gic-v3-its.o + obj-$(CONFIG_HAS_ITS) += gic-v3-lpi.o + obj-y += guestcopy.o ++obj-y += guest_atomics.o + obj-y += guest_walk.o + obj-y += hvm.o + obj-y += io.o +diff --git a/xen/arch/arm/guest_atomics.c b/xen/arch/arm/guest_atomics.c +new file mode 100644 +index 0000000000..1b78a062f0 +--- /dev/null ++++ b/xen/arch/arm/guest_atomics.c +@@ -0,0 +1,91 @@ ++/* ++ * arch/arm/guest_atomics.c ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; If not, see . ++ */ ++#include ++ ++#include ++ ++DEFINE_PER_CPU_READ_MOSTLY(unsigned int, guest_safe_atomic_max); ++ ++/* ++ * Heuristic to find a safe upper-limit for load-store exclusive ++ * operations on memory shared with guest. ++ * ++ * At the moment, we calculate the number of iterations of a simple ++ * load-store atomic loop in 1uS. ++ */ ++static void calibrate_safe_atomic(void) ++{ ++ s_time_t deadline = NOW() + MICROSECS(1); ++ unsigned int counter = 0; ++ unsigned long mem = 0; ++ ++ do ++ { ++ unsigned long res, tmp; ++ ++#ifdef CONFIG_ARM_32 ++ asm volatile (" ldrex %2, %1\n" ++ " add %2, %2, #1\n" ++ " strex %0, %2, %1\n" ++ : "=&r" (res), "+Q" (mem), "=&r" (tmp)); ++#else ++ asm volatile (" ldxr %w2, %1\n" ++ " add %w2, %w2, #1\n" ++ " stxr %w0, %w2, %1\n" ++ : "=&r" (res), "+Q" (mem), "=&r" (tmp)); ++#endif ++ counter++; ++ } while (NOW() < deadline); ++ ++ this_cpu(guest_safe_atomic_max) = counter; ++ ++ printk(XENLOG_DEBUG ++ "CPU%u: Guest atomics will try %u times before pausing the domain\n", ++ smp_processor_id(), counter); ++} ++ ++static int cpu_guest_safe_atomic_callback(struct notifier_block *nfb, ++ unsigned long action, ++ void *hcpu) ++{ ++ if ( action == CPU_STARTING ) ++ calibrate_safe_atomic(); ++ ++ return NOTIFY_DONE; ++} ++ ++static struct notifier_block cpu_guest_safe_atomic_nfb = { ++ .notifier_call = cpu_guest_safe_atomic_callback, ++}; ++ ++static int __init guest_safe_atomic_init(void) ++{ ++ register_cpu_notifier(&cpu_guest_safe_atomic_nfb); ++ ++ calibrate_safe_atomic(); ++ ++ return 0; ++} ++presmp_initcall(guest_safe_atomic_init); ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-file-style: "BSD" ++ * c-basic-offset: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +diff --git a/xen/include/asm-arm/guest_atomics.h b/xen/include/asm-arm/guest_atomics.h +new file mode 100644 +index 0000000000..4f127fda41 +--- /dev/null ++++ b/xen/include/asm-arm/guest_atomics.h +@@ -0,0 +1,76 @@ ++#ifndef _ARM_GUEST_ATOMICS_H ++#define _ARM_GUEST_ATOMICS_H ++ ++#include ++#include ++ ++/* ++ * The guest atomics helpers shares the same logic. We first try to use ++ * the *_timeout version of the operation. If it didn't timeout, then we ++ * successfully updated the memory. Nothing else to do. ++ * ++ * If it did timeout, then it means we didn't manage to update the ++ * memory. This is possibly because the guest is misbehaving (i.e tight ++ * store loop) but can also happen for other reasons (i.e nested Xen). ++ * In that case pause the domain and retry the operation, this time ++ * without a timeout. ++ * ++ * Note, those helpers rely on other part of the code to prevent sharing ++ * a page between Xen and multiple domain. ++ */ ++ ++DECLARE_PER_CPU(unsigned int, guest_safe_atomic_max); ++ ++#define guest_bitop(name) \ ++static inline void guest_##name(struct domain *d, int nr, volatile void *p) \ ++{ \ ++ if ( name##_timeout(nr, p, this_cpu(guest_safe_atomic_max)) ) \ ++ return; \ ++ \ ++ domain_pause_nosync(d); \ ++ name(nr, p); \ ++ domain_unpause(d); \ ++} ++ ++#define guest_testop(name) \ ++static inline int guest_##name(struct domain *d, int nr, volatile void *p) \ ++{ \ ++ bool succeed; \ ++ int oldbit; \ ++ \ ++ succeed = name##_timeout(nr, p, &oldbit, \ ++ this_cpu(guest_safe_atomic_max)); \ ++ if ( succeed ) \ ++ return oldbit; \ ++ \ ++ domain_pause_nosync(d); \ ++ oldbit = name(nr, p); \ ++ domain_unpause(d); \ ++ \ ++ return oldbit; \ ++} ++ ++guest_bitop(set_bit) ++guest_bitop(clear_bit) ++guest_bitop(change_bit) ++ ++#undef guest_bitop ++ ++/* test_bit does not use load-store atomic operations */ ++#define guest_test_bit(d, nr, p) ((void)(d), test_bit(nr, p)) ++ ++guest_testop(test_and_set_bit) ++guest_testop(test_and_clear_bit) ++guest_testop(test_and_change_bit) ++ ++#undef guest_testop ++ ++#endif /* _ARM_GUEST_ATOMICS_H */ ++/* ++ * Local variables: ++ * mode: C ++ * c-file-style: "BSD" ++ * c-basic-offset: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +diff --git a/xen/include/asm-x86/guest_atomics.h b/xen/include/asm-x86/guest_atomics.h +new file mode 100644 +index 0000000000..0c71d2d278 +--- /dev/null ++++ b/xen/include/asm-x86/guest_atomics.h +@@ -0,0 +1,30 @@ ++#ifndef _X86_GUEST_ATOMICS_H ++#define _X86_GUEST_ATOMICS_H ++ ++#include ++ ++/* ++ * It is safe to use the atomics helpers on x86 on memory shared with ++ * the guests. ++ */ ++#define guest_set_bit(d, nr, p) ((void)(d), set_bit(nr, p)) ++#define guest_clear_bit(d, nr, p) ((void)(d), clear_bit(nr, p)) ++#define guest_change_bit(d, nr, p) ((void)(d), change_bit(nr, p)) ++#define guest_test_bit(d, nr, p) ((void)(d), test_bit(nr, p)) ++ ++#define guest_test_and_set_bit(d, nr, p) \ ++ ((void)(d), test_and_set_bit(nr, p)) ++#define guest_test_and_clear_bit(d, nr, p) \ ++ ((void)(d), test_and_clear_bit(nr, p)) ++#define guest_test_and_change_bit(d, nr, p) \ ++ ((void)(d), test_and_change_bit(nr, p)) ++ ++#endif /* _X86_GUEST_ATOMICS_H */ ++/* ++ * Local variables: ++ * mode: C ++ * c-file-style: "BSD" ++ * c-basic-offset: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +-- +2.17.1 + diff --git a/xsa295-4.12-14.patch b/xsa295-4.12-14.patch new file mode 100644 index 0000000..381a9c8 --- /dev/null +++ b/xsa295-4.12-14.patch @@ -0,0 +1,93 @@ +From fefcb78a412f99ea2781595448d60abb690e3246 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:27 +0100 +Subject: [PATCH v2 4.12 14/17] xen/cmpxchg: Provide helper to safely modify + guest memory atomically + +On Arm, exclusive load-store atomics should only be used between trusted +thread. As not all the guests are trusted, it may be possible to DoS Xen +when updating shared memory with guest atomically. + +This patch adds a new helper that will update the guest memory safely. +For x86, it is already possible to use the current helper safely. So +just wrap it. + +For Arm, we will first attempt to update the guest memory with the +loop bounded by a maximum number of iterations. If it fails, we will +pause the domain and try again. + +Note that this heuristics assumes that a page can only +be shared between Xen and one domain. Not Xen and multiple domain. + +The maximum number of iterations is based on how many times atomic_inc() +can be executed in 1uS. The maximum value is per-CPU to cater big.LITTLE +and calculated when the CPU is booting. + +The maximum number of iterations is based on how many times a simple +load-store atomic operation can be executed in 1uS. The maximum +value is per-CPU to cater big.LITTLE and calculated when the CPU is +booting. The heuristic was randomly chosen and can be modified if +impact too much good-behaving guest. + +This is part of XSA-295. + +Signed-of-by: Julien Grall +Reviewed-by: Stefano Stabellini +Acked-by: Jan Beulich +--- + xen/include/asm-arm/guest_atomics.h | 25 +++++++++++++++++++++++++ + xen/include/asm-x86/guest_atomics.h | 2 ++ + 2 files changed, 27 insertions(+) + +diff --git a/xen/include/asm-arm/guest_atomics.h b/xen/include/asm-arm/guest_atomics.h +index 4f127fda41..61925d313d 100644 +--- a/xen/include/asm-arm/guest_atomics.h ++++ b/xen/include/asm-arm/guest_atomics.h +@@ -65,6 +65,31 @@ guest_testop(test_and_change_bit) + + #undef guest_testop + ++static inline unsigned long __guest_cmpxchg(struct domain *d, ++ volatile void *ptr, ++ unsigned long old, ++ unsigned long new, ++ unsigned int size) ++{ ++ unsigned long oldval = old; ++ ++ if ( __cmpxchg_mb_timeout(ptr, &oldval, new, size, ++ this_cpu(guest_safe_atomic_max)) ) ++ return oldval; ++ ++ domain_pause_nosync(d); ++ oldval = __cmpxchg_mb(ptr, old, new, size); ++ domain_unpause(d); ++ ++ return oldval; ++} ++ ++#define guest_cmpxchg(d, ptr, o, n) \ ++ ((__typeof__(*(ptr)))__guest_cmpxchg(d, ptr, \ ++ (unsigned long)(o),\ ++ (unsigned long)(n),\ ++ sizeof (*(ptr)))) ++ + #endif /* _ARM_GUEST_ATOMICS_H */ + /* + * Local variables: +diff --git a/xen/include/asm-x86/guest_atomics.h b/xen/include/asm-x86/guest_atomics.h +index 0c71d2d278..029417c8ff 100644 +--- a/xen/include/asm-x86/guest_atomics.h ++++ b/xen/include/asm-x86/guest_atomics.h +@@ -19,6 +19,8 @@ + #define guest_test_and_change_bit(d, nr, p) \ + ((void)(d), test_and_change_bit(nr, p)) + ++#define guest_cmpxchg(d, ptr, o, n) ((void)(d), cmpxchg(ptr, o, n)) ++ + #endif /* _X86_GUEST_ATOMICS_H */ + /* + * Local variables: +-- +2.17.1 + diff --git a/xsa295-4.12-15.patch b/xsa295-4.12-15.patch new file mode 100644 index 0000000..976344b --- /dev/null +++ b/xsa295-4.12-15.patch @@ -0,0 +1,577 @@ +From d7bf1476465d084d062459ff437396a517facd37 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:28 +0100 +Subject: [PATCH v2 4.12 15/17] xen: Use guest atomics helpers when modifying + atomically guest memory + +On Arm, exclusive load-store atomics should only be used between trusted +thread. As not all the guests are trusted, it may be possible to DoS Xen +when updating shared memory with guest atomically. + +This patch replaces all the atomics operations on shared memory with +a guest by the new guest atomics helpers. The x86 code was not audited +to know where guest atomics helpers could be used. I will leave that +to the x86 folks. + +Note that some rework was required in order to plumb use the new guest +atomics in event channel and grant-table. + +Because guest_test_bit is ignoring the parameter "d" for now, it +means there a lot of places do not need to drop the const. We may want +to revisit this in the future if the parameter "d" becomes necessary. + +This is part of XSA-295. + +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +--- + xen/arch/arm/domain.c | 3 +- + xen/arch/arm/mm.c | 6 ++-- + xen/common/event_2l.c | 26 ++++++++------ + xen/common/event_fifo.c | 44 ++++++++++++----------- + xen/common/grant_table.c | 59 +++++++++++++++++-------------- + xen/include/asm-arm/grant_table.h | 2 +- + xen/include/asm-x86/grant_table.h | 3 +- + 7 files changed, 79 insertions(+), 64 deletions(-) + +diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c +index 6dc633ed50..c3d9d42ada 100644 +--- a/xen/arch/arm/domain.c ++++ b/xen/arch/arm/domain.c +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1017,7 +1018,7 @@ void arch_dump_vcpu_info(struct vcpu *v) + + void vcpu_mark_events_pending(struct vcpu *v) + { +- int already_pending = test_and_set_bit( ++ bool already_pending = guest_test_and_set_bit(v->domain, + 0, (unsigned long *)&vcpu_info(v, evtchn_upcall_pending)); + + if ( already_pending ) +diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c +index 01ae2cccc0..3a7cfb1b50 100644 +--- a/xen/arch/arm/mm.c ++++ b/xen/arch/arm/mm.c +@@ -40,6 +40,8 @@ + #include + #include + #include ++ ++#include + #include + + struct domain *dom_xen, *dom_io, *dom_cow; +@@ -1380,7 +1382,7 @@ void put_page_type(struct page_info *page) + return; + } + +-void gnttab_clear_flag(unsigned long nr, uint16_t *addr) ++void gnttab_clear_flag(struct domain *d, unsigned long nr, uint16_t *addr) + { + /* + * Note that this cannot be clear_bit(), as the access must be +@@ -1390,7 +1392,7 @@ void gnttab_clear_flag(unsigned long nr, uint16_t *addr) + + do { + old = *addr; +- } while (cmpxchg(addr, old, old & mask) != old); ++ } while (guest_cmpxchg(d, addr, old, old & mask) != old); + } + + void gnttab_mark_dirty(struct domain *d, mfn_t mfn) +diff --git a/xen/common/event_2l.c b/xen/common/event_2l.c +index 8ca90899ab..e1dbb860f4 100644 +--- a/xen/common/event_2l.c ++++ b/xen/common/event_2l.c +@@ -13,6 +13,8 @@ + #include + #include + ++#include ++ + static void evtchn_2l_set_pending(struct vcpu *v, struct evtchn *evtchn) + { + struct domain *d = v->domain; +@@ -25,12 +27,12 @@ static void evtchn_2l_set_pending(struct vcpu *v, struct evtchn *evtchn) + * others may require explicit memory barriers. + */ + +- if ( test_and_set_bit(port, &shared_info(d, evtchn_pending)) ) ++ if ( guest_test_and_set_bit(d, port, &shared_info(d, evtchn_pending)) ) + return; + +- if ( !test_bit (port, &shared_info(d, evtchn_mask)) && +- !test_and_set_bit(port / BITS_PER_EVTCHN_WORD(d), +- &vcpu_info(v, evtchn_pending_sel)) ) ++ if ( !guest_test_bit(d, port, &shared_info(d, evtchn_mask)) && ++ !guest_test_and_set_bit(d, port / BITS_PER_EVTCHN_WORD(d), ++ &vcpu_info(v, evtchn_pending_sel)) ) + { + vcpu_mark_events_pending(v); + } +@@ -40,7 +42,7 @@ static void evtchn_2l_set_pending(struct vcpu *v, struct evtchn *evtchn) + + static void evtchn_2l_clear_pending(struct domain *d, struct evtchn *evtchn) + { +- clear_bit(evtchn->port, &shared_info(d, evtchn_pending)); ++ guest_clear_bit(d, evtchn->port, &shared_info(d, evtchn_pending)); + } + + static void evtchn_2l_unmask(struct domain *d, struct evtchn *evtchn) +@@ -52,10 +54,10 @@ static void evtchn_2l_unmask(struct domain *d, struct evtchn *evtchn) + * These operations must happen in strict order. Based on + * evtchn_2l_set_pending() above. + */ +- if ( test_and_clear_bit(port, &shared_info(d, evtchn_mask)) && +- test_bit (port, &shared_info(d, evtchn_pending)) && +- !test_and_set_bit (port / BITS_PER_EVTCHN_WORD(d), +- &vcpu_info(v, evtchn_pending_sel)) ) ++ if ( guest_test_and_clear_bit(d, port, &shared_info(d, evtchn_mask)) && ++ guest_test_bit(d, port, &shared_info(d, evtchn_pending)) && ++ !guest_test_and_set_bit(d, port / BITS_PER_EVTCHN_WORD(d), ++ &vcpu_info(v, evtchn_pending_sel)) ) + { + vcpu_mark_events_pending(v); + } +@@ -66,7 +68,8 @@ static bool evtchn_2l_is_pending(const struct domain *d, evtchn_port_t port) + unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); + + ASSERT(port < max_ports); +- return port < max_ports && test_bit(port, &shared_info(d, evtchn_pending)); ++ return (port < max_ports && ++ guest_test_bit(d, port, &shared_info(d, evtchn_pending))); + } + + static bool evtchn_2l_is_masked(const struct domain *d, evtchn_port_t port) +@@ -74,7 +77,8 @@ static bool evtchn_2l_is_masked(const struct domain *d, evtchn_port_t port) + unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); + + ASSERT(port < max_ports); +- return port >= max_ports || test_bit(port, &shared_info(d, evtchn_mask)); ++ return (port >= max_ports || ++ guest_test_bit(d, port, &shared_info(d, evtchn_mask))); + } + + static void evtchn_2l_print_state(struct domain *d, +diff --git a/xen/common/event_fifo.c b/xen/common/event_fifo.c +index 3eecab3f22..230f440f14 100644 +--- a/xen/common/event_fifo.c ++++ b/xen/common/event_fifo.c +@@ -17,6 +17,8 @@ + #include + #include + ++#include ++ + #include + + static inline event_word_t *evtchn_fifo_word_from_port(const struct domain *d, +@@ -51,7 +53,7 @@ static void evtchn_fifo_init(struct domain *d, struct evtchn *evtchn) + * on the wrong VCPU or with an unexpected priority. + */ + word = evtchn_fifo_word_from_port(d, evtchn->port); +- if ( word && test_bit(EVTCHN_FIFO_LINKED, word) ) ++ if ( word && guest_test_bit(d, EVTCHN_FIFO_LINKED, word) ) + gdprintk(XENLOG_WARNING, "domain %d, port %d already on a queue\n", + d->domain_id, evtchn->port); + } +@@ -116,7 +118,7 @@ static int try_set_link(event_word_t *word, event_word_t *w, uint32_t link) + * We block unmasking by the guest by marking the tail word as BUSY, + * therefore, the cmpxchg() may fail at most 4 times. + */ +-static bool_t evtchn_fifo_set_link(const struct domain *d, event_word_t *word, ++static bool_t evtchn_fifo_set_link(struct domain *d, event_word_t *word, + uint32_t link) + { + event_word_t w; +@@ -130,7 +132,7 @@ static bool_t evtchn_fifo_set_link(const struct domain *d, event_word_t *word, + return ret; + + /* Lock the word to prevent guest unmasking. */ +- set_bit(EVTCHN_FIFO_BUSY, word); ++ guest_set_bit(d, EVTCHN_FIFO_BUSY, word); + + w = read_atomic(word); + +@@ -140,13 +142,13 @@ static bool_t evtchn_fifo_set_link(const struct domain *d, event_word_t *word, + if ( ret >= 0 ) + { + if ( ret == 0 ) +- clear_bit(EVTCHN_FIFO_BUSY, word); ++ guest_clear_bit(d, EVTCHN_FIFO_BUSY, word); + return ret; + } + } + gdprintk(XENLOG_WARNING, "domain %d, port %d not linked\n", + d->domain_id, link); +- clear_bit(EVTCHN_FIFO_BUSY, word); ++ guest_clear_bit(d, EVTCHN_FIFO_BUSY, word); + return 1; + } + +@@ -171,13 +173,13 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) + return; + } + +- was_pending = test_and_set_bit(EVTCHN_FIFO_PENDING, word); ++ was_pending = guest_test_and_set_bit(d, EVTCHN_FIFO_PENDING, word); + + /* + * Link the event if it unmasked and not already linked. + */ +- if ( !test_bit(EVTCHN_FIFO_MASKED, word) +- && !test_bit(EVTCHN_FIFO_LINKED, word) ) ++ if ( !guest_test_bit(d, EVTCHN_FIFO_MASKED, word) && ++ !guest_test_bit(d, EVTCHN_FIFO_LINKED, word) ) + { + struct evtchn_fifo_queue *q, *old_q; + event_word_t *tail_word; +@@ -206,7 +208,7 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) + if ( !old_q ) + goto done; + +- if ( test_and_set_bit(EVTCHN_FIFO_LINKED, word) ) ++ if ( guest_test_and_set_bit(d, EVTCHN_FIFO_LINKED, word) ) + { + spin_unlock_irqrestore(&old_q->lock, flags); + goto done; +@@ -252,8 +254,8 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) + spin_unlock_irqrestore(&q->lock, flags); + + if ( !linked +- && !test_and_set_bit(q->priority, +- &v->evtchn_fifo->control_block->ready) ) ++ && !guest_test_and_set_bit(d, q->priority, ++ &v->evtchn_fifo->control_block->ready) ) + vcpu_mark_events_pending(v); + } + done: +@@ -275,7 +277,7 @@ static void evtchn_fifo_clear_pending(struct domain *d, struct evtchn *evtchn) + * No need to unlink as the guest will unlink and ignore + * non-pending events. + */ +- clear_bit(EVTCHN_FIFO_PENDING, word); ++ guest_clear_bit(d, EVTCHN_FIFO_PENDING, word); + } + + static void evtchn_fifo_unmask(struct domain *d, struct evtchn *evtchn) +@@ -287,10 +289,10 @@ static void evtchn_fifo_unmask(struct domain *d, struct evtchn *evtchn) + if ( unlikely(!word) ) + return; + +- clear_bit(EVTCHN_FIFO_MASKED, word); ++ guest_clear_bit(d, EVTCHN_FIFO_MASKED, word); + + /* Relink if pending. */ +- if ( test_bit(EVTCHN_FIFO_PENDING, word) ) ++ if ( guest_test_bit(d, EVTCHN_FIFO_PENDING, word) ) + evtchn_fifo_set_pending(v, evtchn); + } + +@@ -298,21 +300,21 @@ static bool evtchn_fifo_is_pending(const struct domain *d, evtchn_port_t port) + { + const event_word_t *word = evtchn_fifo_word_from_port(d, port); + +- return word && test_bit(EVTCHN_FIFO_PENDING, word); ++ return word && guest_test_bit(d, EVTCHN_FIFO_PENDING, word); + } + + static bool_t evtchn_fifo_is_masked(const struct domain *d, evtchn_port_t port) + { + const event_word_t *word = evtchn_fifo_word_from_port(d, port); + +- return !word || test_bit(EVTCHN_FIFO_MASKED, word); ++ return !word || guest_test_bit(d, EVTCHN_FIFO_MASKED, word); + } + + static bool_t evtchn_fifo_is_busy(const struct domain *d, evtchn_port_t port) + { + const event_word_t *word = evtchn_fifo_word_from_port(d, port); + +- return word && test_bit(EVTCHN_FIFO_LINKED, word); ++ return word && guest_test_bit(d, EVTCHN_FIFO_LINKED, word); + } + + static int evtchn_fifo_set_priority(struct domain *d, struct evtchn *evtchn, +@@ -339,11 +341,11 @@ static void evtchn_fifo_print_state(struct domain *d, + word = evtchn_fifo_word_from_port(d, evtchn->port); + if ( !word ) + printk("? "); +- else if ( test_bit(EVTCHN_FIFO_LINKED, word) ) +- printk("%c %-4u", test_bit(EVTCHN_FIFO_BUSY, word) ? 'B' : ' ', ++ else if ( guest_test_bit(d, EVTCHN_FIFO_LINKED, word) ) ++ printk("%c %-4u", guest_test_bit(d, EVTCHN_FIFO_BUSY, word) ? 'B' : ' ', + *word & EVTCHN_FIFO_LINK_MASK); + else +- printk("%c - ", test_bit(EVTCHN_FIFO_BUSY, word) ? 'B' : ' '); ++ printk("%c - ", guest_test_bit(d, EVTCHN_FIFO_BUSY, word) ? 'B' : ' '); + } + + static const struct evtchn_port_ops evtchn_port_ops_fifo = +@@ -495,7 +497,7 @@ static void setup_ports(struct domain *d) + + evtchn = evtchn_from_port(d, port); + +- if ( test_bit(port, &shared_info(d, evtchn_pending)) ) ++ if ( guest_test_bit(d, port, &shared_info(d, evtchn_pending)) ) + evtchn->pending = 1; + + evtchn_fifo_set_priority(d, evtchn, EVTCHN_FIFO_PRIORITY_DEFAULT); +diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c +index 1b82d534a3..e9ce0ac473 100644 +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -39,6 +39,7 @@ + #include + #include + #include ++#include + + /* Per-domain grant information. */ + struct grant_table { +@@ -646,6 +647,7 @@ static unsigned int nr_grant_entries(struct grant_table *gt) + } + + static int _set_status_v1(const grant_entry_header_t *shah, ++ struct domain *rd, + struct active_grant_entry *act, + int readonly, + int mapflag, +@@ -701,8 +703,8 @@ static int _set_status_v1(const grant_entry_header_t *shah, + "Attempt to write-pin a r/o grant entry\n"); + } + +- prev_scombo.word = cmpxchg((u32 *)shah, +- scombo.word, new_scombo.word); ++ prev_scombo.word = guest_cmpxchg(rd, (u32 *)shah, ++ scombo.word, new_scombo.word); + if ( likely(prev_scombo.word == scombo.word) ) + break; + +@@ -719,6 +721,7 @@ done: + + static int _set_status_v2(const grant_entry_header_t *shah, + grant_status_t *status, ++ struct domain *rd, + struct active_grant_entry *act, + int readonly, + int mapflag, +@@ -781,8 +784,8 @@ static int _set_status_v2(const grant_entry_header_t *shah, + (id != ldomid) || + (!readonly && (flags & GTF_readonly)) ) + { +- gnttab_clear_flag(_GTF_writing, status); +- gnttab_clear_flag(_GTF_reading, status); ++ gnttab_clear_flag(rd, _GTF_writing, status); ++ gnttab_clear_flag(rd, _GTF_reading, status); + PIN_FAIL(done, GNTST_general_error, + "Unstable flags (%x) or dom (%d); expected d%d (r/w: %d)\n", + flags, id, ldomid, !readonly); +@@ -792,7 +795,7 @@ static int _set_status_v2(const grant_entry_header_t *shah, + { + if ( unlikely(flags & GTF_readonly) ) + { +- gnttab_clear_flag(_GTF_writing, status); ++ gnttab_clear_flag(rd, _GTF_writing, status); + PIN_FAIL(done, GNTST_general_error, + "Unstable grant readonly flag\n"); + } +@@ -805,6 +808,7 @@ done: + + static int _set_status(const grant_entry_header_t *shah, + grant_status_t *status, ++ struct domain *rd, + unsigned rgt_version, + struct active_grant_entry *act, + int readonly, +@@ -813,9 +817,9 @@ static int _set_status(const grant_entry_header_t *shah, + { + + if ( rgt_version == 1 ) +- return _set_status_v1(shah, act, readonly, mapflag, ldomid); ++ return _set_status_v1(shah, rd, act, readonly, mapflag, ldomid); + else +- return _set_status_v2(shah, status, act, readonly, mapflag, ldomid); ++ return _set_status_v2(shah, status, rd, act, readonly, mapflag, ldomid); + } + + static struct active_grant_entry *grant_map_exists(const struct domain *ld, +@@ -980,7 +984,7 @@ map_grant_ref( + (!(op->flags & GNTMAP_readonly) && + !(act->pin & (GNTPIN_hstw_mask|GNTPIN_devw_mask))) ) + { +- if ( (rc = _set_status(shah, status, rgt->gt_version, act, ++ if ( (rc = _set_status(shah, status, rd, rgt->gt_version, act, + op->flags & GNTMAP_readonly, 1, + ld->domain_id) != GNTST_okay) ) + goto act_release_out; +@@ -1204,10 +1208,10 @@ map_grant_ref( + unlock_out_clear: + if ( !(op->flags & GNTMAP_readonly) && + !(act->pin & (GNTPIN_hstw_mask|GNTPIN_devw_mask)) ) +- gnttab_clear_flag(_GTF_writing, status); ++ gnttab_clear_flag(rd, _GTF_writing, status); + + if ( !act->pin ) +- gnttab_clear_flag(_GTF_reading, status); ++ gnttab_clear_flag(rd, _GTF_reading, status); + + act_release_out: + active_entry_release(act); +@@ -1477,10 +1481,10 @@ unmap_common_complete(struct gnttab_unmap_common *op) + + if ( ((act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)) == 0) && + !(op->done & GNTMAP_readonly) ) +- gnttab_clear_flag(_GTF_writing, status); ++ gnttab_clear_flag(rd, _GTF_writing, status); + + if ( act->pin == 0 ) +- gnttab_clear_flag(_GTF_reading, status); ++ gnttab_clear_flag(rd, _GTF_reading, status); + + active_entry_release(act); + grant_read_unlock(rgt); +@@ -2045,8 +2049,8 @@ gnttab_prepare_for_transfer( + new_scombo = scombo; + new_scombo.shorts.flags |= GTF_transfer_committed; + +- prev_scombo.word = cmpxchg((u32 *)&sha->flags, +- scombo.word, new_scombo.word); ++ prev_scombo.word = guest_cmpxchg(rd, (u32 *)&sha->flags, ++ scombo.word, new_scombo.word); + if ( likely(prev_scombo.word == scombo.word) ) + break; + +@@ -2339,11 +2343,11 @@ release_grant_for_copy( + + act->pin -= GNTPIN_hstw_inc; + if ( !(act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)) ) +- gnttab_clear_flag(_GTF_writing, status); ++ gnttab_clear_flag(rd, _GTF_writing, status); + } + + if ( !act->pin ) +- gnttab_clear_flag(_GTF_reading, status); ++ gnttab_clear_flag(rd, _GTF_reading, status); + + active_entry_release(act); + grant_read_unlock(rgt); +@@ -2365,14 +2369,15 @@ release_grant_for_copy( + under the domain's grant table lock. */ + /* Only safe on transitive grants. Even then, note that we don't + attempt to drop any pin on the referent grant. */ +-static void fixup_status_for_copy_pin(const struct active_grant_entry *act, ++static void fixup_status_for_copy_pin(struct domain *rd, ++ const struct active_grant_entry *act, + uint16_t *status) + { + if ( !(act->pin & (GNTPIN_hstw_mask | GNTPIN_devw_mask)) ) +- gnttab_clear_flag(_GTF_writing, status); ++ gnttab_clear_flag(rd, _GTF_writing, status); + + if ( !act->pin ) +- gnttab_clear_flag(_GTF_reading, status); ++ gnttab_clear_flag(rd, _GTF_reading, status); + } + + /* +@@ -2434,7 +2439,7 @@ acquire_grant_for_copy( + { + if ( (!old_pin || (!readonly && + !(old_pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)))) && +- (rc = _set_status_v2(shah, status, act, readonly, 0, ++ (rc = _set_status_v2(shah, status, rd, act, readonly, 0, + ldom)) != GNTST_okay ) + goto unlock_out; + +@@ -2483,7 +2488,7 @@ acquire_grant_for_copy( + + if ( rc != GNTST_okay ) + { +- fixup_status_for_copy_pin(act, status); ++ fixup_status_for_copy_pin(rd, act, status); + rcu_unlock_domain(td); + active_entry_release(act); + grant_read_unlock(rgt); +@@ -2506,7 +2511,7 @@ acquire_grant_for_copy( + !act->is_sub_page)) ) + { + release_grant_for_copy(td, trans_gref, readonly); +- fixup_status_for_copy_pin(act, status); ++ fixup_status_for_copy_pin(rd, act, status); + rcu_unlock_domain(td); + active_entry_release(act); + grant_read_unlock(rgt); +@@ -2535,7 +2540,7 @@ acquire_grant_for_copy( + else if ( !old_pin || + (!readonly && !(old_pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask))) ) + { +- if ( (rc = _set_status(shah, status, rgt->gt_version, act, ++ if ( (rc = _set_status(shah, status, rd, rgt->gt_version, act, + readonly, 0, ldom)) != GNTST_okay ) + goto unlock_out; + +@@ -2623,10 +2628,10 @@ acquire_grant_for_copy( + unlock_out_clear: + if ( !(readonly) && + !(act->pin & (GNTPIN_hstw_mask | GNTPIN_devw_mask)) ) +- gnttab_clear_flag(_GTF_writing, status); ++ gnttab_clear_flag(rd, _GTF_writing, status); + + if ( !act->pin ) +- gnttab_clear_flag(_GTF_reading, status); ++ gnttab_clear_flag(rd, _GTF_reading, status); + + unlock_out: + active_entry_release(act); +@@ -3661,11 +3666,11 @@ gnttab_release_mappings( + } + + if ( (act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)) == 0 ) +- gnttab_clear_flag(_GTF_writing, status); ++ gnttab_clear_flag(rd, _GTF_writing, status); + } + + if ( act->pin == 0 ) +- gnttab_clear_flag(_GTF_reading, status); ++ gnttab_clear_flag(rd, _GTF_reading, status); + + active_entry_release(act); + grant_read_unlock(rgt); +diff --git a/xen/include/asm-arm/grant_table.h b/xen/include/asm-arm/grant_table.h +index 816e3c6d68..5e9aa53814 100644 +--- a/xen/include/asm-arm/grant_table.h ++++ b/xen/include/asm-arm/grant_table.h +@@ -14,7 +14,7 @@ struct grant_table_arch { + gfn_t *status_gfn; + }; + +-void gnttab_clear_flag(unsigned long nr, uint16_t *addr); ++void gnttab_clear_flag(struct domain *d, unsigned long nr, uint16_t *addr); + int create_grant_host_mapping(unsigned long gpaddr, mfn_t mfn, + unsigned int flags, unsigned int cache_flags); + #define gnttab_host_mapping_get_page_type(ro, ld, rd) (0) +diff --git a/xen/include/asm-x86/grant_table.h b/xen/include/asm-x86/grant_table.h +index 4b8c4f9160..11f061aa2d 100644 +--- a/xen/include/asm-x86/grant_table.h ++++ b/xen/include/asm-x86/grant_table.h +@@ -64,7 +64,8 @@ static inline int replace_grant_host_mapping(uint64_t addr, mfn_t frame, + + #define gnttab_mark_dirty(d, f) paging_mark_dirty((d), f) + +-static inline void gnttab_clear_flag(unsigned int nr, uint16_t *st) ++static inline void gnttab_clear_flag(struct domain *d, unsigned int nr, ++ uint16_t *st) + { + /* + * Note that this cannot be clear_bit(), as the access must be +-- +2.17.1 + diff --git a/xsa295-4.12-16.patch b/xsa295-4.12-16.patch new file mode 100644 index 0000000..fa9263f --- /dev/null +++ b/xsa295-4.12-16.patch @@ -0,0 +1,84 @@ +From b5142d29a96d6c5bbd505d2ba299b1ef0416391a Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:29 +0100 +Subject: [PATCH v2 4.12 16/17] xen/arm: Add performance counters in guest + atomic helpers + +Add performance counters in guest atomic helpers to be able to detect +whether a guest is often paused during the operations. + +This is part of XSA-295. + +Signed-off-by: Julien Grall +Acked-by: Stefano Stabellini +--- + xen/include/asm-arm/guest_atomics.h | 12 ++++++++++++ + xen/include/asm-arm/perfc_defn.h | 3 +++ + 2 files changed, 15 insertions(+) + +diff --git a/xen/include/asm-arm/guest_atomics.h b/xen/include/asm-arm/guest_atomics.h +index 61925d313d..698508bf87 100644 +--- a/xen/include/asm-arm/guest_atomics.h ++++ b/xen/include/asm-arm/guest_atomics.h +@@ -24,9 +24,13 @@ DECLARE_PER_CPU(unsigned int, guest_safe_atomic_max); + #define guest_bitop(name) \ + static inline void guest_##name(struct domain *d, int nr, volatile void *p) \ + { \ ++ perfc_incr(atomics_guest); \ ++ \ + if ( name##_timeout(nr, p, this_cpu(guest_safe_atomic_max)) ) \ + return; \ + \ ++ perfc_incr(atomics_guest_paused); \ ++ \ + domain_pause_nosync(d); \ + name(nr, p); \ + domain_unpause(d); \ +@@ -38,11 +42,15 @@ static inline int guest_##name(struct domain *d, int nr, volatile void *p) \ + bool succeed; \ + int oldbit; \ + \ ++ perfc_incr(atomics_guest); \ ++ \ + succeed = name##_timeout(nr, p, &oldbit, \ + this_cpu(guest_safe_atomic_max)); \ + if ( succeed ) \ + return oldbit; \ + \ ++ perfc_incr(atomics_guest_paused); \ ++ \ + domain_pause_nosync(d); \ + oldbit = name(nr, p); \ + domain_unpause(d); \ +@@ -73,10 +81,14 @@ static inline unsigned long __guest_cmpxchg(struct domain *d, + { + unsigned long oldval = old; + ++ perfc_incr(atomics_guest); ++ + if ( __cmpxchg_mb_timeout(ptr, &oldval, new, size, + this_cpu(guest_safe_atomic_max)) ) + return oldval; + ++ perfc_incr(atomics_guest_paused); ++ + domain_pause_nosync(d); + oldval = __cmpxchg_mb(ptr, old, new, size); + domain_unpause(d); +diff --git a/xen/include/asm-arm/perfc_defn.h b/xen/include/asm-arm/perfc_defn.h +index 8922e9525a..6a83185163 100644 +--- a/xen/include/asm-arm/perfc_defn.h ++++ b/xen/include/asm-arm/perfc_defn.h +@@ -73,6 +73,9 @@ PERFCOUNTER(phys_timer_irqs, "Physical timer interrupts") + PERFCOUNTER(virt_timer_irqs, "Virtual timer interrupts") + PERFCOUNTER(maintenance_irqs, "Maintenance interrupts") + ++PERFCOUNTER(atomics_guest, "atomics: guest access") ++PERFCOUNTER(atomics_guest_paused, "atomics: guest paused") ++ + /*#endif*/ /* __XEN_PERFC_DEFN_H__ */ + + /* +-- +2.17.1 + diff --git a/xsa295-4.12-17.patch b/xsa295-4.12-17.patch new file mode 100644 index 0000000..d8884db --- /dev/null +++ b/xsa295-4.12-17.patch @@ -0,0 +1,199 @@ +From c338acb8b71b8aad258f4c3c0fda28ef294ed95c Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 29 Apr 2019 15:05:30 +0100 +Subject: [PATCH v2 4.12 17/17] xen/arm: grant-table: Protect gnttab_clear_flag + against guest misbehavior + +The function gnttab_clear_flag is used to clear the access flags. On +Arm, it is implemented using a loop and guest_cmpxchg. + +It is possible that guest_cmpxchg will always return a different value +than old. This can happen if the guest updated the memory before Xen has +time to do the exchange. Because of that, there are no way for to +promise the loop will end. + +It is possible to make the current code safe by re-using the same +principle as applied on the guest atomic helper. However this patch +takes a different approach that should lead to more efficient code in +the default case. + +A new helper is introduced to clear a set of bits on a 16-bits word. +This should avoid a an extra loop to check cmpxchg succeeded. + +Note that a mask is used instead of a bit, so the helper can be re-used +later on for clearing multiple flags at the same time. + +This is part of XSA-295. + +Reported-by: Andrew Cooper +Signed-off-by: Julien Grall +Signed-off-by: Stefano Stabellini +Reviewed-by: Stefano Stabellini +--- + xen/arch/arm/arm32/lib/bitops.c | 35 +++++++++++++++++++++++++++++ + xen/arch/arm/arm64/lib/bitops.c | 33 +++++++++++++++++++++++++++ + xen/arch/arm/mm.c | 10 +-------- + xen/include/asm-arm/bitops.h | 4 ++++ + xen/include/asm-arm/guest_atomics.h | 13 +++++++++++ + 5 files changed, 86 insertions(+), 9 deletions(-) + +diff --git a/xen/arch/arm/arm32/lib/bitops.c b/xen/arch/arm/arm32/lib/bitops.c +index 08750314fc..3dca769bf0 100644 +--- a/xen/arch/arm/arm32/lib/bitops.c ++++ b/xen/arch/arm/arm32/lib/bitops.c +@@ -126,6 +126,41 @@ testop(test_and_change_bit, eor) + testop(test_and_clear_bit, bic) + testop(test_and_set_bit, orr) + ++static always_inline bool int_clear_mask16(uint16_t mask, volatile uint16_t *p, ++ bool timeout, unsigned int max_try) ++{ ++ unsigned long res, tmp; ++ ++ prefetchw((const uint16_t *)p); ++ ++ do ++ { ++ asm volatile ("// int_clear_mask16\n" ++ " ldrexh %2, %1\n" ++ " bic %2, %2, %3\n" ++ " strexh %0, %2, %1\n" ++ : "=&r" (res), "+Qo" (*p), "=&r" (tmp) ++ : "r" (mask)); ++ ++ if ( !res ) ++ break; ++ } while ( !timeout || ((--max_try) > 0) ); ++ ++ return !res; ++} ++ ++void clear_mask16(uint16_t mask, volatile void *p) ++{ ++ if ( !int_clear_mask16(mask, p, false, 0) ) ++ ASSERT_UNREACHABLE(); ++} ++ ++bool clear_mask16_timeout(uint16_t mask, volatile void *p, ++ unsigned int max_try) ++{ ++ return int_clear_mask16(mask, p, true, max_try); ++} ++ + /* + * Local variables: + * mode: C +diff --git a/xen/arch/arm/arm64/lib/bitops.c b/xen/arch/arm/arm64/lib/bitops.c +index 78bf4ed8c5..27688e5418 100644 +--- a/xen/arch/arm/arm64/lib/bitops.c ++++ b/xen/arch/arm/arm64/lib/bitops.c +@@ -118,6 +118,39 @@ testop(test_and_change_bit, eor) + testop(test_and_clear_bit, bic) + testop(test_and_set_bit, orr) + ++static always_inline bool int_clear_mask16(uint16_t mask, volatile uint16_t *p, ++ bool timeout, unsigned int max_try) ++{ ++ unsigned long res, tmp; ++ ++ do ++ { ++ asm volatile ("// int_clear_mask16\n" ++ " ldxrh %w2, %1\n" ++ " bic %w2, %w2, %w3\n" ++ " stxrh %w0, %w2, %1\n" ++ : "=&r" (res), "+Q" (*p), "=&r" (tmp) ++ : "r" (mask)); ++ ++ if ( !res ) ++ break; ++ } while ( !timeout || ((--max_try) > 0) ); ++ ++ return !res; ++} ++ ++void clear_mask16(uint16_t mask, volatile void *p) ++{ ++ if ( !int_clear_mask16(mask, p, false, 0) ) ++ ASSERT_UNREACHABLE(); ++} ++ ++bool clear_mask16_timeout(uint16_t mask, volatile void *p, ++ unsigned int max_try) ++{ ++ return int_clear_mask16(mask, p, true, max_try); ++} ++ + /* + * Local variables: + * mode: C +diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c +index 3a7cfb1b50..8a53544975 100644 +--- a/xen/arch/arm/mm.c ++++ b/xen/arch/arm/mm.c +@@ -1384,15 +1384,7 @@ void put_page_type(struct page_info *page) + + void gnttab_clear_flag(struct domain *d, unsigned long nr, uint16_t *addr) + { +- /* +- * Note that this cannot be clear_bit(), as the access must be +- * confined to the specified 2 bytes. +- */ +- uint16_t mask = ~(1 << nr), old; +- +- do { +- old = *addr; +- } while (guest_cmpxchg(d, addr, old, old & mask) != old); ++ guest_clear_mask16(d, BIT(nr), addr); + } + + void gnttab_mark_dirty(struct domain *d, mfn_t mfn) +diff --git a/xen/include/asm-arm/bitops.h b/xen/include/asm-arm/bitops.h +index f6782b33be..f989bc726c 100644 +--- a/xen/include/asm-arm/bitops.h ++++ b/xen/include/asm-arm/bitops.h +@@ -53,6 +53,8 @@ int test_and_set_bit(int nr, volatile void *p); + int test_and_clear_bit(int nr, volatile void *p); + int test_and_change_bit(int nr, volatile void *p); + ++void clear_mask16(uint16_t mask, volatile void *p); ++ + /* + * The helpers below may fail to update the memory if the action takes + * too long. +@@ -71,6 +73,8 @@ bool test_and_clear_bit_timeout(int nr, volatile void *p, + int *oldbit, unsigned int max_try); + bool test_and_change_bit_timeout(int nr, volatile void *p, + int *oldbit, unsigned int max_try); ++bool clear_mask16_timeout(uint16_t mask, volatile void *p, ++ unsigned int max_try); + + /** + * __test_and_set_bit - Set a bit and return its old value +diff --git a/xen/include/asm-arm/guest_atomics.h b/xen/include/asm-arm/guest_atomics.h +index 698508bf87..af27cc627b 100644 +--- a/xen/include/asm-arm/guest_atomics.h ++++ b/xen/include/asm-arm/guest_atomics.h +@@ -73,6 +73,19 @@ guest_testop(test_and_change_bit) + + #undef guest_testop + ++static inline void guest_clear_mask16(struct domain *d, uint16_t mask, ++ volatile uint16_t *p) ++{ ++ perfc_incr(atomics_guest); ++ ++ if ( clear_mask16_timeout(mask, p, this_cpu(guest_safe_atomic_max)) ) ++ return; ++ ++ domain_pause_nosync(d); ++ clear_mask16(mask, p); ++ domain_unpause(d); ++} ++ + static inline unsigned long __guest_cmpxchg(struct domain *d, + volatile void *ptr, + unsigned long old, +-- +2.17.1 + From fcaf32cd714b6fe11c1e416ea772223585302cf2 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 19 Jun 2019 19:36:49 +0100 Subject: [PATCH 011/194] some debug files are now properly packaged in debuginfo rpms --- xen.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xen.spec b/xen.spec index f79bf2d..1bd1a80 100644 --- a/xen.spec +++ b/xen.spec @@ -867,7 +867,6 @@ fi %endif %{_sbindir}/xen-livepatch %{_sbindir}/xen-diag -/usr/lib/debug/usr/bin/xenstore* # Xen logfiles %dir %attr(0700,root,root) %{_localstatedir}/log/xen @@ -930,8 +929,9 @@ fi %endif %changelog -* Tue Jun 18 2019 Michael Young - 4.12.0-4 +* Wed Jun 19 2019 Michael Young - 4.12.0-4 - Unlimited Arm Atomics Operations [XSA-295] (#1720760) +- some debug files are now properly packaged in debuginfo rpms * Tue Jun 18 2019 Zbigniew Jędrzejewski-Szmek - Fix build with python3.8 (#1704807) From 842cba0ff778261bfd137072ff10a10953650e66 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Mon, 1 Jul 2019 21:56:32 +0100 Subject: [PATCH 012/194] update to xen-4.11.2 --- .gitignore | 2 +- sources | 2 +- xen.gcc9.fixes.patch | 11 ----- xen.spec | 92 ++++----------------------------------- xen.use.fedora.ipxe.patch | 2 +- 5 files changed, 11 insertions(+), 98 deletions(-) diff --git a/.gitignore b/.gitignore index 130be34..767ea3b 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.11.1.tar.gz +/xen-4.11.2.tar.gz diff --git a/sources b/sources index f2266d9..ef56fa4 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.11.1.tar.gz) = c1655c5decdaed95a2b9a99652318cfc72f6cfdae957cfe60d635f7787e8850f33e8fafc4c4b8d61fb579c9b9d93028a6382903e71808a0418b931e76d72a649 +SHA512 (xen-4.11.2.tar.gz) = 48d3d926d35eb56c79c06d0abc6e6be2564fadb43367cc7f46881c669a75016707672179c2cca1c4cfb14af2cefd46e2e7f99470cddf7df2886d8435a2de814e diff --git a/xen.gcc9.fixes.patch b/xen.gcc9.fixes.patch index 7c8244b..73b8e44 100644 --- a/xen.gcc9.fixes.patch +++ b/xen.gcc9.fixes.patch @@ -9,17 +9,6 @@ union { u64 val; struct { ---- xen-4.11.1/xen/common/trace.c.orig 2018-11-29 14:04:11.000000000 +0000 -+++ xen-4.11.1/xen/common/trace.c 2019-02-06 20:31:27.370256971 +0000 -@@ -819,7 +819,7 @@ - void __trace_hypercall(uint32_t event, unsigned long op, - const xen_ulong_t *args) - { -- struct __packed { -+ struct { - uint32_t op; - uint32_t args[6]; - } d; --- xen-4.11.1/xen/arch/x86/cpu/mtrr/generic.c.orig 2018-11-29 14:04:11.000000000 +0000 +++ xen-4.11.1/xen/arch/x86/cpu/mtrr/generic.c 2019-02-10 19:24:09.378805103 +0000 @@ -171,6 +171,9 @@ diff --git a/xen.spec b/xen.spec index 6c4fbd2..dd90da2 100644 --- a/xen.spec +++ b/xen.spec @@ -66,8 +66,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.11.1 -Release: 6%{?dist} +Version: 4.11.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 @@ -126,47 +126,6 @@ Patch42: xen.stubdom.build.patch Patch44: xen.vwprintw.fix.patch Patch45: xen.python.env.patch Patch46: xen.gcc9.fixes.patch -Patch47: xsa284.patch -Patch48: xsa285-4.11.patch -Patch49: xsa287-4.11.patch -Patch50: xsa288-4.11.patch -Patch51: xsa290-4.11-1.patch -Patch52: xsa290-4.11-2.patch -Patch53: xsa291-4.11.patch -Patch54: xsa292.patch -Patch55: xsa293-4.11-1.patch -Patch56: xsa293-4.11-2.patch -Patch57: xsa294-4.11.patch -Patch58: xen.git-0825fbdd62724577febeff11ae50d440992a8f11.patch -Patch59: xen.git-e202feb7131e66ed9186ad8766c9582502c98998.patch -Patch60: xen.git-fc46e159a6b1c379a0dac918120267b606d4defd.patch -Patch61: xsa297-4.11-1.patch -Patch62: xsa297-4.11-2.patch -Patch63: xsa297-4.11-3.patch -Patch64: xsa297-4.11-4.patch -Patch65: xsa297-4.11-5.patch -Patch66: xsa297-4.11-6.patch -Patch67: xsa297-4.11-7.patch -Patch68: xsa295-4.11-01.patch -Patch69: xsa295-4.11-02.patch -Patch70: xsa295-4.11-03.patch -Patch71: xsa295-4.11-04.patch -Patch72: xsa295-4.11-05.patch -Patch73: xsa295-4.11-06.patch -Patch74: xsa295-4.11-07.patch -Patch75: xsa295-4.11-08.patch -Patch76: xsa295-4.11-09.patch -Patch77: xsa295-4.11-10.patch -Patch78: xsa295-4.11-11.patch -Patch79: xsa295-4.11-12.patch -Patch80: xsa295-4.11-13.patch -Patch81: xsa295-4.11-14.patch -Patch82: xsa295-4.11-15.patch -Patch83: xsa295-4.11-16.patch -Patch84: xsa295-4.11-17.patch -Patch85: xsa295-4.11-18.patch -Patch86: xsa295-4.11-19.patch -Patch87: xsa295-4.11-20.patch %if %build_qemutrad @@ -373,47 +332,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 -%patch80 -p1 -%patch81 -p1 -%patch82 -p1 -%patch83 -p1 -%patch84 -p1 -%patch85 -p1 -%patch86 -p1 -%patch87 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -1013,6 +931,12 @@ fi %endif %changelog +* Mon Jul 01 2019 Michael Young - 4.11.2-1 +- update to 4.11.2 + remove patches now fixed upstream + adjust xen.use.fedora.ipxe.patch + drop parts of xen.gcc9.fixes.patch + * Sat Jun 15 2019 Michael Young - 4.11.1-6 - Unlimited Arm Atomics Operations [XSA-295] (#1720760) diff --git a/xen.use.fedora.ipxe.patch b/xen.use.fedora.ipxe.patch index 49871c6..25ab8d7 100644 --- a/xen.use.fedora.ipxe.patch +++ b/xen.use.fedora.ipxe.patch @@ -19,7 +19,7 @@ +ETHERBOOT_NICS ?= 10ec8139 8086100e - QEMU_TRADITIONAL_REVISION ?= xen-4.11.1 + QEMU_TRADITIONAL_REVISION ?= xen-4.11.2 --- xen-4.2.0/tools/firmware/Makefile.orig 2012-05-27 21:57:04.480812871 +0100 +++ xen-4.2.0/tools/firmware/Makefile 2012-06-02 19:03:52.254691484 +0100 @@ -10,7 +10,7 @@ From cf804a7f3f28cd1603f5f26cb16f2b407b2bd686 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Mon, 1 Jul 2019 22:00:35 +0100 Subject: [PATCH 013/194] delete dropped patches --- ...fbdd62724577febeff11ae50d440992a8f11.patch | 71 --- ...feb7131e66ed9186ad8766c9582502c98998.patch | 464 -------------- ...e159a6b1c379a0dac918120267b606d4defd.patch | 194 ------ xsa284.patch | 31 - xsa285-4.11.patch | 43 -- xsa287-4.11.patch | 328 ---------- xsa288-4.11.patch | 308 ---------- xsa290-4.11-1.patch | 237 ------- xsa290-4.11-2.patch | 71 --- xsa291-4.11.patch | 53 -- xsa292.patch | 95 --- xsa293-4.11-1.patch | 317 ---------- xsa293-4.11-2.patch | 260 -------- xsa294-4.11.patch | 71 --- xsa295-4.11-01.patch | 84 --- xsa295-4.11-02.patch | 90 --- xsa295-4.11-03.patch | 71 --- xsa295-4.11-04.patch | 171 ------ xsa295-4.11-05.patch | 202 ------ xsa295-4.11-06.patch | 442 -------------- xsa295-4.11-07.patch | 83 --- xsa295-4.11-08.patch | 145 ----- xsa295-4.11-09.patch | 135 ---- xsa295-4.11-10.patch | 255 -------- xsa295-4.11-11.patch | 258 -------- xsa295-4.11-12.patch | 30 - xsa295-4.11-13.patch | 138 ----- xsa295-4.11-14.patch | 280 --------- xsa295-4.11-15.patch | 112 ---- xsa295-4.11-16.patch | 277 --------- xsa295-4.11-17.patch | 93 --- xsa295-4.11-18.patch | 577 ------------------ xsa295-4.11-19.patch | 84 --- xsa295-4.11-20.patch | 199 ------ xsa297-4.11-1.patch | 163 ----- xsa297-4.11-2.patch | 54 -- xsa297-4.11-3.patch | 109 ---- xsa297-4.11-4.patch | 55 -- xsa297-4.11-5.patch | 141 ----- xsa297-4.11-6.patch | 134 ---- xsa297-4.11-7.patch | 316 ---------- 41 files changed, 7241 deletions(-) delete mode 100644 xen.git-0825fbdd62724577febeff11ae50d440992a8f11.patch delete mode 100644 xen.git-e202feb7131e66ed9186ad8766c9582502c98998.patch delete mode 100644 xen.git-fc46e159a6b1c379a0dac918120267b606d4defd.patch delete mode 100644 xsa284.patch delete mode 100644 xsa285-4.11.patch delete mode 100644 xsa287-4.11.patch delete mode 100644 xsa288-4.11.patch delete mode 100644 xsa290-4.11-1.patch delete mode 100644 xsa290-4.11-2.patch delete mode 100644 xsa291-4.11.patch delete mode 100644 xsa292.patch delete mode 100644 xsa293-4.11-1.patch delete mode 100644 xsa293-4.11-2.patch delete mode 100644 xsa294-4.11.patch delete mode 100644 xsa295-4.11-01.patch delete mode 100644 xsa295-4.11-02.patch delete mode 100644 xsa295-4.11-03.patch delete mode 100644 xsa295-4.11-04.patch delete mode 100644 xsa295-4.11-05.patch delete mode 100644 xsa295-4.11-06.patch delete mode 100644 xsa295-4.11-07.patch delete mode 100644 xsa295-4.11-08.patch delete mode 100644 xsa295-4.11-09.patch delete mode 100644 xsa295-4.11-10.patch delete mode 100644 xsa295-4.11-11.patch delete mode 100644 xsa295-4.11-12.patch delete mode 100644 xsa295-4.11-13.patch delete mode 100644 xsa295-4.11-14.patch delete mode 100644 xsa295-4.11-15.patch delete mode 100644 xsa295-4.11-16.patch delete mode 100644 xsa295-4.11-17.patch delete mode 100644 xsa295-4.11-18.patch delete mode 100644 xsa295-4.11-19.patch delete mode 100644 xsa295-4.11-20.patch delete mode 100644 xsa297-4.11-1.patch delete mode 100644 xsa297-4.11-2.patch delete mode 100644 xsa297-4.11-3.patch delete mode 100644 xsa297-4.11-4.patch delete mode 100644 xsa297-4.11-5.patch delete mode 100644 xsa297-4.11-6.patch delete mode 100644 xsa297-4.11-7.patch diff --git a/xen.git-0825fbdd62724577febeff11ae50d440992a8f11.patch b/xen.git-0825fbdd62724577febeff11ae50d440992a8f11.patch deleted file mode 100644 index 0bfbb2d..0000000 --- a/xen.git-0825fbdd62724577febeff11ae50d440992a8f11.patch +++ /dev/null @@ -1,71 +0,0 @@ -From 0825fbdd62724577febeff11ae50d440992a8f11 Mon Sep 17 00:00:00 2001 -From: Andrew Cooper -Date: Fri, 3 May 2019 10:55:10 +0200 -Subject: [PATCH] x86/msr: Shorten ARCH_CAPABILITIES_* constants - -They are unnecesserily verbose, and ARCH_CAPS_* is already the more common -version. - -Signed-off-by: Andrew Cooper -Acked-by: Jan Beulich -master commit: ba27aaa88548c824a47dcf5609288ee1c05d2946 -master date: 2019-03-18 16:26:40 +0000 ---- - xen/arch/x86/spec_ctrl.c | 10 +++++----- - xen/include/asm-x86/msr-index.h | 4 ++-- - 2 files changed, 7 insertions(+), 7 deletions(-) - -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index e641894f17..27b1158d84 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -286,8 +286,8 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - (_7d0 & cpufeat_mask(X86_FEATURE_L1D_FLUSH)) ? " L1D_FLUSH" : "", - (_7d0 & cpufeat_mask(X86_FEATURE_SSBD)) ? " SSBD" : "", - (e8b & cpufeat_mask(X86_FEATURE_IBPB)) ? " IBPB" : "", -- (caps & ARCH_CAPABILITIES_IBRS_ALL) ? " IBRS_ALL" : "", -- (caps & ARCH_CAPABILITIES_RDCL_NO) ? " RDCL_NO" : "", -+ (caps & ARCH_CAPS_IBRS_ALL) ? " IBRS_ALL" : "", -+ (caps & ARCH_CAPS_RDCL_NO) ? " RDCL_NO" : "", - (caps & ARCH_CAPS_RSBA) ? " RSBA" : "", - (caps & ARCH_CAPS_SKIP_L1DFL) ? " SKIP_L1DFL": "", - (caps & ARCH_CAPS_SSB_NO) ? " SSB_NO" : ""); -@@ -598,7 +598,7 @@ static __init void l1tf_calculations(uint64_t caps) - } - - /* Any processor advertising RDCL_NO should be not vulnerable to L1TF. */ -- if ( caps & ARCH_CAPABILITIES_RDCL_NO ) -+ if ( caps & ARCH_CAPS_RDCL_NO ) - cpu_has_bug_l1tf = false; - - if ( cpu_has_bug_l1tf && hit_default ) -@@ -662,9 +662,9 @@ int8_t __read_mostly opt_xpti_domu = -1; - static __init void xpti_init_default(uint64_t caps) - { - if ( boot_cpu_data.x86_vendor == X86_VENDOR_AMD ) -- caps = ARCH_CAPABILITIES_RDCL_NO; -+ caps = ARCH_CAPS_RDCL_NO; - -- if ( caps & ARCH_CAPABILITIES_RDCL_NO ) -+ if ( caps & ARCH_CAPS_RDCL_NO ) - { - if ( opt_xpti_hwdom < 0 ) - opt_xpti_hwdom = 0; -diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h -index d13308ffe0..7588fc1567 100644 ---- a/xen/include/asm-x86/msr-index.h -+++ b/xen/include/asm-x86/msr-index.h -@@ -44,8 +44,8 @@ - #define PRED_CMD_IBPB (_AC(1, ULL) << 0) - - #define MSR_ARCH_CAPABILITIES 0x0000010a --#define ARCH_CAPABILITIES_RDCL_NO (_AC(1, ULL) << 0) --#define ARCH_CAPABILITIES_IBRS_ALL (_AC(1, ULL) << 1) -+#define ARCH_CAPS_RDCL_NO (_AC(1, ULL) << 0) -+#define ARCH_CAPS_IBRS_ALL (_AC(1, ULL) << 1) - #define ARCH_CAPS_RSBA (_AC(1, ULL) << 2) - #define ARCH_CAPS_SKIP_L1DFL (_AC(1, ULL) << 3) - #define ARCH_CAPS_SSB_NO (_AC(1, ULL) << 4) --- -2.11.0 - diff --git a/xen.git-e202feb7131e66ed9186ad8766c9582502c98998.patch b/xen.git-e202feb7131e66ed9186ad8766c9582502c98998.patch deleted file mode 100644 index dfd2ccd..0000000 --- a/xen.git-e202feb7131e66ed9186ad8766c9582502c98998.patch +++ /dev/null @@ -1,464 +0,0 @@ -From e202feb7131e66ed9186ad8766c9582502c98998 Mon Sep 17 00:00:00 2001 -From: Andrew Cooper -Date: Fri, 1 Feb 2019 11:34:35 +0100 -Subject: [PATCH] xen/cmdline: Fix buggy strncmp(s, LITERAL, ss - s) construct - -When the command line parsing was updated to use const strings and no longer -tokenise with NUL characters, string matches could no longer be made with -strcmp(). - -Unfortunately, the replacement was buggy. strncmp(s, "opt", ss - s) matches -"o", "op" and "opt" on the command line, as ss - s may be shorter than the -passed literal. Furthermore, parse_bool() is affected by this, so substrings -such as "d", "e" and "o" are considered valid, with the latter being ambiguous -between "on" and "off". - -Introduce a new strcmp-like function for the task, which looks for exact -string matches, but declares success when the NUL of the literal matches a -comma, colon or semicolon in the command line fragment. - -No change to the intended parsing functionality, but fixes cases where a -partial string on the command line will inadvertently trigger options. - -A few areas were more than just a trivial change: - - * parse_irq_vector_map_param() gained some style corrections. - * parse_vpmu_params() was rewritten to use the normal list-of-options form, - rather than just fixing up parse_vpmu_param() and leaving the parsing being - hard to follow. - * Instead of making the trivial fix of adding an explicit length check in - parse_bool(), use the length to select which token to we search for, which - is more efficient than the previous linear search over all possible tokens. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -Acked-by: Julien Grall -master commit: 2ddf7e3e341df3ccf21613ff7ffd4b7693abe9e9 -master date: 2019-01-15 12:58:34 +0000 ---- - xen/arch/x86/cpu/vpmu.c | 49 ++++++++-------------- - xen/arch/x86/irq.c | 12 +++--- - xen/arch/x86/psr.c | 4 +- - xen/arch/x86/spec_ctrl.c | 12 +++--- - xen/arch/x86/x86_64/mmconfig-shared.c | 4 +- - xen/common/efi/boot.c | 4 +- - xen/common/kernel.c | 79 ++++++++++++++++++++++++++++------- - xen/drivers/cpufreq/cpufreq.c | 6 +-- - xen/drivers/passthrough/iommu.c | 28 ++++++------- - xen/drivers/passthrough/pci.c | 4 +- - xen/include/xen/lib.h | 7 ++++ - 11 files changed, 124 insertions(+), 85 deletions(-) - -diff --git a/xen/arch/x86/cpu/vpmu.c b/xen/arch/x86/cpu/vpmu.c -index b978e05613..2be61606b4 100644 ---- a/xen/arch/x86/cpu/vpmu.c -+++ b/xen/arch/x86/cpu/vpmu.c -@@ -61,42 +61,31 @@ static unsigned vpmu_count; - - static DEFINE_PER_CPU(struct vcpu *, last_vcpu); - --static int parse_vpmu_param(const char *s, unsigned int len) --{ -- if ( !*s || !len ) -- return 0; -- if ( !strncmp(s, "bts", len) ) -- vpmu_features |= XENPMU_FEATURE_INTEL_BTS; -- else if ( !strncmp(s, "ipc", len) ) -- vpmu_features |= XENPMU_FEATURE_IPC_ONLY; -- else if ( !strncmp(s, "arch", len) ) -- vpmu_features |= XENPMU_FEATURE_ARCH_ONLY; -- else -- return 1; -- return 0; --} -- - static int __init parse_vpmu_params(const char *s) - { -- const char *sep, *p = s; -+ const char *ss; - - switch ( parse_bool(s, NULL) ) - { - case 0: - break; - default: -- for ( ; ; ) -- { -- sep = strchr(p, ','); -- if ( sep == NULL ) -- sep = strchr(p, 0); -- if ( parse_vpmu_param(p, sep - p) ) -- goto error; -- if ( !*sep ) -- /* reached end of flags */ -- break; -- p = sep + 1; -- } -+ do { -+ ss = strchr(s, ','); -+ if ( !ss ) -+ ss = strchr(s, '\0'); -+ -+ if ( !cmdline_strcmp(s, "bts") ) -+ vpmu_features |= XENPMU_FEATURE_INTEL_BTS; -+ else if ( !cmdline_strcmp(s, "ipc") ) -+ vpmu_features |= XENPMU_FEATURE_IPC_ONLY; -+ else if ( !cmdline_strcmp(s, "arch") ) -+ vpmu_features |= XENPMU_FEATURE_ARCH_ONLY; -+ else -+ return -EINVAL; -+ -+ s = ss + 1; -+ } while ( *ss ); - /* fall through */ - case 1: - /* Default VPMU mode */ -@@ -105,10 +94,6 @@ static int __init parse_vpmu_params(const char *s) - break; - } - return 0; -- -- error: -- printk("VPMU: unknown flags: %s - vpmu disabled!\n", s); -- return -EINVAL; - } - - void vpmu_lvtpc_update(uint32_t val) -diff --git a/xen/arch/x86/irq.c b/xen/arch/x86/irq.c -index 87ef2e801f..0ceb9b9a1c 100644 ---- a/xen/arch/x86/irq.c -+++ b/xen/arch/x86/irq.c -@@ -70,12 +70,12 @@ static int __init parse_irq_vector_map_param(const char *s) - if ( !ss ) - ss = strchr(s, '\0'); - -- if ( !strncmp(s, "none", ss - s)) -- opt_irq_vector_map=OPT_IRQ_VECTOR_MAP_NONE; -- else if ( !strncmp(s, "global", ss - s)) -- opt_irq_vector_map=OPT_IRQ_VECTOR_MAP_GLOBAL; -- else if ( !strncmp(s, "per-device", ss - s)) -- opt_irq_vector_map=OPT_IRQ_VECTOR_MAP_PERDEV; -+ if ( !cmdline_strcmp(s, "none") ) -+ opt_irq_vector_map = OPT_IRQ_VECTOR_MAP_NONE; -+ else if ( !cmdline_strcmp(s, "global") ) -+ opt_irq_vector_map = OPT_IRQ_VECTOR_MAP_GLOBAL; -+ else if ( !cmdline_strcmp(s, "per-device") ) -+ opt_irq_vector_map = OPT_IRQ_VECTOR_MAP_PERDEV; - else - rc = -EINVAL; - -diff --git a/xen/arch/x86/psr.c b/xen/arch/x86/psr.c -index 0ba8ef88d4..5866a261e3 100644 ---- a/xen/arch/x86/psr.c -+++ b/xen/arch/x86/psr.c -@@ -591,13 +591,13 @@ static int __init parse_psr_param(const char *s) - if ( val_delim > ss ) - val_delim = ss; - -- if ( *val_delim && !strncmp(s, "rmid_max", val_delim - s) ) -+ if ( *val_delim && !cmdline_strcmp(s, "rmid_max") ) - { - opt_rmid_max = simple_strtoul(val_delim + 1, &q, 0); - if ( *q && *q != ',' ) - rc = -EINVAL; - } -- else if ( *val_delim && !strncmp(s, "cos_max", val_delim - s) ) -+ else if ( *val_delim && !cmdline_strcmp(s, "cos_max") ) - { - opt_cos_max = simple_strtoul(val_delim + 1, &q, 0); - if ( *q && *q != ',' ) -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index eb480c1f08..e641894f17 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -83,11 +83,11 @@ static int __init parse_bti(const char *s) - { - s += 6; - -- if ( !strncmp(s, "retpoline", ss - s) ) -+ if ( !cmdline_strcmp(s, "retpoline") ) - opt_thunk = THUNK_RETPOLINE; -- else if ( !strncmp(s, "lfence", ss - s) ) -+ else if ( !cmdline_strcmp(s, "lfence") ) - opt_thunk = THUNK_LFENCE; -- else if ( !strncmp(s, "jmp", ss - s) ) -+ else if ( !cmdline_strcmp(s, "jmp") ) - opt_thunk = THUNK_JMP; - else - rc = -EINVAL; -@@ -194,11 +194,11 @@ static int __init parse_spec_ctrl(const char *s) - { - s += 10; - -- if ( !strncmp(s, "retpoline", ss - s) ) -+ if ( !cmdline_strcmp(s, "retpoline") ) - opt_thunk = THUNK_RETPOLINE; -- else if ( !strncmp(s, "lfence", ss - s) ) -+ else if ( !cmdline_strcmp(s, "lfence") ) - opt_thunk = THUNK_LFENCE; -- else if ( !strncmp(s, "jmp", ss - s) ) -+ else if ( !cmdline_strcmp(s, "jmp") ) - opt_thunk = THUNK_JMP; - else - rc = -EINVAL; -diff --git a/xen/arch/x86/x86_64/mmconfig-shared.c b/xen/arch/x86/x86_64/mmconfig-shared.c -index 7c3b7fd30b..01b5720445 100644 ---- a/xen/arch/x86/x86_64/mmconfig-shared.c -+++ b/xen/arch/x86/x86_64/mmconfig-shared.c -@@ -46,8 +46,8 @@ static int __init parse_mmcfg(const char *s) - case 1: - break; - default: -- if ( !strncmp(s, "amd_fam10", ss - s) || -- !strncmp(s, "amd-fam10", ss - s) ) -+ if ( !cmdline_strcmp(s, "amd_fam10") || -+ !cmdline_strcmp(s, "amd-fam10") ) - pci_probe |= PCI_CHECK_ENABLE_AMD_MMCONF; - else - rc = -EINVAL; -diff --git a/xen/common/efi/boot.c b/xen/common/efi/boot.c -index 6be0b3986f..a9917f31f1 100644 ---- a/xen/common/efi/boot.c -+++ b/xen/common/efi/boot.c -@@ -1323,14 +1323,14 @@ static int __init parse_efi_param(const char *s) - if ( !ss ) - ss = strchr(s, '\0'); - -- if ( !strncmp(s, "rs", ss - s) ) -+ if ( !cmdline_strcmp(s, "rs") ) - { - if ( val ) - __set_bit(EFI_RS, &efi_flags); - else - __clear_bit(EFI_RS, &efi_flags); - } -- else if ( !strncmp(s, "attr=uc", ss - s) ) -+ else if ( !cmdline_strcmp(s, "attr=uc") ) - efi_map_uc = val; - else - rc = -EINVAL; -diff --git a/xen/common/kernel.c b/xen/common/kernel.c -index 5766a0f784..053c31d391 100644 ---- a/xen/common/kernel.c -+++ b/xen/common/kernel.c -@@ -221,25 +221,51 @@ void __init cmdline_parse(const char *cmdline) - - int parse_bool(const char *s, const char *e) - { -- unsigned int len; -+ size_t len = e ? ({ ASSERT(e >= s); e - s; }) : strlen(s); - -- len = e ? ({ ASSERT(e >= s); e - s; }) : strlen(s); -- if ( !len ) -- return -1; -+ switch ( len ) -+ { -+ case 1: -+ if ( *s == '1' ) -+ return 1; -+ if ( *s == '0' ) -+ return 0; -+ break; - -- if ( !strncmp("no", s, len) || -- !strncmp("off", s, len) || -- !strncmp("false", s, len) || -- !strncmp("disable", s, len) || -- !strncmp("0", s, len) ) -- return 0; -+ case 2: -+ if ( !strncmp("on", s, 2) ) -+ return 1; -+ if ( !strncmp("no", s, 2) ) -+ return 0; -+ break; -+ -+ case 3: -+ if ( !strncmp("yes", s, 3) ) -+ return 1; -+ if ( !strncmp("off", s, 3) ) -+ return 0; -+ break; -+ -+ case 4: -+ if ( !strncmp("true", s, 4) ) -+ return 1; -+ break; -+ -+ case 5: -+ if ( !strncmp("false", s, 5) ) -+ return 0; -+ break; - -- if ( !strncmp("yes", s, len) || -- !strncmp("on", s, len) || -- !strncmp("true", s, len) || -- !strncmp("enable", s, len) || -- !strncmp("1", s, len) ) -- return 1; -+ case 6: -+ if ( !strncmp("enable", s, 6) ) -+ return 1; -+ break; -+ -+ case 7: -+ if ( !strncmp("disable", s, 7) ) -+ return 0; -+ break; -+ } - - return -1; - } -@@ -271,6 +297,27 @@ int parse_boolean(const char *name, const char *s, const char *e) - return -1; - } - -+int cmdline_strcmp(const char *frag, const char *name) -+{ -+ for ( ; ; frag++, name++ ) -+ { -+ unsigned char f = *frag, n = *name; -+ int res = f - n; -+ -+ if ( res || n == '\0' ) -+ { -+ /* -+ * NUL in 'name' matching a comma, colon or semicolon in 'frag' -+ * implies success. -+ */ -+ if ( n == '\0' && (f == ',' || f == ':' || f == ';') ) -+ res = 0; -+ -+ return res; -+ } -+ } -+} -+ - unsigned int tainted; - - /** -diff --git a/xen/drivers/cpufreq/cpufreq.c b/xen/drivers/cpufreq/cpufreq.c -index 212f48f9f4..6152a045d9 100644 ---- a/xen/drivers/cpufreq/cpufreq.c -+++ b/xen/drivers/cpufreq/cpufreq.c -@@ -73,7 +73,7 @@ static int __init setup_cpufreq_option(const char *str) - arg = strchr(str, '\0'); - choice = parse_bool(str, arg); - -- if ( choice < 0 && !strncmp(str, "dom0-kernel", arg - str) ) -+ if ( choice < 0 && !cmdline_strcmp(str, "dom0-kernel") ) - { - xen_processor_pmbits &= ~XEN_PROCESSOR_PM_PX; - cpufreq_controller = FREQCTL_dom0_kernel; -@@ -81,14 +81,14 @@ static int __init setup_cpufreq_option(const char *str) - return 0; - } - -- if ( choice == 0 || !strncmp(str, "none", arg - str) ) -+ if ( choice == 0 || !cmdline_strcmp(str, "none") ) - { - xen_processor_pmbits &= ~XEN_PROCESSOR_PM_PX; - cpufreq_controller = FREQCTL_none; - return 0; - } - -- if ( choice > 0 || !strncmp(str, "xen", arg - str) ) -+ if ( choice > 0 || !cmdline_strcmp(str, "xen") ) - { - xen_processor_pmbits |= XEN_PROCESSOR_PM_PX; - cpufreq_controller = FREQCTL_xen; -diff --git a/xen/drivers/passthrough/iommu.c b/xen/drivers/passthrough/iommu.c -index 2c44fabf99..f9b13b018c 100644 ---- a/xen/drivers/passthrough/iommu.c -+++ b/xen/drivers/passthrough/iommu.c -@@ -95,36 +95,36 @@ static int __init parse_iommu_param(const char *s) - b = parse_bool(s, ss); - if ( b >= 0 ) - iommu_enable = b; -- else if ( !strncmp(s, "force", ss - s) || -- !strncmp(s, "required", ss - s) ) -+ else if ( !cmdline_strcmp(s, "force") || -+ !cmdline_strcmp(s, "required") ) - force_iommu = val; -- else if ( !strncmp(s, "workaround_bios_bug", ss - s) ) -+ else if ( !cmdline_strcmp(s, "workaround_bios_bug") ) - iommu_workaround_bios_bug = val; -- else if ( !strncmp(s, "igfx", ss - s) ) -+ else if ( !cmdline_strcmp(s, "igfx") ) - iommu_igfx = val; -- else if ( !strncmp(s, "verbose", ss - s) ) -+ else if ( !cmdline_strcmp(s, "verbose") ) - iommu_verbose = val; -- else if ( !strncmp(s, "snoop", ss - s) ) -+ else if ( !cmdline_strcmp(s, "snoop") ) - iommu_snoop = val; -- else if ( !strncmp(s, "qinval", ss - s) ) -+ else if ( !cmdline_strcmp(s, "qinval") ) - iommu_qinval = val; -- else if ( !strncmp(s, "intremap", ss - s) ) -+ else if ( !cmdline_strcmp(s, "intremap") ) - iommu_intremap = val; -- else if ( !strncmp(s, "intpost", ss - s) ) -+ else if ( !cmdline_strcmp(s, "intpost") ) - iommu_intpost = val; -- else if ( !strncmp(s, "debug", ss - s) ) -+ else if ( !cmdline_strcmp(s, "debug") ) - { - iommu_debug = val; - if ( val ) - iommu_verbose = 1; - } -- else if ( !strncmp(s, "amd-iommu-perdev-intremap", ss - s) ) -+ else if ( !cmdline_strcmp(s, "amd-iommu-perdev-intremap") ) - amd_iommu_perdev_intremap = val; -- else if ( !strncmp(s, "dom0-passthrough", ss - s) ) -+ else if ( !cmdline_strcmp(s, "dom0-passthrough") ) - iommu_passthrough = val; -- else if ( !strncmp(s, "dom0-strict", ss - s) ) -+ else if ( !cmdline_strcmp(s, "dom0-strict") ) - iommu_dom0_strict = val; -- else if ( !strncmp(s, "sharept", ss - s) ) -+ else if ( !cmdline_strcmp(s, "sharept") ) - iommu_hap_pt_share = val; - else - rc = -EINVAL; -diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c -index 1db69d5b99..f51cae7f4e 100644 ---- a/xen/drivers/passthrough/pci.c -+++ b/xen/drivers/passthrough/pci.c -@@ -212,12 +212,12 @@ static int __init parse_pci_param(const char *s) - if ( !ss ) - ss = strchr(s, '\0'); - -- if ( !strncmp(s, "serr", ss - s) ) -+ if ( !cmdline_strcmp(s, "serr") ) - { - cmd_mask = PCI_COMMAND_SERR; - brctl_mask = PCI_BRIDGE_CTL_SERR | PCI_BRIDGE_CTL_DTMR_SERR; - } -- else if ( !strncmp(s, "perr", ss - s) ) -+ else if ( !cmdline_strcmp(s, "perr") ) - { - cmd_mask = PCI_COMMAND_PARITY; - brctl_mask = PCI_BRIDGE_CTL_PARITY; -diff --git a/xen/include/xen/lib.h b/xen/include/xen/lib.h -index 1d9771340c..750f809968 100644 ---- a/xen/include/xen/lib.h -+++ b/xen/include/xen/lib.h -@@ -81,6 +81,13 @@ int parse_bool(const char *s, const char *e); - */ - int parse_boolean(const char *name, const char *s, const char *e); - -+/** -+ * Very similar to strcmp(), but will declare a match if the NUL in 'name' -+ * lines up with comma, colon or semicolon in 'frag'. Designed for picking -+ * exact string matches out of a delimited command line list. -+ */ -+int cmdline_strcmp(const char *frag, const char *name); -+ - /*#define DEBUG_TRACE_DUMP*/ - #ifdef DEBUG_TRACE_DUMP - extern void debugtrace_dump(void); --- -2.11.0 - diff --git a/xen.git-fc46e159a6b1c379a0dac918120267b606d4defd.patch b/xen.git-fc46e159a6b1c379a0dac918120267b606d4defd.patch deleted file mode 100644 index 200a2a9..0000000 --- a/xen.git-fc46e159a6b1c379a0dac918120267b606d4defd.patch +++ /dev/null @@ -1,194 +0,0 @@ -From fc46e159a6b1c379a0dac918120267b606d4defd Mon Sep 17 00:00:00 2001 -From: Andrew Cooper -Date: Mon, 18 Mar 2019 17:08:25 +0100 -Subject: [PATCH] x86/tsx: Implement controls for RTM force-abort mode - -The CPUID bit and MSR are deliberately not exposed to guests, because they -won't exist on newer processors. As vPMU isn't security supported, the -misbehaviour of PCR3 isn't expected to impact production deployments. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -master commit: 6be613f29b4205349275d24367bd4c82fb2960dd -master date: 2019-03-12 17:05:21 +0000 ---- - docs/misc/xen-command-line.markdown | 17 ++++++++++++++++- - tools/misc/xen-cpuid.c | 2 ++ - xen/arch/x86/cpu/intel.c | 3 +++ - xen/arch/x86/cpu/vpmu.c | 5 +++++ - xen/arch/x86/msr.c | 4 ++++ - xen/include/asm-x86/cpufeature.h | 3 +++ - xen/include/asm-x86/msr-index.h | 3 +++ - xen/include/asm-x86/vpmu.h | 1 + - xen/include/public/arch-x86/cpufeatureset.h | 1 + - 9 files changed, 38 insertions(+), 1 deletion(-) - -diff --git a/docs/misc/xen-command-line.markdown b/docs/misc/xen-command-line.markdown -index 8046cc8333..8e243808a1 100644 ---- a/docs/misc/xen-command-line.markdown -+++ b/docs/misc/xen-command-line.markdown -@@ -2008,7 +2008,7 @@ Use Virtual Processor ID support if available. This prevents the need for TLB - flushes on VM entry and exit, increasing performance. - - ### vpmu (x86) --> `= ( | { bts | ipc | arch [, ...] } )` -+> `= ( | { bts | ipc | arch | rtm-abort= [, ...] } )` - - > Default: `off` - -@@ -2034,6 +2034,21 @@ in the Pre-Defined Architectural Performance Events table from the Intel 64 - and IA-32 Architectures Software Developer's Manual, Volume 3B, System - Programming Guide, Part 2. - -+vpmu=rtm-abort controls a trade-off between working Restricted Transactional -+Memory, and working performance counters. -+ -+All processors released to date (Q1 2019) supporting Transactional Memory -+Extensions suffer an erratum which has been addressed in microcode. -+ -+Processors based on the Skylake microarchitecture with up-to-date -+microcode internally use performance counter 3 to work around the erratum. -+A consequence is that the counter gets reprogrammed whenever an `XBEGIN` -+instruction is executed. -+ -+An alternative mode exists where PCR3 behaves as before, at the cost of -+`XBEGIN` unconditionally aborting. Enabling `rtm-abort` mode will -+activate this alternative mode. -+ - If a boolean is not used, combinations of flags are allowed, comma separated. - For example, vpmu=arch,bts. - -diff --git a/tools/misc/xen-cpuid.c b/tools/misc/xen-cpuid.c -index 3888b4e158..0ac903a931 100644 ---- a/tools/misc/xen-cpuid.c -+++ b/tools/misc/xen-cpuid.c -@@ -142,6 +142,8 @@ static const char *str_7d0[32] = - { - [ 2] = "avx512_4vnniw", [ 3] = "avx512_4fmaps", - -+ /* 12 */ [13] = "tsx-force-abort", -+ - [26] = "ibrsb", [27] = "stibp", - [28] = "l1d_flush", [29] = "arch_caps", - /* 30 */ [31] = "ssbd", -diff --git a/xen/arch/x86/cpu/intel.c b/xen/arch/x86/cpu/intel.c -index 9477965321..8e23ed6379 100644 ---- a/xen/arch/x86/cpu/intel.c -+++ b/xen/arch/x86/cpu/intel.c -@@ -287,6 +287,9 @@ static void Intel_errata_workarounds(struct cpuinfo_x86 *c) - if (c->x86 == 6 && cpu_has_clflush && - (c->x86_model == 29 || c->x86_model == 46 || c->x86_model == 47)) - __set_bit(X86_FEATURE_CLFLUSH_MONITOR, c->x86_capability); -+ -+ if (cpu_has_tsx_force_abort && opt_rtm_abort) -+ wrmsrl(MSR_TSX_FORCE_ABORT, TSX_FORCE_ABORT_RTM); - } - - -diff --git a/xen/arch/x86/cpu/vpmu.c b/xen/arch/x86/cpu/vpmu.c -index 2be61606b4..639ae0ca63 100644 ---- a/xen/arch/x86/cpu/vpmu.c -+++ b/xen/arch/x86/cpu/vpmu.c -@@ -53,6 +53,7 @@ CHECK_pmu_params; - static unsigned int __read_mostly opt_vpmu_enabled; - unsigned int __read_mostly vpmu_mode = XENPMU_MODE_OFF; - unsigned int __read_mostly vpmu_features = 0; -+bool __read_mostly opt_rtm_abort; - static int parse_vpmu_params(const char *s); - custom_param("vpmu", parse_vpmu_params); - -@@ -71,6 +72,8 @@ static int __init parse_vpmu_params(const char *s) - break; - default: - do { -+ int val; -+ - ss = strchr(s, ','); - if ( !ss ) - ss = strchr(s, '\0'); -@@ -81,6 +84,8 @@ static int __init parse_vpmu_params(const char *s) - vpmu_features |= XENPMU_FEATURE_IPC_ONLY; - else if ( !cmdline_strcmp(s, "arch") ) - vpmu_features |= XENPMU_FEATURE_ARCH_ONLY; -+ else if ( (val = parse_boolean("rtm-abort", s, ss)) >= 0 ) -+ opt_rtm_abort = val; - else - return -EINVAL; - -diff --git a/xen/arch/x86/msr.c b/xen/arch/x86/msr.c -index 1a591dd2b5..b49fbd8077 100644 ---- a/xen/arch/x86/msr.c -+++ b/xen/arch/x86/msr.c -@@ -152,6 +152,8 @@ int guest_rdmsr(const struct vcpu *v, uint32_t msr, uint64_t *val) - case MSR_PRED_CMD: - case MSR_FLUSH_CMD: - /* Write-only */ -+ case MSR_TSX_FORCE_ABORT: -+ /* Not offered to guests. */ - goto gp_fault; - - case MSR_SPEC_CTRL: -@@ -203,6 +205,8 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) - case MSR_INTEL_PLATFORM_INFO: - case MSR_ARCH_CAPABILITIES: - /* Read-only */ -+ case MSR_TSX_FORCE_ABORT: -+ /* Not offered to guests. */ - goto gp_fault; - - case MSR_AMD_PATCHLOADER: -diff --git a/xen/include/asm-x86/cpufeature.h b/xen/include/asm-x86/cpufeature.h -index 861cb0af93..1c699a8def 100644 ---- a/xen/include/asm-x86/cpufeature.h -+++ b/xen/include/asm-x86/cpufeature.h -@@ -106,6 +106,9 @@ - /* CPUID level 0x80000007.edx */ - #define cpu_has_itsc boot_cpu_has(X86_FEATURE_ITSC) - -+/* CPUID level 0x00000007:0.edx */ -+#define cpu_has_tsx_force_abort boot_cpu_has(X86_FEATURE_TSX_FORCE_ABORT) -+ - /* Synthesized. */ - #define cpu_has_arch_perfmon boot_cpu_has(X86_FEATURE_ARCH_PERFMON) - #define cpu_has_cpuid_faulting boot_cpu_has(X86_FEATURE_CPUID_FAULTING) -diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h -index 7235623c86..d13308ffe0 100644 ---- a/xen/include/asm-x86/msr-index.h -+++ b/xen/include/asm-x86/msr-index.h -@@ -53,6 +53,9 @@ - #define MSR_FLUSH_CMD 0x0000010b - #define FLUSH_CMD_L1D (_AC(1, ULL) << 0) - -+#define MSR_TSX_FORCE_ABORT 0x0000010f -+#define TSX_FORCE_ABORT_RTM (_AC(1, ULL) << 0) -+ - /* Intel MSRs. Some also available on other CPUs */ - #define MSR_IA32_PERFCTR0 0x000000c1 - #define MSR_IA32_A_PERFCTR0 0x000004c1 -diff --git a/xen/include/asm-x86/vpmu.h b/xen/include/asm-x86/vpmu.h -index 5e778ab7ba..1287b9fb6e 100644 ---- a/xen/include/asm-x86/vpmu.h -+++ b/xen/include/asm-x86/vpmu.h -@@ -125,6 +125,7 @@ static inline int vpmu_do_rdmsr(unsigned int msr, uint64_t *msr_content) - - extern unsigned int vpmu_mode; - extern unsigned int vpmu_features; -+extern bool opt_rtm_abort; - - /* Context switch */ - static inline void vpmu_switch_from(struct vcpu *prev) -diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h -index 6c82816fd3..aa2656d792 100644 ---- a/xen/include/public/arch-x86/cpufeatureset.h -+++ b/xen/include/public/arch-x86/cpufeatureset.h -@@ -242,6 +242,7 @@ XEN_CPUFEATURE(IBPB, 8*32+12) /*A IBPB support only (no IBRS, used by - /* Intel-defined CPU features, CPUID level 0x00000007:0.edx, word 9 */ - XEN_CPUFEATURE(AVX512_4VNNIW, 9*32+ 2) /*A AVX512 Neural Network Instructions */ - XEN_CPUFEATURE(AVX512_4FMAPS, 9*32+ 3) /*A AVX512 Multiply Accumulation Single Precision */ -+XEN_CPUFEATURE(TSX_FORCE_ABORT, 9*32+13) /* MSR_TSX_FORCE_ABORT.RTM_ABORT */ - XEN_CPUFEATURE(IBRSB, 9*32+26) /*A IBRS and IBPB support (used by Intel) */ - XEN_CPUFEATURE(STIBP, 9*32+27) /*A STIBP */ - XEN_CPUFEATURE(L1D_FLUSH, 9*32+28) /*S MSR_FLUSH_CMD and L1D flush. */ --- -2.11.0 - diff --git a/xsa284.patch b/xsa284.patch deleted file mode 100644 index 0b5dcd0..0000000 --- a/xsa284.patch +++ /dev/null @@ -1,31 +0,0 @@ -From: Jan Beulich -Subject: gnttab: set page refcount for copy-on-grant-transfer - -Commit 5cc77f9098 ("32-on-64: Fix domain address-size clamping, -implement"), which introduced this functionality, took care of clearing -the old page's PGC_allocated, but failed to set the bit (and install the -associated reference) on the newly allocated one. Furthermore the "mfn" -local variable was never updated, and hence the wrong MFN was passed to -guest_physmap_add_page() (and back to the destination domain) in this -case, leading to an IOMMU mapping into an unowned page. - -Ideally the code would use assign_pages(), but the call to -gnttab_prepare_for_transfer() sits in the middle of the actions -mirroring that function. - -This is XSA-284. - -Signed-off-by: Jan Beulich -Acked-by: George Dunlap - ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -2183,6 +2183,8 @@ gnttab_transfer( - page->count_info &= ~(PGC_count_mask|PGC_allocated); - free_domheap_page(page); - page = new_page; -+ page->count_info = PGC_allocated | 1; -+ mfn = page_to_mfn(page); - } - - spin_lock(&e->page_alloc_lock); diff --git a/xsa285-4.11.patch b/xsa285-4.11.patch deleted file mode 100644 index 4d1cde4..0000000 --- a/xsa285-4.11.patch +++ /dev/null @@ -1,43 +0,0 @@ -From: Jan Beulich -Subject: IOMMU/x86: fix type ref-counting race upon IOMMU page table construction - -When arch_iommu_populate_page_table() gets invoked for an already -running guest, simply looking at page types once isn't enough, as they -may change at any time. Add logic to re-check the type after having -mapped the page, unmapping it again if needed. - -This is XSA-285. - -Signed-off-by: Jan Beulich -Tentatively-Acked-by: Andrew Cooper - ---- a/xen/drivers/passthrough/x86/iommu.c -+++ b/xen/drivers/passthrough/x86/iommu.c -@@ -68,6 +68,27 @@ int arch_iommu_populate_page_table(struct domain *d) - rc = hd->platform_ops->map_page(d, gfn, mfn, - IOMMUF_readable | - IOMMUF_writable); -+ -+ /* -+ * We may be working behind the back of a running guest, which -+ * may change the type of a page at any time. We can't prevent -+ * this (for instance, by bumping the type count while mapping -+ * the page) without causing legitimate guest type-change -+ * operations to fail. So after adding the page to the IOMMU, -+ * check again to make sure this is still valid. NB that the -+ * writable entry in the iommu is harmless until later, when -+ * the actual device gets assigned. -+ */ -+ if ( !rc && !is_hvm_domain(d) && -+ ((page->u.inuse.type_info & PGT_type_mask) != -+ PGT_writable_page) ) -+ { -+ rc = hd->platform_ops->unmap_page(d, gfn); -+ /* If the type changed yet again, simply force a retry. */ -+ if ( !rc && ((page->u.inuse.type_info & PGT_type_mask) == -+ PGT_writable_page) ) -+ rc = -ERESTART; -+ } - } - if ( rc ) - { diff --git a/xsa287-4.11.patch b/xsa287-4.11.patch deleted file mode 100644 index 8563560..0000000 --- a/xsa287-4.11.patch +++ /dev/null @@ -1,328 +0,0 @@ -From 67620c1ccb13f7b58645f48248ba1f408b021fdc Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Fri, 18 Jan 2019 15:00:34 +0000 -Subject: [PATCH] steal_page: Get rid of bogus struct page states - -The original rules for `struct page` required the following invariants -at all times: - -- refcount > 0 implies owner != NULL -- PGC_allocated implies refcount > 0 - -steal_page, in a misguided attempt to protect against unknown races, -violates both of these rules, thus introducing other races: - -- Temporarily, the count_info has the refcount go to 0 while - PGC_allocated is set - -- It explicitly returns the page PGC_allocated set, but owner == NULL - and page not on the page_list. - -The second one meant that page_get_owner_and_reference() could return -NULL even after having successfully grabbed a reference on the page, -leading the caller to leak the reference (since "couldn't get ref" and -"got ref but no owner" look the same). - -Furthermore, rather than grabbing a page reference to ensure that the -owner doesn't change under its feet, it appears to rely on holding -d->page_alloc lock to prevent this. - -Unfortunately, this is ineffective: page->owner remains non-NULL for -some time after the count has been set to 0; meaning that it would be -entirely possible for the page to be freed and re-allocated to a -different domain between the page_get_owner() check and the count_info -check. - -Modify steal_page to instead follow the appropriate access discipline, -taking the page through series of states similar to being freed and -then re-allocated with MEMF_no_owner: - -- Grab an extra reference to make sure we don't race with anyone else - freeing the page - -- Drop both references and PGC_allocated atomically, so that (if -successful), anyone else trying to grab a reference will fail - -- Attempt to reset Xen's mappings - -- Reset the rest of the state. - -Then, modify the two callers appropriately: - -- Leave count_info alone (it's already been cleared) -- Call free_domheap_page() directly if appropriate -- Call assign_pages() rather than open-coding a partial assign - -With all callers to assign_pages() now passing in pages with the -type_info field clear, tighten the respective assertion there. - -This is XSA-287. - -Signed-off-by: George Dunlap -Signed-off-by: Jan Beulich ---- - xen/arch/x86/mm.c | 84 ++++++++++++++++++++++++++++------------ - xen/common/grant_table.c | 20 +++++----- - xen/common/memory.c | 19 +++++---- - xen/common/page_alloc.c | 2 +- - 4 files changed, 83 insertions(+), 42 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 6509035a5c..d8ff58c901 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -3966,70 +3966,106 @@ int donate_page( - return -EINVAL; - } - -+/* -+ * Steal page will attempt to remove `page` from domain `d`. Upon -+ * return, `page` will be in a state similar to the state of a page -+ * returned from alloc_domheap_page() with MEMF_no_owner set: -+ * - refcount 0 -+ * - type count cleared -+ * - owner NULL -+ * - page caching attributes cleaned up -+ * - removed from the domain's page_list -+ * -+ * If MEMF_no_refcount is not set, the domain's tot_pages will be -+ * adjusted. If this results in the page count falling to 0, -+ * put_domain() will be called. -+ * -+ * The caller should either call free_domheap_page() to free the -+ * page, or assign_pages() to put it back on some domain's page list. -+ */ - int steal_page( - struct domain *d, struct page_info *page, unsigned int memflags) - { - unsigned long x, y; - bool drop_dom_ref = false; -- const struct domain *owner = dom_xen; -+ const struct domain *owner; -+ int rc; - - if ( paging_mode_external(d) ) - return -EOPNOTSUPP; - -- spin_lock(&d->page_alloc_lock); -- -- if ( is_xen_heap_page(page) || ((owner = page_get_owner(page)) != d) ) -+ /* Grab a reference to make sure the page doesn't change under our feet */ -+ rc = -EINVAL; -+ if ( !(owner = page_get_owner_and_reference(page)) ) - goto fail; - -+ if ( owner != d || is_xen_heap_page(page) ) -+ goto fail_put; -+ - /* -- * We require there is just one reference (PGC_allocated). We temporarily -- * drop this reference now so that we can safely swizzle the owner. -+ * We require there are exactly two references -- the one we just -+ * took, and PGC_allocated. We temporarily drop both these -+ * references so that the page becomes effectively non-"live" for -+ * the domain. - */ - y = page->count_info; - do { - x = y; -- if ( (x & (PGC_count_mask|PGC_allocated)) != (1 | PGC_allocated) ) -- goto fail; -- y = cmpxchg(&page->count_info, x, x & ~PGC_count_mask); -+ if ( (x & (PGC_count_mask|PGC_allocated)) != (2 | PGC_allocated) ) -+ goto fail_put; -+ y = cmpxchg(&page->count_info, x, x & ~(PGC_count_mask|PGC_allocated)); - } while ( y != x ); - - /* -- * With the sole reference dropped temporarily, no-one can update type -- * information. Type count also needs to be zero in this case, but e.g. -- * PGT_seg_desc_page may still have PGT_validated set, which we need to -- * clear before transferring ownership (as validation criteria vary -- * depending on domain type). -+ * NB this is safe even if the page ends up being given back to -+ * the domain, because the count is zero: subsequent mappings will -+ * cause the cache attributes to be re-instated inside -+ * get_page_from_l1e(). -+ */ -+ if ( (rc = cleanup_page_cacheattr(page)) ) -+ { -+ /* -+ * Couldn't fixup Xen's mappings; put things the way we found -+ * it and return an error -+ */ -+ page->count_info |= PGC_allocated | 1; -+ goto fail; -+ } -+ -+ /* -+ * With the reference count now zero, nobody can grab references -+ * to do anything else with the page. Return the page to a state -+ * that it might be upon return from alloc_domheap_pages with -+ * MEMF_no_owner set. - */ -+ spin_lock(&d->page_alloc_lock); -+ - BUG_ON(page->u.inuse.type_info & (PGT_count_mask | PGT_locked | - PGT_pinned)); - page->u.inuse.type_info = 0; -- -- /* Swizzle the owner then reinstate the PGC_allocated reference. */ - page_set_owner(page, NULL); -- y = page->count_info; -- do { -- x = y; -- BUG_ON((x & (PGC_count_mask|PGC_allocated)) != PGC_allocated); -- } while ( (y = cmpxchg(&page->count_info, x, x | 1)) != x ); -+ page_list_del(page, &d->page_list); - - /* Unlink from original owner. */ - if ( !(memflags & MEMF_no_refcount) && !domain_adjust_tot_pages(d, -1) ) - drop_dom_ref = true; -- page_list_del(page, &d->page_list); - - spin_unlock(&d->page_alloc_lock); -+ - if ( unlikely(drop_dom_ref) ) - put_domain(d); -+ - return 0; - -+ fail_put: -+ put_page(page); - fail: -- spin_unlock(&d->page_alloc_lock); - gdprintk(XENLOG_WARNING, "Bad steal mfn %" PRI_mfn - " from d%d (owner d%d) caf=%08lx taf=%" PRtype_info "\n", - mfn_x(page_to_mfn(page)), d->domain_id, - owner ? owner->domain_id : DOMID_INVALID, - page->count_info, page->u.inuse.type_info); -- return -EINVAL; -+ return rc; - } - - static int __do_update_va_mapping( -diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c -index c0585d33f4..656fad1b42 100644 ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -2179,7 +2179,7 @@ gnttab_transfer( - rcu_unlock_domain(e); - put_gfn_and_copyback: - put_gfn(d, gop.mfn); -- page->count_info &= ~(PGC_count_mask|PGC_allocated); -+ /* The count_info has already been cleaned */ - free_domheap_page(page); - goto copyback; - } -@@ -2202,10 +2202,9 @@ gnttab_transfer( - - copy_domain_page(page_to_mfn(new_page), mfn); - -- page->count_info &= ~(PGC_count_mask|PGC_allocated); -+ /* The count_info has already been cleared */ - free_domheap_page(page); - page = new_page; -- page->count_info = PGC_allocated | 1; - mfn = page_to_mfn(page); - } - -@@ -2245,12 +2244,17 @@ gnttab_transfer( - */ - spin_unlock(&e->page_alloc_lock); - okay = gnttab_prepare_for_transfer(e, d, gop.ref); -- spin_lock(&e->page_alloc_lock); - -- if ( unlikely(!okay) || unlikely(e->is_dying) ) -+ if ( unlikely(!okay || assign_pages(e, page, 0, MEMF_no_refcount)) ) - { -- bool_t drop_dom_ref = !domain_adjust_tot_pages(e, -1); -+ bool drop_dom_ref; - -+ /* -+ * Need to grab this again to safely free our "reserved" -+ * page in the page total -+ */ -+ spin_lock(&e->page_alloc_lock); -+ drop_dom_ref = !domain_adjust_tot_pages(e, -1); - spin_unlock(&e->page_alloc_lock); - - if ( okay /* i.e. e->is_dying due to the surrounding if() */ ) -@@ -2263,10 +2267,6 @@ gnttab_transfer( - goto unlock_and_copyback; - } - -- page_list_add_tail(page, &e->page_list); -- page_set_owner(page, e); -- -- spin_unlock(&e->page_alloc_lock); - put_gfn(d, gop.mfn); - - TRACE_1D(TRC_MEM_PAGE_GRANT_TRANSFER, e->domain_id); -diff --git a/xen/common/memory.c b/xen/common/memory.c -index 4fb7962c79..f71163221f 100644 ---- a/xen/common/memory.c -+++ b/xen/common/memory.c -@@ -675,20 +675,22 @@ static long memory_exchange(XEN_GUEST_HANDLE_PARAM(xen_memory_exchange_t) arg) - * Success! Beyond this point we cannot fail for this chunk. - */ - -- /* Destroy final reference to each input page. */ -+ /* -+ * These pages have already had owner and reference cleared. -+ * Do the final two steps: Remove from the physmap, and free -+ * them. -+ */ - while ( (page = page_list_remove_head(&in_chunk_list)) ) - { - unsigned long gfn; - -- if ( !test_and_clear_bit(_PGC_allocated, &page->count_info) ) -- BUG(); - mfn = page_to_mfn(page); - gfn = mfn_to_gmfn(d, mfn_x(mfn)); - /* Pages were unshared above */ - BUG_ON(SHARED_M2P(gfn)); - if ( guest_physmap_remove_page(d, _gfn(gfn), mfn, 0) ) - domain_crash(d); -- put_page(page); -+ free_domheap_page(page); - } - - /* Assign each output page to the domain. */ -@@ -761,13 +763,16 @@ static long memory_exchange(XEN_GUEST_HANDLE_PARAM(xen_memory_exchange_t) arg) - * chunks succeeded. - */ - fail: -- /* Reassign any input pages we managed to steal. */ -+ /* -+ * Reassign any input pages we managed to steal. NB that if the assign -+ * fails again, we're on the hook for freeing the page, since we've already -+ * cleared PGC_allocated. -+ */ - while ( (page = page_list_remove_head(&in_chunk_list)) ) - if ( assign_pages(d, page, 0, MEMF_no_refcount) ) - { - BUG_ON(!d->is_dying); -- if ( test_and_clear_bit(_PGC_allocated, &page->count_info) ) -- put_page(page); -+ free_domheap_page(page); - } - - dying: -diff --git a/xen/common/page_alloc.c b/xen/common/page_alloc.c -index 482f0988f7..52da7762e3 100644 ---- a/xen/common/page_alloc.c -+++ b/xen/common/page_alloc.c -@@ -2221,7 +2221,7 @@ int assign_pages( - for ( i = 0; i < (1 << order); i++ ) - { - ASSERT(page_get_owner(&pg[i]) == NULL); -- ASSERT((pg[i].count_info & ~(PGC_allocated | 1)) == 0); -+ ASSERT(!pg[i].count_info); - page_set_owner(&pg[i], d); - smp_wmb(); /* Domain pointer must be visible before updating refcnt. */ - pg[i].count_info = PGC_allocated | 1; --- -2.20.1 - diff --git a/xsa288-4.11.patch b/xsa288-4.11.patch deleted file mode 100644 index 9dc4e37..0000000 --- a/xsa288-4.11.patch +++ /dev/null @@ -1,308 +0,0 @@ -From 5d3a02e320f88747b75e3794c2e694284ae64c3e Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Wed, 23 Jan 2019 11:57:46 +0000 -Subject: [PATCH] xen: Make coherent PV IOMMU discipline - -In order for a PV domain to set up DMA from a passed-through device to -one of its pages, the page must be mapped in the IOMMU. On the other -hand, before a PV page may be used as a "special" page type (such as a -pagetable or descriptor table), it _must not_ be writable in the IOMMU -(otherwise a malicious guest could DMA arbitrary page tables into the -memory, bypassing Xen's safety checks); and Xen's current rule is to -have such pages not in the IOMMU at all. - -At the moment, in order to accomplish this, the code borrows HVM -domain's "physmap" concept: When a page is assigned to a guest, -guess_physmap_add_entry() is called, which for PV guests, will create -a writable IOMMU mapping; and when a page is removed, -guest_physmap_remove_entry() is called, which will remove the mapping. - -Additionally, when a page gains the PGT_writable page type, the page -will be added into the IOMMU; and when the page changes away from a -PGT_writable type, the page will be removed from the IOMMU. - -Unfortunately, borrowing the "physmap" concept from HVM domains is -problematic. HVM domains have a lock on their p2m tables, ensuring -synchronization between modifications to the p2m; and all hypercall -parameters must first be translated through the p2m before being used. - -Trying to mix this locked-and-gated approach with PV's lock-free -approach leads to several races and inconsistencies: - -* A race between a page being assigned and it being put into the - physmap; for example: - - P1: call populate_physmap() { A = allocate_domheap_pages() } - - P2: Guess page A's mfn, and call decrease_reservation(A). A is owned by the domain, - and so Xen will clear the PGC_allocated bit and free the page - - P1: finishes populate_physmap() { guest_physmap_add_entry() } - - Now the domain has a writable IOMMU mapping to a page it no longer owns. - -* Pages start out as type PGT_none, but with a writable IOMMU mapping. - If a guest uses a page as a page table without ever having created a - writable mapping, the IOMMU mapping will not be removed; the guest - will have a writable IOMMU mapping to a page it is currently using - as a page table. - -* A newly-allocated page can be DMA'd into with no special actions on - the part of the guest; However, if a page is promoted to a - non-writable type, the page must be mapped with a writable type before - DMA'ing to it again, or the transaction will fail. - -To fix this, do away with the "PV physmap" concept entirely, and -replace it with the following IOMMU discipline for PV guests: - - (type == PGT_writable) <=> in iommu (even if type_count == 0) - - Upon a final put_page(), check to see if type is PGT_writable; if so, - iommu_unmap. - -In order to achieve that: - -- Remove PV IOMMU related code from guest_physmap_* - -- Repurpose cleanup_page_cacheattr() into a general - cleanup_page_mappings() function, which will both fix up Xen - mappings for pages with special cache attributes, and also check for - a PGT_writable type and remove pages if appropriate. - -- For compatibility with current guests, grab-and-release a - PGT_writable_page type for PV guests in guest_physmap_add_entry(). - This will cause most "normal" guest pages to start out life with - PGT_writable_page type (and thus an IOMMU mapping), but no type - count (so that they can be used as special cases at will). - -Also, note that there is one exception to to the "PGT_writable => in -iommu" rule: xenheap pages shared with guests may be given a -PGT_writable type with one type reference. This reference prevents -the type from changing, which in turn prevents page from gaining an -IOMMU mapping in get_page_type(). It's not clear whether this was -intentional or not, but it's not something to change in a security -update. - -This is XSA-288. - -Reported-by: Paul Durrant -Signed-off-by: George Dunlap -Signed-off-by: Jan Beulich ---- - xen/arch/x86/mm.c | 95 +++++++++++++++++++++++++++++++++++++++---- - xen/arch/x86/mm/p2m.c | 57 ++++++++++++-------------- - 2 files changed, 111 insertions(+), 41 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index d8ff58c901..ad8aacad68 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -81,6 +81,22 @@ - * OS's, which will generally use the WP bit to simplify copy-on-write - * implementation (in that case, OS wants a fault when it writes to - * an application-supplied buffer). -+ * -+ * PV domUs and IOMMUs: -+ * -------------------- -+ * For a guest to be able to DMA into a page, that page must be in the -+ * domain's IOMMU. However, we *must not* allow DMA into 'special' -+ * pages (such as page table pages, descriptor tables, &c); and we -+ * must also ensure that mappings are removed from the IOMMU when the -+ * page is freed. Finally, it is inherently racy to make any changes -+ * based on a page with a non-zero type count. -+ * -+ * To that end, we put the page in the IOMMU only when a page gains -+ * the PGT_writeable type; and we remove the page when it loses the -+ * PGT_writeable type (not when the type count goes to zero). This -+ * effectively protects the IOMMU status update with the type count we -+ * have just acquired. We must also check for PGT_writable type when -+ * doing the final put_page(), and remove it from the iommu if so. - */ - - #include -@@ -2275,19 +2291,79 @@ static int mod_l4_entry(l4_pgentry_t *pl4e, - return rc; - } - --static int cleanup_page_cacheattr(struct page_info *page) -+/* -+ * In the course of a page's use, it may have caused other secondary -+ * mappings to have changed: -+ * - Xen's mappings may have been changed to accomodate the requested -+ * cache attibutes -+ * - A page may have been put into the IOMMU of a PV guest when it -+ * gained a writable mapping. -+ * -+ * Now that the page is being freed, clean up these mappings if -+ * appropriate. NB that at this point the page is still "allocated", -+ * but not "live" (i.e., its refcount is 0), so it's safe to read the -+ * count_info, owner, and type_info without synchronization. -+ */ -+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 ( likely(cacheattr == 0) ) -- return 0; -+ /* -+ * 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; - -- page->count_info &= ~PGC_cacheattr_mask; -+ BUG_ON(is_xen_heap_page(page)); - -- BUG_ON(is_xen_heap_page(page)); -+ rc = update_xen_mappings(mfn, 0); -+ } - -- return update_xen_mappings(mfn_x(page_to_mfn(page)), 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 -+ * implementation-specific code, rather than by get_page_type(). As such: -+ * - They aren't expected to have an IOMMU mapping, and -+ * - We don't necessarily expect the type count to be zero when the final -+ * put_page happens. -+ * -+ * Go ahead and attemp to call iommu_unmap() on xenheap pages anyway, just -+ * in case; but only ASSERT() that the type count is zero and remove the -+ * PGT_writable type for non-xenheap pages. -+ */ -+ if ( (page->u.inuse.type_info & PGT_type_mask) == PGT_writable_page ) -+ { -+ struct domain *d = page_get_owner(page); -+ -+ if ( d && is_pv_domain(d) && unlikely(need_iommu(d)) ) -+ { -+ int rc2 = iommu_unmap_page(d, mfn); -+ -+ if ( !rc ) -+ rc = rc2; -+ } -+ -+ if ( likely(!is_xen_heap_page(page)) ) -+ { -+ ASSERT((page->u.inuse.type_info & -+ (PGT_type_mask | PGT_count_mask)) == PGT_writable_page); -+ /* -+ * Clear the type to record the fact that all writable mappings -+ * have been removed. But if either operation failed, leave -+ * type_info alone. -+ */ -+ if ( likely(!rc) ) -+ page->u.inuse.type_info &= ~(PGT_type_mask | PGT_count_mask); -+ } -+ } -+ -+ return rc; - } - - void put_page(struct page_info *page) -@@ -2303,7 +2379,7 @@ void put_page(struct page_info *page) - - if ( unlikely((nx & PGC_count_mask) == 0) ) - { -- if ( cleanup_page_cacheattr(page) == 0 ) -+ if ( !cleanup_page_mappings(page) ) - free_domheap_page(page); - else - gdprintk(XENLOG_WARNING, -@@ -4020,9 +4096,10 @@ int steal_page( - * NB this is safe even if the page ends up being given back to - * the domain, because the count is zero: subsequent mappings will - * cause the cache attributes to be re-instated inside -- * get_page_from_l1e(). -+ * get_page_from_l1e(), or the page to be added back to the IOMMU -+ * upon the type changing to PGT_writeable, as appropriate. - */ -- if ( (rc = cleanup_page_cacheattr(page)) ) -+ if ( (rc = cleanup_page_mappings(page)) ) - { - /* - * Couldn't fixup Xen's mappings; put things the way we found -diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c -index c53cab44d9..2b62bc61dd 100644 ---- a/xen/arch/x86/mm/p2m.c -+++ b/xen/arch/x86/mm/p2m.c -@@ -708,23 +708,9 @@ p2m_remove_page(struct p2m_domain *p2m, unsigned long gfn_l, unsigned long mfn, - p2m_type_t t; - p2m_access_t a; - -+ /* IOMMU for PV guests is handled in get_page_type() and put_page(). */ - if ( !paging_mode_translate(p2m->domain) ) -- { -- int rc = 0; -- -- if ( need_iommu(p2m->domain) ) -- { -- for ( i = 0; i < (1 << page_order); i++ ) -- { -- int ret = iommu_unmap_page(p2m->domain, mfn + i); -- -- if ( !rc ) -- rc = ret; -- } -- } -- -- return rc; -- } -+ return 0; - - ASSERT(gfn_locked_by_me(p2m, gfn)); - P2M_DEBUG("removing gfn=%#lx mfn=%#lx\n", gfn_l, mfn); -@@ -769,26 +755,33 @@ guest_physmap_add_entry(struct domain *d, gfn_t gfn, mfn_t mfn, - int pod_count = 0; - int rc = 0; - -+ /* IOMMU for PV guests is handled in get_page_type() and put_page(). */ - if ( !paging_mode_translate(d) ) - { -- if ( need_iommu(d) && t == p2m_ram_rw ) -- { -- for ( i = 0; i < (1 << page_order); i++ ) -- { -- rc = iommu_map_page(d, mfn_x(mfn_add(mfn, i)), -- mfn_x(mfn_add(mfn, i)), -- IOMMUF_readable|IOMMUF_writable); -- if ( rc != 0 ) -- { -- while ( i-- > 0 ) -- /* If statement to satisfy __must_check. */ -- if ( iommu_unmap_page(d, mfn_x(mfn_add(mfn, i))) ) -- continue; -+ struct page_info *page = mfn_to_page(mfn); - -- return rc; -- } -- } -+ /* -+ * Our interface for PV guests wrt IOMMU entries hasn't been very -+ * clear; but historically, pages have started out with IOMMU mappings, -+ * and only lose them when changed to a different page type. -+ * -+ * Retain this property by grabbing a writable type ref and then -+ * dropping it immediately. The result will be pages that have a -+ * writable type (and an IOMMU entry), but a count of 0 (such that -+ * any guest-requested type changes succeed and remove the IOMMU -+ * entry). -+ */ -+ if ( !need_iommu(d) || t != p2m_ram_rw ) -+ return 0; -+ -+ for ( i = 0; i < (1UL << page_order); ++i, ++page ) -+ { -+ if ( get_page_and_type(page, d, PGT_writable_page) ) -+ put_page_and_type(page); -+ else -+ return -EINVAL; - } -+ - return 0; - } - --- -2.20.1 - diff --git a/xsa290-4.11-1.patch b/xsa290-4.11-1.patch deleted file mode 100644 index 38ddb56..0000000 --- a/xsa290-4.11-1.patch +++ /dev/null @@ -1,237 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: also allow L2 (un)validation to be preemptible - -Commit c612481d1c ("x86/mm: Plumbing to allow any PTE update to fail -with -ERESTART") added assertions next to the {alloc,free}_l2_table() -invocations to document (and validate in debug builds) that L2 -(un)validations are always preemptible. - -The assertion in free_page_type() was now observed to trigger when -recursive L2 page tables get cleaned up. - -In particular put_page_from_l2e()'s assumption that _put_page_type() -would always succeed is now wrong, resulting in a partially un-validated -page left in a domain, which has no other means of getting cleaned up -later on. If not causing any problems earlier, this would ultimately -trigger the check for ->u.inuse.type_info having a zero count when -freeing the page during cleanup after the domain has died. - -As a result it should be considered a mistake to not have extended -preemption fully to L2 when it was added to L3/L4 table handling, which -this change aims to correct. - -The validation side additions are done just for symmetry. - -This is part of XSA-290. - -Reported-by: Manuel Bouyer -Tested-by: Manuel Bouyer -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper - ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1126,7 +1126,7 @@ get_page_from_l1e( - define_get_linear_pagetable(l2); - static int - get_page_from_l2e( -- l2_pgentry_t l2e, unsigned long pfn, struct domain *d) -+ l2_pgentry_t l2e, unsigned long pfn, struct domain *d, int partial) - { - unsigned long mfn = l2e_get_pfn(l2e); - int rc; -@@ -1141,7 +1141,8 @@ get_page_from_l2e( - return -EINVAL; - } - -- rc = get_page_and_type_from_mfn(_mfn(mfn), PGT_l1_page_table, d, 0, 0); -+ rc = get_page_and_type_from_mfn(_mfn(mfn), PGT_l1_page_table, d, -+ partial, false); - if ( unlikely(rc == -EINVAL) && get_l2_linear_pagetable(l2e, pfn, d) ) - rc = 0; - -@@ -1295,8 +1296,11 @@ void put_page_from_l1e(l1_pgentry_t l1e, - * NB. Virtual address 'l2e' maps to a machine address within frame 'pfn'. - * Note also that this automatically deals correctly with linear p.t.'s. - */ --static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn) -+static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, -+ int partial, bool defer) - { -+ int rc = 0; -+ - if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) || (l2e_get_pfn(l2e) == pfn) ) - return 1; - -@@ -1311,13 +1315,27 @@ static int put_page_from_l2e(l2_pgentry_ - else - { - struct page_info *pg = l2e_get_page(l2e); -- int rc = _put_page_type(pg, false, mfn_to_page(_mfn(pfn))); -+ struct page_info *ptpg = mfn_to_page(_mfn(pfn)); - -- ASSERT(!rc); -- put_page(pg); -+ if ( unlikely(partial > 0) ) -+ { -+ ASSERT(!defer); -+ rc = _put_page_type(pg, true, ptpg); -+ } -+ else if ( defer ) -+ { -+ current->arch.old_guest_ptpg = ptpg; -+ current->arch.old_guest_table = pg; -+ } -+ else -+ { -+ rc = _put_page_type(pg, true, ptpg); -+ if ( likely(!rc) ) -+ put_page(pg); -+ } - } - -- return 0; -+ return rc; - } - - static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, -@@ -1487,11 +1505,12 @@ static int alloc_l2_table(struct page_in - unsigned long pfn = mfn_x(page_to_mfn(page)); - l2_pgentry_t *pl2e; - unsigned int i; -- int rc = 0; -+ int rc = 0, partial = page->partial_pte; - - pl2e = map_domain_page(_mfn(pfn)); - -- for ( i = page->nr_validated_ptes; i < L2_PAGETABLE_ENTRIES; i++ ) -+ for ( i = page->nr_validated_ptes; i < L2_PAGETABLE_ENTRIES; -+ i++, partial = 0 ) - { - if ( i > page->nr_validated_ptes && hypercall_preempt_check() ) - { -@@ -1501,23 +1520,33 @@ static int alloc_l2_table(struct page_in - } - - if ( !is_guest_l2_slot(d, type, i) || -- (rc = get_page_from_l2e(pl2e[i], pfn, d)) > 0 ) -+ (rc = get_page_from_l2e(pl2e[i], pfn, d, partial)) > 0 ) - continue; - -- if ( unlikely(rc == -ERESTART) ) -+ if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- break; -+ page->partial_pte = partial ?: 1; - } -- -- if ( rc < 0 ) -+ else if ( rc == -EINTR && i ) -+ { -+ page->nr_validated_ptes = i; -+ page->partial_pte = 0; -+ rc = -ERESTART; -+ } -+ else if ( rc < 0 && rc != -EINTR ) - { - gdprintk(XENLOG_WARNING, "Failure in alloc_l2_table: slot %#x\n", i); -- while ( i-- > 0 ) -- if ( is_guest_l2_slot(d, type, i) ) -- put_page_from_l2e(pl2e[i], pfn); -- break; -+ if ( i ) -+ { -+ page->nr_validated_ptes = i; -+ page->partial_pte = 0; -+ current->arch.old_guest_ptpg = NULL; -+ current->arch.old_guest_table = page; -+ } - } -+ if ( rc < 0 ) -+ break; - - pl2e[i] = adjust_guest_l2e(pl2e[i], d); - } -@@ -1797,28 +1826,50 @@ static int free_l2_table(struct page_inf - struct domain *d = page_get_owner(page); - unsigned long pfn = mfn_x(page_to_mfn(page)); - l2_pgentry_t *pl2e; -- unsigned int i = page->nr_validated_ptes - 1; -- int err = 0; -+ int rc = 0, partial = page->partial_pte; -+ unsigned int i = page->nr_validated_ptes - !partial; - - pl2e = map_domain_page(_mfn(pfn)); - -- ASSERT(page->nr_validated_ptes); -- do { -- if ( is_guest_l2_slot(d, page->u.inuse.type_info, i) && -- put_page_from_l2e(pl2e[i], pfn) == 0 && -- i && hypercall_preempt_check() ) -+ for ( ; ; ) -+ { -+ if ( is_guest_l2_slot(d, page->u.inuse.type_info, i) ) -+ rc = put_page_from_l2e(pl2e[i], pfn, partial, false); -+ if ( rc < 0 ) -+ break; -+ -+ partial = 0; -+ -+ if ( !i-- ) -+ break; -+ -+ if ( hypercall_preempt_check() ) - { -- page->nr_validated_ptes = i; -- err = -ERESTART; -+ rc = -EINTR; -+ break; - } -- } while ( !err && i-- ); -+ } - - unmap_domain_page(pl2e); - -- if ( !err ) -+ if ( rc >= 0 ) -+ { - page->u.inuse.type_info &= ~PGT_pae_xen_l2; -+ rc = 0; -+ } -+ else if ( rc == -ERESTART ) -+ { -+ page->nr_validated_ptes = i; -+ page->partial_pte = partial ?: -1; -+ } -+ else if ( rc == -EINTR && i < L2_PAGETABLE_ENTRIES - 1 ) -+ { -+ page->nr_validated_ptes = i + 1; -+ page->partial_pte = 0; -+ rc = -ERESTART; -+ } - -- return err; -+ return rc; - } - - static int free_l3_table(struct page_info *page) -@@ -2138,7 +2189,7 @@ static int mod_l2_entry(l2_pgentry_t *pl - return -EBUSY; - } - -- if ( unlikely((rc = get_page_from_l2e(nl2e, pfn, d)) < 0) ) -+ if ( unlikely((rc = get_page_from_l2e(nl2e, pfn, d, 0)) < 0) ) - return rc; - - nl2e = adjust_guest_l2e(nl2e, d); -@@ -2157,7 +2208,8 @@ static int mod_l2_entry(l2_pgentry_t *pl - return -EBUSY; - } - -- put_page_from_l2e(ol2e, pfn); -+ put_page_from_l2e(ol2e, pfn, 0, true); -+ - return rc; - } - diff --git a/xsa290-4.11-2.patch b/xsa290-4.11-2.patch deleted file mode 100644 index 9355e02..0000000 --- a/xsa290-4.11-2.patch +++ /dev/null @@ -1,71 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: add explicit preemption checks to L3 (un)validation - -When recursive page tables are used at the L3 level, unvalidation of a -single L4 table may incur unvalidation of two levels of L3 tables, i.e. -a maximum iteration count of 512^3 for unvalidating an L4 table. The -preemption check in free_l2_table() as well as the one in -_put_page_type() may never be reached, so explicit checking is needed in -free_l3_table(). - -When recursive page tables are used at the L4 level, the iteration count -at L4 alone is capped at 512^2. As soon as a present L3 entry is hit -which itself needs unvalidation (and hence requiring another nested loop -with 512 iterations), the preemption checks added here kick in, so no -further preemption checking is needed at L4 (until we decide to permit -5-level paging for PV guests). - -The validation side additions are done just for symmetry. - -This is part of XSA-290. - -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper - ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1581,6 +1581,13 @@ static int alloc_l3_table(struct page_in - for ( i = page->nr_validated_ptes; i < L3_PAGETABLE_ENTRIES; - i++, partial = 0 ) - { -+ if ( i > page->nr_validated_ptes && hypercall_preempt_check() ) -+ { -+ page->nr_validated_ptes = i; -+ rc = -ERESTART; -+ break; -+ } -+ - if ( is_pv_32bit_domain(d) && (i == 3) ) - { - if ( !(l3e_get_flags(pl3e[i]) & _PAGE_PRESENT) || -@@ -1882,15 +1889,25 @@ static int free_l3_table(struct page_inf - - pl3e = map_domain_page(_mfn(pfn)); - -- do { -+ for ( ; ; ) -+ { - rc = put_page_from_l3e(pl3e[i], pfn, partial, 0); - if ( rc < 0 ) - break; -+ - partial = 0; -- if ( rc > 0 ) -- continue; -- pl3e[i] = unadjust_guest_l3e(pl3e[i], d); -- } while ( i-- ); -+ if ( rc == 0 ) -+ pl3e[i] = unadjust_guest_l3e(pl3e[i], d); -+ -+ if ( !i-- ) -+ break; -+ -+ if ( hypercall_preempt_check() ) -+ { -+ rc = -EINTR; -+ break; -+ } -+ } - - unmap_domain_page(pl3e); - diff --git a/xsa291-4.11.patch b/xsa291-4.11.patch deleted file mode 100644 index 392e8e7..0000000 --- a/xsa291-4.11.patch +++ /dev/null @@ -1,53 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: don't retain page type reference when IOMMU operation fails - -The IOMMU update in _get_page_type() happens between recording of the -new reference and validation of the page for its new type (if -necessary). If the IOMMU operation fails, there's no point in actually -carrying out validation. Furthermore, with this resulting in failure -getting indicated to the caller, the recorded type reference also needs -to be dropped again. - -Note that in case of failure of alloc_page_type() there's no need to -undo the IOMMU operation: Only special types get handed to the function. -The function, upon failure, clears ->u.inuse.type_info, effectively -converting the page to PGT_none. The IOMMU mapping, however, solely -depends on whether the type is PGT_writable_page. - -This is XSA-291. - -Reported-by: Igor Druzhinin -Reported-by: Andrew Cooper -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper - ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -2751,6 +2751,13 @@ static int _get_page_type(struct page_in - iommu_ret = iommu_map_page(d, gfn_x(gfn), - mfn_x(page_to_mfn(page)), - IOMMUF_readable|IOMMUF_writable); -+ -+ if ( unlikely(iommu_ret) ) -+ { -+ _put_page_type(page, false, NULL); -+ rc = iommu_ret; -+ goto out; -+ } - } - } - -@@ -2765,12 +2772,10 @@ static int _get_page_type(struct page_in - rc = alloc_page_type(page, type, preemptible); - } - -+ out: - if ( (x & PGT_partial) && !(nx & PGT_partial) ) - put_page(page); - -- if ( !rc ) -- rc = iommu_ret; -- - return rc; - } - diff --git a/xsa292.patch b/xsa292.patch deleted file mode 100644 index 198fd4f..0000000 --- a/xsa292.patch +++ /dev/null @@ -1,95 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: properly flush TLB in switch_cr3_cr4() - -The CR3 values used for contexts run with PCID enabled uniformly have -CR3.NOFLUSH set, resulting in the CR3 write itself to not cause any -flushing at all. When the second CR4 write is skipped or doesn't do any -flushing, there's nothing so far which would purge TLB entries which may -have accumulated again if the PCID doesn't change; the "just in case" -flush only affects the case where the PCID actually changes. (There may -be particularly many TLB entries re-accumulated in case of a watchdog -NMI kicking in during the critical time window.) - -Suppress the no-flush behavior of the CR3 write in this particular case. - -Similarly the second CR4 write may not cause any flushing of TLB entries -established again while the original PCID was still in use - it may get -performed because of unrelated bits changing. The flush of the old PCID -needs to happen nevertheless. - -At the same time also eliminate a possible race with lazy context -switch: Just like for CR4, CR3 may change at any time while interrupts -are enabled, due to the __sync_local_execstate() invocation from the -flush IPI handler. It is for that reason that the CR3 read, just like -the CR4 one, must happen only after interrupts have been turned off. - -This is XSA-292. - -Reported-by: Sergey Dyasli -Reported-by: Andrew Cooper -Tested-by: Sergey Dyasli -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper ---- -v3: Adjust comments. Drop old_cr4 from the PGE check in the expression - controlling the invocation of invpcid_flush_single_context(), as PGE - is always clear there. -v2: Decouple invpcid_flush_single_context() from 2nd CR4 write. - ---- a/xen/arch/x86/flushtlb.c -+++ b/xen/arch/x86/flushtlb.c -@@ -103,9 +103,8 @@ static void do_tlb_flush(void) - - void switch_cr3_cr4(unsigned long cr3, unsigned long cr4) - { -- unsigned long flags, old_cr4; -+ unsigned long flags, old_cr4, old_pcid; - u32 t; -- unsigned long old_pcid = cr3_pcid(read_cr3()); - - /* This non-reentrant function is sometimes called in interrupt context. */ - local_irq_save(flags); -@@ -133,15 +132,38 @@ void switch_cr3_cr4(unsigned long cr3, u - */ - invpcid_flush_all_nonglobals(); - -+ /* -+ * If we don't change PCIDs, the CR3 write below needs to flush this very -+ * PCID, even when a full flush was performed above, as we are currently -+ * accumulating TLB entries again from the old address space. -+ * NB: Clearing the bit when we don't use PCID is benign (as it is clear -+ * already in that case), but allows the if() to be more simple. -+ */ -+ old_pcid = cr3_pcid(read_cr3()); -+ if ( old_pcid == cr3_pcid(cr3) ) -+ cr3 &= ~X86_CR3_NOFLUSH; -+ - write_cr3(cr3); - - if ( old_cr4 != cr4 ) - write_cr4(cr4); -- else if ( old_pcid != cr3_pcid(cr3) ) -- /* -- * Make sure no TLB entries related to the old PCID created between -- * flushing the TLB and writing the new %cr3 value remain in the TLB. -- */ -+ -+ /* -+ * Make sure no TLB entries related to the old PCID created between -+ * flushing the TLB and writing the new %cr3 value remain in the TLB. -+ * -+ * The write to CR4 just above has performed a wider flush in certain -+ * cases, which therefore get excluded here. Since that write is -+ * conditional, note in particular that it won't be skipped if PCIDE -+ * transitions from 1 to 0. This is because the CR4 write further up will -+ * have been skipped in this case, as PCIDE and PGE won't both be set at -+ * the same time. -+ * -+ * Note also that PGE is always clear in old_cr4. -+ */ -+ if ( old_pcid != cr3_pcid(cr3) && -+ !(cr4 & X86_CR4_PGE) && -+ (old_cr4 & X86_CR4_PCIDE) <= (cr4 & X86_CR4_PCIDE) ) - invpcid_flush_single_context(old_pcid); - - post_flush(t); diff --git a/xsa293-4.11-1.patch b/xsa293-4.11-1.patch deleted file mode 100644 index 0b8499f..0000000 --- a/xsa293-4.11-1.patch +++ /dev/null @@ -1,317 +0,0 @@ -From: Andrew Cooper -Subject: x86/pv: Rewrite guest %cr4 handling from scratch - -The PV cr4 logic is almost impossible to follow, and leaks bits into guest -context which definitely shouldn't be visible (in particular, VMXE). - -The biggest problem however, and source of the complexity, is that it derives -new real and guest cr4 values from the current value in hardware - this is -context dependent and an inappropriate source of information. - -Rewrite the cr4 logic to be invariant of the current value in hardware. - -First of all, modify write_ptbase() to always use mmu_cr4_features for IDLE -and HVM contexts. mmu_cr4_features *is* the correct value to use, and makes -the ASSERT() obviously redundant. - -For PV guests, curr->arch.pv.ctrlreg[4] remains the guests view of cr4, but -all logic gets reworked in terms of this and mmu_cr4_features only. - -Two masks are introduced; bits which the guest has control over, and bits -which are forwarded from Xen's settings. One guest-visible change here is -that Xen's VMXE setting is no longer visible at all. - -pv_make_cr4() follows fairly closely from pv_guest_cr4_to_real_cr4(), but -deliberately starts with mmu_cr4_features, and only alters the minimal subset -of bits. - -The boot-time {compat_,}pv_cr4_mask variables are removed, as they are a -remnant of the pre-CPUID policy days. pv_fixup_guest_cr4() gains a related -derivation from the policy. - -Another guest visible change here is that a 32bit PV guest can now flip -FSGSBASE in its view of CR4. While the {RD,WR}{FS,GS}BASE instructions are -unusable outside of a 64bit code segment, the ability to modify FSGSBASE -matches real hardware behaviour, and avoids the need for any 32bit/64bit -differences in the logic. - -Overall, this patch shouldn't have a practical change in guest behaviour. -VMXE will disappear from view, and an inquisitive 32bit kernel can now see -FSGSBASE changing, but this new logic is otherwise bug-compatible with before. - -This is part of XSA-293 - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c -index b1e50d1..675152a 100644 ---- a/xen/arch/x86/domain.c -+++ b/xen/arch/x86/domain.c -@@ -733,49 +733,6 @@ int arch_domain_soft_reset(struct domain *d) - return ret; - } - --/* -- * These are the masks of CR4 bits (subject to hardware availability) which a -- * PV guest may not legitimiately attempt to modify. -- */ --static unsigned long __read_mostly pv_cr4_mask, compat_pv_cr4_mask; -- --static int __init init_pv_cr4_masks(void) --{ -- unsigned long common_mask = ~X86_CR4_TSD; -- -- /* -- * All PV guests may attempt to modify TSD, DE and OSXSAVE. -- */ -- if ( cpu_has_de ) -- common_mask &= ~X86_CR4_DE; -- if ( cpu_has_xsave ) -- common_mask &= ~X86_CR4_OSXSAVE; -- -- pv_cr4_mask = compat_pv_cr4_mask = common_mask; -- -- /* -- * 64bit PV guests may attempt to modify FSGSBASE. -- */ -- if ( cpu_has_fsgsbase ) -- pv_cr4_mask &= ~X86_CR4_FSGSBASE; -- -- return 0; --} --__initcall(init_pv_cr4_masks); -- --unsigned long pv_guest_cr4_fixup(const struct vcpu *v, unsigned long guest_cr4) --{ -- unsigned long hv_cr4 = real_cr4_to_pv_guest_cr4(read_cr4()); -- unsigned long mask = is_pv_32bit_vcpu(v) ? compat_pv_cr4_mask : pv_cr4_mask; -- -- if ( (guest_cr4 & mask) != (hv_cr4 & mask) ) -- printk(XENLOG_G_WARNING -- "d%d attempted to change %pv's CR4 flags %08lx -> %08lx\n", -- current->domain->domain_id, v, hv_cr4, guest_cr4); -- -- return (hv_cr4 & mask) | (guest_cr4 & ~mask); --} -- - #define xen_vcpu_guest_context vcpu_guest_context - #define fpu_ctxt fpu_ctxt.x - CHECK_FIELD_(struct, vcpu_guest_context, fpu_ctxt); -@@ -789,7 +746,7 @@ int arch_set_info_guest( - struct domain *d = v->domain; - unsigned long cr3_gfn; - struct page_info *cr3_page; -- unsigned long flags, cr4; -+ unsigned long flags; - unsigned int i; - int rc = 0, compat; - -@@ -978,9 +935,8 @@ int arch_set_info_guest( - v->arch.pv_vcpu.ctrlreg[0] &= X86_CR0_TS; - v->arch.pv_vcpu.ctrlreg[0] |= read_cr0() & ~X86_CR0_TS; - -- cr4 = v->arch.pv_vcpu.ctrlreg[4]; -- v->arch.pv_vcpu.ctrlreg[4] = cr4 ? pv_guest_cr4_fixup(v, cr4) : -- real_cr4_to_pv_guest_cr4(mmu_cr4_features); -+ v->arch.pv_vcpu.ctrlreg[4] = -+ pv_fixup_guest_cr4(v, v->arch.pv_vcpu.ctrlreg[4]); - - memset(v->arch.debugreg, 0, sizeof(v->arch.debugreg)); - for ( i = 0; i < 8; i++ ) -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 6509035..08634b7 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -505,33 +505,13 @@ void make_cr3(struct vcpu *v, mfn_t mfn) - v->arch.cr3 |= get_pcid_bits(v, false); - } - --unsigned long pv_guest_cr4_to_real_cr4(const struct vcpu *v) --{ -- const struct domain *d = v->domain; -- unsigned long cr4; -- -- cr4 = v->arch.pv_vcpu.ctrlreg[4] & ~X86_CR4_DE; -- cr4 |= mmu_cr4_features & (X86_CR4_PSE | X86_CR4_SMEP | X86_CR4_SMAP | -- X86_CR4_OSXSAVE | X86_CR4_FSGSBASE); -- -- if ( d->arch.pv_domain.pcid ) -- cr4 |= X86_CR4_PCIDE; -- else if ( !d->arch.pv_domain.xpti ) -- cr4 |= X86_CR4_PGE; -- -- cr4 |= d->arch.vtsc ? X86_CR4_TSD : 0; -- -- return cr4; --} -- - void write_ptbase(struct vcpu *v) - { - struct cpu_info *cpu_info = get_cpu_info(); - unsigned long new_cr4; - - new_cr4 = (is_pv_vcpu(v) && !is_idle_vcpu(v)) -- ? pv_guest_cr4_to_real_cr4(v) -- : ((read_cr4() & ~(X86_CR4_PCIDE | X86_CR4_TSD)) | X86_CR4_PGE); -+ ? pv_make_cr4(v) : mmu_cr4_features; - - if ( is_pv_vcpu(v) && v->domain->arch.pv_domain.xpti ) - { -@@ -550,8 +530,6 @@ void write_ptbase(struct vcpu *v) - switch_cr3_cr4(v->arch.cr3, new_cr4); - cpu_info->pv_cr3 = 0; - } -- -- ASSERT(is_pv_vcpu(v) || read_cr4() == mmu_cr4_features); - } - - /* -diff --git a/xen/arch/x86/pv/domain.c b/xen/arch/x86/pv/domain.c -index b75ff6b..3965959 100644 ---- a/xen/arch/x86/pv/domain.c -+++ b/xen/arch/x86/pv/domain.c -@@ -97,6 +97,52 @@ static void release_compat_l4(struct vcpu *v) - v->arch.guest_table_user = pagetable_null(); - } - -+unsigned long pv_fixup_guest_cr4(const struct vcpu *v, unsigned long cr4) -+{ -+ const struct cpuid_policy *p = v->domain->arch.cpuid; -+ -+ /* Discard attempts to set guest controllable bits outside of the policy. */ -+ cr4 &= ~((p->basic.tsc ? 0 : X86_CR4_TSD) | -+ (p->basic.de ? 0 : X86_CR4_DE) | -+ (p->feat.fsgsbase ? 0 : X86_CR4_FSGSBASE) | -+ (p->basic.xsave ? 0 : X86_CR4_OSXSAVE)); -+ -+ /* Masks expected to be disjoint sets. */ -+ BUILD_BUG_ON(PV_CR4_GUEST_MASK & PV_CR4_GUEST_VISIBLE_MASK); -+ -+ /* -+ * A guest sees the policy subset of its own choice of guest controllable -+ * bits, and a subset of Xen's choice of certain hardware settings. -+ */ -+ return ((cr4 & PV_CR4_GUEST_MASK) | -+ (mmu_cr4_features & PV_CR4_GUEST_VISIBLE_MASK)); -+} -+ -+unsigned long pv_make_cr4(const struct vcpu *v) -+{ -+ const struct domain *d = v->domain; -+ unsigned long cr4 = mmu_cr4_features & -+ ~(X86_CR4_PCIDE | X86_CR4_PGE | X86_CR4_TSD); -+ -+ /* -+ * PCIDE or PGE depends on the PCID/XPTI settings, but must not both be -+ * set, as it impacts the safety of TLB flushing. -+ */ -+ if ( d->arch.pv_domain.pcid ) -+ cr4 |= X86_CR4_PCIDE; -+ else if ( !d->arch.pv_domain.xpti ) -+ cr4 |= X86_CR4_PGE; -+ -+ /* -+ * TSD is needed if either the guest has elected to use it, or Xen is -+ * virtualising the TSC value the guest sees. -+ */ -+ if ( d->arch.vtsc || (v->arch.pv_vcpu.ctrlreg[4] & X86_CR4_TSD) ) -+ cr4 |= X86_CR4_TSD; -+ -+ return cr4; -+} -+ - int switch_compat(struct domain *d) - { - struct vcpu *v; -@@ -191,7 +237,7 @@ int pv_vcpu_initialise(struct vcpu *v) - /* PV guests by default have a 100Hz ticker. */ - v->periodic_period = MILLISECS(10); - -- v->arch.pv_vcpu.ctrlreg[4] = real_cr4_to_pv_guest_cr4(mmu_cr4_features); -+ v->arch.pv_vcpu.ctrlreg[4] = pv_fixup_guest_cr4(v, 0); - - if ( is_pv_32bit_domain(d) ) - { -diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c -index ce2ec76..4abbc14 100644 ---- a/xen/arch/x86/pv/emul-priv-op.c -+++ b/xen/arch/x86/pv/emul-priv-op.c -@@ -32,6 +32,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -785,8 +786,8 @@ static int write_cr(unsigned int reg, unsigned long val, - } - - case 4: /* Write CR4 */ -- curr->arch.pv_vcpu.ctrlreg[4] = pv_guest_cr4_fixup(curr, val); -- write_cr4(pv_guest_cr4_to_real_cr4(curr)); -+ curr->arch.pv_vcpu.ctrlreg[4] = pv_fixup_guest_cr4(curr, val); -+ write_cr4(pv_make_cr4(curr)); - ctxt_switch_levelling(curr); - return X86EMUL_OKAY; - } -diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h -index ec81d78..c8aa8a5 100644 ---- a/xen/include/asm-x86/domain.h -+++ b/xen/include/asm-x86/domain.h -@@ -610,17 +610,6 @@ bool update_secondary_system_time(struct vcpu *, - void vcpu_show_execution_state(struct vcpu *); - void vcpu_show_registers(const struct vcpu *); - --/* Clean up CR4 bits that are not under guest control. */ --unsigned long pv_guest_cr4_fixup(const struct vcpu *, unsigned long guest_cr4); -- --/* Convert between guest-visible and real CR4 values. */ --unsigned long pv_guest_cr4_to_real_cr4(const struct vcpu *v); -- --#define real_cr4_to_pv_guest_cr4(c) \ -- ((c) & ~(X86_CR4_PGE | X86_CR4_PSE | X86_CR4_TSD | \ -- X86_CR4_OSXSAVE | X86_CR4_SMEP | \ -- X86_CR4_FSGSBASE | X86_CR4_SMAP | X86_CR4_PCIDE)) -- - #define domain_max_vcpus(d) (is_hvm_domain(d) ? HVM_MAX_VCPUS : MAX_VIRT_CPUS) - - static inline struct vcpu_guest_context *alloc_vcpu_guest_context(void) -diff --git a/xen/include/asm-x86/pv/domain.h b/xen/include/asm-x86/pv/domain.h -index 4fea764..4e4710c 100644 ---- a/xen/include/asm-x86/pv/domain.h -+++ b/xen/include/asm-x86/pv/domain.h -@@ -59,6 +59,23 @@ int pv_vcpu_initialise(struct vcpu *v); - void pv_domain_destroy(struct domain *d); - int pv_domain_initialise(struct domain *d); - -+/* -+ * Bits which a PV guest can toggle in its view of cr4. Some are loaded into -+ * hardware, while some are fully emulated. -+ */ -+#define PV_CR4_GUEST_MASK \ -+ (X86_CR4_TSD | X86_CR4_DE | X86_CR4_FSGSBASE | X86_CR4_OSXSAVE) -+ -+/* Bits which a PV guest may observe from the real hardware settings. */ -+#define PV_CR4_GUEST_VISIBLE_MASK \ -+ (X86_CR4_PAE | X86_CR4_MCE | X86_CR4_OSFXSR | X86_CR4_OSXMMEXCPT) -+ -+/* Given a new cr4 value, construct the resulting guest-visible cr4 value. */ -+unsigned long pv_fixup_guest_cr4(const struct vcpu *v, unsigned long cr4); -+ -+/* Create a cr4 value to load into hardware, based on vcpu settings. */ -+unsigned long pv_make_cr4(const struct vcpu *v); -+ - #else /* !CONFIG_PV */ - - #include -@@ -68,6 +85,8 @@ static inline int pv_vcpu_initialise(struct vcpu *v) { return -EOPNOTSUPP; } - static inline void pv_domain_destroy(struct domain *d) {} - static inline int pv_domain_initialise(struct domain *d) { return -EOPNOTSUPP; } - -+static inline unsigned long pv_make_cr4(const struct vcpu *v) { return ~0ul; } -+ - #endif /* CONFIG_PV */ - - void paravirt_ctxt_switch_from(struct vcpu *v); diff --git a/xsa293-4.11-2.patch b/xsa293-4.11-2.patch deleted file mode 100644 index a005021..0000000 --- a/xsa293-4.11-2.patch +++ /dev/null @@ -1,260 +0,0 @@ -From: Andrew Cooper -Subject: x86/pv: Don't have %cr4.fsgsbase active behind a guest kernels back - -Currently, a 64bit PV guest can appear to set and clear FSGSBASE in %cr4, but -the bit remains set in hardware. Therefore, the {RD,WR}{FS,GS}BASE are usable -even when the guest kernel believes that they are disabled. - -The FSGSBASE feature isn't currently supported in Linux, and its context -switch path has some optimisations which rely on userspace being unable to use -the WR{FS,GS}BASE instructions. Xen's current behaviour undermines this -expectation. - -In 64bit PV guest context, always load the guest kernels setting of FSGSBASE -into %cr4. This requires adjusting how Xen uses the {RD,WR}{FS,GS}BASE -instructions. - - * Delete the cpu_has_fsgsbase helper. It is no longer safe, as users need to - check %cr4 directly. - * The raw __rd{fs,gs}base() helpers are only safe to use when %cr4.fsgsbase - is set. Comment this property. - * The {rd,wr}{fs,gs}{base,shadow}() and read_msr() helpers are updated to use - the current %cr4 value to determine which mechanism to use. - * toggle_guest_mode() and save_segments() are update to avoid reading - fs/gsbase if the values in hardware cannot be stale WRT struct vcpu. A - consequence of this is that the write_cr() path needs to cache the current - bases, as subsequent context switches will skip saving the values. - * write_cr4() is updated to ensure that the shadow %cr4.fsgsbase value is - observed in a safe way WRT the hardware setting, if an interrupt happens to - hit in the middle. - * pv_make_cr4() is updated for 64bit PV guests to use the guest kernels - choice of FSGSBASE. - -This is part of XSA-293 - -Reported-by: Andy Lutomirski -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c -index 675152a..29f892c 100644 ---- a/xen/arch/x86/domain.c -+++ b/xen/arch/x86/domain.c -@@ -1433,7 +1433,8 @@ static void save_segments(struct vcpu *v) - regs->fs = read_sreg(fs); - regs->gs = read_sreg(gs); - -- if ( cpu_has_fsgsbase && !is_pv_32bit_vcpu(v) ) -+ /* %fs/%gs bases can only be stale if WR{FS,GS}BASE are usable. */ -+ if ( (read_cr4() & X86_CR4_FSGSBASE) && !is_pv_32bit_vcpu(v) ) - { - v->arch.pv_vcpu.fs_base = __rdfsbase(); - if ( v->arch.flags & TF_kernel_mode ) -diff --git a/xen/arch/x86/pv/domain.c b/xen/arch/x86/pv/domain.c -index 3965959..228a174 100644 ---- a/xen/arch/x86/pv/domain.c -+++ b/xen/arch/x86/pv/domain.c -@@ -140,6 +140,16 @@ unsigned long pv_make_cr4(const struct vcpu *v) - if ( d->arch.vtsc || (v->arch.pv_vcpu.ctrlreg[4] & X86_CR4_TSD) ) - cr4 |= X86_CR4_TSD; - -+ /* -+ * The {RD,WR}{FS,GS}BASE are only useable in 64bit code segments. While -+ * we must not have CR4.FSGSBASE set behind the back of a 64bit PV kernel, -+ * we do leave it set in 32bit PV context to speed up Xen's context switch -+ * path. -+ */ -+ if ( !is_pv_32bit_domain(d) && -+ !(v->arch.pv_vcpu.ctrlreg[4] & X86_CR4_FSGSBASE) ) -+ cr4 &= ~X86_CR4_FSGSBASE; -+ - return cr4; - } - -@@ -375,7 +385,8 @@ void toggle_guest_mode(struct vcpu *v) - { - ASSERT(!is_pv_32bit_vcpu(v)); - -- if ( cpu_has_fsgsbase ) -+ /* %fs/%gs bases can only be stale if WR{FS,GS}BASE are usable. */ -+ if ( read_cr4() & X86_CR4_FSGSBASE ) - { - if ( v->arch.flags & TF_kernel_mode ) - v->arch.pv_vcpu.gs_base_kernel = __rdgsbase(); -diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c -index 4abbc14..312c1ee 100644 ---- a/xen/arch/x86/pv/emul-priv-op.c -+++ b/xen/arch/x86/pv/emul-priv-op.c -@@ -786,6 +786,17 @@ static int write_cr(unsigned int reg, unsigned long val, - } - - case 4: /* Write CR4 */ -+ /* -+ * If this write will disable FSGSBASE, refresh Xen's idea of the -+ * guest bases now that they can no longer change. -+ */ -+ if ( (curr->arch.pv_vcpu.ctrlreg[4] & X86_CR4_FSGSBASE) && -+ !(val & X86_CR4_FSGSBASE) ) -+ { -+ curr->arch.pv_vcpu.fs_base = __rdfsbase(); -+ curr->arch.pv_vcpu.gs_base_kernel = __rdgsbase(); -+ } -+ - curr->arch.pv_vcpu.ctrlreg[4] = pv_fixup_guest_cr4(curr, val); - write_cr4(pv_make_cr4(curr)); - ctxt_switch_levelling(curr); -@@ -835,14 +846,15 @@ static int read_msr(unsigned int reg, uint64_t *val, - case MSR_FS_BASE: - if ( is_pv_32bit_domain(currd) ) - break; -- *val = cpu_has_fsgsbase ? __rdfsbase() : curr->arch.pv_vcpu.fs_base; -+ *val = (read_cr4() & X86_CR4_FSGSBASE) ? __rdfsbase() -+ : curr->arch.pv_vcpu.fs_base; - return X86EMUL_OKAY; - - case MSR_GS_BASE: - if ( is_pv_32bit_domain(currd) ) - break; -- *val = cpu_has_fsgsbase ? __rdgsbase() -- : curr->arch.pv_vcpu.gs_base_kernel; -+ *val = (read_cr4() & X86_CR4_FSGSBASE) ? __rdgsbase() -+ : curr->arch.pv_vcpu.gs_base_kernel; - return X86EMUL_OKAY; - - case MSR_SHADOW_GS_BASE: -diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c -index ecb0149..a353d76 100644 ---- a/xen/arch/x86/setup.c -+++ b/xen/arch/x86/setup.c -@@ -1567,7 +1567,7 @@ void __init noreturn __start_xen(unsigned long mbi_p) - - cr4_pv32_mask = mmu_cr4_features & XEN_CR4_PV32_BITS; - -- if ( cpu_has_fsgsbase ) -+ if ( boot_cpu_has(X86_FEATURE_FSGSBASE) ) - set_in_cr4(X86_CR4_FSGSBASE); - - if ( opt_invpcid && cpu_has_invpcid ) -diff --git a/xen/include/asm-x86/cpufeature.h b/xen/include/asm-x86/cpufeature.h -index b237da1..861cb0a 100644 ---- a/xen/include/asm-x86/cpufeature.h -+++ b/xen/include/asm-x86/cpufeature.h -@@ -90,7 +90,6 @@ - #define cpu_has_xsaves boot_cpu_has(X86_FEATURE_XSAVES) - - /* CPUID level 0x00000007:0.ebx */ --#define cpu_has_fsgsbase boot_cpu_has(X86_FEATURE_FSGSBASE) - #define cpu_has_bmi1 boot_cpu_has(X86_FEATURE_BMI1) - #define cpu_has_hle boot_cpu_has(X86_FEATURE_HLE) - #define cpu_has_avx2 boot_cpu_has(X86_FEATURE_AVX2) -diff --git a/xen/include/asm-x86/msr.h b/xen/include/asm-x86/msr.h -index afbeb7f..1ba6ee3 100644 ---- a/xen/include/asm-x86/msr.h -+++ b/xen/include/asm-x86/msr.h -@@ -120,6 +120,14 @@ static inline uint64_t rdtsc_ordered(void) - : "=a" (low), "=d" (high) \ - : "c" (counter)) - -+/* -+ * On hardware supporting FSGSBASE, the value loaded into hardware is the -+ * guest kernel's choice for 64bit PV guests (Xen's choice for Idle, HVM and -+ * 32bit PV). -+ * -+ * Therefore, the {RD,WR}{FS,GS}BASE instructions are only safe to use if -+ * %cr4.fsgsbase is set. -+ */ - static inline unsigned long __rdfsbase(void) - { - unsigned long base; -@@ -150,7 +158,7 @@ static inline unsigned long rdfsbase(void) - { - unsigned long base; - -- if ( cpu_has_fsgsbase ) -+ if ( read_cr4() & X86_CR4_FSGSBASE ) - return __rdfsbase(); - - rdmsrl(MSR_FS_BASE, base); -@@ -162,7 +170,7 @@ static inline unsigned long rdgsbase(void) - { - unsigned long base; - -- if ( cpu_has_fsgsbase ) -+ if ( read_cr4() & X86_CR4_FSGSBASE ) - return __rdgsbase(); - - rdmsrl(MSR_GS_BASE, base); -@@ -174,7 +182,7 @@ static inline unsigned long rdgsshadow(void) - { - unsigned long base; - -- if ( cpu_has_fsgsbase ) -+ if ( read_cr4() & X86_CR4_FSGSBASE ) - { - asm volatile ( "swapgs" ); - base = __rdgsbase(); -@@ -188,7 +196,7 @@ static inline unsigned long rdgsshadow(void) - - static inline void wrfsbase(unsigned long base) - { -- if ( cpu_has_fsgsbase ) -+ if ( read_cr4() & X86_CR4_FSGSBASE ) - #ifdef HAVE_AS_FSGSBASE - asm volatile ( "wrfsbase %0" :: "r" (base) ); - #else -@@ -200,7 +208,7 @@ static inline void wrfsbase(unsigned long base) - - static inline void wrgsbase(unsigned long base) - { -- if ( cpu_has_fsgsbase ) -+ if ( read_cr4() & X86_CR4_FSGSBASE ) - #ifdef HAVE_AS_FSGSBASE - asm volatile ( "wrgsbase %0" :: "r" (base) ); - #else -@@ -212,7 +220,7 @@ static inline void wrgsbase(unsigned long base) - - static inline void wrgsshadow(unsigned long base) - { -- if ( cpu_has_fsgsbase ) -+ if ( read_cr4() & X86_CR4_FSGSBASE ) - { - asm volatile ( "swapgs\n\t" - #ifdef HAVE_AS_FSGSBASE -diff --git a/xen/include/asm-x86/processor.h b/xen/include/asm-x86/processor.h -index 2bd9e69..8e253dc 100644 ---- a/xen/include/asm-x86/processor.h -+++ b/xen/include/asm-x86/processor.h -@@ -305,11 +305,31 @@ static inline unsigned long read_cr4(void) - - static inline void write_cr4(unsigned long val) - { -+ struct cpu_info *info = get_cpu_info(); -+ - /* No global pages in case of PCIDs enabled! */ - ASSERT(!(val & X86_CR4_PGE) || !(val & X86_CR4_PCIDE)); - -- get_cpu_info()->cr4 = val; -- asm volatile ( "mov %0,%%cr4" : : "r" (val) ); -+ /* -+ * On hardware supporting FSGSBASE, the value in %cr4 is the kernel's -+ * choice for 64bit PV guests, which impacts whether Xen can use the -+ * instructions. -+ * -+ * The {rd,wr}{fs,gs}base() helpers use info->cr4 to work out whether it -+ * is safe to execute the {RD,WR}{FS,GS}BASE instruction, falling back to -+ * the MSR path if not. Some users require interrupt safety. -+ * -+ * If FSGSBASE is currently or about to become clear, reflect this in -+ * info->cr4 before updating %cr4, so an interrupt which hits in the -+ * middle won't observe FSGSBASE set in info->cr4 but clear in %cr4. -+ */ -+ info->cr4 = val & (info->cr4 | ~X86_CR4_FSGSBASE); -+ -+ asm volatile ( "mov %[val], %%cr4" -+ : "+m" (info->cr4) /* Force ordering without a barrier. */ -+ : [val] "r" (val) ); -+ -+ info->cr4 = val; - } - - /* Clear and set 'TS' bit respectively */ diff --git a/xsa294-4.11.patch b/xsa294-4.11.patch deleted file mode 100644 index a0784d9..0000000 --- a/xsa294-4.11.patch +++ /dev/null @@ -1,71 +0,0 @@ -From: Jan Beulich -Subject: x86/pv: _toggle_guest_pt() may not skip TLB flush for shadow mode guests - -For shadow mode guests (e.g. PV ones forced into that mode as L1TF -mitigation, or during migration) update_cr3() -> sh_update_cr3() may -result in a change to the (shadow) root page table (compared to the -previous one when running the same vCPU with the same PCID). This can, -first and foremost, be a result of memory pressure on the shadow memory -pool of the domain. Shadow code legitimately relies on the original -(prior to commit 5c81d260c2 ["xen/x86: use PCID feature"]) behavior of -the subsequent CR3 write to flush the TLB of entries still left from -walks with an earlier, different (shadow) root page table. - -Restore the flushing behavior, also for the second CR3 write on the exit -path to guest context when XPTI is active. For the moment accept that -this will introduce more flushes than are strictly necessary - no flush -would be needed when the (shadow) root page table doesn't actually -change, but this information isn't readily (i.e. without introducing a -layering violation) available here. - -This is XSA-294. - -Reported-by: XXX PERSON -Signed-off-by: Jan Beulich -Tested-by: Juergen Gross -Reviewed-by: Andrew Cooper - -diff --git a/xen/arch/x86/pv/domain.c b/xen/arch/x86/pv/domain.c -index b75ff6b..528413a 100644 ---- a/xen/arch/x86/pv/domain.c -+++ b/xen/arch/x86/pv/domain.c -@@ -296,21 +296,35 @@ int pv_domain_initialise(struct domain *d) - static void _toggle_guest_pt(struct vcpu *v) - { - const struct domain *d = v->domain; -+ struct cpu_info *cpu_info = get_cpu_info(); -+ unsigned long cr3; - - v->arch.flags ^= TF_kernel_mode; - update_cr3(v); - if ( d->arch.pv_domain.xpti ) - { -- struct cpu_info *cpu_info = get_cpu_info(); -- - cpu_info->root_pgt_changed = true; - cpu_info->pv_cr3 = __pa(this_cpu(root_pgt)) | - (d->arch.pv_domain.pcid - ? get_pcid_bits(v, true) : 0); - } - -- /* Don't flush user global mappings from the TLB. Don't tick TLB clock. */ -- write_cr3(v->arch.cr3); -+ /* -+ * Don't flush user global mappings from the TLB. Don't tick TLB clock. -+ * -+ * In shadow mode, though, update_cr3() may need to be accompanied by a -+ * 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. The XPTI CR3 write, if enabled, -+ * will then need to be a flushing one too. -+ */ -+ cr3 = v->arch.cr3; -+ if ( shadow_mode_enabled(d) ) -+ { -+ cr3 &= ~X86_CR3_NOFLUSH; -+ cpu_info->pv_cr3 &= ~X86_CR3_NOFLUSH; -+ } -+ write_cr3(cr3); - - if ( !(v->arch.flags & TF_kernel_mode) ) - return; diff --git a/xsa295-4.11-01.patch b/xsa295-4.11-01.patch deleted file mode 100644 index 05ecdf1..0000000 --- a/xsa295-4.11-01.patch +++ /dev/null @@ -1,84 +0,0 @@ -From 66db8e4c095491ae795c8eebafd778b2dab6513d Mon Sep 17 00:00:00 2001 -From: Jan Beulich -Date: Tue, 12 Mar 2019 14:40:24 +0100 -Subject: [PATCH v2 4.11 01/20] events: drop arch_evtchn_inject() - -Have the only user call vcpu_mark_events_pending() instead, at the same -time arranging for correct ordering of the writes (evtchn_pending_sel -should be written before evtchn_upcall_pending). - -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper -Reviewed-by: Julien Grall ---- - xen/arch/arm/vgic.c | 5 ----- - xen/arch/arm/vgic/vgic.c | 5 ----- - xen/common/domain.c | 3 +-- - xen/include/xen/event.h | 3 --- - 4 files changed, 1 insertion(+), 15 deletions(-) - -diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c -index 3fafdd0b66..07c704edff 100644 ---- a/xen/arch/arm/vgic.c -+++ b/xen/arch/arm/vgic.c -@@ -597,11 +597,6 @@ out: - return; - } - --void arch_evtchn_inject(struct vcpu *v) --{ -- vgic_inject_irq(v->domain, v, v->domain->arch.evtchn_irq, true); --} -- - bool vgic_evtchn_irq_pending(struct vcpu *v) - { - struct pending_irq *p; -diff --git a/xen/arch/arm/vgic/vgic.c b/xen/arch/arm/vgic/vgic.c -index a35449bf11..bd4e268e57 100644 ---- a/xen/arch/arm/vgic/vgic.c -+++ b/xen/arch/arm/vgic/vgic.c -@@ -692,11 +692,6 @@ void vgic_kick_vcpus(struct domain *d) - } - } - --void arch_evtchn_inject(struct vcpu *v) --{ -- vgic_inject_irq(v->domain, v, v->domain->arch.evtchn_irq, true); --} -- - bool vgic_evtchn_irq_pending(struct vcpu *v) - { - struct vgic_irq *irq; -diff --git a/xen/common/domain.c b/xen/common/domain.c -index 6cbf135457..f0458c200f 100644 ---- a/xen/common/domain.c -+++ b/xen/common/domain.c -@@ -1239,10 +1239,9 @@ int map_vcpu_info(struct vcpu *v, unsigned long gfn, unsigned offset) - * Mark everything as being pending just to make sure nothing gets - * lost. The domain will get a spurious event, but it can cope. - */ -- vcpu_info(v, evtchn_upcall_pending) = 1; - for ( i = 0; i < BITS_PER_EVTCHN_WORD(d); i++ ) - set_bit(i, &vcpu_info(v, evtchn_pending_sel)); -- arch_evtchn_inject(v); -+ vcpu_mark_events_pending(v); - - return 0; - } -diff --git a/xen/include/xen/event.h b/xen/include/xen/event.h -index ebb879e88d..b8152a9831 100644 ---- a/xen/include/xen/event.h -+++ b/xen/include/xen/event.h -@@ -83,9 +83,6 @@ int guest_enabled_event(struct vcpu *v, uint32_t virq); - /* Notify remote end of a Xen-attached event channel.*/ - void notify_via_xen_event_channel(struct domain *ld, int lport); - --/* Inject an event channel notification into the guest */ --void arch_evtchn_inject(struct vcpu *v); -- - /* - * Internal event channel object storage. - * --- -2.17.1 - diff --git a/xsa295-4.11-02.patch b/xsa295-4.11-02.patch deleted file mode 100644 index 6d5daa9..0000000 --- a/xsa295-4.11-02.patch +++ /dev/null @@ -1,90 +0,0 @@ -From 6284dd531adc18c71180b30f8c027c8ac13af945 Mon Sep 17 00:00:00 2001 -From: Jan Beulich -Date: Tue, 12 Mar 2019 14:40:56 +0100 -Subject: [PATCH v2 4.11 02/20] common: avoid atomic read-modify-write accesses - in map_vcpu_info() - -There's no need to set the evtchn_pending_sel bits one by one. Simply -write full words with all ones. - -For Arm this requires extending write_atomic() to also handle 64-bit -values; for symmetry read_atomic() gets adjusted as well. - -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper -Reviewed-by: Julien Grall ---- - xen/common/domain.c | 9 ++++++--- - xen/include/asm-arm/atomic.h | 15 +++++++++++++++ - 2 files changed, 21 insertions(+), 3 deletions(-) - -diff --git a/xen/common/domain.c b/xen/common/domain.c -index f0458c200f..6ee2bba753 100644 ---- a/xen/common/domain.c -+++ b/xen/common/domain.c -@@ -1186,7 +1186,6 @@ int map_vcpu_info(struct vcpu *v, unsigned long gfn, unsigned offset) - void *mapping; - vcpu_info_t *new_info; - struct page_info *page; -- int i; - - if ( offset > (PAGE_SIZE - sizeof(vcpu_info_t)) ) - return -EINVAL; -@@ -1239,8 +1238,12 @@ int map_vcpu_info(struct vcpu *v, unsigned long gfn, unsigned offset) - * Mark everything as being pending just to make sure nothing gets - * lost. The domain will get a spurious event, but it can cope. - */ -- for ( i = 0; i < BITS_PER_EVTCHN_WORD(d); i++ ) -- set_bit(i, &vcpu_info(v, evtchn_pending_sel)); -+#ifdef CONFIG_COMPAT -+ if ( !has_32bit_shinfo(d) ) -+ write_atomic(&new_info->native.evtchn_pending_sel, ~0); -+ else -+#endif -+ write_atomic(&vcpu_info(v, evtchn_pending_sel), ~0); - vcpu_mark_events_pending(v); - - return 0; -diff --git a/xen/include/asm-arm/atomic.h b/xen/include/asm-arm/atomic.h -index afb3eeea5b..7b4c987fa7 100644 ---- a/xen/include/asm-arm/atomic.h -+++ b/xen/include/asm-arm/atomic.h -@@ -55,6 +55,19 @@ build_atomic_write(write_int_atomic, "", WORD, int, "r") - #if defined (CONFIG_ARM_64) - build_atomic_read(read_u64_atomic, "", "", uint64_t, "=r") - build_atomic_write(write_u64_atomic, "", "", uint64_t, "r") -+#elif defined (CONFIG_ARM_32) -+static inline uint64_t read_u64_atomic(const volatile uint64_t *addr) -+{ -+ uint64_t val; -+ -+ asm volatile ( "ldrd %0,%H0,%1" : "=r" (val) : "m" (*addr) ); -+ -+ return val; -+} -+static inline void write_u64_atomic(volatile uint64_t *addr, uint64_t val) -+{ -+ asm volatile ( "strd %1,%H1,%0" : "=m" (*addr) : "r" (val) ); -+} - #endif - - build_add_sized(add_u8_sized, "b", BYTE, uint8_t, "ri") -@@ -69,6 +82,7 @@ void __bad_atomic_size(void); - case 1: __x = (typeof(*p))read_u8_atomic((uint8_t *)p); break; \ - case 2: __x = (typeof(*p))read_u16_atomic((uint16_t *)p); break; \ - case 4: __x = (typeof(*p))read_u32_atomic((uint32_t *)p); break; \ -+ case 8: __x = (typeof(*p))read_u64_atomic((uint64_t *)p); break; \ - default: __x = 0; __bad_atomic_size(); break; \ - } \ - __x; \ -@@ -80,6 +94,7 @@ void __bad_atomic_size(void); - case 1: write_u8_atomic((uint8_t *)p, (uint8_t)__x); break; \ - case 2: write_u16_atomic((uint16_t *)p, (uint16_t)__x); break; \ - case 4: write_u32_atomic((uint32_t *)p, (uint32_t)__x); break; \ -+ case 8: write_u64_atomic((uint64_t *)p, (uint64_t)__x); break; \ - default: __bad_atomic_size(); break; \ - } \ - __x; \ --- -2.17.1 - diff --git a/xsa295-4.11-03.patch b/xsa295-4.11-03.patch deleted file mode 100644 index 3c4756e..0000000 --- a/xsa295-4.11-03.patch +++ /dev/null @@ -1,71 +0,0 @@ -From 71df6499812025175e37781e161d90c0e3b43f3c Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:16 +0100 -Subject: [PATCH v2 4.11 03/20] xen/arm: Add an isb() before reading CNTPCT_EL0 - to prevent re-ordering - -Per D8.2.1 in ARM DDI 0487C.a, "a read to CNTPCT_EL0 can occur -speculatively and out of order relative to other instructions executed -on the same PE." - -Add an instruction barrier to get accurate number of cycles when -requested in get_cycles(). For the other users of CNPCT_EL0, replace by -a call to get_cycles(). - -This is part of XSA-295. - -Signed-off-by: Julien Grall -Acked-by: Stefano Stabellini ---- - xen/arch/arm/time.c | 4 ++-- - xen/include/asm-arm/time.h | 5 ++++- - 2 files changed, 6 insertions(+), 3 deletions(-) - -diff --git a/xen/arch/arm/time.c b/xen/arch/arm/time.c -index c11fcfeadd..a15b4a0dc7 100644 ---- a/xen/arch/arm/time.c -+++ b/xen/arch/arm/time.c -@@ -149,7 +149,7 @@ void __init preinit_xen_time(void) - if ( res ) - panic("Timer: Cannot initialize platform timer"); - -- boot_count = READ_SYSREG64(CNTPCT_EL0); -+ boot_count = get_cycles(); - } - - static void __init init_dt_xen_time(void) -@@ -190,7 +190,7 @@ int __init init_xen_time(void) - /* Return number of nanoseconds since boot */ - s_time_t get_s_time(void) - { -- uint64_t ticks = READ_SYSREG64(CNTPCT_EL0) - boot_count; -+ uint64_t ticks = get_cycles() - boot_count; - return ticks_to_ns(ticks); - } - -diff --git a/xen/include/asm-arm/time.h b/xen/include/asm-arm/time.h -index 5b9a31de91..ca30406669 100644 ---- a/xen/include/asm-arm/time.h -+++ b/xen/include/asm-arm/time.h -@@ -1,6 +1,8 @@ - #ifndef __ARM_TIME_H__ - #define __ARM_TIME_H__ - -+#include -+ - #define DT_MATCH_TIMER \ - DT_MATCH_COMPATIBLE("arm,armv7-timer"), \ - DT_MATCH_COMPATIBLE("arm,armv8-timer") -@@ -9,7 +11,8 @@ typedef unsigned long cycles_t; - - static inline cycles_t get_cycles (void) - { -- return 0; -+ isb(); -+ return READ_SYSREG64(CNTPCT_EL0); - } - - /* List of timer's IRQ */ --- -2.17.1 - diff --git a/xsa295-4.11-04.patch b/xsa295-4.11-04.patch deleted file mode 100644 index 2ed0362..0000000 --- a/xsa295-4.11-04.patch +++ /dev/null @@ -1,171 +0,0 @@ -From 7e481e7147f81cc739611670e30a03c9d7e06c5b Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:17 +0100 -Subject: [PATCH v2 4.11 04/20] xen/grant_table: Rework the prototype of - _set_status* for lisibility - -It is not clear from the parameters name whether domid and gt_version -correspond to the local or remote domain. A follow-up patch will make -them more confusing. - -So rename domid (resp. gt_version) to ldomid (resp. rgt_version). At -the same time re-order the parameters to hopefully make it more -readable. - -This is part of XSA-295. - -Suggested-by: Jan Beulich -Signed-off-by: Julien Grall -Reviewed-by: Jan Beulich -Acked-by: Stefano Stabellini ---- - xen/common/grant_table.c | 57 ++++++++++++++++++++-------------------- - 1 file changed, 28 insertions(+), 29 deletions(-) - -diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c -index 656fad1b42..c3a806fe47 100644 ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -652,11 +652,11 @@ static unsigned int nr_grant_entries(struct grant_table *gt) - return 0; - } - --static int _set_status_v1(domid_t domid, -+static int _set_status_v1(const grant_entry_header_t *shah, -+ struct active_grant_entry *act, - int readonly, - int mapflag, -- grant_entry_header_t *shah, -- struct active_grant_entry *act) -+ domid_t ldomid) - { - int rc = GNTST_okay; - union grant_combo scombo, prev_scombo, new_scombo; -@@ -691,11 +691,11 @@ static int _set_status_v1(domid_t domid, - if ( !act->pin && - (((scombo.shorts.flags & mask) != - GTF_permit_access) || -- (scombo.shorts.domid != domid)) ) -+ (scombo.shorts.domid != ldomid)) ) - PIN_FAIL(done, GNTST_general_error, - "Bad flags (%x) or dom (%d); expected d%d\n", - scombo.shorts.flags, scombo.shorts.domid, -- domid); -+ ldomid); - - new_scombo = scombo; - new_scombo.shorts.flags |= GTF_reading; -@@ -724,12 +724,12 @@ done: - return rc; - } - --static int _set_status_v2(domid_t domid, -+static int _set_status_v2(const grant_entry_header_t *shah, -+ grant_status_t *status, -+ struct active_grant_entry *act, - int readonly, - int mapflag, -- grant_entry_header_t *shah, -- struct active_grant_entry *act, -- grant_status_t *status) -+ domid_t ldomid) - { - int rc = GNTST_okay; - union grant_combo scombo; -@@ -755,10 +755,10 @@ static int _set_status_v2(domid_t domid, - if ( !act->pin && - ( (((flags & mask) != GTF_permit_access) && - ((flags & mask) != GTF_transitive)) || -- (id != domid)) ) -+ (id != ldomid)) ) - PIN_FAIL(done, GNTST_general_error, - "Bad flags (%x) or dom (%d); expected d%d, flags %x\n", -- flags, id, domid, mask); -+ flags, id, ldomid, mask); - - if ( readonly ) - { -@@ -785,14 +785,14 @@ static int _set_status_v2(domid_t domid, - { - if ( (((flags & mask) != GTF_permit_access) && - ((flags & mask) != GTF_transitive)) || -- (id != domid) || -+ (id != ldomid) || - (!readonly && (flags & GTF_readonly)) ) - { - gnttab_clear_flag(_GTF_writing, status); - gnttab_clear_flag(_GTF_reading, status); - PIN_FAIL(done, GNTST_general_error, - "Unstable flags (%x) or dom (%d); expected d%d (r/w: %d)\n", -- flags, id, domid, !readonly); -+ flags, id, ldomid, !readonly); - } - } - else -@@ -810,19 +810,19 @@ done: - } - - --static int _set_status(unsigned gt_version, -- domid_t domid, -+static int _set_status(const grant_entry_header_t *shah, -+ grant_status_t *status, -+ unsigned rgt_version, -+ struct active_grant_entry *act, - int readonly, - int mapflag, -- grant_entry_header_t *shah, -- struct active_grant_entry *act, -- grant_status_t *status) -+ domid_t ldomid) - { - -- if ( gt_version == 1 ) -- return _set_status_v1(domid, readonly, mapflag, shah, act); -+ if ( rgt_version == 1 ) -+ return _set_status_v1(shah, act, readonly, mapflag, ldomid); - else -- return _set_status_v2(domid, readonly, mapflag, shah, act, status); -+ return _set_status_v2(shah, status, act, readonly, mapflag, ldomid); - } - - static struct active_grant_entry *grant_map_exists(const struct domain *ld, -@@ -994,9 +994,9 @@ map_grant_ref( - (!(op->flags & GNTMAP_readonly) && - !(act->pin & (GNTPIN_hstw_mask|GNTPIN_devw_mask))) ) - { -- if ( (rc = _set_status(rgt->gt_version, ld->domain_id, -- op->flags & GNTMAP_readonly, -- 1, shah, act, status) ) != GNTST_okay ) -+ if ( (rc = _set_status(shah, status, rgt->gt_version, act, -+ op->flags & GNTMAP_readonly, 1, -+ ld->domain_id) != GNTST_okay) ) - goto act_release_out; - - if ( !act->pin ) -@@ -2452,8 +2452,8 @@ acquire_grant_for_copy( - { - if ( (!old_pin || (!readonly && - !(old_pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)))) && -- (rc = _set_status_v2(ldom, readonly, 0, shah, act, -- status)) != GNTST_okay ) -+ (rc = _set_status_v2(shah, status, act, readonly, 0, -+ ldom)) != GNTST_okay ) - goto unlock_out; - - if ( !allow_transitive ) -@@ -2553,9 +2553,8 @@ acquire_grant_for_copy( - else if ( !old_pin || - (!readonly && !(old_pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask))) ) - { -- if ( (rc = _set_status(rgt->gt_version, ldom, -- readonly, 0, shah, act, -- status) ) != GNTST_okay ) -+ if ( (rc = _set_status(shah, status, rgt->gt_version, act, -+ readonly, 0, ldom)) != GNTST_okay ) - goto unlock_out; - - td = rd; --- -2.17.1 - diff --git a/xsa295-4.11-05.patch b/xsa295-4.11-05.patch deleted file mode 100644 index dc10440..0000000 --- a/xsa295-4.11-05.patch +++ /dev/null @@ -1,202 +0,0 @@ -From 7e536ea72c87b76f13144d3713d5696d94b777f5 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:18 +0100 -Subject: [PATCH v2 4.11 05/20] xen/arm64: bitops: Rewrite bitop helpers in C - -This is part of XSA-295. - -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini -Signed-off-by: Stefano Stabellini ---- - xen/arch/arm/README.LinuxPrimitives | 1 - - xen/arch/arm/arm64/lib/bitops.S | 67 --------------------- - xen/arch/arm/arm64/lib/bitops.c | 90 +++++++++++++++++++++++++++++ - 3 files changed, 90 insertions(+), 68 deletions(-) - delete mode 100644 xen/arch/arm/arm64/lib/bitops.S - create mode 100644 xen/arch/arm/arm64/lib/bitops.c - -diff --git a/xen/arch/arm/README.LinuxPrimitives b/xen/arch/arm/README.LinuxPrimitives -index 028e8721f9..891667a5da 100644 ---- a/xen/arch/arm/README.LinuxPrimitives -+++ b/xen/arch/arm/README.LinuxPrimitives -@@ -8,7 +8,6 @@ arm64: - - bitops: last sync @ v3.16-rc6 (last commit: 8715466b6027) - --linux/arch/arm64/lib/bitops.S xen/arch/arm/arm64/lib/bitops.S - linux/arch/arm64/include/asm/bitops.h xen/include/asm-arm/arm64/bitops.h - - --------------------------------------------------------------------- -diff --git a/xen/arch/arm/arm64/lib/bitops.S b/xen/arch/arm/arm64/lib/bitops.S -deleted file mode 100644 -index 6471dd1875..0000000000 ---- a/xen/arch/arm/arm64/lib/bitops.S -+++ /dev/null -@@ -1,67 +0,0 @@ --/* -- * Based on linux/arch/arm64/lib/bitops.h which in turn is -- * Based on arch/arm/lib/bitops.h -- * -- * Copyright (C) 2013 ARM Ltd. -- * -- * 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 distributed in the hope that it will be useful, -- * but WITHOUT ANY WARRANTY; without even the implied warranty of -- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- * GNU General Public License for more details. -- * -- * You should have received a copy of the GNU General Public License -- * along with this program. If not, see . -- */ -- --/* -- * x0: bits 4:0 bit offset -- * bits 31:5 word offset -- * x1: address -- */ -- .macro bitop, name, instr --ENTRY( \name ) -- and w3, w0, #31 // Get bit offset -- eor w0, w0, w3 // Clear low bits -- mov x2, #1 -- add x1, x1, x0, lsr #3 // Get word offset -- lsl x3, x2, x3 // Create mask --1: ldxr w2, [x1] -- \instr w2, w2, w3 -- stxr w0, w2, [x1] -- cbnz w0, 1b -- ret --ENDPROC(\name ) -- .endm -- -- .macro testop, name, instr --ENTRY( \name ) -- and w3, w0, #31 // Get bit offset -- eor w0, w0, w3 // Clear low bits -- mov x2, #1 -- add x1, x1, x0, lsr #3 // Get word offset -- lsl x4, x2, x3 // Create mask --1: ldxr w2, [x1] -- lsr w0, w2, w3 // Save old value of bit -- \instr w2, w2, w4 // toggle bit -- stlxr w5, w2, [x1] -- cbnz w5, 1b -- dmb ish -- and w0, w0, #1 --3: ret --ENDPROC(\name ) -- .endm -- --/* -- * Atomic bit operations. -- */ -- bitop change_bit, eor -- bitop clear_bit, bic -- bitop set_bit, orr -- -- testop test_and_change_bit, eor -- testop test_and_clear_bit, bic -- testop test_and_set_bit, orr -diff --git a/xen/arch/arm/arm64/lib/bitops.c b/xen/arch/arm/arm64/lib/bitops.c -new file mode 100644 -index 0000000000..b1c681c642 ---- /dev/null -+++ b/xen/arch/arm/arm64/lib/bitops.c -@@ -0,0 +1,90 @@ -+/* -+ * Copyright (C) 2018 ARM Ltd. -+ * -+ * 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 distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+ -+#include -+#include -+ -+/* -+ * The atomic bit operations pass the number of bit in a signed number -+ * (not sure why). This has the drawback to increase the complexity of -+ * the resulting assembly. -+ * -+ * To generate simpler code, the number of bit (nr) will be cast to -+ * unsigned int. -+ * -+ * XXX: Rework the interface to use unsigned int. -+ */ -+ -+#define bitop(name, instr) \ -+void name(int nr, volatile void *p) \ -+{ \ -+ volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ -+ const uint32_t mask = BIT_MASK((unsigned int)nr); \ -+ unsigned long res, tmp; \ -+ \ -+ do \ -+ { \ -+ asm volatile ("// " __stringify(name) "\n" \ -+ " ldxr %w2, %1\n" \ -+ " " __stringify(instr) " %w2, %w2, %w3\n" \ -+ " stxr %w0, %w2, %1\n" \ -+ : "=&r" (res), "+Q" (*ptr), "=&r" (tmp) \ -+ : "r" (mask)); \ -+ } while ( res ); \ -+} \ -+ -+#define testop(name, instr) \ -+int name(int nr, volatile void *p) \ -+{ \ -+ volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ -+ unsigned int bit = (unsigned int)nr % BITS_PER_WORD; \ -+ const uint32_t mask = BIT_MASK(bit); \ -+ unsigned long res, tmp; \ -+ unsigned long oldbit; \ -+ \ -+ do \ -+ { \ -+ asm volatile ("// " __stringify(name) "\n" \ -+ " ldxr %w3, %2\n" \ -+ " lsr %w1, %w3, %w5 // Save old value of bit\n" \ -+ " " __stringify(instr) " %w3, %w3, %w4 // Toggle bit\n" \ -+ " stlxr %w0, %w3, %2\n" \ -+ : "=&r" (res), "=&r" (oldbit), "+Q" (*ptr), "=&r" (tmp) \ -+ : "r" (mask), "r" (bit) \ -+ : "memory"); \ -+ } while ( res ); \ -+ \ -+ dmb(ish); \ -+ \ -+ return oldbit & 1; \ -+} -+ -+bitop(change_bit, eor) -+bitop(clear_bit, bic) -+bitop(set_bit, orr) -+ -+testop(test_and_change_bit, eor) -+testop(test_and_clear_bit, bic) -+testop(test_and_set_bit, orr) -+ -+/* -+ * Local variables: -+ * mode: C -+ * c-file-style: "BSD" -+ * c-basic-offset: 4 -+ * indent-tabs-mode: nil -+ * End: -+ */ --- -2.17.1 - diff --git a/xsa295-4.11-06.patch b/xsa295-4.11-06.patch deleted file mode 100644 index c723d79..0000000 --- a/xsa295-4.11-06.patch +++ /dev/null @@ -1,442 +0,0 @@ -From d7519197af2489a856fd928541e32b29a74f85ba Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:19 +0100 -Subject: [PATCH v2 4.11 06/20] xen/arm32: bitops: Rewrite bitop helpers in C - -This is part of XSA-295. - -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini -Signed-off-by: Stefano Stabellini ---- - xen/arch/arm/README.LinuxPrimitives | 14 +--- - xen/arch/arm/arm32/lib/Makefile | 5 +- - xen/arch/arm/arm32/lib/bitops.c | 98 +++++++++++++++++++++++ - xen/arch/arm/arm32/lib/bitops.h | 104 ------------------------- - xen/arch/arm/arm32/lib/changebit.S | 14 ---- - xen/arch/arm/arm32/lib/clearbit.S | 14 ---- - xen/arch/arm/arm32/lib/setbit.S | 15 ---- - xen/arch/arm/arm32/lib/testchangebit.S | 15 ---- - xen/arch/arm/arm32/lib/testclearbit.S | 15 ---- - xen/arch/arm/arm32/lib/testsetbit.S | 15 ---- - xen/include/asm-arm/arm32/bitops.h | 19 ++--- - 11 files changed, 108 insertions(+), 220 deletions(-) - create mode 100644 xen/arch/arm/arm32/lib/bitops.c - delete mode 100644 xen/arch/arm/arm32/lib/bitops.h - delete mode 100644 xen/arch/arm/arm32/lib/changebit.S - delete mode 100644 xen/arch/arm/arm32/lib/clearbit.S - delete mode 100644 xen/arch/arm/arm32/lib/setbit.S - delete mode 100644 xen/arch/arm/arm32/lib/testchangebit.S - delete mode 100644 xen/arch/arm/arm32/lib/testclearbit.S - delete mode 100644 xen/arch/arm/arm32/lib/testsetbit.S - -diff --git a/xen/arch/arm/README.LinuxPrimitives b/xen/arch/arm/README.LinuxPrimitives -index 891667a5da..664a9f89ed 100644 ---- a/xen/arch/arm/README.LinuxPrimitives -+++ b/xen/arch/arm/README.LinuxPrimitives -@@ -68,19 +68,9 @@ arm32 - - bitops: last sync @ v3.16-rc6 (last commit: c32ffce0f66e) - --linux/arch/arm/lib/bitops.h xen/arch/arm/arm32/lib/bitops.h --linux/arch/arm/lib/changebit.S xen/arch/arm/arm32/lib/changebit.S --linux/arch/arm/lib/clearbit.S xen/arch/arm/arm32/lib/clearbit.S - linux/arch/arm/lib/findbit.S xen/arch/arm/arm32/lib/findbit.S --linux/arch/arm/lib/setbit.S xen/arch/arm/arm32/lib/setbit.S --linux/arch/arm/lib/testchangebit.S xen/arch/arm/arm32/lib/testchangebit.S --linux/arch/arm/lib/testclearbit.S xen/arch/arm/arm32/lib/testclearbit.S --linux/arch/arm/lib/testsetbit.S xen/arch/arm/arm32/lib/testsetbit.S -- --for i in bitops.h changebit.S clearbit.S findbit.S setbit.S testchangebit.S \ -- testclearbit.S testsetbit.S; do -- diff -u ../linux/arch/arm/lib/$i xen/arch/arm/arm32/lib/$i; --done -+ -+diff -u ../linux/arch/arm/lib/findbit.S xen/arch/arm/arm32/lib/findbit.S - - --------------------------------------------------------------------- - -diff --git a/xen/arch/arm/arm32/lib/Makefile b/xen/arch/arm/arm32/lib/Makefile -index e9fbc595b9..b1457c89dc 100644 ---- a/xen/arch/arm/arm32/lib/Makefile -+++ b/xen/arch/arm/arm32/lib/Makefile -@@ -1,6 +1,5 @@ - obj-y += memcpy.o memmove.o memset.o memchr.o memzero.o --obj-y += findbit.o setbit.o --obj-y += setbit.o clearbit.o changebit.o --obj-y += testsetbit.o testclearbit.o testchangebit.o -+obj-y += findbit.o -+obj-y += bitops.o - obj-y += strchr.o strrchr.o - obj-y += lib1funcs.o lshrdi3.o div64.o -diff --git a/xen/arch/arm/arm32/lib/bitops.c b/xen/arch/arm/arm32/lib/bitops.c -new file mode 100644 -index 0000000000..c69bb53037 ---- /dev/null -+++ b/xen/arch/arm/arm32/lib/bitops.c -@@ -0,0 +1,98 @@ -+/* -+ * Copyright (C) 2018 ARM Ltd. -+ * -+ * 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 distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+ -+#include -+#include -+#include -+ -+/* -+ * The atomic bit operations pass the number of bit in a signed number -+ * (not sure why). This has the drawback to increase the complexity of -+ * the resulting assembly. -+ * -+ * To generate simpler code, the number of bit (nr) will be cast to -+ * unsigned int. -+ * -+ * XXX: Rework the interface to use unsigned int. -+ */ -+ -+#define bitop(name, instr) \ -+void name(int nr, volatile void *p) \ -+{ \ -+ volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ -+ const uint32_t mask = BIT_MASK((unsigned int)nr); \ -+ unsigned long res, tmp; \ -+ \ -+ ASSERT(((vaddr_t)p & 0x3) == 0); \ -+ prefetchw((const void *)ptr); \ -+ \ -+ do \ -+ { \ -+ asm volatile ("// " __stringify(name) "\n" \ -+ " ldrex %2, %1\n" \ -+ " " __stringify(instr) " %2, %2, %3\n" \ -+ " strex %0, %2, %1\n" \ -+ : "=&r" (res), "+Qo" (*ptr), "=&r" (tmp) \ -+ : "r" (mask)); \ -+ } while ( res ); \ -+} -+ -+#define testop(name, instr) \ -+int name(int nr, volatile void *p) \ -+{ \ -+ volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ -+ unsigned int bit = (unsigned int)nr % BITS_PER_WORD; \ -+ const uint32_t mask = BIT_MASK(bit); \ -+ unsigned long res, tmp; \ -+ int oldbit; \ -+ \ -+ ASSERT(((vaddr_t)p & 0x3) == 0); \ -+ smp_mb(); \ -+ \ -+ prefetchw((const void *)ptr); \ -+ \ -+ do \ -+ { \ -+ asm volatile ("// " __stringify(name) "\n" \ -+ " ldrex %3, %2\n" \ -+ " lsr %1, %3, %5 // Save old value of bit\n" \ -+ " " __stringify(instr) " %3, %3, %4 // Toggle bit\n" \ -+ " strex %0, %3, %2\n" \ -+ : "=&r" (res), "=&r" (oldbit), "+Qo" (*ptr), "=&r" (tmp) \ -+ : "r" (mask), "r" (bit)); \ -+ } while ( res ); \ -+ \ -+ smp_mb(); \ -+ \ -+ return oldbit & 1; \ -+} \ -+ -+bitop(change_bit, eor) -+bitop(clear_bit, bic) -+bitop(set_bit, orr) -+ -+testop(test_and_change_bit, eor) -+testop(test_and_clear_bit, bic) -+testop(test_and_set_bit, orr) -+ -+/* -+ * Local variables: -+ * mode: C -+ * c-file-style: "BSD" -+ * c-basic-offset: 4 -+ * indent-tabs-mode: nil -+ * End: -+ */ -diff --git a/xen/arch/arm/arm32/lib/bitops.h b/xen/arch/arm/arm32/lib/bitops.h -deleted file mode 100644 -index d5e13476f4..0000000000 ---- a/xen/arch/arm/arm32/lib/bitops.h -+++ /dev/null -@@ -1,104 +0,0 @@ -- --#if __LINUX_ARM_ARCH__ >= 6 -- .macro bitop, name, instr --ENTRY( \name ) --UNWIND( .fnstart ) -- ands ip, r1, #3 -- strneb r1, [ip] @ assert word-aligned -- mov r2, #1 -- and r3, r0, #31 @ Get bit offset -- mov r0, r0, lsr #5 -- add r1, r1, r0, lsl #2 @ Get word offset --#if __LINUX_ARM_ARCH__ >= 7 && defined(CONFIG_SMP) -- .arch_extension mp -- ALT_SMP(W(pldw) [r1]) -- ALT_UP(W(nop)) --#endif -- mov r3, r2, lsl r3 --1: ldrex r2, [r1] -- \instr r2, r2, r3 -- strex r0, r2, [r1] -- cmp r0, #0 -- bne 1b -- bx lr --UNWIND( .fnend ) --ENDPROC(\name ) -- .endm -- -- .macro testop, name, instr, store --ENTRY( \name ) --UNWIND( .fnstart ) -- ands ip, r1, #3 -- strneb r1, [ip] @ assert word-aligned -- mov r2, #1 -- and r3, r0, #31 @ Get bit offset -- mov r0, r0, lsr #5 -- add r1, r1, r0, lsl #2 @ Get word offset -- mov r3, r2, lsl r3 @ create mask -- smp_dmb --#if __LINUX_ARM_ARCH__ >= 7 && defined(CONFIG_SMP) -- .arch_extension mp -- ALT_SMP(W(pldw) [r1]) -- ALT_UP(W(nop)) --#endif --1: ldrex r2, [r1] -- ands r0, r2, r3 @ save old value of bit -- \instr r2, r2, r3 @ toggle bit -- strex ip, r2, [r1] -- cmp ip, #0 -- bne 1b -- smp_dmb -- cmp r0, #0 -- movne r0, #1 --2: bx lr --UNWIND( .fnend ) --ENDPROC(\name ) -- .endm --#else -- .macro bitop, name, instr --ENTRY( \name ) --UNWIND( .fnstart ) -- ands ip, r1, #3 -- strneb r1, [ip] @ assert word-aligned -- and r2, r0, #31 -- mov r0, r0, lsr #5 -- mov r3, #1 -- mov r3, r3, lsl r2 -- save_and_disable_irqs ip -- ldr r2, [r1, r0, lsl #2] -- \instr r2, r2, r3 -- str r2, [r1, r0, lsl #2] -- restore_irqs ip -- mov pc, lr --UNWIND( .fnend ) --ENDPROC(\name ) -- .endm -- --/** -- * testop - implement a test_and_xxx_bit operation. -- * @instr: operational instruction -- * @store: store instruction -- * -- * Note: we can trivially conditionalise the store instruction -- * to avoid dirtying the data cache. -- */ -- .macro testop, name, instr, store --ENTRY( \name ) --UNWIND( .fnstart ) -- ands ip, r1, #3 -- strneb r1, [ip] @ assert word-aligned -- and r3, r0, #31 -- mov r0, r0, lsr #5 -- save_and_disable_irqs ip -- ldr r2, [r1, r0, lsl #2]! -- mov r0, #1 -- tst r2, r0, lsl r3 -- \instr r2, r2, r0, lsl r3 -- \store r2, [r1] -- moveq r0, #0 -- restore_irqs ip -- mov pc, lr --UNWIND( .fnend ) --ENDPROC(\name ) -- .endm --#endif -diff --git a/xen/arch/arm/arm32/lib/changebit.S b/xen/arch/arm/arm32/lib/changebit.S -deleted file mode 100644 -index 6e4ae7594a..0000000000 ---- a/xen/arch/arm/arm32/lib/changebit.S -+++ /dev/null -@@ -1,14 +0,0 @@ --/* -- * linux/arch/arm/lib/changebit.S -- * -- * Copyright (C) 1995-1996 Russell King -- * -- * 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. -- */ --#include "assembler.h" --#include "bitops.h" -- .text -- --bitop _change_bit, eor -diff --git a/xen/arch/arm/arm32/lib/clearbit.S b/xen/arch/arm/arm32/lib/clearbit.S -deleted file mode 100644 -index fda553f246..0000000000 ---- a/xen/arch/arm/arm32/lib/clearbit.S -+++ /dev/null -@@ -1,14 +0,0 @@ --/* -- * linux/arch/arm/lib/clearbit.S -- * -- * Copyright (C) 1995-1996 Russell King -- * -- * 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. -- */ --#include "assembler.h" --#include "bitops.h" -- .text -- --bitop _clear_bit, bic -diff --git a/xen/arch/arm/arm32/lib/setbit.S b/xen/arch/arm/arm32/lib/setbit.S -deleted file mode 100644 -index d52f0ab65c..0000000000 ---- a/xen/arch/arm/arm32/lib/setbit.S -+++ /dev/null -@@ -1,15 +0,0 @@ --/* -- * linux/arch/arm/lib/setbit.S -- * -- * Copyright (C) 1995-1996 Russell King -- * -- * 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. -- */ -- --#include "assembler.h" --#include "bitops.h" -- .text -- --bitop _set_bit, orr -diff --git a/xen/arch/arm/arm32/lib/testchangebit.S b/xen/arch/arm/arm32/lib/testchangebit.S -deleted file mode 100644 -index d83b04c057..0000000000 ---- a/xen/arch/arm/arm32/lib/testchangebit.S -+++ /dev/null -@@ -1,15 +0,0 @@ --/* -- * linux/arch/arm/lib/testchangebit.S -- * -- * Copyright (C) 1995-1996 Russell King -- * -- * 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. -- */ -- --#include "assembler.h" --#include "bitops.h" -- .text -- --testop _test_and_change_bit, eor, str -diff --git a/xen/arch/arm/arm32/lib/testclearbit.S b/xen/arch/arm/arm32/lib/testclearbit.S -deleted file mode 100644 -index 6f5b7b92d1..0000000000 ---- a/xen/arch/arm/arm32/lib/testclearbit.S -+++ /dev/null -@@ -1,15 +0,0 @@ --/* -- * linux/arch/arm/lib/testclearbit.S -- * -- * Copyright (C) 1995-1996 Russell King -- * -- * 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. -- */ -- --#include "assembler.h" --#include "bitops.h" -- .text -- --testop _test_and_clear_bit, bicne, strne -diff --git a/xen/arch/arm/arm32/lib/testsetbit.S b/xen/arch/arm/arm32/lib/testsetbit.S -deleted file mode 100644 -index 30425a842a..0000000000 ---- a/xen/arch/arm/arm32/lib/testsetbit.S -+++ /dev/null -@@ -1,15 +0,0 @@ --/* -- * linux/arch/arm/lib/testsetbit.S -- * -- * Copyright (C) 1995-1996 Russell King -- * -- * 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. -- */ -- --#include "assembler.h" --#include "bitops.h" -- .text -- --testop _test_and_set_bit, orreq, streq -diff --git a/xen/include/asm-arm/arm32/bitops.h b/xen/include/asm-arm/arm32/bitops.h -index 8be3564540..67c4c3f55c 100644 ---- a/xen/include/asm-arm/arm32/bitops.h -+++ b/xen/include/asm-arm/arm32/bitops.h -@@ -1,19 +1,12 @@ - #ifndef _ARM_ARM32_BITOPS_H - #define _ARM_ARM32_BITOPS_H - --extern void _set_bit(int nr, volatile void * p); --extern void _clear_bit(int nr, volatile void * p); --extern void _change_bit(int nr, volatile void * p); --extern int _test_and_set_bit(int nr, volatile void * p); --extern int _test_and_clear_bit(int nr, volatile void * p); --extern int _test_and_change_bit(int nr, volatile void * p); -- --#define set_bit(n,p) _set_bit(n,p) --#define clear_bit(n,p) _clear_bit(n,p) --#define change_bit(n,p) _change_bit(n,p) --#define test_and_set_bit(n,p) _test_and_set_bit(n,p) --#define test_and_clear_bit(n,p) _test_and_clear_bit(n,p) --#define test_and_change_bit(n,p) _test_and_change_bit(n,p) -+extern void set_bit(int nr, volatile void * p); -+extern void clear_bit(int nr, volatile void * p); -+extern void change_bit(int nr, volatile void * p); -+extern int test_and_set_bit(int nr, volatile void * p); -+extern int test_and_clear_bit(int nr, volatile void * p); -+extern int test_and_change_bit(int nr, volatile void * p); - - #define flsl fls - --- -2.17.1 - diff --git a/xsa295-4.11-07.patch b/xsa295-4.11-07.patch deleted file mode 100644 index 3a80b80..0000000 --- a/xsa295-4.11-07.patch +++ /dev/null @@ -1,83 +0,0 @@ -From f2d572a74dbf78c3e0c4ae2ef87dae0110d2f3f3 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:20 +0100 -Subject: [PATCH v2 4.11 07/20] xen/arm: bitops: Consolidate prototypes in one - place - -The prototype are the same between arm32 and arm64. Consolidate them in -asm-arm/bitops.h. - -This change will help the introductions of new helpers in a follow-up -patch. - -This is part of XSA-295. - -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini ---- - xen/include/asm-arm/arm32/bitops.h | 7 ------- - xen/include/asm-arm/arm64/bitops.h | 10 ---------- - xen/include/asm-arm/bitops.h | 8 ++++++++ - 3 files changed, 8 insertions(+), 17 deletions(-) - -diff --git a/xen/include/asm-arm/arm32/bitops.h b/xen/include/asm-arm/arm32/bitops.h -index 67c4c3f55c..57938a5874 100644 ---- a/xen/include/asm-arm/arm32/bitops.h -+++ b/xen/include/asm-arm/arm32/bitops.h -@@ -1,13 +1,6 @@ - #ifndef _ARM_ARM32_BITOPS_H - #define _ARM_ARM32_BITOPS_H - --extern void set_bit(int nr, volatile void * p); --extern void clear_bit(int nr, volatile void * p); --extern void change_bit(int nr, volatile void * p); --extern int test_and_set_bit(int nr, volatile void * p); --extern int test_and_clear_bit(int nr, volatile void * p); --extern int test_and_change_bit(int nr, volatile void * p); -- - #define flsl fls - - /* -diff --git a/xen/include/asm-arm/arm64/bitops.h b/xen/include/asm-arm/arm64/bitops.h -index 6bf1922680..6cc224ad13 100644 ---- a/xen/include/asm-arm/arm64/bitops.h -+++ b/xen/include/asm-arm/arm64/bitops.h -@@ -1,16 +1,6 @@ - #ifndef _ARM_ARM64_BITOPS_H - #define _ARM_ARM64_BITOPS_H - --/* -- * Little endian assembly atomic bitops. -- */ --extern void set_bit(int nr, volatile void *p); --extern void clear_bit(int nr, volatile void *p); --extern void change_bit(int nr, volatile void *p); --extern int test_and_set_bit(int nr, volatile void *p); --extern int test_and_clear_bit(int nr, volatile void *p); --extern int test_and_change_bit(int nr, volatile void *p); -- - /* Based on linux/include/asm-generic/bitops/builtin-__ffs.h */ - /** - * __ffs - find first bit in word. -diff --git a/xen/include/asm-arm/bitops.h b/xen/include/asm-arm/bitops.h -index 1cbfb9edb2..c69b08adf6 100644 ---- a/xen/include/asm-arm/bitops.h -+++ b/xen/include/asm-arm/bitops.h -@@ -38,6 +38,14 @@ - # error "unknown ARM variant" - #endif - -+/* Atomics bitops */ -+void set_bit(int nr, volatile void *p); -+void clear_bit(int nr, volatile void *p); -+void change_bit(int nr, volatile void *p); -+int test_and_set_bit(int nr, volatile void *p); -+int test_and_clear_bit(int nr, volatile void *p); -+int test_and_change_bit(int nr, volatile void *p); -+ - /** - * __test_and_set_bit - Set a bit and return its old value - * @nr: Bit to set --- -2.17.1 - diff --git a/xsa295-4.11-08.patch b/xsa295-4.11-08.patch deleted file mode 100644 index f756ff3..0000000 --- a/xsa295-4.11-08.patch +++ /dev/null @@ -1,145 +0,0 @@ -From 550682d9a82f7e32e64b38b78ee58f17ab0f9559 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Wed, 22 May 2019 13:37:53 -0700 -Subject: [PATCH v2 4.11 08/20] xen/arm64: cmpxchg: Simplify the cmpxchg - implementation - -The only difference between each case of the cmpxchg is the size of -used. Rather than duplicating the code, provide a macro to generate each -cases. - -This makes the code easier to read and modify. - -This is part of XSA-295. - -Signed-off-by; Julien Grall -Reviewed-by: Stefano Stabellini -Signed-off-by: Stefano Stabellini ---- - xen/include/asm-arm/arm64/cmpxchg.h | 98 +++++++++++------------------ - 1 file changed, 36 insertions(+), 62 deletions(-) - -diff --git a/xen/include/asm-arm/arm64/cmpxchg.h b/xen/include/asm-arm/arm64/cmpxchg.h -index ae42b2f5ff..393fbca6a5 100644 ---- a/xen/include/asm-arm/arm64/cmpxchg.h -+++ b/xen/include/asm-arm/arm64/cmpxchg.h -@@ -61,80 +61,54 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size - __ret; \ - }) - --extern void __bad_cmpxchg(volatile void *ptr, int size); -+extern unsigned long __bad_cmpxchg(volatile void *ptr, int size); -+ -+#define __CMPXCHG_CASE(w, sz, name) \ -+static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ -+ unsigned long old, \ -+ unsigned long new) \ -+{ \ -+ unsigned long res, oldval; \ -+ \ -+ do { \ -+ asm volatile("// __cmpxchg_case_" #name "\n" \ -+ " ldxr" #sz " %" #w "1, %2\n" \ -+ " mov %w0, #0\n" \ -+ " cmp %" #w "1, %" #w "3\n" \ -+ " b.ne 1f\n" \ -+ " stxr" #sz " %w0, %" #w "4, %2\n" \ -+ "1:\n" \ -+ : "=&r" (res), "=&r" (oldval), \ -+ "+Q" (*(unsigned long *)ptr) \ -+ : "Ir" (old), "r" (new) \ -+ : "cc"); \ -+ } while (res); \ -+ \ -+ return oldval; \ -+} -+ -+__CMPXCHG_CASE(w, b, 1) -+__CMPXCHG_CASE(w, h, 2) -+__CMPXCHG_CASE(w, , 4) -+__CMPXCHG_CASE( , , 8) - - static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, - unsigned long new, int size) - { -- unsigned long oldval = 0, res; -- - switch (size) { - case 1: -- do { -- asm volatile("// __cmpxchg1\n" -- " ldxrb %w1, %2\n" -- " mov %w0, #0\n" -- " cmp %w1, %w3\n" -- " b.ne 1f\n" -- " stxrb %w0, %w4, %2\n" -- "1:\n" -- : "=&r" (res), "=&r" (oldval), "+Q" (*(u8 *)ptr) -- : "Ir" (old), "r" (new) -- : "cc"); -- } while (res); -- break; -- -+ return __cmpxchg_case_1(ptr, old, new); - case 2: -- do { -- asm volatile("// __cmpxchg2\n" -- " ldxrh %w1, %2\n" -- " mov %w0, #0\n" -- " cmp %w1, %w3\n" -- " b.ne 1f\n" -- " stxrh %w0, %w4, %2\n" -- "1:\n" -- : "=&r" (res), "=&r" (oldval), "+Q" (*(u16 *)ptr) -- : "Ir" (old), "r" (new) -- : "cc"); -- } while (res); -- break; -- -+ return __cmpxchg_case_2(ptr, old, new); - case 4: -- do { -- asm volatile("// __cmpxchg4\n" -- " ldxr %w1, %2\n" -- " mov %w0, #0\n" -- " cmp %w1, %w3\n" -- " b.ne 1f\n" -- " stxr %w0, %w4, %2\n" -- "1:\n" -- : "=&r" (res), "=&r" (oldval), "+Q" (*(u32 *)ptr) -- : "Ir" (old), "r" (new) -- : "cc"); -- } while (res); -- break; -- -+ return __cmpxchg_case_4(ptr, old, new); - case 8: -- do { -- asm volatile("// __cmpxchg8\n" -- " ldxr %1, %2\n" -- " mov %w0, #0\n" -- " cmp %1, %3\n" -- " b.ne 1f\n" -- " stxr %w0, %4, %2\n" -- "1:\n" -- : "=&r" (res), "=&r" (oldval), "+Q" (*(u64 *)ptr) -- : "Ir" (old), "r" (new) -- : "cc"); -- } while (res); -- break; -- -+ return __cmpxchg_case_8(ptr, old, new); - default: -- __bad_cmpxchg(ptr, size); -- oldval = 0; -+ return __bad_cmpxchg(ptr, size); - } - -- return oldval; -+ ASSERT_UNREACHABLE(); - } - - static inline unsigned long __cmpxchg_mb(volatile void *ptr, unsigned long old, --- -2.17.1 - diff --git a/xsa295-4.11-09.patch b/xsa295-4.11-09.patch deleted file mode 100644 index 24a81f3..0000000 --- a/xsa295-4.11-09.patch +++ /dev/null @@ -1,135 +0,0 @@ -From b824f26e89e6c9637eefea42c16eb632b33ad6ba Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:22 +0100 -Subject: [PATCH v2 4.11 09/20] xen/arm32: cmpxchg: Simplify the cmpxchg - implementation - -The only difference between each case of the cmpxchg is the size of -used. Rather than duplicating the code, provide a macro to generate each -cases. - -This makes the code easier to read and modify. - -While doing the rework, the case for 64-bit cmpxchg is removed. This is -unused today (already commented) and it would not be possible to use -it directly. - -This is part of XSA-295. - -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini ---- - xen/include/asm-arm/arm32/cmpxchg.h | 84 +++++++++++------------------ - 1 file changed, 31 insertions(+), 53 deletions(-) - -diff --git a/xen/include/asm-arm/arm32/cmpxchg.h b/xen/include/asm-arm/arm32/cmpxchg.h -index 03e0bed3a6..471a9e3a3f 100644 ---- a/xen/include/asm-arm/arm32/cmpxchg.h -+++ b/xen/include/asm-arm/arm32/cmpxchg.h -@@ -52,72 +52,50 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size - * indicated by comparing RETURN with OLD. - */ - --extern void __bad_cmpxchg(volatile void *ptr, int size); -+extern unsigned long __bad_cmpxchg(volatile void *ptr, int size); -+ -+#define __CMPXCHG_CASE(sz, name) \ -+static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ -+ unsigned long old, \ -+ unsigned long new) \ -+{ \ -+ unsigned long oldval, res; \ -+ \ -+ do { \ -+ asm volatile("@ __cmpxchg_case_" #name "\n" \ -+ " ldrex" #sz " %1, [%2]\n" \ -+ " mov %0, #0\n" \ -+ " teq %1, %3\n" \ -+ " strex" #sz "eq %0, %4, [%2]\n" \ -+ : "=&r" (res), "=&r" (oldval) \ -+ : "r" (ptr), "Ir" (old), "r" (new) \ -+ : "memory", "cc"); \ -+ } while (res); \ -+ \ -+ return oldval; \ -+} -+ -+__CMPXCHG_CASE(b, 1) -+__CMPXCHG_CASE(h, 2) -+__CMPXCHG_CASE( , 4) - - static always_inline unsigned long __cmpxchg( - volatile void *ptr, unsigned long old, unsigned long new, int size) - { -- unsigned long oldval, res; -- - prefetchw((const void *)ptr); - - switch (size) { - case 1: -- do { -- asm volatile("@ __cmpxchg1\n" -- " ldrexb %1, [%2]\n" -- " mov %0, #0\n" -- " teq %1, %3\n" -- " strexbeq %0, %4, [%2]\n" -- : "=&r" (res), "=&r" (oldval) -- : "r" (ptr), "Ir" (old), "r" (new) -- : "memory", "cc"); -- } while (res); -- break; -+ return __cmpxchg_case_1(ptr, old, new); - case 2: -- do { -- asm volatile("@ __cmpxchg2\n" -- " ldrexh %1, [%2]\n" -- " mov %0, #0\n" -- " teq %1, %3\n" -- " strexheq %0, %4, [%2]\n" -- : "=&r" (res), "=&r" (oldval) -- : "r" (ptr), "Ir" (old), "r" (new) -- : "memory", "cc"); -- } while (res); -- break; -+ return __cmpxchg_case_2(ptr, old, new); - case 4: -- do { -- asm volatile("@ __cmpxchg4\n" -- " ldrex %1, [%2]\n" -- " mov %0, #0\n" -- " teq %1, %3\n" -- " strexeq %0, %4, [%2]\n" -- : "=&r" (res), "=&r" (oldval) -- : "r" (ptr), "Ir" (old), "r" (new) -- : "memory", "cc"); -- } while (res); -- break; --#if 0 -- case 8: -- do { -- asm volatile("@ __cmpxchg8\n" -- " ldrexd %1, [%2]\n" -- " mov %0, #0\n" -- " teq %1, %3\n" -- " strexdeq %0, %4, [%2]\n" -- : "=&r" (res), "=&r" (oldval) -- : "r" (ptr), "Ir" (old), "r" (new) -- : "memory", "cc"); -- } while (res); -- break; --#endif -+ return __cmpxchg_case_4(ptr, old, new); - default: -- __bad_cmpxchg(ptr, size); -- oldval = 0; -+ return __bad_cmpxchg(ptr, size); - } - -- return oldval; -+ ASSERT_UNREACHABLE(); - } - - static always_inline unsigned long __cmpxchg_mb(volatile void *ptr, --- -2.17.1 - diff --git a/xsa295-4.11-10.patch b/xsa295-4.11-10.patch deleted file mode 100644 index 07d571c..0000000 --- a/xsa295-4.11-10.patch +++ /dev/null @@ -1,255 +0,0 @@ -From 307aee21ae39696bffcaf1f1e5a1434ec6a1908b Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:23 +0100 -Subject: [PATCH v2 4.11 10/20] xen/arm: bitops: Implement a new set of helpers - that can timeout - -Exclusive load-store atomics should only be used between trusted -threads. As not all the guests are trusted, it may be possible to DoS -Xen when updating shared memory with guest atomically. - -To prevent the infinite loop, we introduce a new set of helpers that can -timeout. The timeout is based on the maximum number of iterations. - -They will be used in follow-up patch to make atomic operations -on shared memory safe. - -This is part of XSA-295. - -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini ---- - xen/arch/arm/arm32/lib/bitops.c | 52 ++++++++++++++++++++++++++++----- - xen/arch/arm/arm64/lib/bitops.c | 52 ++++++++++++++++++++++++++++----- - xen/include/asm-arm/bitops.h | 28 +++++++++++++++++- - 3 files changed, 117 insertions(+), 15 deletions(-) - -diff --git a/xen/arch/arm/arm32/lib/bitops.c b/xen/arch/arm/arm32/lib/bitops.c -index c69bb53037..08750314fc 100644 ---- a/xen/arch/arm/arm32/lib/bitops.c -+++ b/xen/arch/arm/arm32/lib/bitops.c -@@ -30,7 +30,8 @@ - */ - - #define bitop(name, instr) \ --void name(int nr, volatile void *p) \ -+static always_inline bool int_##name(int nr, volatile void *p, bool timeout,\ -+ unsigned int max_try) \ - { \ - volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ - const uint32_t mask = BIT_MASK((unsigned int)nr); \ -@@ -47,17 +48,33 @@ void name(int nr, volatile void *p) \ - " strex %0, %2, %1\n" \ - : "=&r" (res), "+Qo" (*ptr), "=&r" (tmp) \ - : "r" (mask)); \ -- } while ( res ); \ -+ \ -+ if ( !res ) \ -+ break; \ -+ } while ( !timeout || ((--max_try) > 0) ); \ -+ \ -+ return !res; \ -+} \ -+ \ -+void name(int nr, volatile void *p) \ -+{ \ -+ if ( !int_##name(nr, p, false, 0) ) \ -+ ASSERT_UNREACHABLE(); \ -+} \ -+ \ -+bool name##_timeout(int nr, volatile void *p, unsigned int max_try) \ -+{ \ -+ return int_##name(nr, p, true, max_try); \ - } - - #define testop(name, instr) \ --int name(int nr, volatile void *p) \ -+static always_inline bool int_##name(int nr, volatile void *p, int *oldbit, \ -+ bool timeout, unsigned int max_try) \ - { \ - volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ - unsigned int bit = (unsigned int)nr % BITS_PER_WORD; \ - const uint32_t mask = BIT_MASK(bit); \ - unsigned long res, tmp; \ -- int oldbit; \ - \ - ASSERT(((vaddr_t)p & 0x3) == 0); \ - smp_mb(); \ -@@ -71,14 +88,35 @@ int name(int nr, volatile void *p) \ - " lsr %1, %3, %5 // Save old value of bit\n" \ - " " __stringify(instr) " %3, %3, %4 // Toggle bit\n" \ - " strex %0, %3, %2\n" \ -- : "=&r" (res), "=&r" (oldbit), "+Qo" (*ptr), "=&r" (tmp) \ -+ : "=&r" (res), "=&r" (*oldbit), "+Qo" (*ptr), "=&r" (tmp) \ - : "r" (mask), "r" (bit)); \ -- } while ( res ); \ -+ \ -+ if ( !res ) \ -+ break; \ -+ } while ( !timeout || ((--max_try) > 0) ); \ - \ - smp_mb(); \ - \ -- return oldbit & 1; \ -+ *oldbit &= 1; \ -+ \ -+ return !res; \ -+} \ -+ \ -+int name(int nr, volatile void *p) \ -+{ \ -+ int oldbit; \ -+ \ -+ if ( !int_##name(nr, p, &oldbit, false, 0) ) \ -+ ASSERT_UNREACHABLE(); \ -+ \ -+ return oldbit; \ - } \ -+ \ -+bool name##_timeout(int nr, volatile void *p, \ -+ int *oldbit, unsigned int max_try) \ -+{ \ -+ return int_##name(nr, p, oldbit, true, max_try); \ -+} - - bitop(change_bit, eor) - bitop(clear_bit, bic) -diff --git a/xen/arch/arm/arm64/lib/bitops.c b/xen/arch/arm/arm64/lib/bitops.c -index b1c681c642..78bf4ed8c5 100644 ---- a/xen/arch/arm/arm64/lib/bitops.c -+++ b/xen/arch/arm/arm64/lib/bitops.c -@@ -29,7 +29,8 @@ - */ - - #define bitop(name, instr) \ --void name(int nr, volatile void *p) \ -+static always_inline bool int_##name(int nr, volatile void *p, bool timeout,\ -+ unsigned int max_try) \ - { \ - volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ - const uint32_t mask = BIT_MASK((unsigned int)nr); \ -@@ -43,17 +44,33 @@ void name(int nr, volatile void *p) \ - " stxr %w0, %w2, %1\n" \ - : "=&r" (res), "+Q" (*ptr), "=&r" (tmp) \ - : "r" (mask)); \ -- } while ( res ); \ -+ \ -+ if ( !res ) \ -+ break; \ -+ } while ( !timeout || ((--max_try) > 0) ); \ -+ \ -+ return !res; \ - } \ -+ \ -+void name(int nr, volatile void *p) \ -+{ \ -+ if ( !int_##name(nr, p, false, 0) ) \ -+ ASSERT_UNREACHABLE(); \ -+} \ -+ \ -+bool name##_timeout(int nr, volatile void *p, unsigned int max_try) \ -+{ \ -+ return int_##name(nr, p, true, max_try); \ -+} - - #define testop(name, instr) \ --int name(int nr, volatile void *p) \ -+static always_inline bool int_##name(int nr, volatile void *p, int *oldbit, \ -+ bool timeout, unsigned int max_try) \ - { \ - volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ - unsigned int bit = (unsigned int)nr % BITS_PER_WORD; \ - const uint32_t mask = BIT_MASK(bit); \ - unsigned long res, tmp; \ -- unsigned long oldbit; \ - \ - do \ - { \ -@@ -62,14 +79,35 @@ int name(int nr, volatile void *p) \ - " lsr %w1, %w3, %w5 // Save old value of bit\n" \ - " " __stringify(instr) " %w3, %w3, %w4 // Toggle bit\n" \ - " stlxr %w0, %w3, %2\n" \ -- : "=&r" (res), "=&r" (oldbit), "+Q" (*ptr), "=&r" (tmp) \ -+ : "=&r" (res), "=&r" (*oldbit), "+Q" (*ptr), "=&r" (tmp) \ - : "r" (mask), "r" (bit) \ - : "memory"); \ -- } while ( res ); \ -+ \ -+ if ( !res ) \ -+ break; \ -+ } while ( !timeout || ((--max_try) > 0) ); \ - \ - dmb(ish); \ - \ -- return oldbit & 1; \ -+ *oldbit &= 1; \ -+ \ -+ return !res; \ -+} \ -+ \ -+int name(int nr, volatile void *p) \ -+{ \ -+ int oldbit; \ -+ \ -+ if ( !int_##name(nr, p, &oldbit, false, 0) ) \ -+ ASSERT_UNREACHABLE(); \ -+ \ -+ return oldbit; \ -+} \ -+ \ -+bool name##_timeout(int nr, volatile void *p, \ -+ int *oldbit, unsigned int max_try) \ -+{ \ -+ return int_##name(nr, p, oldbit, true, max_try); \ - } - - bitop(change_bit, eor) -diff --git a/xen/include/asm-arm/bitops.h b/xen/include/asm-arm/bitops.h -index c69b08adf6..f6782b33be 100644 ---- a/xen/include/asm-arm/bitops.h -+++ b/xen/include/asm-arm/bitops.h -@@ -38,7 +38,14 @@ - # error "unknown ARM variant" - #endif - --/* Atomics bitops */ -+/* -+ * Atomic bitops -+ * -+ * The helpers below *should* only be used on memory shared between -+ * trusted threads or we know the memory cannot be accessed by another -+ * thread. -+ */ -+ - void set_bit(int nr, volatile void *p); - void clear_bit(int nr, volatile void *p); - void change_bit(int nr, volatile void *p); -@@ -46,6 +53,25 @@ int test_and_set_bit(int nr, volatile void *p); - int test_and_clear_bit(int nr, volatile void *p); - int test_and_change_bit(int nr, volatile void *p); - -+/* -+ * The helpers below may fail to update the memory if the action takes -+ * too long. -+ * -+ * @max_try: Maximum number of iterations -+ * -+ * The helpers will return true when the update has succeeded (i.e no -+ * timeout) and false if the update has failed. -+ */ -+bool set_bit_timeout(int nr, volatile void *p, unsigned int max_try); -+bool clear_bit_timeout(int nr, volatile void *p, unsigned int max_try); -+bool change_bit_timeout(int nr, volatile void *p, unsigned int max_try); -+bool test_and_set_bit_timeout(int nr, volatile void *p, -+ int *oldbit, unsigned int max_try); -+bool test_and_clear_bit_timeout(int nr, volatile void *p, -+ int *oldbit, unsigned int max_try); -+bool test_and_change_bit_timeout(int nr, volatile void *p, -+ int *oldbit, unsigned int max_try); -+ - /** - * __test_and_set_bit - Set a bit and return its old value - * @nr: Bit to set --- -2.17.1 - diff --git a/xsa295-4.11-11.patch b/xsa295-4.11-11.patch deleted file mode 100644 index 3357343..0000000 --- a/xsa295-4.11-11.patch +++ /dev/null @@ -1,258 +0,0 @@ -From 94736d1a81c259acb16eccb1b2f2f7286bf71180 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Wed, 22 May 2019 13:39:17 -0700 -Subject: [PATCH v2 4.11 11/20] xen/arm: cmpxchg: Provide a new helper that can - timeout - -Exclusive load-store atomics should only be used between trusted -threads. As not all the guests are trusted, it may be possible to DoS -Xen when updating shared memory with guest atomically. - -To prevent the infinite loop, we introduce a new helper that can timeout. -The timeout is based on the maximum number of iterations. - -It will be used in follow-up patch to make atomic operations on shared -memory safe. - -This is part of XSA-295. - -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini -Signed-off-by: Stefano Stabellini ---- - xen/include/asm-arm/arm32/cmpxchg.h | 63 +++++++++++++++++++++----- - xen/include/asm-arm/arm64/cmpxchg.h | 70 ++++++++++++++++++++++------- - 2 files changed, 106 insertions(+), 27 deletions(-) - -diff --git a/xen/include/asm-arm/arm32/cmpxchg.h b/xen/include/asm-arm/arm32/cmpxchg.h -index 471a9e3a3f..49ca2a0d7a 100644 ---- a/xen/include/asm-arm/arm32/cmpxchg.h -+++ b/xen/include/asm-arm/arm32/cmpxchg.h -@@ -55,11 +55,14 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size - extern unsigned long __bad_cmpxchg(volatile void *ptr, int size); - - #define __CMPXCHG_CASE(sz, name) \ --static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ -- unsigned long old, \ -- unsigned long new) \ -+static inline bool __cmpxchg_case_##name(volatile void *ptr, \ -+ unsigned long *old, \ -+ unsigned long new, \ -+ bool timeout, \ -+ unsigned int max_try) \ - { \ -- unsigned long oldval, res; \ -+ unsigned long oldval; \ -+ unsigned long res; \ - \ - do { \ - asm volatile("@ __cmpxchg_case_" #name "\n" \ -@@ -68,29 +71,35 @@ static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ - " teq %1, %3\n" \ - " strex" #sz "eq %0, %4, [%2]\n" \ - : "=&r" (res), "=&r" (oldval) \ -- : "r" (ptr), "Ir" (old), "r" (new) \ -+ : "r" (ptr), "Ir" (*old), "r" (new) \ - : "memory", "cc"); \ -- } while (res); \ - \ -- return oldval; \ -+ if (!res) \ -+ break; \ -+ } while (!timeout || ((--max_try) > 0)); \ -+ \ -+ *old = oldval; \ -+ \ -+ return !res; \ - } - - __CMPXCHG_CASE(b, 1) - __CMPXCHG_CASE(h, 2) - __CMPXCHG_CASE( , 4) - --static always_inline unsigned long __cmpxchg( -- volatile void *ptr, unsigned long old, unsigned long new, int size) -+static always_inline bool __int_cmpxchg(volatile void *ptr, unsigned long *old, -+ unsigned long new, int size, -+ bool timeout, unsigned int max_try) - { - prefetchw((const void *)ptr); - - switch (size) { - case 1: -- return __cmpxchg_case_1(ptr, old, new); -+ return __cmpxchg_case_1(ptr, old, new, timeout, max_try); - case 2: -- return __cmpxchg_case_2(ptr, old, new); -+ return __cmpxchg_case_2(ptr, old, new, timeout, max_try); - case 4: -- return __cmpxchg_case_4(ptr, old, new); -+ return __cmpxchg_case_4(ptr, old, new, timeout, max_try); - default: - return __bad_cmpxchg(ptr, size); - } -@@ -98,6 +107,17 @@ static always_inline unsigned long __cmpxchg( - ASSERT_UNREACHABLE(); - } - -+static always_inline unsigned long __cmpxchg(volatile void *ptr, -+ unsigned long old, -+ unsigned long new, -+ int size) -+{ -+ if (!__int_cmpxchg(ptr, &old, new, size, false, 0)) -+ ASSERT_UNREACHABLE(); -+ -+ return old; -+} -+ - static always_inline unsigned long __cmpxchg_mb(volatile void *ptr, - unsigned long old, - unsigned long new, int size) -@@ -111,6 +131,25 @@ static always_inline unsigned long __cmpxchg_mb(volatile void *ptr, - return ret; - } - -+/* -+ * The helper may fail to update the memory if the action takes too long. -+ * -+ * @old: On call the value pointed contains the expected old value. It will be -+ * updated to the actual old value. -+ * @max_try: Maximum number of iterations -+ * -+ * The helper will return true when the update has succeeded (i.e no -+ * timeout) and false if the update has failed. -+ */ -+static always_inline bool __cmpxchg_mb_timeout(volatile void *ptr, -+ unsigned long *old, -+ unsigned long new, -+ int size, -+ unsigned int max_try) -+{ -+ return __int_cmpxchg(ptr, old, new, size, true, max_try); -+} -+ - #define cmpxchg(ptr,o,n) \ - ((__typeof__(*(ptr)))__cmpxchg_mb((ptr), \ - (unsigned long)(o), \ -diff --git a/xen/include/asm-arm/arm64/cmpxchg.h b/xen/include/asm-arm/arm64/cmpxchg.h -index 393fbca6a5..5bc2e1f786 100644 ---- a/xen/include/asm-arm/arm64/cmpxchg.h -+++ b/xen/include/asm-arm/arm64/cmpxchg.h -@@ -64,11 +64,14 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size - extern unsigned long __bad_cmpxchg(volatile void *ptr, int size); - - #define __CMPXCHG_CASE(w, sz, name) \ --static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ -- unsigned long old, \ -- unsigned long new) \ -+static inline bool __cmpxchg_case_##name(volatile void *ptr, \ -+ unsigned long *old, \ -+ unsigned long new, \ -+ bool timeout, \ -+ unsigned int max_try) \ - { \ -- unsigned long res, oldval; \ -+ unsigned long oldval; \ -+ unsigned long res; \ - \ - do { \ - asm volatile("// __cmpxchg_case_" #name "\n" \ -@@ -80,11 +83,16 @@ static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ - "1:\n" \ - : "=&r" (res), "=&r" (oldval), \ - "+Q" (*(unsigned long *)ptr) \ -- : "Ir" (old), "r" (new) \ -+ : "Ir" (*old), "r" (new) \ - : "cc"); \ -- } while (res); \ - \ -- return oldval; \ -+ if (!res) \ -+ break; \ -+ } while (!timeout || ((--max_try) > 0)); \ -+ \ -+ *old = oldval; \ -+ \ -+ return !res; \ - } - - __CMPXCHG_CASE(w, b, 1) -@@ -92,18 +100,19 @@ __CMPXCHG_CASE(w, h, 2) - __CMPXCHG_CASE(w, , 4) - __CMPXCHG_CASE( , , 8) - --static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, -- unsigned long new, int size) -+static always_inline bool __int_cmpxchg(volatile void *ptr, unsigned long *old, -+ unsigned long new, int size, -+ bool timeout, unsigned int max_try) - { - switch (size) { - case 1: -- return __cmpxchg_case_1(ptr, old, new); -+ return __cmpxchg_case_1(ptr, old, new, timeout, max_try); - case 2: -- return __cmpxchg_case_2(ptr, old, new); -+ return __cmpxchg_case_2(ptr, old, new, timeout, max_try); - case 4: -- return __cmpxchg_case_4(ptr, old, new); -+ return __cmpxchg_case_4(ptr, old, new, timeout, max_try); - case 8: -- return __cmpxchg_case_8(ptr, old, new); -+ return __cmpxchg_case_8(ptr, old, new, timeout, max_try); - default: - return __bad_cmpxchg(ptr, size); - } -@@ -111,8 +120,20 @@ static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, - ASSERT_UNREACHABLE(); - } - --static inline unsigned long __cmpxchg_mb(volatile void *ptr, unsigned long old, -- unsigned long new, int size) -+static always_inline unsigned long __cmpxchg(volatile void *ptr, -+ unsigned long old, -+ unsigned long new, -+ int size) -+{ -+ if (!__int_cmpxchg(ptr, &old, new, size, false, 0)) -+ ASSERT_UNREACHABLE(); -+ -+ return old; -+} -+ -+static always_inline unsigned long __cmpxchg_mb(volatile void *ptr, -+ unsigned long old, -+ unsigned long new, int size) - { - unsigned long ret; - -@@ -123,6 +144,25 @@ static inline unsigned long __cmpxchg_mb(volatile void *ptr, unsigned long old, - return ret; - } - -+/* -+ * The helper may fail to update the memory if the action takes too long. -+ * -+ * @old: On call the value pointed contains the expected old value. It will be -+ * updated to the actual old value. -+ * @max_try: Maximum number of iterations -+ * -+ * The helper will return true when the update has succeeded (i.e no -+ * timeout) and false if the update has failed. -+ */ -+static always_inline bool __cmpxchg_mb_timeout(volatile void *ptr, -+ unsigned long *old, -+ unsigned long new, -+ int size, -+ unsigned int max_try) -+{ -+ return __int_cmpxchg(ptr, old, new, size, true, max_try); -+} -+ - #define cmpxchg(ptr, o, n) \ - ({ \ - __typeof__(*(ptr)) __ret; \ --- -2.17.1 - diff --git a/xsa295-4.11-12.patch b/xsa295-4.11-12.patch deleted file mode 100644 index 011dd2e..0000000 --- a/xsa295-4.11-12.patch +++ /dev/null @@ -1,30 +0,0 @@ -From fc01557fe8d60cc9a29d8f29568f940a7873d063 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Tue, 9 Oct 2018 17:33:18 +0800 -Subject: [PATCH v2 4.11 12/20] xen/xsm: remove unnecessary #define - -this #define is unnecessary since XSM_INLINE is redefined in -xsm/dummy.h, it's a risk of build breakage, so remove it. - -Signed-off-by: Xin Li -Reviewed-by: Jan Beulich -Acked-by: Daniel De Graaf ---- - xen/xsm/dummy.c | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/xen/xsm/dummy.c b/xen/xsm/dummy.c -index 6e751199ee..f371a841c1 100644 ---- a/xen/xsm/dummy.c -+++ b/xen/xsm/dummy.c -@@ -11,7 +11,6 @@ - */ - - #define XSM_NO_WRAPPERS --#define XSM_INLINE /* */ - #include - - struct xsm_operations dummy_xsm_ops; --- -2.17.1 - diff --git a/xsa295-4.11-13.patch b/xsa295-4.11-13.patch deleted file mode 100644 index afd1de7..0000000 --- a/xsa295-4.11-13.patch +++ /dev/null @@ -1,138 +0,0 @@ -From 046280b23fa40176006b9ab300a46452e166e6ca Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Tue, 9 Oct 2018 17:33:19 +0800 -Subject: [PATCH v2 4.11 13/20] xen/xsm: Introduce new boot parameter xsm - -Introduce new boot parameter xsm to choose which xsm module is enabled, -and set default to dummy. And add new option in Kconfig to choose the -default XSM implementation. - -Signed-off-by: Xin Li -Acked-by: Daniel De Graaf -Acked-by: Andrew Cooper ---- - docs/misc/xen-command-line.markdown | 13 +++++++++ - xen/common/Kconfig | 13 ++++++++- - xen/xsm/xsm_core.c | 44 ++++++++++++++++++++++++++++- - 3 files changed, 68 insertions(+), 2 deletions(-) - -diff --git a/docs/misc/xen-command-line.markdown b/docs/misc/xen-command-line.markdown -index 8108bbf117..1025fb8a6b 100644 ---- a/docs/misc/xen-command-line.markdown -+++ b/docs/misc/xen-command-line.markdown -@@ -865,6 +865,19 @@ hardware domain is architecture dependent. - Note that specifying zero as domU value means zero, while for dom0 it means - to use the default. - -+### xsm -+> `= dummy | flask` -+ -+> Default: `dummy` -+ -+Specify which XSM module should be enabled. This option is only available if -+the hypervisor was compiled with XSM support. -+ -+* `dummy`: this is the default choice. Basic restriction for common deployment -+ (the dummy module) will be applied. It's also used when XSM is compiled out. -+* `flask`: this is the policy based access control. To choose this, the -+ separated option in kconfig must also be enabled. -+ - ### flask - > `= permissive | enforcing | late | disabled` - -diff --git a/xen/common/Kconfig b/xen/common/Kconfig -index 9043dce937..448c2888fe 100644 ---- a/xen/common/Kconfig -+++ b/xen/common/Kconfig -@@ -105,7 +105,7 @@ config XSM - - config FLASK - def_bool y -- prompt "FLux Advanced Security Kernel support" if EXPERT = "y" -+ prompt "FLux Advanced Security Kernel support" - depends on XSM - ---help--- - Enables FLASK (FLux Advanced Security Kernel) as the access control -@@ -143,6 +143,17 @@ config XSM_POLICY - - If unsure, say Y. - -+choice -+ prompt "Default XSM implementation" -+ depends on XSM -+ default XSM_FLASK_DEFAULT if XSM_FLASK -+ default XSM_DUMMY_DEFAULT -+ config XSM_DUMMY_DEFAULT -+ bool "Match non-XSM behavior" -+ config XSM_FLASK_DEFAULT -+ bool "FLux Advanced Security Kernel" if XSM_FLASK -+endchoice -+ - config LATE_HWDOM - bool "Dedicated hardware domain" - default n -diff --git a/xen/xsm/xsm_core.c b/xen/xsm/xsm_core.c -index 949dfcff75..e2f17e4b41 100644 ---- a/xen/xsm/xsm_core.c -+++ b/xen/xsm/xsm_core.c -@@ -31,6 +31,35 @@ - - struct xsm_operations *xsm_ops; - -+enum xsm_bootparam { -+ XSM_BOOTPARAM_DUMMY, -+ XSM_BOOTPARAM_FLASK, -+}; -+ -+static enum xsm_bootparam __initdata xsm_bootparam = -+#ifdef CONFIG_XSM_FLASK_DEFAULT -+ XSM_BOOTPARAM_FLASK; -+#else -+ XSM_BOOTPARAM_DUMMY; -+#endif -+ -+static int __init parse_xsm_param(const char *s) -+{ -+ int rc = 0; -+ -+ if ( !strcmp(s, "dummy") ) -+ xsm_bootparam = XSM_BOOTPARAM_DUMMY; -+#ifdef CONFIG_XSM_FLASK -+ else if ( !strcmp(s, "flask") ) -+ xsm_bootparam = XSM_BOOTPARAM_FLASK; -+#endif -+ else -+ rc = -EINVAL; -+ -+ return rc; -+} -+custom_param("xsm", parse_xsm_param); -+ - static inline int verify(struct xsm_operations *ops) - { - /* verify the security_operations structure exists */ -@@ -57,7 +86,20 @@ static int __init xsm_core_init(const void *policy_buffer, size_t policy_size) - } - - xsm_ops = &dummy_xsm_ops; -- flask_init(policy_buffer, policy_size); -+ -+ switch ( xsm_bootparam ) -+ { -+ case XSM_BOOTPARAM_DUMMY: -+ break; -+ -+ case XSM_BOOTPARAM_FLASK: -+ flask_init(policy_buffer, policy_size); -+ break; -+ -+ default: -+ ASSERT_UNREACHABLE(); -+ break; -+ } - - return 0; - } --- -2.17.1 - diff --git a/xsa295-4.11-14.patch b/xsa295-4.11-14.patch deleted file mode 100644 index 31744e9..0000000 --- a/xsa295-4.11-14.patch +++ /dev/null @@ -1,280 +0,0 @@ -From 0b4a5f09333267c6ee57ff1ee99cb9e7667b55df Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Tue, 9 Oct 2018 17:33:20 +0800 -Subject: [PATCH v2 4.11 14/20] xen/xsm: Add new SILO mode for XSM - -When SILO is enabled, there would be no page-sharing or event notifications -between unprivileged VMs (no grant tables or event channels). - -Signed-off-by: Xin Li -Acked-by: Daniel De Graaf -Acked-by: Andrew Cooper ---- - docs/misc/xen-command-line.markdown | 5 +- - xen/common/Kconfig | 15 ++++ - xen/include/xsm/dummy.h | 3 +- - xen/include/xsm/xsm.h | 6 ++ - xen/xsm/Makefile | 1 + - xen/xsm/silo.c | 108 ++++++++++++++++++++++++++++ - xen/xsm/xsm_core.c | 11 +++ - 7 files changed, 147 insertions(+), 2 deletions(-) - create mode 100644 xen/xsm/silo.c - -diff --git a/docs/misc/xen-command-line.markdown b/docs/misc/xen-command-line.markdown -index 1025fb8a6b..c63a07d29b 100644 ---- a/docs/misc/xen-command-line.markdown -+++ b/docs/misc/xen-command-line.markdown -@@ -866,7 +866,7 @@ Note that specifying zero as domU value means zero, while for dom0 it means - to use the default. - - ### xsm --> `= dummy | flask` -+> `= dummy | flask | silo` - - > Default: `dummy` - -@@ -877,6 +877,9 @@ the hypervisor was compiled with XSM support. - (the dummy module) will be applied. It's also used when XSM is compiled out. - * `flask`: this is the policy based access control. To choose this, the - separated option in kconfig must also be enabled. -+* `silo`: this will deny any unmediated communication channels between -+ unprivileged VMs. To choose this, the separated option in kconfig must also -+ be enabled. - - ### flask - > `= permissive | enforcing | late | disabled` -diff --git a/xen/common/Kconfig b/xen/common/Kconfig -index 448c2888fe..512f6446a3 100644 ---- a/xen/common/Kconfig -+++ b/xen/common/Kconfig -@@ -143,15 +143,30 @@ config XSM_POLICY - - If unsure, say Y. - -+config XSM_SILO -+ def_bool y -+ prompt "SILO support" -+ depends on XSM -+ ---help--- -+ Enables SILO as the access control mechanism used by the XSM framework. -+ This is not the default module, add boot parameter xsm=silo to choose -+ it. This will deny any unmediated communication channels (grant tables -+ and event channels) between unprivileged VMs. -+ -+ If unsure, say Y. -+ - choice - prompt "Default XSM implementation" - depends on XSM - default XSM_FLASK_DEFAULT if XSM_FLASK -+ default XSM_SILO_DEFAULT if XSM_SILO - default XSM_DUMMY_DEFAULT - config XSM_DUMMY_DEFAULT - bool "Match non-XSM behavior" - config XSM_FLASK_DEFAULT - bool "FLux Advanced Security Kernel" if XSM_FLASK -+ config XSM_SILO_DEFAULT -+ bool "SILO" if XSM_SILO - endchoice - - config LATE_HWDOM -diff --git a/xen/include/xsm/dummy.h b/xen/include/xsm/dummy.h -index ff6b2dbf39..a507fa43f5 100644 ---- a/xen/include/xsm/dummy.h -+++ b/xen/include/xsm/dummy.h -@@ -48,7 +48,8 @@ void __xsm_action_mismatch_detected(void); - * There is no xsm_default_t argument available, so the value from the assertion - * is used to initialize the variable. - */ --#define XSM_INLINE /* */ -+#define XSM_INLINE __maybe_unused -+ - #define XSM_DEFAULT_ARG /* */ - #define XSM_DEFAULT_VOID void - #define XSM_ASSERT_ACTION(def) xsm_default_t action = def; (void)action -diff --git a/xen/include/xsm/xsm.h b/xen/include/xsm/xsm.h -index f0c6fc7e29..b16a1b5b18 100644 ---- a/xen/include/xsm/xsm.h -+++ b/xen/include/xsm/xsm.h -@@ -733,6 +733,12 @@ extern const unsigned char xsm_init_policy[]; - extern const unsigned int xsm_init_policy_size; - #endif - -+#ifdef CONFIG_XSM_SILO -+extern void silo_init(void); -+#else -+static inline void silo_init(void) {} -+#endif -+ - #else /* CONFIG_XSM */ - - #include -diff --git a/xen/xsm/Makefile b/xen/xsm/Makefile -index 3252c46e64..50e21303b3 100644 ---- a/xen/xsm/Makefile -+++ b/xen/xsm/Makefile -@@ -1,5 +1,6 @@ - obj-y += xsm_core.o - obj-$(CONFIG_XSM) += xsm_policy.o - obj-$(CONFIG_XSM) += dummy.o -+obj-$(CONFIG_XSM_SILO) += silo.o - - subdir-$(CONFIG_FLASK) += flask -diff --git a/xen/xsm/silo.c b/xen/xsm/silo.c -new file mode 100644 -index 0000000000..4850756a3d ---- /dev/null -+++ b/xen/xsm/silo.c -@@ -0,0 +1,108 @@ -+/****************************************************************************** -+ * xsm/silo.c -+ * -+ * SILO module for XSM (Xen Security Modules) -+ * -+ * Copyright (c) 2018 Citrix Systems Ltd. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License along with -+ * this program; If not, see . -+ */ -+#define XSM_NO_WRAPPERS -+#include -+ -+/* -+ * Check if inter-domain communication is allowed. -+ * Return true when pass check. -+ */ -+static bool silo_mode_dom_check(const struct domain *ldom, -+ const struct domain *rdom) -+{ -+ const struct domain *currd = current->domain; -+ -+ return (is_control_domain(currd) || is_control_domain(ldom) || -+ is_control_domain(rdom) || ldom == rdom); -+} -+ -+static int silo_evtchn_unbound(struct domain *d1, struct evtchn *chn, -+ domid_t id2) -+{ -+ int rc = -EPERM; -+ struct domain *d2 = rcu_lock_domain_by_any_id(id2); -+ -+ if ( d2 == NULL ) -+ rc = -ESRCH; -+ else -+ { -+ if ( silo_mode_dom_check(d1, d2) ) -+ rc = xsm_evtchn_unbound(d1, chn, id2); -+ rcu_unlock_domain(d2); -+ } -+ -+ return rc; -+} -+ -+static int silo_evtchn_interdomain(struct domain *d1, struct evtchn *chan1, -+ struct domain *d2, struct evtchn *chan2) -+{ -+ if ( silo_mode_dom_check(d1, d2) ) -+ return xsm_evtchn_interdomain(d1, chan1, d2, chan2); -+ return -EPERM; -+} -+ -+static int silo_grant_mapref(struct domain *d1, struct domain *d2, -+ uint32_t flags) -+{ -+ if ( silo_mode_dom_check(d1, d2) ) -+ return xsm_grant_mapref(d1, d2, flags); -+ return -EPERM; -+} -+ -+static int silo_grant_transfer(struct domain *d1, struct domain *d2) -+{ -+ if ( silo_mode_dom_check(d1, d2) ) -+ return xsm_grant_transfer(d1, d2); -+ return -EPERM; -+} -+ -+static int silo_grant_copy(struct domain *d1, struct domain *d2) -+{ -+ if ( silo_mode_dom_check(d1, d2) ) -+ return xsm_grant_copy(d1, d2); -+ return -EPERM; -+} -+ -+static struct xsm_operations silo_xsm_ops = { -+ .evtchn_unbound = silo_evtchn_unbound, -+ .evtchn_interdomain = silo_evtchn_interdomain, -+ .grant_mapref = silo_grant_mapref, -+ .grant_transfer = silo_grant_transfer, -+ .grant_copy = silo_grant_copy, -+}; -+ -+void __init silo_init(void) -+{ -+ printk("Initialising XSM SILO mode\n"); -+ -+ if ( register_xsm(&silo_xsm_ops) ) -+ panic("SILO: Unable to register with XSM\n"); -+} -+ -+/* -+ * Local variables: -+ * mode: C -+ * c-file-style: "BSD" -+ * c-basic-offset: 4 -+ * tab-width: 4 -+ * indent-tabs-mode: nil -+ * End: -+ */ -diff --git a/xen/xsm/xsm_core.c b/xen/xsm/xsm_core.c -index e2f17e4b41..7b862ea79d 100644 ---- a/xen/xsm/xsm_core.c -+++ b/xen/xsm/xsm_core.c -@@ -34,11 +34,14 @@ struct xsm_operations *xsm_ops; - enum xsm_bootparam { - XSM_BOOTPARAM_DUMMY, - XSM_BOOTPARAM_FLASK, -+ XSM_BOOTPARAM_SILO, - }; - - static enum xsm_bootparam __initdata xsm_bootparam = - #ifdef CONFIG_XSM_FLASK_DEFAULT - XSM_BOOTPARAM_FLASK; -+#elif CONFIG_XSM_SILO_DEFAULT -+ XSM_BOOTPARAM_SILO; - #else - XSM_BOOTPARAM_DUMMY; - #endif -@@ -52,6 +55,10 @@ static int __init parse_xsm_param(const char *s) - #ifdef CONFIG_XSM_FLASK - else if ( !strcmp(s, "flask") ) - xsm_bootparam = XSM_BOOTPARAM_FLASK; -+#endif -+#ifdef CONFIG_XSM_SILO -+ else if ( !strcmp(s, "silo") ) -+ xsm_bootparam = XSM_BOOTPARAM_SILO; - #endif - else - rc = -EINVAL; -@@ -96,6 +103,10 @@ static int __init xsm_core_init(const void *policy_buffer, size_t policy_size) - flask_init(policy_buffer, policy_size); - break; - -+ case XSM_BOOTPARAM_SILO: -+ silo_init(); -+ break; -+ - default: - ASSERT_UNREACHABLE(); - break; --- -2.17.1 - diff --git a/xsa295-4.11-15.patch b/xsa295-4.11-15.patch deleted file mode 100644 index f4c1678..0000000 --- a/xsa295-4.11-15.patch +++ /dev/null @@ -1,112 +0,0 @@ -From 7f77f648cec9a30cfdca4222686dc999ff113789 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:25 +0100 -Subject: [PATCH v2 4.11 15/20] xen/arm: Turn on SILO mode by default on Arm - -On Arm, exclusive load-store atomics should only be used between trusted -thread. As not all the guests are trusted, it may be possible to DoS Xen -when updating shared memory with guest atomically. - -Recent patches introduced new helpers to update shared memory with guest -atomically. Those helpers relies on a memory region to be be shared with -Xen and a single guest. - -At the moment, nothing prevent a guest sharing a page with Xen and as -well with another guest (e.g via grant table). - -For the scope of the XSA, the quickest way is to deny communications -between unprivileged guest. So this patch is enabling and using SILO -mode by default on Arm. - -Users wanted finer graine policy could wrote their own Flask policy. - -This is part of XSA-295. - -Signed-off-by: Julien Grall -Acked-by: Jan Beulich ---- - xen/arch/arm/setup.c | 8 ++++++-- - xen/common/Kconfig | 3 ++- - xen/include/xsm/xsm.h | 5 +++++ - xen/xsm/xsm_core.c | 2 +- - 4 files changed, 14 insertions(+), 4 deletions(-) - -diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c -index 1d6f6bf37e..ff949f545a 100644 ---- a/xen/arch/arm/setup.c -+++ b/xen/arch/arm/setup.c -@@ -37,6 +37,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -787,8 +788,11 @@ void __init start_xen(unsigned long boot_phys_offset, - - tasklet_subsys_init(); - -- -- xsm_dt_init(); -+ if ( xsm_dt_init() != 1 ) -+ warning_add("WARNING: SILO mode is not enabled.\n" -+ "It has implications on the security of the system,\n" -+ "unless the communications have been forbidden between\n" -+ "untrusted domains.\n"); - - init_maintenance_interrupt(); - init_timer_interrupt(); -diff --git a/xen/common/Kconfig b/xen/common/Kconfig -index 512f6446a3..e4af3f13eb 100644 ---- a/xen/common/Kconfig -+++ b/xen/common/Kconfig -@@ -93,7 +93,7 @@ config XENOPROF - - config XSM - bool "Xen Security Modules support" -- default n -+ default ARM - ---help--- - Enables the security framework known as Xen Security Modules which - allows administrators fine-grained control over a Xen domain and -@@ -158,6 +158,7 @@ config XSM_SILO - choice - prompt "Default XSM implementation" - depends on XSM -+ default XSM_SILO_DEFAULT if XSM_SILO && ARM - default XSM_FLASK_DEFAULT if XSM_FLASK - default XSM_SILO_DEFAULT if XSM_SILO - default XSM_DUMMY_DEFAULT -diff --git a/xen/include/xsm/xsm.h b/xen/include/xsm/xsm.h -index b16a1b5b18..0c803531eb 100644 ---- a/xen/include/xsm/xsm.h -+++ b/xen/include/xsm/xsm.h -@@ -710,6 +710,11 @@ extern int xsm_multiboot_policy_init(unsigned long *module_map, - #endif - - #ifdef CONFIG_HAS_DEVICE_TREE -+/* -+ * Initialize XSM -+ * -+ * On success, return 1 if using SILO mode else 0. -+ */ - extern int xsm_dt_init(void); - extern int xsm_dt_policy_init(void **policy_buffer, size_t *policy_size); - extern bool has_xsm_magic(paddr_t); -diff --git a/xen/xsm/xsm_core.c b/xen/xsm/xsm_core.c -index 7b862ea79d..1179cdf610 100644 ---- a/xen/xsm/xsm_core.c -+++ b/xen/xsm/xsm_core.c -@@ -167,7 +167,7 @@ int __init xsm_dt_init(void) - - xfree(policy_buffer); - -- return ret; -+ return ret ?: (xsm_bootparam == XSM_BOOTPARAM_SILO); - } - - /** --- -2.17.1 - diff --git a/xsa295-4.11-16.patch b/xsa295-4.11-16.patch deleted file mode 100644 index 8acd963..0000000 --- a/xsa295-4.11-16.patch +++ /dev/null @@ -1,277 +0,0 @@ -From 63966ce61c86b68592a87353176210ab42d9f8fd Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:26 +0100 -Subject: [PATCH v2 4.11 16/20] xen/bitops: Provide helpers to safely modify - guest memory atomically - -On Arm, exclusive load-store atomics should only be used between trusted -thread. As not all the guests are trusted, it may be possible to DoS Xen -when updating shared memory with guest atomically. - -This patch adds a new set of helper that will update the guest memory -safely. For x86, it is already possible to use the current helpers -safely. So just wrap them. - -For Arm, we will first attempt to update the guest memory with the loop -bounded by a maximum number of iterations. If it fails, we will pause the -domain and try again. - -Note that this heuristics assumes that a page can only be shared between -Xen and one domain. Not Xen and multiple domain. - -The maximum number of iterations is based on how many times a simple -load-store atomic operation can be executed in 1uS. The maximum value is -per-CPU to cater big.LITTLE and calculated when the CPU is booting. The -heuristic was randomly chosen and can be modified if impact too much -good-behaving guest. - -Note, while test_bit does not requires to use atomic operation, a -wrapper for test_bit was added for completeness. In this case, the -domain stays constified to avoid major rework in the caller for the -time-being. - -This is part of XSA-295. - -Signed-of-by: Julien Grall -Acked-by: Jan Beulich -Reviewed-by: Stefano Stabellini ---- - xen/arch/arm/Makefile | 1 + - xen/arch/arm/guest_atomics.c | 91 +++++++++++++++++++++++++++++ - xen/include/asm-arm/guest_atomics.h | 76 ++++++++++++++++++++++++ - xen/include/asm-x86/guest_atomics.h | 30 ++++++++++ - 4 files changed, 198 insertions(+) - create mode 100644 xen/arch/arm/guest_atomics.c - create mode 100644 xen/include/asm-arm/guest_atomics.h - create mode 100644 xen/include/asm-x86/guest_atomics.h - -diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile -index a9533b107e..3559e3ac46 100644 ---- a/xen/arch/arm/Makefile -+++ b/xen/arch/arm/Makefile -@@ -21,6 +21,7 @@ obj-$(CONFIG_HAS_GICV3) += gic-v3.o - obj-$(CONFIG_HAS_ITS) += gic-v3-its.o - obj-$(CONFIG_HAS_ITS) += gic-v3-lpi.o - obj-y += guestcopy.o -+obj-y += guest_atomics.o - obj-y += guest_walk.o - obj-y += hvm.o - obj-y += io.o -diff --git a/xen/arch/arm/guest_atomics.c b/xen/arch/arm/guest_atomics.c -new file mode 100644 -index 0000000000..1b78a062f0 ---- /dev/null -+++ b/xen/arch/arm/guest_atomics.c -@@ -0,0 +1,91 @@ -+/* -+ * arch/arm/guest_atomics.c -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License along with -+ * this program; If not, see . -+ */ -+#include -+ -+#include -+ -+DEFINE_PER_CPU_READ_MOSTLY(unsigned int, guest_safe_atomic_max); -+ -+/* -+ * Heuristic to find a safe upper-limit for load-store exclusive -+ * operations on memory shared with guest. -+ * -+ * At the moment, we calculate the number of iterations of a simple -+ * load-store atomic loop in 1uS. -+ */ -+static void calibrate_safe_atomic(void) -+{ -+ s_time_t deadline = NOW() + MICROSECS(1); -+ unsigned int counter = 0; -+ unsigned long mem = 0; -+ -+ do -+ { -+ unsigned long res, tmp; -+ -+#ifdef CONFIG_ARM_32 -+ asm volatile (" ldrex %2, %1\n" -+ " add %2, %2, #1\n" -+ " strex %0, %2, %1\n" -+ : "=&r" (res), "+Q" (mem), "=&r" (tmp)); -+#else -+ asm volatile (" ldxr %w2, %1\n" -+ " add %w2, %w2, #1\n" -+ " stxr %w0, %w2, %1\n" -+ : "=&r" (res), "+Q" (mem), "=&r" (tmp)); -+#endif -+ counter++; -+ } while (NOW() < deadline); -+ -+ this_cpu(guest_safe_atomic_max) = counter; -+ -+ printk(XENLOG_DEBUG -+ "CPU%u: Guest atomics will try %u times before pausing the domain\n", -+ smp_processor_id(), counter); -+} -+ -+static int cpu_guest_safe_atomic_callback(struct notifier_block *nfb, -+ unsigned long action, -+ void *hcpu) -+{ -+ if ( action == CPU_STARTING ) -+ calibrate_safe_atomic(); -+ -+ return NOTIFY_DONE; -+} -+ -+static struct notifier_block cpu_guest_safe_atomic_nfb = { -+ .notifier_call = cpu_guest_safe_atomic_callback, -+}; -+ -+static int __init guest_safe_atomic_init(void) -+{ -+ register_cpu_notifier(&cpu_guest_safe_atomic_nfb); -+ -+ calibrate_safe_atomic(); -+ -+ return 0; -+} -+presmp_initcall(guest_safe_atomic_init); -+ -+/* -+ * Local variables: -+ * mode: C -+ * c-file-style: "BSD" -+ * c-basic-offset: 4 -+ * indent-tabs-mode: nil -+ * End: -+ */ -diff --git a/xen/include/asm-arm/guest_atomics.h b/xen/include/asm-arm/guest_atomics.h -new file mode 100644 -index 0000000000..4f127fda41 ---- /dev/null -+++ b/xen/include/asm-arm/guest_atomics.h -@@ -0,0 +1,76 @@ -+#ifndef _ARM_GUEST_ATOMICS_H -+#define _ARM_GUEST_ATOMICS_H -+ -+#include -+#include -+ -+/* -+ * The guest atomics helpers shares the same logic. We first try to use -+ * the *_timeout version of the operation. If it didn't timeout, then we -+ * successfully updated the memory. Nothing else to do. -+ * -+ * If it did timeout, then it means we didn't manage to update the -+ * memory. This is possibly because the guest is misbehaving (i.e tight -+ * store loop) but can also happen for other reasons (i.e nested Xen). -+ * In that case pause the domain and retry the operation, this time -+ * without a timeout. -+ * -+ * Note, those helpers rely on other part of the code to prevent sharing -+ * a page between Xen and multiple domain. -+ */ -+ -+DECLARE_PER_CPU(unsigned int, guest_safe_atomic_max); -+ -+#define guest_bitop(name) \ -+static inline void guest_##name(struct domain *d, int nr, volatile void *p) \ -+{ \ -+ if ( name##_timeout(nr, p, this_cpu(guest_safe_atomic_max)) ) \ -+ return; \ -+ \ -+ domain_pause_nosync(d); \ -+ name(nr, p); \ -+ domain_unpause(d); \ -+} -+ -+#define guest_testop(name) \ -+static inline int guest_##name(struct domain *d, int nr, volatile void *p) \ -+{ \ -+ bool succeed; \ -+ int oldbit; \ -+ \ -+ succeed = name##_timeout(nr, p, &oldbit, \ -+ this_cpu(guest_safe_atomic_max)); \ -+ if ( succeed ) \ -+ return oldbit; \ -+ \ -+ domain_pause_nosync(d); \ -+ oldbit = name(nr, p); \ -+ domain_unpause(d); \ -+ \ -+ return oldbit; \ -+} -+ -+guest_bitop(set_bit) -+guest_bitop(clear_bit) -+guest_bitop(change_bit) -+ -+#undef guest_bitop -+ -+/* test_bit does not use load-store atomic operations */ -+#define guest_test_bit(d, nr, p) ((void)(d), test_bit(nr, p)) -+ -+guest_testop(test_and_set_bit) -+guest_testop(test_and_clear_bit) -+guest_testop(test_and_change_bit) -+ -+#undef guest_testop -+ -+#endif /* _ARM_GUEST_ATOMICS_H */ -+/* -+ * Local variables: -+ * mode: C -+ * c-file-style: "BSD" -+ * c-basic-offset: 4 -+ * indent-tabs-mode: nil -+ * End: -+ */ -diff --git a/xen/include/asm-x86/guest_atomics.h b/xen/include/asm-x86/guest_atomics.h -new file mode 100644 -index 0000000000..0c71d2d278 ---- /dev/null -+++ b/xen/include/asm-x86/guest_atomics.h -@@ -0,0 +1,30 @@ -+#ifndef _X86_GUEST_ATOMICS_H -+#define _X86_GUEST_ATOMICS_H -+ -+#include -+ -+/* -+ * It is safe to use the atomics helpers on x86 on memory shared with -+ * the guests. -+ */ -+#define guest_set_bit(d, nr, p) ((void)(d), set_bit(nr, p)) -+#define guest_clear_bit(d, nr, p) ((void)(d), clear_bit(nr, p)) -+#define guest_change_bit(d, nr, p) ((void)(d), change_bit(nr, p)) -+#define guest_test_bit(d, nr, p) ((void)(d), test_bit(nr, p)) -+ -+#define guest_test_and_set_bit(d, nr, p) \ -+ ((void)(d), test_and_set_bit(nr, p)) -+#define guest_test_and_clear_bit(d, nr, p) \ -+ ((void)(d), test_and_clear_bit(nr, p)) -+#define guest_test_and_change_bit(d, nr, p) \ -+ ((void)(d), test_and_change_bit(nr, p)) -+ -+#endif /* _X86_GUEST_ATOMICS_H */ -+/* -+ * Local variables: -+ * mode: C -+ * c-file-style: "BSD" -+ * c-basic-offset: 4 -+ * indent-tabs-mode: nil -+ * End: -+ */ --- -2.17.1 - diff --git a/xsa295-4.11-17.patch b/xsa295-4.11-17.patch deleted file mode 100644 index f3db6ea..0000000 --- a/xsa295-4.11-17.patch +++ /dev/null @@ -1,93 +0,0 @@ -From d410b20098cfceb17eff3c12031e528542569197 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:27 +0100 -Subject: [PATCH v2 4.11 17/20] xen/cmpxchg: Provide helper to safely modify - guest memory atomically - -On Arm, exclusive load-store atomics should only be used between trusted -thread. As not all the guests are trusted, it may be possible to DoS Xen -when updating shared memory with guest atomically. - -This patch adds a new helper that will update the guest memory safely. -For x86, it is already possible to use the current helper safely. So -just wrap it. - -For Arm, we will first attempt to update the guest memory with the -loop bounded by a maximum number of iterations. If it fails, we will -pause the domain and try again. - -Note that this heuristics assumes that a page can only -be shared between Xen and one domain. Not Xen and multiple domain. - -The maximum number of iterations is based on how many times atomic_inc() -can be executed in 1uS. The maximum value is per-CPU to cater big.LITTLE -and calculated when the CPU is booting. - -The maximum number of iterations is based on how many times a simple -load-store atomic operation can be executed in 1uS. The maximum -value is per-CPU to cater big.LITTLE and calculated when the CPU is -booting. The heuristic was randomly chosen and can be modified if -impact too much good-behaving guest. - -This is part of XSA-295. - -Signed-of-by: Julien Grall -Reviewed-by: Stefano Stabellini -Acked-by: Jan Beulich ---- - xen/include/asm-arm/guest_atomics.h | 25 +++++++++++++++++++++++++ - xen/include/asm-x86/guest_atomics.h | 2 ++ - 2 files changed, 27 insertions(+) - -diff --git a/xen/include/asm-arm/guest_atomics.h b/xen/include/asm-arm/guest_atomics.h -index 4f127fda41..61925d313d 100644 ---- a/xen/include/asm-arm/guest_atomics.h -+++ b/xen/include/asm-arm/guest_atomics.h -@@ -65,6 +65,31 @@ guest_testop(test_and_change_bit) - - #undef guest_testop - -+static inline unsigned long __guest_cmpxchg(struct domain *d, -+ volatile void *ptr, -+ unsigned long old, -+ unsigned long new, -+ unsigned int size) -+{ -+ unsigned long oldval = old; -+ -+ if ( __cmpxchg_mb_timeout(ptr, &oldval, new, size, -+ this_cpu(guest_safe_atomic_max)) ) -+ return oldval; -+ -+ domain_pause_nosync(d); -+ oldval = __cmpxchg_mb(ptr, old, new, size); -+ domain_unpause(d); -+ -+ return oldval; -+} -+ -+#define guest_cmpxchg(d, ptr, o, n) \ -+ ((__typeof__(*(ptr)))__guest_cmpxchg(d, ptr, \ -+ (unsigned long)(o),\ -+ (unsigned long)(n),\ -+ sizeof (*(ptr)))) -+ - #endif /* _ARM_GUEST_ATOMICS_H */ - /* - * Local variables: -diff --git a/xen/include/asm-x86/guest_atomics.h b/xen/include/asm-x86/guest_atomics.h -index 0c71d2d278..029417c8ff 100644 ---- a/xen/include/asm-x86/guest_atomics.h -+++ b/xen/include/asm-x86/guest_atomics.h -@@ -19,6 +19,8 @@ - #define guest_test_and_change_bit(d, nr, p) \ - ((void)(d), test_and_change_bit(nr, p)) - -+#define guest_cmpxchg(d, ptr, o, n) ((void)(d), cmpxchg(ptr, o, n)) -+ - #endif /* _X86_GUEST_ATOMICS_H */ - /* - * Local variables: --- -2.17.1 - diff --git a/xsa295-4.11-18.patch b/xsa295-4.11-18.patch deleted file mode 100644 index 4babc55..0000000 --- a/xsa295-4.11-18.patch +++ /dev/null @@ -1,577 +0,0 @@ -From 0689672212e95d65d2dd574c1fb620d3b88073cc Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:28 +0100 -Subject: [PATCH v2 4.11 18/20] xen: Use guest atomics helpers when modifying - atomically guest memory - -On Arm, exclusive load-store atomics should only be used between trusted -thread. As not all the guests are trusted, it may be possible to DoS Xen -when updating shared memory with guest atomically. - -This patch replaces all the atomics operations on shared memory with -a guest by the new guest atomics helpers. The x86 code was not audited -to know where guest atomics helpers could be used. I will leave that -to the x86 folks. - -Note that some rework was required in order to plumb use the new guest -atomics in event channel and grant-table. - -Because guest_test_bit is ignoring the parameter "d" for now, it -means there a lot of places do not need to drop the const. We may want -to revisit this in the future if the parameter "d" becomes necessary. - -This is part of XSA-295. - -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini ---- - xen/arch/arm/domain.c | 3 +- - xen/arch/arm/mm.c | 6 ++-- - xen/common/event_2l.c | 26 ++++++++------ - xen/common/event_fifo.c | 44 ++++++++++++----------- - xen/common/grant_table.c | 59 +++++++++++++++++-------------- - xen/include/asm-arm/grant_table.h | 2 +- - xen/include/asm-x86/grant_table.h | 3 +- - 7 files changed, 79 insertions(+), 64 deletions(-) - -diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c -index ec0f042bf7..112a146fd9 100644 ---- a/xen/arch/arm/domain.c -+++ b/xen/arch/arm/domain.c -@@ -26,6 +26,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -961,7 +962,7 @@ void arch_dump_vcpu_info(struct vcpu *v) - - void vcpu_mark_events_pending(struct vcpu *v) - { -- int already_pending = test_and_set_bit( -+ bool already_pending = guest_test_and_set_bit(v->domain, - 0, (unsigned long *)&vcpu_info(v, evtchn_upcall_pending)); - - if ( already_pending ) -diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c -index a6de77c28c..6b7e6b675b 100644 ---- a/xen/arch/arm/mm.c -+++ b/xen/arch/arm/mm.c -@@ -40,6 +40,8 @@ - #include - #include - #include -+ -+#include - #include - - struct domain *dom_xen, *dom_io, *dom_cow; -@@ -1395,7 +1397,7 @@ void put_page_type(struct page_info *page) - return; - } - --void gnttab_clear_flag(unsigned long nr, uint16_t *addr) -+void gnttab_clear_flag(struct domain *d, unsigned long nr, uint16_t *addr) - { - /* - * Note that this cannot be clear_bit(), as the access must be -@@ -1405,7 +1407,7 @@ void gnttab_clear_flag(unsigned long nr, uint16_t *addr) - - do { - old = *addr; -- } while (cmpxchg(addr, old, old & mask) != old); -+ } while (guest_cmpxchg(d, addr, old, old & mask) != old); - } - - void gnttab_mark_dirty(struct domain *d, mfn_t mfn) -diff --git a/xen/common/event_2l.c b/xen/common/event_2l.c -index 8ca90899ab..e1dbb860f4 100644 ---- a/xen/common/event_2l.c -+++ b/xen/common/event_2l.c -@@ -13,6 +13,8 @@ - #include - #include - -+#include -+ - static void evtchn_2l_set_pending(struct vcpu *v, struct evtchn *evtchn) - { - struct domain *d = v->domain; -@@ -25,12 +27,12 @@ static void evtchn_2l_set_pending(struct vcpu *v, struct evtchn *evtchn) - * others may require explicit memory barriers. - */ - -- if ( test_and_set_bit(port, &shared_info(d, evtchn_pending)) ) -+ if ( guest_test_and_set_bit(d, port, &shared_info(d, evtchn_pending)) ) - return; - -- if ( !test_bit (port, &shared_info(d, evtchn_mask)) && -- !test_and_set_bit(port / BITS_PER_EVTCHN_WORD(d), -- &vcpu_info(v, evtchn_pending_sel)) ) -+ if ( !guest_test_bit(d, port, &shared_info(d, evtchn_mask)) && -+ !guest_test_and_set_bit(d, port / BITS_PER_EVTCHN_WORD(d), -+ &vcpu_info(v, evtchn_pending_sel)) ) - { - vcpu_mark_events_pending(v); - } -@@ -40,7 +42,7 @@ static void evtchn_2l_set_pending(struct vcpu *v, struct evtchn *evtchn) - - static void evtchn_2l_clear_pending(struct domain *d, struct evtchn *evtchn) - { -- clear_bit(evtchn->port, &shared_info(d, evtchn_pending)); -+ guest_clear_bit(d, evtchn->port, &shared_info(d, evtchn_pending)); - } - - static void evtchn_2l_unmask(struct domain *d, struct evtchn *evtchn) -@@ -52,10 +54,10 @@ static void evtchn_2l_unmask(struct domain *d, struct evtchn *evtchn) - * These operations must happen in strict order. Based on - * evtchn_2l_set_pending() above. - */ -- if ( test_and_clear_bit(port, &shared_info(d, evtchn_mask)) && -- test_bit (port, &shared_info(d, evtchn_pending)) && -- !test_and_set_bit (port / BITS_PER_EVTCHN_WORD(d), -- &vcpu_info(v, evtchn_pending_sel)) ) -+ if ( guest_test_and_clear_bit(d, port, &shared_info(d, evtchn_mask)) && -+ guest_test_bit(d, port, &shared_info(d, evtchn_pending)) && -+ !guest_test_and_set_bit(d, port / BITS_PER_EVTCHN_WORD(d), -+ &vcpu_info(v, evtchn_pending_sel)) ) - { - vcpu_mark_events_pending(v); - } -@@ -66,7 +68,8 @@ static bool evtchn_2l_is_pending(const struct domain *d, evtchn_port_t port) - unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); - - ASSERT(port < max_ports); -- return port < max_ports && test_bit(port, &shared_info(d, evtchn_pending)); -+ return (port < max_ports && -+ guest_test_bit(d, port, &shared_info(d, evtchn_pending))); - } - - static bool evtchn_2l_is_masked(const struct domain *d, evtchn_port_t port) -@@ -74,7 +77,8 @@ static bool evtchn_2l_is_masked(const struct domain *d, evtchn_port_t port) - unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); - - ASSERT(port < max_ports); -- return port >= max_ports || test_bit(port, &shared_info(d, evtchn_mask)); -+ return (port >= max_ports || -+ guest_test_bit(d, port, &shared_info(d, evtchn_mask))); - } - - static void evtchn_2l_print_state(struct domain *d, -diff --git a/xen/common/event_fifo.c b/xen/common/event_fifo.c -index c49f446754..3f4c835518 100644 ---- a/xen/common/event_fifo.c -+++ b/xen/common/event_fifo.c -@@ -17,6 +17,8 @@ - #include - #include - -+#include -+ - #include - - static inline event_word_t *evtchn_fifo_word_from_port(const struct domain *d, -@@ -50,7 +52,7 @@ static void evtchn_fifo_init(struct domain *d, struct evtchn *evtchn) - * on the wrong VCPU or with an unexpected priority. - */ - word = evtchn_fifo_word_from_port(d, evtchn->port); -- if ( word && test_bit(EVTCHN_FIFO_LINKED, word) ) -+ if ( word && guest_test_bit(d, EVTCHN_FIFO_LINKED, word) ) - gdprintk(XENLOG_WARNING, "domain %d, port %d already on a queue\n", - d->domain_id, evtchn->port); - } -@@ -115,7 +117,7 @@ static int try_set_link(event_word_t *word, event_word_t *w, uint32_t link) - * We block unmasking by the guest by marking the tail word as BUSY, - * therefore, the cmpxchg() may fail at most 4 times. - */ --static bool_t evtchn_fifo_set_link(const struct domain *d, event_word_t *word, -+static bool_t evtchn_fifo_set_link(struct domain *d, event_word_t *word, - uint32_t link) - { - event_word_t w; -@@ -129,7 +131,7 @@ static bool_t evtchn_fifo_set_link(const struct domain *d, event_word_t *word, - return ret; - - /* Lock the word to prevent guest unmasking. */ -- set_bit(EVTCHN_FIFO_BUSY, word); -+ guest_set_bit(d, EVTCHN_FIFO_BUSY, word); - - w = read_atomic(word); - -@@ -139,13 +141,13 @@ static bool_t evtchn_fifo_set_link(const struct domain *d, event_word_t *word, - if ( ret >= 0 ) - { - if ( ret == 0 ) -- clear_bit(EVTCHN_FIFO_BUSY, word); -+ guest_clear_bit(d, EVTCHN_FIFO_BUSY, word); - return ret; - } - } - gdprintk(XENLOG_WARNING, "domain %d, port %d not linked\n", - d->domain_id, link); -- clear_bit(EVTCHN_FIFO_BUSY, word); -+ guest_clear_bit(d, EVTCHN_FIFO_BUSY, word); - return 1; - } - -@@ -170,13 +172,13 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) - return; - } - -- was_pending = test_and_set_bit(EVTCHN_FIFO_PENDING, word); -+ was_pending = guest_test_and_set_bit(d, EVTCHN_FIFO_PENDING, word); - - /* - * Link the event if it unmasked and not already linked. - */ -- if ( !test_bit(EVTCHN_FIFO_MASKED, word) -- && !test_bit(EVTCHN_FIFO_LINKED, word) ) -+ if ( !guest_test_bit(d, EVTCHN_FIFO_MASKED, word) && -+ !guest_test_bit(d, EVTCHN_FIFO_LINKED, word) ) - { - struct evtchn_fifo_queue *q, *old_q; - event_word_t *tail_word; -@@ -205,7 +207,7 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) - if ( !old_q ) - goto done; - -- if ( test_and_set_bit(EVTCHN_FIFO_LINKED, word) ) -+ if ( guest_test_and_set_bit(d, EVTCHN_FIFO_LINKED, word) ) - { - spin_unlock_irqrestore(&old_q->lock, flags); - goto done; -@@ -251,8 +253,8 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) - spin_unlock_irqrestore(&q->lock, flags); - - if ( !linked -- && !test_and_set_bit(q->priority, -- &v->evtchn_fifo->control_block->ready) ) -+ && !guest_test_and_set_bit(d, q->priority, -+ &v->evtchn_fifo->control_block->ready) ) - vcpu_mark_events_pending(v); - } - done: -@@ -274,7 +276,7 @@ static void evtchn_fifo_clear_pending(struct domain *d, struct evtchn *evtchn) - * No need to unlink as the guest will unlink and ignore - * non-pending events. - */ -- clear_bit(EVTCHN_FIFO_PENDING, word); -+ guest_clear_bit(d, EVTCHN_FIFO_PENDING, word); - } - - static void evtchn_fifo_unmask(struct domain *d, struct evtchn *evtchn) -@@ -286,10 +288,10 @@ static void evtchn_fifo_unmask(struct domain *d, struct evtchn *evtchn) - if ( unlikely(!word) ) - return; - -- clear_bit(EVTCHN_FIFO_MASKED, word); -+ guest_clear_bit(d, EVTCHN_FIFO_MASKED, word); - - /* Relink if pending. */ -- if ( test_bit(EVTCHN_FIFO_PENDING, word) ) -+ if ( guest_test_bit(d, EVTCHN_FIFO_PENDING, word) ) - evtchn_fifo_set_pending(v, evtchn); - } - -@@ -297,21 +299,21 @@ static bool evtchn_fifo_is_pending(const struct domain *d, evtchn_port_t port) - { - const event_word_t *word = evtchn_fifo_word_from_port(d, port); - -- return word && test_bit(EVTCHN_FIFO_PENDING, word); -+ return word && guest_test_bit(d, EVTCHN_FIFO_PENDING, word); - } - - static bool_t evtchn_fifo_is_masked(const struct domain *d, evtchn_port_t port) - { - const event_word_t *word = evtchn_fifo_word_from_port(d, port); - -- return !word || test_bit(EVTCHN_FIFO_MASKED, word); -+ return !word || guest_test_bit(d, EVTCHN_FIFO_MASKED, word); - } - - static bool_t evtchn_fifo_is_busy(const struct domain *d, evtchn_port_t port) - { - const event_word_t *word = evtchn_fifo_word_from_port(d, port); - -- return word && test_bit(EVTCHN_FIFO_LINKED, word); -+ return word && guest_test_bit(d, EVTCHN_FIFO_LINKED, word); - } - - static int evtchn_fifo_set_priority(struct domain *d, struct evtchn *evtchn, -@@ -338,11 +340,11 @@ static void evtchn_fifo_print_state(struct domain *d, - word = evtchn_fifo_word_from_port(d, evtchn->port); - if ( !word ) - printk("? "); -- else if ( test_bit(EVTCHN_FIFO_LINKED, word) ) -- printk("%c %-4u", test_bit(EVTCHN_FIFO_BUSY, word) ? 'B' : ' ', -+ else if ( guest_test_bit(d, EVTCHN_FIFO_LINKED, word) ) -+ printk("%c %-4u", guest_test_bit(d, EVTCHN_FIFO_BUSY, word) ? 'B' : ' ', - *word & EVTCHN_FIFO_LINK_MASK); - else -- printk("%c - ", test_bit(EVTCHN_FIFO_BUSY, word) ? 'B' : ' '); -+ printk("%c - ", guest_test_bit(d, EVTCHN_FIFO_BUSY, word) ? 'B' : ' '); - } - - static const struct evtchn_port_ops evtchn_port_ops_fifo = -@@ -494,7 +496,7 @@ static void setup_ports(struct domain *d) - - evtchn = evtchn_from_port(d, port); - -- if ( test_bit(port, &shared_info(d, evtchn_pending)) ) -+ if ( guest_test_bit(d, port, &shared_info(d, evtchn_pending)) ) - evtchn->pending = 1; - - evtchn_fifo_set_priority(d, evtchn, EVTCHN_FIFO_PRIORITY_DEFAULT); -diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c -index c3a806fe47..221563a232 100644 ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -39,6 +39,7 @@ - #include - #include - #include -+#include - - /* Per-domain grant information. */ - struct grant_table { -@@ -653,6 +654,7 @@ static unsigned int nr_grant_entries(struct grant_table *gt) - } - - static int _set_status_v1(const grant_entry_header_t *shah, -+ struct domain *rd, - struct active_grant_entry *act, - int readonly, - int mapflag, -@@ -708,8 +710,8 @@ static int _set_status_v1(const grant_entry_header_t *shah, - "Attempt to write-pin a r/o grant entry\n"); - } - -- prev_scombo.word = cmpxchg((u32 *)shah, -- scombo.word, new_scombo.word); -+ prev_scombo.word = guest_cmpxchg(rd, (u32 *)shah, -+ scombo.word, new_scombo.word); - if ( likely(prev_scombo.word == scombo.word) ) - break; - -@@ -726,6 +728,7 @@ done: - - static int _set_status_v2(const grant_entry_header_t *shah, - grant_status_t *status, -+ struct domain *rd, - struct active_grant_entry *act, - int readonly, - int mapflag, -@@ -788,8 +791,8 @@ static int _set_status_v2(const grant_entry_header_t *shah, - (id != ldomid) || - (!readonly && (flags & GTF_readonly)) ) - { -- gnttab_clear_flag(_GTF_writing, status); -- gnttab_clear_flag(_GTF_reading, status); -+ gnttab_clear_flag(rd, _GTF_writing, status); -+ gnttab_clear_flag(rd, _GTF_reading, status); - PIN_FAIL(done, GNTST_general_error, - "Unstable flags (%x) or dom (%d); expected d%d (r/w: %d)\n", - flags, id, ldomid, !readonly); -@@ -799,7 +802,7 @@ static int _set_status_v2(const grant_entry_header_t *shah, - { - if ( unlikely(flags & GTF_readonly) ) - { -- gnttab_clear_flag(_GTF_writing, status); -+ gnttab_clear_flag(rd, _GTF_writing, status); - PIN_FAIL(done, GNTST_general_error, - "Unstable grant readonly flag\n"); - } -@@ -812,6 +815,7 @@ done: - - static int _set_status(const grant_entry_header_t *shah, - grant_status_t *status, -+ struct domain *rd, - unsigned rgt_version, - struct active_grant_entry *act, - int readonly, -@@ -820,9 +824,9 @@ static int _set_status(const grant_entry_header_t *shah, - { - - if ( rgt_version == 1 ) -- return _set_status_v1(shah, act, readonly, mapflag, ldomid); -+ return _set_status_v1(shah, rd, act, readonly, mapflag, ldomid); - else -- return _set_status_v2(shah, status, act, readonly, mapflag, ldomid); -+ return _set_status_v2(shah, status, rd, act, readonly, mapflag, ldomid); - } - - static struct active_grant_entry *grant_map_exists(const struct domain *ld, -@@ -994,7 +998,7 @@ map_grant_ref( - (!(op->flags & GNTMAP_readonly) && - !(act->pin & (GNTPIN_hstw_mask|GNTPIN_devw_mask))) ) - { -- if ( (rc = _set_status(shah, status, rgt->gt_version, act, -+ if ( (rc = _set_status(shah, status, rd, rgt->gt_version, act, - op->flags & GNTMAP_readonly, 1, - ld->domain_id) != GNTST_okay) ) - goto act_release_out; -@@ -1218,10 +1222,10 @@ map_grant_ref( - unlock_out_clear: - if ( !(op->flags & GNTMAP_readonly) && - !(act->pin & (GNTPIN_hstw_mask|GNTPIN_devw_mask)) ) -- gnttab_clear_flag(_GTF_writing, status); -+ gnttab_clear_flag(rd, _GTF_writing, status); - - if ( !act->pin ) -- gnttab_clear_flag(_GTF_reading, status); -+ gnttab_clear_flag(rd, _GTF_reading, status); - - act_release_out: - active_entry_release(act); -@@ -1505,10 +1509,10 @@ unmap_common_complete(struct gnttab_unmap_common *op) - - if ( ((act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)) == 0) && - !(op->done & GNTMAP_readonly) ) -- gnttab_clear_flag(_GTF_writing, status); -+ gnttab_clear_flag(rd, _GTF_writing, status); - - if ( act->pin == 0 ) -- gnttab_clear_flag(_GTF_reading, status); -+ gnttab_clear_flag(rd, _GTF_reading, status); - - active_entry_release(act); - grant_read_unlock(rgt); -@@ -2073,8 +2077,8 @@ gnttab_prepare_for_transfer( - new_scombo = scombo; - new_scombo.shorts.flags |= GTF_transfer_committed; - -- prev_scombo.word = cmpxchg((u32 *)&sha->flags, -- scombo.word, new_scombo.word); -+ prev_scombo.word = guest_cmpxchg(rd, (u32 *)&sha->flags, -+ scombo.word, new_scombo.word); - if ( likely(prev_scombo.word == scombo.word) ) - break; - -@@ -2359,11 +2363,11 @@ release_grant_for_copy( - - act->pin -= GNTPIN_hstw_inc; - if ( !(act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)) ) -- gnttab_clear_flag(_GTF_writing, status); -+ gnttab_clear_flag(rd, _GTF_writing, status); - } - - if ( !act->pin ) -- gnttab_clear_flag(_GTF_reading, status); -+ gnttab_clear_flag(rd, _GTF_reading, status); - - active_entry_release(act); - grant_read_unlock(rgt); -@@ -2385,14 +2389,15 @@ release_grant_for_copy( - under the domain's grant table lock. */ - /* Only safe on transitive grants. Even then, note that we don't - attempt to drop any pin on the referent grant. */ --static void fixup_status_for_copy_pin(const struct active_grant_entry *act, -+static void fixup_status_for_copy_pin(struct domain *rd, -+ const struct active_grant_entry *act, - uint16_t *status) - { - if ( !(act->pin & (GNTPIN_hstw_mask | GNTPIN_devw_mask)) ) -- gnttab_clear_flag(_GTF_writing, status); -+ gnttab_clear_flag(rd, _GTF_writing, status); - - if ( !act->pin ) -- gnttab_clear_flag(_GTF_reading, status); -+ gnttab_clear_flag(rd, _GTF_reading, status); - } - - /* Grab a frame number from a grant entry and update the flags and pin -@@ -2452,7 +2457,7 @@ acquire_grant_for_copy( - { - if ( (!old_pin || (!readonly && - !(old_pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)))) && -- (rc = _set_status_v2(shah, status, act, readonly, 0, -+ (rc = _set_status_v2(shah, status, rd, act, readonly, 0, - ldom)) != GNTST_okay ) - goto unlock_out; - -@@ -2501,7 +2506,7 @@ acquire_grant_for_copy( - - if ( rc != GNTST_okay ) - { -- fixup_status_for_copy_pin(act, status); -+ fixup_status_for_copy_pin(rd, act, status); - rcu_unlock_domain(td); - active_entry_release(act); - grant_read_unlock(rgt); -@@ -2524,7 +2529,7 @@ acquire_grant_for_copy( - !act->is_sub_page)) ) - { - release_grant_for_copy(td, trans_gref, readonly); -- fixup_status_for_copy_pin(act, status); -+ fixup_status_for_copy_pin(rd, act, status); - rcu_unlock_domain(td); - active_entry_release(act); - grant_read_unlock(rgt); -@@ -2553,7 +2558,7 @@ acquire_grant_for_copy( - else if ( !old_pin || - (!readonly && !(old_pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask))) ) - { -- if ( (rc = _set_status(shah, status, rgt->gt_version, act, -+ if ( (rc = _set_status(shah, status, rd, rgt->gt_version, act, - readonly, 0, ldom)) != GNTST_okay ) - goto unlock_out; - -@@ -2641,10 +2646,10 @@ acquire_grant_for_copy( - unlock_out_clear: - if ( !(readonly) && - !(act->pin & (GNTPIN_hstw_mask | GNTPIN_devw_mask)) ) -- gnttab_clear_flag(_GTF_writing, status); -+ gnttab_clear_flag(rd, _GTF_writing, status); - - if ( !act->pin ) -- gnttab_clear_flag(_GTF_reading, status); -+ gnttab_clear_flag(rd, _GTF_reading, status); - - unlock_out: - active_entry_release(act); -@@ -3712,11 +3717,11 @@ gnttab_release_mappings( - } - - if ( (act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)) == 0 ) -- gnttab_clear_flag(_GTF_writing, status); -+ gnttab_clear_flag(rd, _GTF_writing, status); - } - - if ( act->pin == 0 ) -- gnttab_clear_flag(_GTF_reading, status); -+ gnttab_clear_flag(rd, _GTF_reading, status); - - active_entry_release(act); - grant_read_unlock(rgt); -diff --git a/xen/include/asm-arm/grant_table.h b/xen/include/asm-arm/grant_table.h -index 24958e4670..8ae8ba47a1 100644 ---- a/xen/include/asm-arm/grant_table.h -+++ b/xen/include/asm-arm/grant_table.h -@@ -14,7 +14,7 @@ struct grant_table_arch { - gfn_t *status_gfn; - }; - --void gnttab_clear_flag(unsigned long nr, uint16_t *addr); -+void gnttab_clear_flag(struct domain *d, unsigned long nr, uint16_t *addr); - int create_grant_host_mapping(unsigned long gpaddr, mfn_t mfn, - unsigned int flags, unsigned int cache_flags); - #define gnttab_host_mapping_get_page_type(ro, ld, rd) (0) -diff --git a/xen/include/asm-x86/grant_table.h b/xen/include/asm-x86/grant_table.h -index e42030936b..2a54fc8e1d 100644 ---- a/xen/include/asm-x86/grant_table.h -+++ b/xen/include/asm-x86/grant_table.h -@@ -82,7 +82,8 @@ static inline unsigned int gnttab_dom0_max(void) - - #define gnttab_mark_dirty(d, f) paging_mark_dirty((d), f) - --static inline void gnttab_clear_flag(unsigned int nr, uint16_t *st) -+static inline void gnttab_clear_flag(struct domain *d, unsigned int nr, -+ uint16_t *st) - { - /* - * Note that this cannot be clear_bit(), as the access must be --- -2.17.1 - diff --git a/xsa295-4.11-19.patch b/xsa295-4.11-19.patch deleted file mode 100644 index 69e18d4..0000000 --- a/xsa295-4.11-19.patch +++ /dev/null @@ -1,84 +0,0 @@ -From 2430cd0dfc21d595c2f5d9b98f41531ee0ca8f53 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:29 +0100 -Subject: [PATCH v2 4.11 19/20] xen/arm: Add performance counters in guest - atomic helpers - -Add performance counters in guest atomic helpers to be able to detect -whether a guest is often paused during the operations. - -This is part of XSA-295. - -Signed-off-by: Julien Grall -Acked-by: Stefano Stabellini ---- - xen/include/asm-arm/guest_atomics.h | 12 ++++++++++++ - xen/include/asm-arm/perfc_defn.h | 3 +++ - 2 files changed, 15 insertions(+) - -diff --git a/xen/include/asm-arm/guest_atomics.h b/xen/include/asm-arm/guest_atomics.h -index 61925d313d..698508bf87 100644 ---- a/xen/include/asm-arm/guest_atomics.h -+++ b/xen/include/asm-arm/guest_atomics.h -@@ -24,9 +24,13 @@ DECLARE_PER_CPU(unsigned int, guest_safe_atomic_max); - #define guest_bitop(name) \ - static inline void guest_##name(struct domain *d, int nr, volatile void *p) \ - { \ -+ perfc_incr(atomics_guest); \ -+ \ - if ( name##_timeout(nr, p, this_cpu(guest_safe_atomic_max)) ) \ - return; \ - \ -+ perfc_incr(atomics_guest_paused); \ -+ \ - domain_pause_nosync(d); \ - name(nr, p); \ - domain_unpause(d); \ -@@ -38,11 +42,15 @@ static inline int guest_##name(struct domain *d, int nr, volatile void *p) \ - bool succeed; \ - int oldbit; \ - \ -+ perfc_incr(atomics_guest); \ -+ \ - succeed = name##_timeout(nr, p, &oldbit, \ - this_cpu(guest_safe_atomic_max)); \ - if ( succeed ) \ - return oldbit; \ - \ -+ perfc_incr(atomics_guest_paused); \ -+ \ - domain_pause_nosync(d); \ - oldbit = name(nr, p); \ - domain_unpause(d); \ -@@ -73,10 +81,14 @@ static inline unsigned long __guest_cmpxchg(struct domain *d, - { - unsigned long oldval = old; - -+ perfc_incr(atomics_guest); -+ - if ( __cmpxchg_mb_timeout(ptr, &oldval, new, size, - this_cpu(guest_safe_atomic_max)) ) - return oldval; - -+ perfc_incr(atomics_guest_paused); -+ - domain_pause_nosync(d); - oldval = __cmpxchg_mb(ptr, old, new, size); - domain_unpause(d); -diff --git a/xen/include/asm-arm/perfc_defn.h b/xen/include/asm-arm/perfc_defn.h -index 8922e9525a..6a83185163 100644 ---- a/xen/include/asm-arm/perfc_defn.h -+++ b/xen/include/asm-arm/perfc_defn.h -@@ -73,6 +73,9 @@ PERFCOUNTER(phys_timer_irqs, "Physical timer interrupts") - PERFCOUNTER(virt_timer_irqs, "Virtual timer interrupts") - PERFCOUNTER(maintenance_irqs, "Maintenance interrupts") - -+PERFCOUNTER(atomics_guest, "atomics: guest access") -+PERFCOUNTER(atomics_guest_paused, "atomics: guest paused") -+ - /*#endif*/ /* __XEN_PERFC_DEFN_H__ */ - - /* --- -2.17.1 - diff --git a/xsa295-4.11-20.patch b/xsa295-4.11-20.patch deleted file mode 100644 index 66f83d9..0000000 --- a/xsa295-4.11-20.patch +++ /dev/null @@ -1,199 +0,0 @@ -From 9294c354e1830be750177ed4e5f3f693aba08de3 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:30 +0100 -Subject: [PATCH v2 4.11 20/20] xen/arm: grant-table: Protect gnttab_clear_flag - against guest misbehavior - -The function gnttab_clear_flag is used to clear the access flags. On -Arm, it is implemented using a loop and guest_cmpxchg. - -It is possible that guest_cmpxchg will always return a different value -than old. This can happen if the guest updated the memory before Xen has -time to do the exchange. Because of that, there are no way for to -promise the loop will end. - -It is possible to make the current code safe by re-using the same -principle as applied on the guest atomic helper. However this patch -takes a different approach that should lead to more efficient code in -the default case. - -A new helper is introduced to clear a set of bits on a 16-bits word. -This should avoid a an extra loop to check cmpxchg succeeded. - -Note that a mask is used instead of a bit, so the helper can be re-used -later on for clearing multiple flags at the same time. - -This is part of XSA-295. - -Reported-by: Andrew Cooper -Signed-off-by: Julien Grall -Signed-off-by: Stefano Stabellini -Reviewed-by: Stefano Stabellini ---- - xen/arch/arm/arm32/lib/bitops.c | 35 +++++++++++++++++++++++++++++ - xen/arch/arm/arm64/lib/bitops.c | 33 +++++++++++++++++++++++++++ - xen/arch/arm/mm.c | 10 +-------- - xen/include/asm-arm/bitops.h | 4 ++++ - xen/include/asm-arm/guest_atomics.h | 13 +++++++++++ - 5 files changed, 86 insertions(+), 9 deletions(-) - -diff --git a/xen/arch/arm/arm32/lib/bitops.c b/xen/arch/arm/arm32/lib/bitops.c -index 08750314fc..3dca769bf0 100644 ---- a/xen/arch/arm/arm32/lib/bitops.c -+++ b/xen/arch/arm/arm32/lib/bitops.c -@@ -126,6 +126,41 @@ testop(test_and_change_bit, eor) - testop(test_and_clear_bit, bic) - testop(test_and_set_bit, orr) - -+static always_inline bool int_clear_mask16(uint16_t mask, volatile uint16_t *p, -+ bool timeout, unsigned int max_try) -+{ -+ unsigned long res, tmp; -+ -+ prefetchw((const uint16_t *)p); -+ -+ do -+ { -+ asm volatile ("// int_clear_mask16\n" -+ " ldrexh %2, %1\n" -+ " bic %2, %2, %3\n" -+ " strexh %0, %2, %1\n" -+ : "=&r" (res), "+Qo" (*p), "=&r" (tmp) -+ : "r" (mask)); -+ -+ if ( !res ) -+ break; -+ } while ( !timeout || ((--max_try) > 0) ); -+ -+ return !res; -+} -+ -+void clear_mask16(uint16_t mask, volatile void *p) -+{ -+ if ( !int_clear_mask16(mask, p, false, 0) ) -+ ASSERT_UNREACHABLE(); -+} -+ -+bool clear_mask16_timeout(uint16_t mask, volatile void *p, -+ unsigned int max_try) -+{ -+ return int_clear_mask16(mask, p, true, max_try); -+} -+ - /* - * Local variables: - * mode: C -diff --git a/xen/arch/arm/arm64/lib/bitops.c b/xen/arch/arm/arm64/lib/bitops.c -index 78bf4ed8c5..27688e5418 100644 ---- a/xen/arch/arm/arm64/lib/bitops.c -+++ b/xen/arch/arm/arm64/lib/bitops.c -@@ -118,6 +118,39 @@ testop(test_and_change_bit, eor) - testop(test_and_clear_bit, bic) - testop(test_and_set_bit, orr) - -+static always_inline bool int_clear_mask16(uint16_t mask, volatile uint16_t *p, -+ bool timeout, unsigned int max_try) -+{ -+ unsigned long res, tmp; -+ -+ do -+ { -+ asm volatile ("// int_clear_mask16\n" -+ " ldxrh %w2, %1\n" -+ " bic %w2, %w2, %w3\n" -+ " stxrh %w0, %w2, %1\n" -+ : "=&r" (res), "+Q" (*p), "=&r" (tmp) -+ : "r" (mask)); -+ -+ if ( !res ) -+ break; -+ } while ( !timeout || ((--max_try) > 0) ); -+ -+ return !res; -+} -+ -+void clear_mask16(uint16_t mask, volatile void *p) -+{ -+ if ( !int_clear_mask16(mask, p, false, 0) ) -+ ASSERT_UNREACHABLE(); -+} -+ -+bool clear_mask16_timeout(uint16_t mask, volatile void *p, -+ unsigned int max_try) -+{ -+ return int_clear_mask16(mask, p, true, max_try); -+} -+ - /* - * Local variables: - * mode: C -diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c -index 6b7e6b675b..2b4d5ed091 100644 ---- a/xen/arch/arm/mm.c -+++ b/xen/arch/arm/mm.c -@@ -1399,15 +1399,7 @@ void put_page_type(struct page_info *page) - - void gnttab_clear_flag(struct domain *d, unsigned long nr, uint16_t *addr) - { -- /* -- * Note that this cannot be clear_bit(), as the access must be -- * confined to the specified 2 bytes. -- */ -- uint16_t mask = ~(1 << nr), old; -- -- do { -- old = *addr; -- } while (guest_cmpxchg(d, addr, old, old & mask) != old); -+ guest_clear_mask16(d, BIT(nr), addr); - } - - void gnttab_mark_dirty(struct domain *d, mfn_t mfn) -diff --git a/xen/include/asm-arm/bitops.h b/xen/include/asm-arm/bitops.h -index f6782b33be..f989bc726c 100644 ---- a/xen/include/asm-arm/bitops.h -+++ b/xen/include/asm-arm/bitops.h -@@ -53,6 +53,8 @@ int test_and_set_bit(int nr, volatile void *p); - int test_and_clear_bit(int nr, volatile void *p); - int test_and_change_bit(int nr, volatile void *p); - -+void clear_mask16(uint16_t mask, volatile void *p); -+ - /* - * The helpers below may fail to update the memory if the action takes - * too long. -@@ -71,6 +73,8 @@ bool test_and_clear_bit_timeout(int nr, volatile void *p, - int *oldbit, unsigned int max_try); - bool test_and_change_bit_timeout(int nr, volatile void *p, - int *oldbit, unsigned int max_try); -+bool clear_mask16_timeout(uint16_t mask, volatile void *p, -+ unsigned int max_try); - - /** - * __test_and_set_bit - Set a bit and return its old value -diff --git a/xen/include/asm-arm/guest_atomics.h b/xen/include/asm-arm/guest_atomics.h -index 698508bf87..af27cc627b 100644 ---- a/xen/include/asm-arm/guest_atomics.h -+++ b/xen/include/asm-arm/guest_atomics.h -@@ -73,6 +73,19 @@ guest_testop(test_and_change_bit) - - #undef guest_testop - -+static inline void guest_clear_mask16(struct domain *d, uint16_t mask, -+ volatile uint16_t *p) -+{ -+ perfc_incr(atomics_guest); -+ -+ if ( clear_mask16_timeout(mask, p, this_cpu(guest_safe_atomic_max)) ) -+ return; -+ -+ domain_pause_nosync(d); -+ clear_mask16(mask, p); -+ domain_unpause(d); -+} -+ - static inline unsigned long __guest_cmpxchg(struct domain *d, - volatile void *ptr, - unsigned long old, --- -2.17.1 - diff --git a/xsa297-4.11-1.patch b/xsa297-4.11-1.patch deleted file mode 100644 index 5dd5035..0000000 --- a/xsa297-4.11-1.patch +++ /dev/null @@ -1,163 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Reposition the XPTI command line parsing logic - -It has ended up in the middle of the mitigation calculation logic. Move it to -be beside the other command line parsing. - -No functional change. - -Signed-off-by: Andrew Cooper -Acked-by: Jan Beulich - -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 8fa6c10..949bbda 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -223,6 +223,73 @@ static int __init parse_spec_ctrl(const char *s) - } - custom_param("spec-ctrl", parse_spec_ctrl); - -+int8_t __read_mostly opt_xpti_hwdom = -1; -+int8_t __read_mostly opt_xpti_domu = -1; -+ -+static __init void xpti_init_default(uint64_t caps) -+{ -+ if ( boot_cpu_data.x86_vendor == X86_VENDOR_AMD ) -+ caps = ARCH_CAPS_RDCL_NO; -+ -+ if ( caps & ARCH_CAPS_RDCL_NO ) -+ { -+ if ( opt_xpti_hwdom < 0 ) -+ opt_xpti_hwdom = 0; -+ if ( opt_xpti_domu < 0 ) -+ opt_xpti_domu = 0; -+ } -+ else -+ { -+ if ( opt_xpti_hwdom < 0 ) -+ opt_xpti_hwdom = 1; -+ if ( opt_xpti_domu < 0 ) -+ opt_xpti_domu = 1; -+ } -+} -+ -+static __init int parse_xpti(const char *s) -+{ -+ const char *ss; -+ int val, rc = 0; -+ -+ /* Interpret 'xpti' alone in its positive boolean form. */ -+ if ( *s == '\0' ) -+ opt_xpti_hwdom = opt_xpti_domu = 1; -+ -+ do { -+ ss = strchr(s, ','); -+ if ( !ss ) -+ ss = strchr(s, '\0'); -+ -+ switch ( parse_bool(s, ss) ) -+ { -+ case 0: -+ opt_xpti_hwdom = opt_xpti_domu = 0; -+ break; -+ -+ case 1: -+ opt_xpti_hwdom = opt_xpti_domu = 1; -+ break; -+ -+ default: -+ if ( !strcmp(s, "default") ) -+ opt_xpti_hwdom = opt_xpti_domu = -1; -+ else if ( (val = parse_boolean("dom0", s, ss)) >= 0 ) -+ opt_xpti_hwdom = val; -+ else if ( (val = parse_boolean("domu", s, ss)) >= 0 ) -+ opt_xpti_domu = val; -+ else if ( *s ) -+ rc = -EINVAL; -+ break; -+ } -+ -+ s = ss + 1; -+ } while ( *ss ); -+ -+ return rc; -+} -+custom_param("xpti", parse_xpti); -+ - int8_t __read_mostly opt_pv_l1tf_hwdom = -1; - int8_t __read_mostly opt_pv_l1tf_domu = -1; - -@@ -676,73 +743,6 @@ static __init void l1tf_calculations(uint64_t caps) - : (3ul << (paddr_bits - 2)))); - } - --int8_t __read_mostly opt_xpti_hwdom = -1; --int8_t __read_mostly opt_xpti_domu = -1; -- --static __init void xpti_init_default(uint64_t caps) --{ -- if ( boot_cpu_data.x86_vendor == X86_VENDOR_AMD ) -- caps = ARCH_CAPS_RDCL_NO; -- -- if ( caps & ARCH_CAPS_RDCL_NO ) -- { -- if ( opt_xpti_hwdom < 0 ) -- opt_xpti_hwdom = 0; -- if ( opt_xpti_domu < 0 ) -- opt_xpti_domu = 0; -- } -- else -- { -- if ( opt_xpti_hwdom < 0 ) -- opt_xpti_hwdom = 1; -- if ( opt_xpti_domu < 0 ) -- opt_xpti_domu = 1; -- } --} -- --static __init int parse_xpti(const char *s) --{ -- const char *ss; -- int val, rc = 0; -- -- /* Interpret 'xpti' alone in its positive boolean form. */ -- if ( *s == '\0' ) -- opt_xpti_hwdom = opt_xpti_domu = 1; -- -- do { -- ss = strchr(s, ','); -- if ( !ss ) -- ss = strchr(s, '\0'); -- -- switch ( parse_bool(s, ss) ) -- { -- case 0: -- opt_xpti_hwdom = opt_xpti_domu = 0; -- break; -- -- case 1: -- opt_xpti_hwdom = opt_xpti_domu = 1; -- break; -- -- default: -- if ( !strcmp(s, "default") ) -- opt_xpti_hwdom = opt_xpti_domu = -1; -- else if ( (val = parse_boolean("dom0", s, ss)) >= 0 ) -- opt_xpti_hwdom = val; -- else if ( (val = parse_boolean("domu", s, ss)) >= 0 ) -- opt_xpti_domu = val; -- else if ( *s ) -- rc = -EINVAL; -- break; -- } -- -- s = ss + 1; -- } while ( *ss ); -- -- return rc; --} --custom_param("xpti", parse_xpti); -- - void __init init_speculation_mitigations(void) - { - enum ind_thunk thunk = THUNK_DEFAULT; diff --git a/xsa297-4.11-2.patch b/xsa297-4.11-2.patch deleted file mode 100644 index 7c6c006..0000000 --- a/xsa297-4.11-2.patch +++ /dev/null @@ -1,54 +0,0 @@ -From: Andrew Cooper -Subject: x86/msr: Definitions for MSR_INTEL_CORE_THREAD_COUNT - -This is a model specific register which details the current configuration -cores and threads in the package. Because of how Hyperthread and Core -configuration works works in firmware, the MSR it is de-facto constant and -will remain unchanged until the next system reset. - -It is a read only MSR (so unilaterally reject writes), but for now retain its -leaky-on-read properties. Further CPUID/MSR work is required before we can -start virtualising a consistent topology to the guest, and retaining the old -behaviour is the safest course of action. - -Signed-off-by: Andrew Cooper -Acked-by: Jan Beulich - -diff --git a/xen/arch/x86/msr.c b/xen/arch/x86/msr.c -index b49fbd8..153f36b 100644 ---- a/xen/arch/x86/msr.c -+++ b/xen/arch/x86/msr.c -@@ -180,6 +180,10 @@ int guest_rdmsr(const struct vcpu *v, uint32_t msr, uint64_t *val) - _MSR_MISC_FEATURES_CPUID_FAULTING; - break; - -+ /* -+ * TODO: Implement when we have better topology representation. -+ case MSR_INTEL_CORE_THREAD_COUNT: -+ */ - default: - return X86EMUL_UNHANDLEABLE; - } -@@ -202,6 +206,7 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) - { - uint64_t rsvd; - -+ case MSR_INTEL_CORE_THREAD_COUNT: - case MSR_INTEL_PLATFORM_INFO: - case MSR_ARCH_CAPABILITIES: - /* Read-only */ -diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h -index 7588fc1..7cddfca 100644 ---- a/xen/include/asm-x86/msr-index.h -+++ b/xen/include/asm-x86/msr-index.h -@@ -34,6 +34,10 @@ - #define EFER_KNOWN_MASK (EFER_SCE | EFER_LME | EFER_LMA | EFER_NX | \ - EFER_SVME | EFER_LMSLE | EFER_FFXSE) - -+#define MSR_INTEL_CORE_THREAD_COUNT 0x00000035 -+#define MSR_CTC_THREAD_MASK 0x0000ffff -+#define MSR_CTC_CORE_MASK 0xffff0000 -+ - /* Speculation Controls. */ - #define MSR_SPEC_CTRL 0x00000048 - #define SPEC_CTRL_IBRS (_AC(1, ULL) << 0) diff --git a/xsa297-4.11-3.patch b/xsa297-4.11-3.patch deleted file mode 100644 index a6e59ef..0000000 --- a/xsa297-4.11-3.patch +++ /dev/null @@ -1,109 +0,0 @@ -From: Andrew Cooper -Subject: x86/boot: Detect the firmware SMT setting correctly on Intel hardware - -While boot_cpu_data.x86_num_siblings is an accurate value to use on AMD -hardware, it isn't on Intel when the user has disabled Hyperthreading in the -firmware. As a result, a user which has chosen to disable HT still gets -nagged on L1TF-vulnerable hardware when they haven't chosen an explicit -smt= setting. - -Make use of the largely-undocumented MSR_INTEL_CORE_THREAD_COUNT which in -practice exists since Nehalem, when booting on real hardware. Fall back to -using the ACPI table APIC IDs. - -While adjusting this logic, fix a latent bug in amd_get_topology(). The -thread count field in CPUID.0x8000001e.ebx is documented as 8 bits wide, -rather than 2 bits wide. - -Signed-off-by: Andrew Cooper -Acked-by: Jan Beulich - -diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c -index 76078b5..894b892 100644 ---- a/xen/arch/x86/cpu/amd.c -+++ b/xen/arch/x86/cpu/amd.c -@@ -505,7 +505,7 @@ static void amd_get_topology(struct cpuinfo_x86 *c) - u32 eax, ebx, ecx, edx; - - cpuid(0x8000001e, &eax, &ebx, &ecx, &edx); -- c->x86_num_siblings = ((ebx >> 8) & 0x3) + 1; -+ c->x86_num_siblings = ((ebx >> 8) & 0xff) + 1; - - if (c->x86 < 0x17) - c->compute_unit_id = ebx & 0xFF; -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 949bbda..ac1be4a 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -417,6 +417,45 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - opt_pv_l1tf_domu ? "enabled" : "disabled"); - } - -+static bool __init check_smt_enabled(void) -+{ -+ uint64_t val; -+ unsigned int cpu; -+ -+ /* -+ * x86_num_siblings defaults to 1 in the absence of other information, and -+ * is adjusted based on other topology information found in CPUID leaves. -+ * -+ * On AMD hardware, it will be the current SMT configuration. On Intel -+ * hardware, it will represent the maximum capability, rather than the -+ * current configuration. -+ */ -+ if ( boot_cpu_data.x86_num_siblings < 2 ) -+ return false; -+ -+ /* -+ * Intel Nehalem and later hardware does have an MSR which reports the -+ * current count of cores/threads in the package. -+ * -+ * At the time of writing, it is almost completely undocumented, so isn't -+ * virtualised reliably. -+ */ -+ if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && !cpu_has_hypervisor && -+ !rdmsr_safe(MSR_INTEL_CORE_THREAD_COUNT, val) ) -+ return (MASK_EXTR(val, MSR_CTC_CORE_MASK) != -+ MASK_EXTR(val, MSR_CTC_THREAD_MASK)); -+ -+ /* -+ * Search over the CPUs reported in the ACPI tables. Any whose APIC ID -+ * has a non-zero thread id component indicates that SMT is active. -+ */ -+ for_each_present_cpu ( cpu ) -+ if ( x86_cpu_to_apicid[cpu] & (boot_cpu_data.x86_num_siblings - 1) ) -+ return true; -+ -+ return false; -+} -+ - /* Calculate whether Retpoline is known-safe on this CPU. */ - static bool __init retpoline_safe(uint64_t caps) - { -@@ -746,12 +785,14 @@ static __init void l1tf_calculations(uint64_t caps) - void __init init_speculation_mitigations(void) - { - enum ind_thunk thunk = THUNK_DEFAULT; -- bool use_spec_ctrl = false, ibrs = false; -+ bool use_spec_ctrl = false, ibrs = false, hw_smt_enabled; - uint64_t caps = 0; - - if ( boot_cpu_has(X86_FEATURE_ARCH_CAPS) ) - rdmsrl(MSR_ARCH_CAPABILITIES, caps); - -+ hw_smt_enabled = check_smt_enabled(); -+ - /* - * Has the user specified any custom BTI mitigations? If so, follow their - * instructions exactly and disable all heuristics. -@@ -927,8 +968,7 @@ void __init init_speculation_mitigations(void) - * However, if we are on affected hardware, with HT enabled, and the user - * hasn't explicitly chosen whether to use HT or not, nag them to do so. - */ -- if ( opt_smt == -1 && cpu_has_bug_l1tf && !pv_shim && -- boot_cpu_data.x86_num_siblings > 1 ) -+ if ( opt_smt == -1 && cpu_has_bug_l1tf && !pv_shim && hw_smt_enabled ) - warning_add( - "Booted on L1TF-vulnerable hardware with SMT/Hyperthreading\n" - "enabled. Please assess your configuration and choose an\n" diff --git a/xsa297-4.11-4.patch b/xsa297-4.11-4.patch deleted file mode 100644 index e6acc9e..0000000 --- a/xsa297-4.11-4.patch +++ /dev/null @@ -1,55 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Misc non-functional cleanup - - * Identify BTI in the spec_ctrl_{enter,exit}_idle() comments, as other - mitigations will shortly appear. - * Use alternative_input() and cover the lack of memory cobber with a further - barrier. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h -index c846354..4983071 100644 ---- a/xen/include/asm-x86/spec_ctrl.h -+++ b/xen/include/asm-x86/spec_ctrl.h -@@ -61,6 +61,8 @@ static always_inline void spec_ctrl_enter_idle(struct cpu_info *info) - uint32_t val = 0; - - /* -+ * Branch Target Injection: -+ * - * Latch the new shadow value, then enable shadowing, then update the MSR. - * There are no SMP issues here; only local processor ordering concerns. - */ -@@ -68,8 +70,9 @@ static always_inline void spec_ctrl_enter_idle(struct cpu_info *info) - barrier(); - info->spec_ctrl_flags |= SCF_use_shadow; - barrier(); -- asm volatile ( ALTERNATIVE(ASM_NOP3, "wrmsr", X86_FEATURE_SC_MSR_IDLE) -- :: "a" (val), "c" (MSR_SPEC_CTRL), "d" (0) : "memory" ); -+ alternative_input(ASM_NOP3, "wrmsr", X86_FEATURE_SC_MSR_IDLE, -+ "a" (val), "c" (MSR_SPEC_CTRL), "d" (0)); -+ barrier(); - } - - /* WARNING! `ret`, `call *`, `jmp *` not safe before this call. */ -@@ -78,13 +81,16 @@ static always_inline void spec_ctrl_exit_idle(struct cpu_info *info) - uint32_t val = info->xen_spec_ctrl; - - /* -+ * Branch Target Injection: -+ * - * Disable shadowing before updating the MSR. There are no SMP issues - * here; only local processor ordering concerns. - */ - info->spec_ctrl_flags &= ~SCF_use_shadow; - barrier(); -- asm volatile ( ALTERNATIVE(ASM_NOP3, "wrmsr", X86_FEATURE_SC_MSR_IDLE) -- :: "a" (val), "c" (MSR_SPEC_CTRL), "d" (0) : "memory" ); -+ alternative_input(ASM_NOP3, "wrmsr", X86_FEATURE_SC_MSR_IDLE, -+ "a" (val), "c" (MSR_SPEC_CTRL), "d" (0)); -+ barrier(); - } - - #endif /* !__X86_SPEC_CTRL_H__ */ diff --git a/xsa297-4.11-5.patch b/xsa297-4.11-5.patch deleted file mode 100644 index f03c3d8..0000000 --- a/xsa297-4.11-5.patch +++ /dev/null @@ -1,141 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: CPUID/MSR definitions for Microarchitectural Data - Sampling - -The MD_CLEAR feature can be automatically offered to guests. No -infrastructure is needed in Xen to support the guest making use of it. - -This is part of XSA-297, CVE-2018-12126, CVE-2018-12127, CVE-2018-12130, CVE-2019-11091. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/docs/misc/xen-command-line.markdown b/docs/misc/xen-command-line.markdown -index 8e24380..8260dfb 100644 ---- a/docs/misc/xen-command-line.markdown -+++ b/docs/misc/xen-command-line.markdown -@@ -489,7 +489,7 @@ accounting for hardware capabilities as enumerated via CPUID. - - Currently accepted: - --The Speculation Control hardware features `ibrsb`, `stibp`, `ibpb`, -+The Speculation Control hardware features `md-clear`, `ibrsb`, `stibp`, `ibpb`, - `l1d-flush` and `ssbd` are used by default if available and applicable. They can - be ignored, e.g. `no-ibrsb`, at which point Xen won't use them itself, and - won't offer them to guests. -diff --git a/tools/libxl/libxl_cpuid.c b/tools/libxl/libxl_cpuid.c -index 52e16c2..5a1702d 100644 ---- a/tools/libxl/libxl_cpuid.c -+++ b/tools/libxl/libxl_cpuid.c -@@ -202,6 +202,7 @@ int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str) - - {"avx512-4vnniw",0x00000007, 0, CPUID_REG_EDX, 2, 1}, - {"avx512-4fmaps",0x00000007, 0, CPUID_REG_EDX, 3, 1}, -+ {"md-clear", 0x00000007, 0, CPUID_REG_EDX, 10, 1}, - {"ibrsb", 0x00000007, 0, CPUID_REG_EDX, 26, 1}, - {"stibp", 0x00000007, 0, CPUID_REG_EDX, 27, 1}, - {"l1d-flush", 0x00000007, 0, CPUID_REG_EDX, 28, 1}, -diff --git a/tools/misc/xen-cpuid.c b/tools/misc/xen-cpuid.c -index 0ac903a..16697c4 100644 ---- a/tools/misc/xen-cpuid.c -+++ b/tools/misc/xen-cpuid.c -@@ -142,6 +142,7 @@ static const char *str_7d0[32] = - { - [ 2] = "avx512_4vnniw", [ 3] = "avx512_4fmaps", - -+ [10] = "md-clear", - /* 12 */ [13] = "tsx-force-abort", - - [26] = "ibrsb", [27] = "stibp", -diff --git a/xen/arch/x86/cpuid.c b/xen/arch/x86/cpuid.c -index 5cc89e2..497bd2a 100644 ---- a/xen/arch/x86/cpuid.c -+++ b/xen/arch/x86/cpuid.c -@@ -28,7 +28,12 @@ static int __init parse_xen_cpuid(const char *s) - if ( !ss ) - ss = strchr(s, '\0'); - -- if ( (val = parse_boolean("ibpb", s, ss)) >= 0 ) -+ if ( (val = parse_boolean("md-clear", s, ss)) >= 0 ) -+ { -+ if ( !val ) -+ setup_clear_cpu_cap(X86_FEATURE_MD_CLEAR); -+ } -+ else if ( (val = parse_boolean("ibpb", s, ss)) >= 0 ) - { - if ( !val ) - setup_clear_cpu_cap(X86_FEATURE_IBPB); -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index ac1be4a..fdd90a8 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -347,17 +347,19 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - printk("Speculative mitigation facilities:\n"); - - /* Hardware features which pertain to speculative mitigations. */ -- 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", - (_7d0 & cpufeat_mask(X86_FEATURE_IBRSB)) ? " IBRS/IBPB" : "", - (_7d0 & cpufeat_mask(X86_FEATURE_STIBP)) ? " STIBP" : "", - (_7d0 & cpufeat_mask(X86_FEATURE_L1D_FLUSH)) ? " L1D_FLUSH" : "", - (_7d0 & cpufeat_mask(X86_FEATURE_SSBD)) ? " SSBD" : "", -+ (_7d0 & cpufeat_mask(X86_FEATURE_MD_CLEAR)) ? " MD_CLEAR" : "", - (e8b & cpufeat_mask(X86_FEATURE_IBPB)) ? " IBPB" : "", - (caps & ARCH_CAPS_IBRS_ALL) ? " IBRS_ALL" : "", - (caps & ARCH_CAPS_RDCL_NO) ? " RDCL_NO" : "", - (caps & ARCH_CAPS_RSBA) ? " RSBA" : "", - (caps & ARCH_CAPS_SKIP_L1DFL) ? " SKIP_L1DFL": "", -- (caps & ARCH_CAPS_SSB_NO) ? " SSB_NO" : ""); -+ (caps & ARCH_CAPS_SSB_NO) ? " SSB_NO" : "", -+ (caps & ARCH_CAPS_MDS_NO) ? " MDS_NO" : ""); - - /* Compiled-in support which pertains to mitigations. */ - if ( IS_ENABLED(CONFIG_INDIRECT_THUNK) || IS_ENABLED(CONFIG_SHADOW_PAGING) ) -@@ -394,19 +396,21 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - * Alternatives blocks for protecting against and/or virtualising - * mitigation support for guests. - */ -- printk(" Support for VMs: PV:%s%s%s%s, HVM:%s%s%s%s\n", -+ printk(" Support for VMs: PV:%s%s%s%s%s, HVM:%s%s%s%s%s\n", - (boot_cpu_has(X86_FEATURE_SC_MSR_PV) || - boot_cpu_has(X86_FEATURE_SC_RSB_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_SC_MSR_HVM) || - boot_cpu_has(X86_FEATURE_SC_RSB_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" : ""); -+ opt_eager_fpu ? " EAGER_FPU" : "", -+ boot_cpu_has(X86_FEATURE_MD_CLEAR) ? " MD_CLEAR" : ""); - - printk(" XPTI (64-bit PV only): Dom0 %s, DomU %s\n", - opt_xpti_hwdom ? "enabled" : "disabled", -diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h -index 7cddfca..b8151d2 100644 ---- a/xen/include/asm-x86/msr-index.h -+++ b/xen/include/asm-x86/msr-index.h -@@ -53,6 +53,7 @@ - #define ARCH_CAPS_RSBA (_AC(1, ULL) << 2) - #define ARCH_CAPS_SKIP_L1DFL (_AC(1, ULL) << 3) - #define ARCH_CAPS_SSB_NO (_AC(1, ULL) << 4) -+#define ARCH_CAPS_MDS_NO (_AC(1, ULL) << 5) - - #define MSR_FLUSH_CMD 0x0000010b - #define FLUSH_CMD_L1D (_AC(1, ULL) << 0) -diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h -index aa2656d..a14d8a7 100644 ---- a/xen/include/public/arch-x86/cpufeatureset.h -+++ b/xen/include/public/arch-x86/cpufeatureset.h -@@ -242,6 +242,7 @@ XEN_CPUFEATURE(IBPB, 8*32+12) /*A IBPB support only (no IBRS, used by - /* Intel-defined CPU features, CPUID level 0x00000007:0.edx, word 9 */ - XEN_CPUFEATURE(AVX512_4VNNIW, 9*32+ 2) /*A AVX512 Neural Network Instructions */ - XEN_CPUFEATURE(AVX512_4FMAPS, 9*32+ 3) /*A AVX512 Multiply Accumulation Single Precision */ -+XEN_CPUFEATURE(MD_CLEAR, 9*32+10) /*A VERW clears microarchitectural buffers */ - XEN_CPUFEATURE(TSX_FORCE_ABORT, 9*32+13) /* MSR_TSX_FORCE_ABORT.RTM_ABORT */ - XEN_CPUFEATURE(IBRSB, 9*32+26) /*A IBRS and IBPB support (used by Intel) */ - XEN_CPUFEATURE(STIBP, 9*32+27) /*A STIBP */ diff --git a/xsa297-4.11-6.patch b/xsa297-4.11-6.patch deleted file mode 100644 index 5d153a9..0000000 --- a/xsa297-4.11-6.patch +++ /dev/null @@ -1,134 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Infrastructure to use VERW to flush pipeline buffers - -Three synthetic features are introduced, as we need individual control of -each, depending on circumstances. A later change will enable them at -appropriate points. - -The verw_sel field doesn't strictly need to live in struct cpu_info. It lives -there because there is a convenient hole it can fill, and it reduces the -complexity of the SPEC_CTRL_EXIT_TO_{PV,HVM} assembly by avoiding the need for -any temporary stack maintenance. - -This is part of XSA-297, CVE-2018-12126, CVE-2018-12127, CVE-2018-12130, CVE-2019-11091. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/x86_64/asm-offsets.c b/xen/arch/x86/x86_64/asm-offsets.c -index 5957c76..97cff49 100644 ---- a/xen/arch/x86/x86_64/asm-offsets.c -+++ b/xen/arch/x86/x86_64/asm-offsets.c -@@ -129,6 +129,7 @@ void __dummy__(void) - - OFFSET(CPUINFO_guest_cpu_user_regs, struct cpu_info, guest_cpu_user_regs); - OFFSET(CPUINFO_processor_id, struct cpu_info, processor_id); -+ OFFSET(CPUINFO_verw_sel, struct cpu_info, verw_sel); - OFFSET(CPUINFO_current_vcpu, struct cpu_info, current_vcpu); - OFFSET(CPUINFO_cr4, struct cpu_info, cr4); - OFFSET(CPUINFO_xen_cr3, struct cpu_info, xen_cr3); -diff --git a/xen/include/asm-x86/cpufeatures.h b/xen/include/asm-x86/cpufeatures.h -index 8e5cc53..96a5a01 100644 ---- a/xen/include/asm-x86/cpufeatures.h -+++ b/xen/include/asm-x86/cpufeatures.h -@@ -33,3 +33,6 @@ XEN_CPUFEATURE(SC_RSB_HVM, (FSCAPINTS+0)*32+19) /* RSB overwrite needed for - XEN_CPUFEATURE(NO_XPTI, (FSCAPINTS+0)*32+20) /* XPTI mitigation not in use */ - XEN_CPUFEATURE(SC_MSR_IDLE, (FSCAPINTS+0)*32+21) /* (SC_MSR_PV || SC_MSR_HVM) && default_xen_spec_ctrl */ - XEN_CPUFEATURE(XEN_LBR, (FSCAPINTS+0)*32+22) /* Xen uses MSR_DEBUGCTL.LBR */ -+XEN_CPUFEATURE(SC_VERW_PV, (FSCAPINTS+0)*32+23) /* VERW used by Xen for PV */ -+XEN_CPUFEATURE(SC_VERW_HVM, (FSCAPINTS+0)*32+24) /* VERW used by Xen for HVM */ -+XEN_CPUFEATURE(SC_VERW_IDLE, (FSCAPINTS+0)*32+25) /* VERW used by Xen for idle */ -diff --git a/xen/include/asm-x86/current.h b/xen/include/asm-x86/current.h -index 5bd64b2..f3508c3 100644 ---- a/xen/include/asm-x86/current.h -+++ b/xen/include/asm-x86/current.h -@@ -38,6 +38,7 @@ struct vcpu; - struct cpu_info { - struct cpu_user_regs guest_cpu_user_regs; - unsigned int processor_id; -+ unsigned int verw_sel; - struct vcpu *current_vcpu; - unsigned long per_cpu_offset; - unsigned long cr4; -diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h -index 4983071..333d180 100644 ---- a/xen/include/asm-x86/spec_ctrl.h -+++ b/xen/include/asm-x86/spec_ctrl.h -@@ -53,6 +53,13 @@ static inline void init_shadow_spec_ctrl_state(void) - info->shadow_spec_ctrl = 0; - info->xen_spec_ctrl = default_xen_spec_ctrl; - info->spec_ctrl_flags = default_spec_ctrl_flags; -+ -+ /* -+ * For least latency, the VERW selector should be a writeable data -+ * descriptor resident in the cache. __HYPERVISOR_DS32 shares a cache -+ * line with __HYPERVISOR_CS, so is expected to be very cache-hot. -+ */ -+ info->verw_sel = __HYPERVISOR_DS32; - } - - /* WARNING! `ret`, `call *`, `jmp *` not safe after this call. */ -@@ -73,6 +80,22 @@ static always_inline void spec_ctrl_enter_idle(struct cpu_info *info) - alternative_input(ASM_NOP3, "wrmsr", X86_FEATURE_SC_MSR_IDLE, - "a" (val), "c" (MSR_SPEC_CTRL), "d" (0)); - barrier(); -+ -+ /* -+ * Microarchitectural Store Buffer Data Sampling: -+ * -+ * On vulnerable systems, store buffer entries are statically partitioned -+ * between active threads. When entering idle, our store buffer entries -+ * are re-partitioned to allow the other threads to use them. -+ * -+ * Flush the buffers to ensure that no sensitive data of ours can be -+ * leaked by a sibling after it gets our store buffer entries. -+ * -+ * Note: VERW must be encoded with a memory operand, as it is only that -+ * form which causes a flush. -+ */ -+ alternative_input("", "verw %[sel]", X86_FEATURE_SC_VERW_IDLE, -+ [sel] "m" (info->verw_sel)); - } - - /* WARNING! `ret`, `call *`, `jmp *` not safe before this call. */ -@@ -91,6 +114,17 @@ static always_inline void spec_ctrl_exit_idle(struct cpu_info *info) - alternative_input(ASM_NOP3, "wrmsr", X86_FEATURE_SC_MSR_IDLE, - "a" (val), "c" (MSR_SPEC_CTRL), "d" (0)); - barrier(); -+ -+ /* -+ * Microarchitectural Store Buffer Data Sampling: -+ * -+ * On vulnerable systems, store buffer entries are statically partitioned -+ * between active threads. When exiting idle, the other threads store -+ * buffer entries are re-partitioned to give us some. -+ * -+ * We now have store buffer entries with stale data from sibling threads. -+ * A flush if necessary will be performed on the return to guest path. -+ */ - } - - #endif /* !__X86_SPEC_CTRL_H__ */ -diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h -index edace2a..9cc15e7 100644 ---- a/xen/include/asm-x86/spec_ctrl_asm.h -+++ b/xen/include/asm-x86/spec_ctrl_asm.h -@@ -245,12 +245,16 @@ - /* 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 -+ DO_SPEC_CTRL_EXIT_TO_GUEST, X86_FEATURE_SC_MSR_PV; \ -+ 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 -+ 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. diff --git a/xsa297-4.11-7.patch b/xsa297-4.11-7.patch deleted file mode 100644 index 940191d..0000000 --- a/xsa297-4.11-7.patch +++ /dev/null @@ -1,316 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Introduce options to control VERW flushing - -The Microarchitectural Data Sampling vulnerability is split into categories -with subtly different properties: - - MLPDS - Microarchitectural Load Port Data Sampling - MSBDS - Microarchitectural Store Buffer Data Sampling - MFBDS - Microarchitectural Fill Buffer Data Sampling - MDSUM - Microarchitectural Data Sampling Uncacheable Memory - -MDSUM is a special case of the other three, and isn't distinguished further. - -These issues pertain to three microarchitectural buffers. The Load Ports, the -Store Buffers and the Fill Buffers. Each of these structures are flushed by -the new enhanced VERW functionality, but the conditions under which flushing -is necessary vary. - -For this concise overview of the issues and default logic, the abbreviations -SP (Store Port), FB (Fill Buffer), LP (Load Port) and HT (Hyperthreading) are -used for brevity: - - * Vulnerable hardware is divided into two categories - parts which suffer - from SP only, and parts with any other combination of vulnerabilities. - - * SP only has an HT interaction when the thread goes idle, due to the static - partitioning of resources. LP and FB have HT interactions at all points, - due to the competitive sharing of resources. All issues potentially leak - data across the return-to-guest transition. - - * The microcode which implements VERW flushing also extends MSR_FLUSH_CMD, so - we don't need to do both on the HVM return-to-guest path. However, some - parts are not vulnerable to L1TF (therefore have no MSR_FLUSH_CMD), but are - vulnerable to MDS, so do require VERW on the HVM path. - -Note that we deliberately support mds=1 even without MD_CLEAR in case the -microcode has been updated but the feature bit not exposed. - -This is part of XSA-297, CVE-2018-12126, CVE-2018-12127, CVE-2018-12130, CVE-2019-11091. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/docs/misc/xen-command-line.markdown b/docs/misc/xen-command-line.markdown -index 8260dfb..8108bbf 100644 ---- a/docs/misc/xen-command-line.markdown -+++ b/docs/misc/xen-command-line.markdown -@@ -1800,7 +1800,7 @@ is being interpreted as a custom timeout in milliseconds. Zero or boolean - false disable the quirk workaround, which is also the default. - - ### spec-ctrl (x86) --> `= List of [ , xen=, {pv,hvm,msr-sc,rsb}=, -+> `= List of [ , xen=, {pv,hvm,msr-sc,rsb,md-clear}=, - > bti-thunk=retpoline|lfence|jmp, {ibrs,ibpb,ssbd,eager-fpu, - > l1d-flush}= ]` - -@@ -1824,9 +1824,10 @@ 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=` and `rsb=` 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. -+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. - - * `pv=` and `hvm=` offer control over all suboptions for PV and HVM guests - respectively. -@@ -1835,6 +1836,11 @@ protect itself, and Xen's ability to virtualise support for 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. -+* `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.* - - 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 fdd90a8..10fcd77 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -34,6 +34,8 @@ static bool __initdata opt_msr_sc_pv = true; - static bool __initdata opt_msr_sc_hvm = true; - static bool __initdata opt_rsb_pv = true; - 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; - - /* Cmdline controls for Xen's speculative settings. */ - static enum ind_thunk { -@@ -58,6 +60,9 @@ paddr_t __read_mostly l1tf_addr_mask, __read_mostly l1tf_safe_maddr; - static bool __initdata cpu_has_bug_l1tf; - static unsigned int __initdata l1d_maxphysaddr; - -+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 int __init parse_bti(const char *s) - { - const char *ss; -@@ -150,6 +155,8 @@ static int __init 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_thunk = THUNK_JMP; - opt_ibrs = 0; -@@ -172,11 +179,13 @@ static int __init parse_spec_ctrl(const char *s) - { - opt_msr_sc_pv = val; - opt_rsb_pv = val; -+ opt_md_clear_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; - } - else if ( (val = parse_boolean("msr-sc", s, ss)) >= 0 ) - { -@@ -188,6 +197,12 @@ static int __init parse_spec_ctrl(const char *s) - opt_rsb_pv = val; - opt_rsb_hvm = val; - } -+ else if ( (val = parse_boolean("md-clear", s, ss)) >= 0 || -+ (val = parse_boolean("mds", s, ss)) >= 0 ) -+ { -+ opt_md_clear_pv = val; -+ opt_md_clear_hvm = val; -+ } - - /* Xen's speculative sidechannel mitigation settings. */ - else if ( !strncmp(s, "bti-thunk=", 10) ) -@@ -373,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, Other:%s%s\n", -+ printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s, Other:%s%s%s\n", - thunk == THUNK_NONE ? "N/A" : - thunk == THUNK_RETPOLINE ? "RETPOLINE" : - thunk == THUNK_LFENCE ? "LFENCE" : -@@ -383,7 +398,8 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - !boot_cpu_has(X86_FEATURE_SSBD) ? "" : - (default_xen_spec_ctrl & SPEC_CTRL_SSBD) ? " SSBD+" : " SSBD-", - opt_ibpb ? " IBPB" : "", -- opt_l1d_flush ? " L1D_FLUSH" : ""); -+ opt_l1d_flush ? " L1D_FLUSH" : "", -+ opt_md_clear_pv || opt_md_clear_hvm ? " VERW" : ""); - - /* L1TF diagnostics, printed if vulnerable or PV shadowing is in use. */ - if ( cpu_has_bug_l1tf || opt_pv_l1tf_hwdom || opt_pv_l1tf_domu ) -@@ -786,6 +802,107 @@ static __init void l1tf_calculations(uint64_t caps) - : (3ul << (paddr_bits - 2)))); - } - -+/* Calculate whether this CPU is vulnerable to MDS. */ -+static __init void mds_calculations(uint64_t caps) -+{ -+ /* MDS 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; -+ -+ /* Any processor advertising MDS_NO should be not vulnerable to MDS. */ -+ if ( caps & ARCH_CAPS_MDS_NO ) -+ return; -+ -+ switch ( boot_cpu_data.x86_model ) -+ { -+ /* -+ * Core processors since at least Nehalem are vulnerable. -+ */ -+ case 0x1f: /* Auburndale / Havendale */ -+ case 0x1e: /* Nehalem */ -+ case 0x1a: /* Nehalem EP */ -+ case 0x2e: /* Nehalem EX */ -+ case 0x25: /* Westmere */ -+ case 0x2c: /* Westmere EP */ -+ case 0x2f: /* Westmere EX */ -+ case 0x2a: /* SandyBridge */ -+ case 0x2d: /* SandyBridge EP/EX */ -+ case 0x3a: /* IvyBridge */ -+ case 0x3e: /* IvyBridge EP/EX */ -+ case 0x3c: /* Haswell */ -+ case 0x3f: /* Haswell EX/EP */ -+ case 0x45: /* Haswell D */ -+ case 0x46: /* Haswell H */ -+ case 0x3d: /* Broadwell */ -+ case 0x47: /* Broadwell H */ -+ case 0x4f: /* Broadwell EP/EX */ -+ case 0x56: /* Broadwell D */ -+ case 0x4e: /* Skylake M */ -+ case 0x5e: /* Skylake D */ -+ cpu_has_bug_mds = true; -+ break; -+ -+ /* -+ * Some Core processors have per-stepping vulnerability. -+ */ -+ case 0x55: /* Skylake-X / Cascade Lake */ -+ if ( boot_cpu_data.x86_mask <= 5 ) -+ cpu_has_bug_mds = true; -+ break; -+ -+ case 0x8e: /* Kaby / Coffee / Whiskey Lake M */ -+ if ( boot_cpu_data.x86_mask <= 0xb ) -+ cpu_has_bug_mds = true; -+ break; -+ -+ case 0x9e: /* Kaby / Coffee / Whiskey Lake D */ -+ if ( boot_cpu_data.x86_mask <= 0xc ) -+ cpu_has_bug_mds = true; -+ break; -+ -+ /* -+ * Very old and very new Atom processors are not vulnerable. -+ */ -+ case 0x1c: /* Pineview */ -+ case 0x26: /* Lincroft */ -+ case 0x27: /* Penwell */ -+ case 0x35: /* Cloverview */ -+ case 0x36: /* Cedarview */ -+ case 0x7a: /* Goldmont */ -+ break; -+ -+ /* -+ * Middling Atom processors are vulnerable to just the Store Buffer -+ * aspect. -+ */ -+ case 0x37: /* Baytrail / Valleyview (Silvermont) */ -+ case 0x4a: /* Merrifield */ -+ case 0x4c: /* Cherrytrail / Brasswell */ -+ case 0x4d: /* Avaton / Rangely (Silvermont) */ -+ case 0x5a: /* Moorefield */ -+ case 0x5d: -+ case 0x65: -+ case 0x6e: -+ case 0x75: -+ /* -+ * Knights processors (which are based on the Silvermont/Airmont -+ * microarchitecture) are similarly only affected by the Store Buffer -+ * aspect. -+ */ -+ case 0x57: /* Knights Landing */ -+ case 0x85: /* Knights Mill */ -+ cpu_has_bug_msbds_only = true; -+ break; -+ -+ default: -+ printk("Unrecognised CPU model %#x - assuming vulnerable to MDS\n", -+ boot_cpu_data.x86_model); -+ cpu_has_bug_mds = true; -+ break; -+ } -+} -+ - void __init init_speculation_mitigations(void) - { - enum ind_thunk thunk = THUNK_DEFAULT; -@@ -978,6 +1095,47 @@ void __init init_speculation_mitigations(void) - "enabled. Please assess your configuration and choose an\n" - "explicit 'smt=' setting. See XSA-273.\n"); - -+ mds_calculations(caps); -+ -+ /* -+ * 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)); -+ -+ /* -+ * 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. -+ * -+ * 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. -+ */ -+ 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); -+ -+ /* -+ * Warn the user if they are on MLPDS/MFBDS-vulnerable hardware with HT -+ * active and no explicit SMT choice. -+ */ -+ if ( opt_smt == -1 && cpu_has_bug_mds && hw_smt_enabled ) -+ warning_add( -+ "Booted on MLPDS/MFBDS-vulnerable hardware with SMT/Hyperthreading\n" -+ "enabled. Mitigations will not be fully effective. Please\n" -+ "choose an explicit smt= setting. See XSA-297.\n"); -+ - print_details(thunk, caps); - - /* From 9aac9aad77b451eb8c1d48d77ae0fed993f3bc34 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Sat, 27 Jul 2019 04:01:59 +0000 Subject: [PATCH 014/194] - Rebuilt for https://fedoraproject.org/wiki/Fedora_31_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 1bd1a80..362e55e 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.12.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 @@ -929,6 +929,9 @@ fi %endif %changelog +* Sat Jul 27 2019 Fedora Release Engineering - 4.12.0-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + * Wed Jun 19 2019 Michael Young - 4.12.0-4 - Unlimited Arm Atomics Operations [XSA-295] (#1720760) - some debug files are now properly packaged in debuginfo rpms From 8e8503393ca609251fa0cd60ae3dcc3fdb92c2f7 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Fri, 9 Aug 2019 18:15:22 +0100 Subject: [PATCH 015/194] update to xen-4.12.1 --- .gitignore | 2 +- sources | 2 +- xen.gcc9.fixes.patch | 11 - ...c15b981ba04c0709e6f25af3b76beb34cafa.patch | 51 -- ...1338e1d8a32e46c808321323c4ad8fc5ba01.patch | 71 --- ...ebee98e61dfe3b8b1eb71043ad5220b3cfd3.patch | 185 ------ xen.spec | 63 +- xsa295-4.12-01.patch | 99 --- xsa295-4.12-02.patch | 90 --- xsa295-4.12-03.patch | 68 --- xsa295-4.12-04.patch | 171 ------ xsa295-4.12-05.patch | 202 ------ xsa295-4.12-06.patch | 442 -------------- xsa295-4.12-07.patch | 83 --- xsa295-4.12-08.patch | 145 ----- xsa295-4.12-09.patch | 135 ---- xsa295-4.12-10.patch | 255 -------- xsa295-4.12-11.patch | 258 -------- xsa295-4.12-12.patch | 112 ---- xsa295-4.12-13.patch | 277 --------- xsa295-4.12-14.patch | 93 --- xsa295-4.12-15.patch | 577 ------------------ xsa295-4.12-16.patch | 84 --- xsa295-4.12-17.patch | 199 ------ xsa297-4.12-1.patch | 163 ----- xsa297-4.12-2.patch | 54 -- xsa297-4.12-3.patch | 109 ---- xsa297-4.12-4.patch | 55 -- xsa297-4.12-5.patch | 147 ----- xsa297-4.12-6.patch | 134 ---- xsa297-4.12-7.patch | 316 ---------- 31 files changed, 9 insertions(+), 4644 deletions(-) delete mode 100644 xen.git-8457c15b981ba04c0709e6f25af3b76beb34cafa.patch delete mode 100644 xen.git-e25d1338e1d8a32e46c808321323c4ad8fc5ba01.patch delete mode 100644 xen.git-e3a1ebee98e61dfe3b8b1eb71043ad5220b3cfd3.patch delete mode 100644 xsa295-4.12-01.patch delete mode 100644 xsa295-4.12-02.patch delete mode 100644 xsa295-4.12-03.patch delete mode 100644 xsa295-4.12-04.patch delete mode 100644 xsa295-4.12-05.patch delete mode 100644 xsa295-4.12-06.patch delete mode 100644 xsa295-4.12-07.patch delete mode 100644 xsa295-4.12-08.patch delete mode 100644 xsa295-4.12-09.patch delete mode 100644 xsa295-4.12-10.patch delete mode 100644 xsa295-4.12-11.patch delete mode 100644 xsa295-4.12-12.patch delete mode 100644 xsa295-4.12-13.patch delete mode 100644 xsa295-4.12-14.patch delete mode 100644 xsa295-4.12-15.patch delete mode 100644 xsa295-4.12-16.patch delete mode 100644 xsa295-4.12-17.patch delete mode 100644 xsa297-4.12-1.patch delete mode 100644 xsa297-4.12-2.patch delete mode 100644 xsa297-4.12-3.patch delete mode 100644 xsa297-4.12-4.patch delete mode 100644 xsa297-4.12-5.patch delete mode 100644 xsa297-4.12-6.patch delete mode 100644 xsa297-4.12-7.patch diff --git a/.gitignore b/.gitignore index 7d62d29..0c4dcc0 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.12.0.tar.gz +/xen-4.12.1.tar.gz diff --git a/sources b/sources index b5efa23..7f28202 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.12.0.tar.gz) = 0ce366dcac607c9b592c5e9c0f40652eef743913b246bed4b0c380b8d59ac23a6adcd05befec37fc799a61476f47df76d4911cbf1da6ceb51441c546bf2464de +SHA512 (xen-4.12.1.tar.gz) = be15d3af6b109771aaf59b3621e4e1560b7d9a963d9b7f6a1f6f4a1c907fd97434b060c15f75849b44fbda33f26eb51b030d14d068c6ad5103ad240fe7a98f40 diff --git a/xen.gcc9.fixes.patch b/xen.gcc9.fixes.patch index 111b063..16c526a 100644 --- a/xen.gcc9.fixes.patch +++ b/xen.gcc9.fixes.patch @@ -9,17 +9,6 @@ union { u64 val; struct { ---- xen-4.11.1/xen/common/trace.c.orig 2018-11-29 14:04:11.000000000 +0000 -+++ xen-4.11.1/xen/common/trace.c 2019-02-06 20:31:27.370256971 +0000 -@@ -819,7 +819,7 @@ - void __trace_hypercall(uint32_t event, unsigned long op, - const xen_ulong_t *args) - { -- struct __packed { -+ struct { - uint32_t op; - uint32_t args[6]; - } d; --- xen-4.11.1/xen/arch/x86/cpu/mtrr/generic.c.orig 2018-11-29 14:04:11.000000000 +0000 +++ xen-4.11.1/xen/arch/x86/cpu/mtrr/generic.c 2019-02-10 19:24:09.378805103 +0000 @@ -171,6 +171,9 @@ diff --git a/xen.git-8457c15b981ba04c0709e6f25af3b76beb34cafa.patch b/xen.git-8457c15b981ba04c0709e6f25af3b76beb34cafa.patch deleted file mode 100644 index 6e18188..0000000 --- a/xen.git-8457c15b981ba04c0709e6f25af3b76beb34cafa.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 8457c15b981ba04c0709e6f25af3b76beb34cafa Mon Sep 17 00:00:00 2001 -From: Igor Druzhinin -Date: Wed, 15 May 2019 09:40:45 +0200 -Subject: [PATCH] x86/vmx: Fixup removals of MSR load/save list entries - -Commit 540d5422 ("x86/vmx: Support removing MSRs from the host/guest -load/save lists") introduced infrastructure finally exposed by -commit fd32dcfe ("x86/vmx: Don't leak EFER.NXE into guest context") -that led to a functional regression on Harpertown and earlier cores -(Gen 1 VT-x) due to MSR count being incorrectly set in VMCS. -As the result, as soon as guest EFER becomes equal to Xen EFER -(which eventually happens in almost every 64-bit VM) and its MSR -entry is supposed to be removed, a stale version of EFER is loaded -into a guest instead causing almost immediate guest failure. - -Signed-off-by: Igor Druzhinin -Reviewed-by: Jan Beulich -Reviewed-by: Andrew Cooper -Acked-by: Kevin Tian -master commit: e28c0ee3356f52f589bbae54e89aaed25c1f599d -master date: 2019-04-09 10:58:18 +0100 ---- - xen/arch/x86/hvm/vmx/vmcs.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/xen/arch/x86/hvm/vmx/vmcs.c b/xen/arch/x86/hvm/vmx/vmcs.c -index 74f2a08cfd..45d18493df 100644 ---- a/xen/arch/x86/hvm/vmx/vmcs.c -+++ b/xen/arch/x86/hvm/vmx/vmcs.c -@@ -1490,15 +1490,15 @@ int vmx_del_msr(struct vcpu *v, uint32_t msr, enum vmx_msr_list_type type) - switch ( type ) - { - case VMX_MSR_HOST: -- __vmwrite(VM_EXIT_MSR_LOAD_COUNT, vmx->host_msr_count--); -+ __vmwrite(VM_EXIT_MSR_LOAD_COUNT, --vmx->host_msr_count); - break; - - case VMX_MSR_GUEST: -- __vmwrite(VM_EXIT_MSR_STORE_COUNT, vmx->msr_save_count--); -+ __vmwrite(VM_EXIT_MSR_STORE_COUNT, --vmx->msr_save_count); - - /* Fallthrough */ - case VMX_MSR_GUEST_LOADONLY: -- __vmwrite(VM_ENTRY_MSR_LOAD_COUNT, vmx->msr_load_count--); -+ __vmwrite(VM_ENTRY_MSR_LOAD_COUNT, --vmx->msr_load_count); - break; - } - --- -2.11.0 - diff --git a/xen.git-e25d1338e1d8a32e46c808321323c4ad8fc5ba01.patch b/xen.git-e25d1338e1d8a32e46c808321323c4ad8fc5ba01.patch deleted file mode 100644 index ece6829..0000000 --- a/xen.git-e25d1338e1d8a32e46c808321323c4ad8fc5ba01.patch +++ /dev/null @@ -1,71 +0,0 @@ -From e25d1338e1d8a32e46c808321323c4ad8fc5ba01 Mon Sep 17 00:00:00 2001 -From: Andrew Cooper -Date: Fri, 3 May 2019 10:44:58 +0200 -Subject: [PATCH] x86/msr: Shorten ARCH_CAPABILITIES_* constants - -They are unnecesserily verbose, and ARCH_CAPS_* is already the more common -version. - -Signed-off-by: Andrew Cooper -Acked-by: Jan Beulich -master commit: ba27aaa88548c824a47dcf5609288ee1c05d2946 -master date: 2019-03-18 16:26:40 +0000 ---- - xen/arch/x86/spec_ctrl.c | 10 +++++----- - xen/include/asm-x86/msr-index.h | 4 ++-- - 2 files changed, 7 insertions(+), 7 deletions(-) - -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index ad72ecd3a5..22bfc5a5e8 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -230,8 +230,8 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - (_7d0 & cpufeat_mask(X86_FEATURE_L1D_FLUSH)) ? " L1D_FLUSH" : "", - (_7d0 & cpufeat_mask(X86_FEATURE_SSBD)) ? " SSBD" : "", - (e8b & cpufeat_mask(X86_FEATURE_IBPB)) ? " IBPB" : "", -- (caps & ARCH_CAPABILITIES_IBRS_ALL) ? " IBRS_ALL" : "", -- (caps & ARCH_CAPABILITIES_RDCL_NO) ? " RDCL_NO" : "", -+ (caps & ARCH_CAPS_IBRS_ALL) ? " IBRS_ALL" : "", -+ (caps & ARCH_CAPS_RDCL_NO) ? " RDCL_NO" : "", - (caps & ARCH_CAPS_RSBA) ? " RSBA" : "", - (caps & ARCH_CAPS_SKIP_L1DFL) ? " SKIP_L1DFL": "", - (caps & ARCH_CAPS_SSB_NO) ? " SSB_NO" : ""); -@@ -549,7 +549,7 @@ static __init void l1tf_calculations(uint64_t caps) - } - - /* Any processor advertising RDCL_NO should be not vulnerable to L1TF. */ -- if ( caps & ARCH_CAPABILITIES_RDCL_NO ) -+ if ( caps & ARCH_CAPS_RDCL_NO ) - cpu_has_bug_l1tf = false; - - if ( cpu_has_bug_l1tf && hit_default ) -@@ -613,9 +613,9 @@ int8_t __read_mostly opt_xpti_domu = -1; - static __init void xpti_init_default(uint64_t caps) - { - if ( boot_cpu_data.x86_vendor == X86_VENDOR_AMD ) -- caps = ARCH_CAPABILITIES_RDCL_NO; -+ caps = ARCH_CAPS_RDCL_NO; - -- if ( caps & ARCH_CAPABILITIES_RDCL_NO ) -+ if ( caps & ARCH_CAPS_RDCL_NO ) - { - if ( opt_xpti_hwdom < 0 ) - opt_xpti_hwdom = 0; -diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h -index c6e1d8768f..11512d4250 100644 ---- a/xen/include/asm-x86/msr-index.h -+++ b/xen/include/asm-x86/msr-index.h -@@ -42,8 +42,8 @@ - #define PRED_CMD_IBPB (_AC(1, ULL) << 0) - - #define MSR_ARCH_CAPABILITIES 0x0000010a --#define ARCH_CAPABILITIES_RDCL_NO (_AC(1, ULL) << 0) --#define ARCH_CAPABILITIES_IBRS_ALL (_AC(1, ULL) << 1) -+#define ARCH_CAPS_RDCL_NO (_AC(1, ULL) << 0) -+#define ARCH_CAPS_IBRS_ALL (_AC(1, ULL) << 1) - #define ARCH_CAPS_RSBA (_AC(1, ULL) << 2) - #define ARCH_CAPS_SKIP_L1DFL (_AC(1, ULL) << 3) - #define ARCH_CAPS_SSB_NO (_AC(1, ULL) << 4) --- -2.11.0 - diff --git a/xen.git-e3a1ebee98e61dfe3b8b1eb71043ad5220b3cfd3.patch b/xen.git-e3a1ebee98e61dfe3b8b1eb71043ad5220b3cfd3.patch deleted file mode 100644 index c0862cd..0000000 --- a/xen.git-e3a1ebee98e61dfe3b8b1eb71043ad5220b3cfd3.patch +++ /dev/null @@ -1,185 +0,0 @@ -From e3a1ebee98e61dfe3b8b1eb71043ad5220b3cfd3 Mon Sep 17 00:00:00 2001 -From: Andrew Cooper -Date: Fri, 3 May 2019 10:39:29 +0200 -Subject: [PATCH] x86/tsx: Implement controls for RTM force-abort mode - -The CPUID bit and MSR are deliberately not exposed to guests, because they -won't exist on newer processors. As vPMU isn't security supported, the -misbehaviour of PCR3 isn't expected to impact production deployments. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -master commit: 6be613f29b4205349275d24367bd4c82fb2960dd -master date: 2019-03-12 17:05:21 +0000 ---- - docs/misc/xen-command-line.pandoc | 17 ++++++++++++++++- - tools/misc/xen-cpuid.c | 2 ++ - xen/arch/x86/cpu/intel.c | 3 +++ - xen/arch/x86/cpu/vpmu.c | 3 +++ - xen/arch/x86/msr.c | 4 ++++ - xen/include/asm-x86/cpufeature.h | 3 +++ - xen/include/asm-x86/msr-index.h | 3 +++ - xen/include/asm-x86/vpmu.h | 1 + - xen/include/public/arch-x86/cpufeatureset.h | 1 + - 9 files changed, 36 insertions(+), 1 deletion(-) - -diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc -index 742555616d..6db82f302e 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -2109,7 +2109,7 @@ Use Virtual Processor ID support if available. This prevents the need for TLB - flushes on VM entry and exit, increasing performance. - - ### vpmu (x86) -- = List of [ , bts, ipc, arch ] -+ = List of [ , bts, ipc, arch, rtm-abort= ] - - Applicability: x86. Default: false - -@@ -2142,6 +2142,21 @@ provide access to a wealth of low level processor information. - - * The `arch` option allows access to the pre-defined architectural events. - -+* The `rtm-abort` boolean controls a trade-off between working Restricted -+ Transactional Memory, and working performance counters. -+ -+ All processors released to date (Q1 2019) supporting Transactional Memory -+ Extensions suffer an erratum which has been addressed in microcode. -+ -+ Processors based on the Skylake microarchitecture with up-to-date -+ microcode internally use performance counter 3 to work around the erratum. -+ A consequence is that the counter gets reprogrammed whenever an `XBEGIN` -+ instruction is executed. -+ -+ An alternative mode exists where PCR3 behaves as before, at the cost of -+ `XBEGIN` unconditionally aborting. Enabling `rtm-abort` mode will -+ activate this alternative mode. -+ - *Warning:* - As the virtualisation is not 100% safe, don't use the vpmu flag on - production systems (see http://xenbits.xen.org/xsa/advisory-163.html)! -diff --git a/tools/misc/xen-cpuid.c b/tools/misc/xen-cpuid.c -index 6e7ca8b9a4..d87a72e3e6 100644 ---- a/tools/misc/xen-cpuid.c -+++ b/tools/misc/xen-cpuid.c -@@ -146,6 +146,8 @@ static const char *str_7d0[32] = - { - [ 2] = "avx512_4vnniw", [ 3] = "avx512_4fmaps", - -+ /* 12 */ [13] = "tsx-force-abort", -+ - [26] = "ibrsb", [27] = "stibp", - [28] = "l1d_flush", [29] = "arch_caps", - /* 30 */ [31] = "ssbd", -diff --git a/xen/arch/x86/cpu/intel.c b/xen/arch/x86/cpu/intel.c -index 65fa3d611f..29c6b87512 100644 ---- a/xen/arch/x86/cpu/intel.c -+++ b/xen/arch/x86/cpu/intel.c -@@ -286,6 +286,9 @@ static void Intel_errata_workarounds(struct cpuinfo_x86 *c) - if (c->x86 == 6 && cpu_has_clflush && - (c->x86_model == 29 || c->x86_model == 46 || c->x86_model == 47)) - __set_bit(X86_FEATURE_CLFLUSH_MONITOR, c->x86_capability); -+ -+ if (cpu_has_tsx_force_abort && opt_rtm_abort) -+ wrmsrl(MSR_TSX_FORCE_ABORT, TSX_FORCE_ABORT_RTM); - } - - -diff --git a/xen/arch/x86/cpu/vpmu.c b/xen/arch/x86/cpu/vpmu.c -index 8324d62f11..8f6daf13fd 100644 ---- a/xen/arch/x86/cpu/vpmu.c -+++ b/xen/arch/x86/cpu/vpmu.c -@@ -45,6 +45,7 @@ CHECK_pmu_params; - static unsigned int __read_mostly opt_vpmu_enabled; - unsigned int __read_mostly vpmu_mode = XENPMU_MODE_OFF; - unsigned int __read_mostly vpmu_features = 0; -+bool __read_mostly opt_rtm_abort; - - static DEFINE_SPINLOCK(vpmu_lock); - static unsigned vpmu_count; -@@ -73,6 +74,8 @@ static int __init parse_vpmu_params(const char *s) - vpmu_features |= XENPMU_FEATURE_IPC_ONLY; - else if ( !cmdline_strcmp(s, "arch") ) - vpmu_features |= XENPMU_FEATURE_ARCH_ONLY; -+ else if ( (val = parse_boolean("rtm-abort", s, ss)) >= 0 ) -+ opt_rtm_abort = val; - else - rc = -EINVAL; - -diff --git a/xen/arch/x86/msr.c b/xen/arch/x86/msr.c -index 9bb38b6d66..4df4a59f4d 100644 ---- a/xen/arch/x86/msr.c -+++ b/xen/arch/x86/msr.c -@@ -131,6 +131,8 @@ int guest_rdmsr(const struct vcpu *v, uint32_t msr, uint64_t *val) - case MSR_PRED_CMD: - case MSR_FLUSH_CMD: - /* Write-only */ -+ case MSR_TSX_FORCE_ABORT: -+ /* Not offered to guests. */ - goto gp_fault; - - case MSR_SPEC_CTRL: -@@ -230,6 +232,8 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) - case MSR_INTEL_PLATFORM_INFO: - case MSR_ARCH_CAPABILITIES: - /* Read-only */ -+ case MSR_TSX_FORCE_ABORT: -+ /* Not offered to guests. */ - goto gp_fault; - - case MSR_AMD_PATCHLOADER: -diff --git a/xen/include/asm-x86/cpufeature.h b/xen/include/asm-x86/cpufeature.h -index 1fb9af4b19..745801f3c0 100644 ---- a/xen/include/asm-x86/cpufeature.h -+++ b/xen/include/asm-x86/cpufeature.h -@@ -112,6 +112,9 @@ - /* CPUID level 0x80000007.edx */ - #define cpu_has_itsc boot_cpu_has(X86_FEATURE_ITSC) - -+/* CPUID level 0x00000007:0.edx */ -+#define cpu_has_tsx_force_abort boot_cpu_has(X86_FEATURE_TSX_FORCE_ABORT) -+ - /* Synthesized. */ - #define cpu_has_arch_perfmon boot_cpu_has(X86_FEATURE_ARCH_PERFMON) - #define cpu_has_cpuid_faulting boot_cpu_has(X86_FEATURE_CPUID_FAULTING) -diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h -index 24d783a72d..c6e1d8768f 100644 ---- a/xen/include/asm-x86/msr-index.h -+++ b/xen/include/asm-x86/msr-index.h -@@ -51,6 +51,9 @@ - #define MSR_FLUSH_CMD 0x0000010b - #define FLUSH_CMD_L1D (_AC(1, ULL) << 0) - -+#define MSR_TSX_FORCE_ABORT 0x0000010f -+#define TSX_FORCE_ABORT_RTM (_AC(1, ULL) << 0) -+ - /* Intel MSRs. Some also available on other CPUs */ - #define MSR_IA32_PERFCTR0 0x000000c1 - #define MSR_IA32_A_PERFCTR0 0x000004c1 -diff --git a/xen/include/asm-x86/vpmu.h b/xen/include/asm-x86/vpmu.h -index 5e778ab7ba..1287b9fb6e 100644 ---- a/xen/include/asm-x86/vpmu.h -+++ b/xen/include/asm-x86/vpmu.h -@@ -125,6 +125,7 @@ static inline int vpmu_do_rdmsr(unsigned int msr, uint64_t *msr_content) - - extern unsigned int vpmu_mode; - extern unsigned int vpmu_features; -+extern bool opt_rtm_abort; - - /* Context switch */ - static inline void vpmu_switch_from(struct vcpu *prev) -diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h -index fbc68fa29f..2bcc5487ac 100644 ---- a/xen/include/public/arch-x86/cpufeatureset.h -+++ b/xen/include/public/arch-x86/cpufeatureset.h -@@ -242,6 +242,7 @@ XEN_CPUFEATURE(IBPB, 8*32+12) /*A IBPB support only (no IBRS, used by - /* Intel-defined CPU features, CPUID level 0x00000007:0.edx, word 9 */ - XEN_CPUFEATURE(AVX512_4VNNIW, 9*32+ 2) /*A AVX512 Neural Network Instructions */ - XEN_CPUFEATURE(AVX512_4FMAPS, 9*32+ 3) /*A AVX512 Multiply Accumulation Single Precision */ -+XEN_CPUFEATURE(TSX_FORCE_ABORT, 9*32+13) /* MSR_TSX_FORCE_ABORT.RTM_ABORT */ - XEN_CPUFEATURE(IBRSB, 9*32+26) /*A IBRS and IBPB support (used by Intel) */ - XEN_CPUFEATURE(STIBP, 9*32+27) /*A STIBP */ - XEN_CPUFEATURE(L1D_FLUSH, 9*32+28) /*S MSR_FLUSH_CMD and L1D flush. */ --- -2.11.0 - diff --git a/xen.spec b/xen.spec index 362e55e..3d6eddc 100644 --- a/xen.spec +++ b/xen.spec @@ -57,8 +57,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.12.0 -Release: 5%{?dist} +Version: 4.12.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,34 +113,7 @@ Patch40: xen.drop.brctl.patch Patch41: xen.python.env.patch Patch42: xen.gcc9.fixes.patch Patch43: xen.python3.patch -Patch44: xen.git-e25d1338e1d8a32e46c808321323c4ad8fc5ba01.patch -Patch45: xen.git-e3a1ebee98e61dfe3b8b1eb71043ad5220b3cfd3.patch -Patch46: xsa297-4.12-1.patch -Patch47: xsa297-4.12-2.patch -Patch48: xsa297-4.12-3.patch -Patch49: xsa297-4.12-4.patch -Patch50: xsa297-4.12-5.patch -Patch51: xsa297-4.12-6.patch -Patch52: xsa297-4.12-7.patch -Patch53: xen.git-8457c15b981ba04c0709e6f25af3b76beb34cafa.patch Patch54: xen.python38.patch -Patch55: xsa295-4.12-01.patch -Patch56: xsa295-4.12-02.patch -Patch57: xsa295-4.12-03.patch -Patch58: xsa295-4.12-04.patch -Patch59: xsa295-4.12-05.patch -Patch60: xsa295-4.12-06.patch -Patch61: xsa295-4.12-07.patch -Patch62: xsa295-4.12-08.patch -Patch63: xsa295-4.12-09.patch -Patch64: xsa295-4.12-10.patch -Patch65: xsa295-4.12-11.patch -Patch66: xsa295-4.12-12.patch -Patch67: xsa295-4.12-13.patch -Patch68: xsa295-4.12-14.patch -Patch69: xsa295-4.12-15.patch -Patch70: xsa295-4.12-16.patch -Patch71: xsa295-4.12-17.patch %if %build_qemutrad @@ -338,34 +311,7 @@ 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 -%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 @@ -929,6 +875,11 @@ fi %endif %changelog +* Fri Aug 09 2019 Michael Young - 4.12.1-1 +- update to 4.12.1 + remove patches for issues now fixed upstream + adjust xen.gcc9.fixes.patch + * Sat Jul 27 2019 Fedora Release Engineering - 4.12.0-5 - Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild diff --git a/xsa295-4.12-01.patch b/xsa295-4.12-01.patch deleted file mode 100644 index 367a821..0000000 --- a/xsa295-4.12-01.patch +++ /dev/null @@ -1,99 +0,0 @@ -From 00617d5cc62d2bf4d02493aeafad14cca4e09a66 Mon Sep 17 00:00:00 2001 -From: Jan Beulich -Date: Tue, 12 Mar 2019 14:40:24 +0100 -Subject: [PATCH v2 4.12 01/17] events: drop arch_evtchn_inject() - -Have the only user call vcpu_mark_events_pending() instead, at the same -time arranging for correct ordering of the writes (evtchn_pending_sel -should be written before evtchn_upcall_pending). - -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper -Reviewed-by: Julien Grall ---- - xen/arch/arm/vgic.c | 5 ----- - xen/arch/arm/vgic/vgic.c | 5 ----- - xen/arch/x86/irq.c | 6 ------ - xen/common/domain.c | 3 +-- - xen/include/xen/event.h | 3 --- - 5 files changed, 1 insertion(+), 21 deletions(-) - -diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c -index f2608b042d..19d8c9b876 100644 ---- a/xen/arch/arm/vgic.c -+++ b/xen/arch/arm/vgic.c -@@ -597,11 +597,6 @@ out: - return; - } - --void arch_evtchn_inject(struct vcpu *v) --{ -- vgic_inject_irq(v->domain, v, v->domain->arch.evtchn_irq, true); --} -- - bool vgic_evtchn_irq_pending(struct vcpu *v) - { - struct pending_irq *p; -diff --git a/xen/arch/arm/vgic/vgic.c b/xen/arch/arm/vgic/vgic.c -index e2844dcc20..f0f2ea5021 100644 ---- a/xen/arch/arm/vgic/vgic.c -+++ b/xen/arch/arm/vgic/vgic.c -@@ -692,11 +692,6 @@ void vgic_kick_vcpus(struct domain *d) - } - } - --void arch_evtchn_inject(struct vcpu *v) --{ -- vgic_inject_irq(v->domain, v, v->domain->arch.evtchn_irq, true); --} -- - bool vgic_evtchn_irq_pending(struct vcpu *v) - { - struct vgic_irq *irq; -diff --git a/xen/arch/x86/irq.c b/xen/arch/x86/irq.c -index 23b4f423e6..8511953977 100644 ---- a/xen/arch/x86/irq.c -+++ b/xen/arch/x86/irq.c -@@ -2724,9 +2724,3 @@ int allocate_and_map_msi_pirq(struct domain *d, int index, int *pirq_p, - - return ret; - } -- --void arch_evtchn_inject(struct vcpu *v) --{ -- if ( is_hvm_vcpu(v) ) -- hvm_assert_evtchn_irq(v); --} -diff --git a/xen/common/domain.c b/xen/common/domain.c -index 32bca8dbf2..0542338862 100644 ---- a/xen/common/domain.c -+++ b/xen/common/domain.c -@@ -1306,10 +1306,9 @@ int map_vcpu_info(struct vcpu *v, unsigned long gfn, unsigned offset) - * Mark everything as being pending just to make sure nothing gets - * lost. The domain will get a spurious event, but it can cope. - */ -- vcpu_info(v, evtchn_upcall_pending) = 1; - for ( i = 0; i < BITS_PER_EVTCHN_WORD(d); i++ ) - set_bit(i, &vcpu_info(v, evtchn_pending_sel)); -- arch_evtchn_inject(v); -+ vcpu_mark_events_pending(v); - - return 0; - } -diff --git a/xen/include/xen/event.h b/xen/include/xen/event.h -index e91097d77e..a7798f6765 100644 ---- a/xen/include/xen/event.h -+++ b/xen/include/xen/event.h -@@ -91,9 +91,6 @@ int guest_enabled_event(struct vcpu *v, uint32_t virq); - /* Notify remote end of a Xen-attached event channel.*/ - void notify_via_xen_event_channel(struct domain *ld, int lport); - --/* Inject an event channel notification into the guest */ --void arch_evtchn_inject(struct vcpu *v); -- - /* - * Internal event channel object storage. - * --- -2.17.1 - diff --git a/xsa295-4.12-02.patch b/xsa295-4.12-02.patch deleted file mode 100644 index b9b24bb..0000000 --- a/xsa295-4.12-02.patch +++ /dev/null @@ -1,90 +0,0 @@ -From 8908fe0d3f42a4764d29e6aa657749d7a1b4c8ca Mon Sep 17 00:00:00 2001 -From: Jan Beulich -Date: Tue, 12 Mar 2019 14:40:56 +0100 -Subject: [PATCH v2 4.12 02/17] common: avoid atomic read-modify-write accesses - in map_vcpu_info() - -There's no need to set the evtchn_pending_sel bits one by one. Simply -write full words with all ones. - -For Arm this requires extending write_atomic() to also handle 64-bit -values; for symmetry read_atomic() gets adjusted as well. - -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper -Reviewed-by: Julien Grall ---- - xen/common/domain.c | 9 ++++++--- - xen/include/asm-arm/atomic.h | 15 +++++++++++++++ - 2 files changed, 21 insertions(+), 3 deletions(-) - -diff --git a/xen/common/domain.c b/xen/common/domain.c -index 0542338862..3b18f11f12 100644 ---- a/xen/common/domain.c -+++ b/xen/common/domain.c -@@ -1253,7 +1253,6 @@ int map_vcpu_info(struct vcpu *v, unsigned long gfn, unsigned offset) - void *mapping; - vcpu_info_t *new_info; - struct page_info *page; -- int i; - - if ( offset > (PAGE_SIZE - sizeof(vcpu_info_t)) ) - return -EINVAL; -@@ -1306,8 +1305,12 @@ int map_vcpu_info(struct vcpu *v, unsigned long gfn, unsigned offset) - * Mark everything as being pending just to make sure nothing gets - * lost. The domain will get a spurious event, but it can cope. - */ -- for ( i = 0; i < BITS_PER_EVTCHN_WORD(d); i++ ) -- set_bit(i, &vcpu_info(v, evtchn_pending_sel)); -+#ifdef CONFIG_COMPAT -+ if ( !has_32bit_shinfo(d) ) -+ write_atomic(&new_info->native.evtchn_pending_sel, ~0); -+ else -+#endif -+ write_atomic(&vcpu_info(v, evtchn_pending_sel), ~0); - vcpu_mark_events_pending(v); - - return 0; -diff --git a/xen/include/asm-arm/atomic.h b/xen/include/asm-arm/atomic.h -index afb3eeea5b..7b4c987fa7 100644 ---- a/xen/include/asm-arm/atomic.h -+++ b/xen/include/asm-arm/atomic.h -@@ -55,6 +55,19 @@ build_atomic_write(write_int_atomic, "", WORD, int, "r") - #if defined (CONFIG_ARM_64) - build_atomic_read(read_u64_atomic, "", "", uint64_t, "=r") - build_atomic_write(write_u64_atomic, "", "", uint64_t, "r") -+#elif defined (CONFIG_ARM_32) -+static inline uint64_t read_u64_atomic(const volatile uint64_t *addr) -+{ -+ uint64_t val; -+ -+ asm volatile ( "ldrd %0,%H0,%1" : "=r" (val) : "m" (*addr) ); -+ -+ return val; -+} -+static inline void write_u64_atomic(volatile uint64_t *addr, uint64_t val) -+{ -+ asm volatile ( "strd %1,%H1,%0" : "=m" (*addr) : "r" (val) ); -+} - #endif - - build_add_sized(add_u8_sized, "b", BYTE, uint8_t, "ri") -@@ -69,6 +82,7 @@ void __bad_atomic_size(void); - case 1: __x = (typeof(*p))read_u8_atomic((uint8_t *)p); break; \ - case 2: __x = (typeof(*p))read_u16_atomic((uint16_t *)p); break; \ - case 4: __x = (typeof(*p))read_u32_atomic((uint32_t *)p); break; \ -+ case 8: __x = (typeof(*p))read_u64_atomic((uint64_t *)p); break; \ - default: __x = 0; __bad_atomic_size(); break; \ - } \ - __x; \ -@@ -80,6 +94,7 @@ void __bad_atomic_size(void); - case 1: write_u8_atomic((uint8_t *)p, (uint8_t)__x); break; \ - case 2: write_u16_atomic((uint16_t *)p, (uint16_t)__x); break; \ - case 4: write_u32_atomic((uint32_t *)p, (uint32_t)__x); break; \ -+ case 8: write_u64_atomic((uint64_t *)p, (uint64_t)__x); break; \ - default: __bad_atomic_size(); break; \ - } \ - __x; \ --- -2.17.1 - diff --git a/xsa295-4.12-03.patch b/xsa295-4.12-03.patch deleted file mode 100644 index 9dacd40..0000000 --- a/xsa295-4.12-03.patch +++ /dev/null @@ -1,68 +0,0 @@ -From bbe4ec0187f66c8afc0a4c55bb94e7ccd4ce68a5 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:16 +0100 -Subject: [PATCH v2 4.12 03/17] xen/arm: Add an isb() before reading CNTPCT_EL0 - to prevent re-ordering - -Per D8.2.1 in ARM DDI 0487C.a, "a read to CNTPCT_EL0 can occur -speculatively and out of order relative to other instructions executed -on the same PE." - -Add an instruction barrier to get accurate number of cycles when -requested in get_cycles(). For the other users of CNPCT_EL0, replace by -a call to get_cycles(). - -This is part of XSA-295. - -Signed-off-by: Julien Grall -Acked-by: Stefano Stabellini ---- - xen/arch/arm/time.c | 4 ++-- - xen/include/asm-arm/time.h | 2 ++ - 2 files changed, 4 insertions(+), 2 deletions(-) - -diff --git a/xen/arch/arm/time.c b/xen/arch/arm/time.c -index bbccee742e..739bcf186c 100644 ---- a/xen/arch/arm/time.c -+++ b/xen/arch/arm/time.c -@@ -151,7 +151,7 @@ void __init preinit_xen_time(void) - if ( res ) - panic("Timer: Cannot initialize platform timer\n"); - -- boot_count = READ_SYSREG64(CNTPCT_EL0); -+ boot_count = get_cycles(); - } - - static void __init init_dt_xen_time(void) -@@ -192,7 +192,7 @@ int __init init_xen_time(void) - /* Return number of nanoseconds since boot */ - s_time_t get_s_time(void) - { -- uint64_t ticks = READ_SYSREG64(CNTPCT_EL0) - boot_count; -+ uint64_t ticks = get_cycles() - boot_count; - return ticks_to_ns(ticks); - } - -diff --git a/xen/include/asm-arm/time.h b/xen/include/asm-arm/time.h -index 9a7071a546..9cb6f9b0b4 100644 ---- a/xen/include/asm-arm/time.h -+++ b/xen/include/asm-arm/time.h -@@ -2,6 +2,7 @@ - #define __ARM_TIME_H__ - - #include -+#include - - #define DT_MATCH_TIMER \ - DT_MATCH_COMPATIBLE("arm,armv7-timer"), \ -@@ -11,6 +12,7 @@ typedef uint64_t cycles_t; - - static inline cycles_t get_cycles (void) - { -+ isb(); - return READ_SYSREG64(CNTPCT_EL0); - } - --- -2.17.1 - diff --git a/xsa295-4.12-04.patch b/xsa295-4.12-04.patch deleted file mode 100644 index 9ac0027..0000000 --- a/xsa295-4.12-04.patch +++ /dev/null @@ -1,171 +0,0 @@ -From d0020117111a77eeee7db6ef52ff87347c8c54b7 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:17 +0100 -Subject: [PATCH v2 4.12 04/17] xen/grant_table: Rework the prototype of - _set_status* for lisibility - -It is not clear from the parameters name whether domid and gt_version -correspond to the local or remote domain. A follow-up patch will make -them more confusing. - -So rename domid (resp. gt_version) to ldomid (resp. rgt_version). At -the same time re-order the parameters to hopefully make it more -readable. - -This is part of XSA-295. - -Suggested-by: Jan Beulich -Signed-off-by: Julien Grall -Reviewed-by: Jan Beulich -Acked-by: Stefano Stabellini ---- - xen/common/grant_table.c | 57 ++++++++++++++++++++-------------------- - 1 file changed, 28 insertions(+), 29 deletions(-) - -diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c -index 80728ea57d..1b82d534a3 100644 ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -645,11 +645,11 @@ static unsigned int nr_grant_entries(struct grant_table *gt) - return 0; - } - --static int _set_status_v1(domid_t domid, -+static int _set_status_v1(const grant_entry_header_t *shah, -+ struct active_grant_entry *act, - int readonly, - int mapflag, -- grant_entry_header_t *shah, -- struct active_grant_entry *act) -+ domid_t ldomid) - { - int rc = GNTST_okay; - union grant_combo scombo, prev_scombo, new_scombo; -@@ -684,11 +684,11 @@ static int _set_status_v1(domid_t domid, - if ( !act->pin && - (((scombo.shorts.flags & mask) != - GTF_permit_access) || -- (scombo.shorts.domid != domid)) ) -+ (scombo.shorts.domid != ldomid)) ) - PIN_FAIL(done, GNTST_general_error, - "Bad flags (%x) or dom (%d); expected d%d\n", - scombo.shorts.flags, scombo.shorts.domid, -- domid); -+ ldomid); - - new_scombo = scombo; - new_scombo.shorts.flags |= GTF_reading; -@@ -717,12 +717,12 @@ done: - return rc; - } - --static int _set_status_v2(domid_t domid, -+static int _set_status_v2(const grant_entry_header_t *shah, -+ grant_status_t *status, -+ struct active_grant_entry *act, - int readonly, - int mapflag, -- grant_entry_header_t *shah, -- struct active_grant_entry *act, -- grant_status_t *status) -+ domid_t ldomid) - { - int rc = GNTST_okay; - union grant_combo scombo; -@@ -748,10 +748,10 @@ static int _set_status_v2(domid_t domid, - if ( !act->pin && - ( (((flags & mask) != GTF_permit_access) && - ((flags & mask) != GTF_transitive)) || -- (id != domid)) ) -+ (id != ldomid)) ) - PIN_FAIL(done, GNTST_general_error, - "Bad flags (%x) or dom (%d); expected d%d, flags %x\n", -- flags, id, domid, mask); -+ flags, id, ldomid, mask); - - if ( readonly ) - { -@@ -778,14 +778,14 @@ static int _set_status_v2(domid_t domid, - { - if ( (((flags & mask) != GTF_permit_access) && - ((flags & mask) != GTF_transitive)) || -- (id != domid) || -+ (id != ldomid) || - (!readonly && (flags & GTF_readonly)) ) - { - gnttab_clear_flag(_GTF_writing, status); - gnttab_clear_flag(_GTF_reading, status); - PIN_FAIL(done, GNTST_general_error, - "Unstable flags (%x) or dom (%d); expected d%d (r/w: %d)\n", -- flags, id, domid, !readonly); -+ flags, id, ldomid, !readonly); - } - } - else -@@ -803,19 +803,19 @@ done: - } - - --static int _set_status(unsigned gt_version, -- domid_t domid, -+static int _set_status(const grant_entry_header_t *shah, -+ grant_status_t *status, -+ unsigned rgt_version, -+ struct active_grant_entry *act, - int readonly, - int mapflag, -- grant_entry_header_t *shah, -- struct active_grant_entry *act, -- grant_status_t *status) -+ domid_t ldomid) - { - -- if ( gt_version == 1 ) -- return _set_status_v1(domid, readonly, mapflag, shah, act); -+ if ( rgt_version == 1 ) -+ return _set_status_v1(shah, act, readonly, mapflag, ldomid); - else -- return _set_status_v2(domid, readonly, mapflag, shah, act, status); -+ return _set_status_v2(shah, status, act, readonly, mapflag, ldomid); - } - - static struct active_grant_entry *grant_map_exists(const struct domain *ld, -@@ -980,9 +980,9 @@ map_grant_ref( - (!(op->flags & GNTMAP_readonly) && - !(act->pin & (GNTPIN_hstw_mask|GNTPIN_devw_mask))) ) - { -- if ( (rc = _set_status(rgt->gt_version, ld->domain_id, -- op->flags & GNTMAP_readonly, -- 1, shah, act, status) ) != GNTST_okay ) -+ if ( (rc = _set_status(shah, status, rgt->gt_version, act, -+ op->flags & GNTMAP_readonly, 1, -+ ld->domain_id) != GNTST_okay) ) - goto act_release_out; - - if ( !act->pin ) -@@ -2434,8 +2434,8 @@ acquire_grant_for_copy( - { - if ( (!old_pin || (!readonly && - !(old_pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)))) && -- (rc = _set_status_v2(ldom, readonly, 0, shah, act, -- status)) != GNTST_okay ) -+ (rc = _set_status_v2(shah, status, act, readonly, 0, -+ ldom)) != GNTST_okay ) - goto unlock_out; - - if ( !allow_transitive ) -@@ -2535,9 +2535,8 @@ acquire_grant_for_copy( - else if ( !old_pin || - (!readonly && !(old_pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask))) ) - { -- if ( (rc = _set_status(rgt->gt_version, ldom, -- readonly, 0, shah, act, -- status) ) != GNTST_okay ) -+ if ( (rc = _set_status(shah, status, rgt->gt_version, act, -+ readonly, 0, ldom)) != GNTST_okay ) - goto unlock_out; - - td = rd; --- -2.17.1 - diff --git a/xsa295-4.12-05.patch b/xsa295-4.12-05.patch deleted file mode 100644 index 3054822..0000000 --- a/xsa295-4.12-05.patch +++ /dev/null @@ -1,202 +0,0 @@ -From e40140db5da810c4ead632ae5d346558f2b93695 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:18 +0100 -Subject: [PATCH v2 4.12 05/17] xen/arm64: bitops: Rewrite bitop helpers in C - -This is part of XSA-295. - -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini -Signed-off-by: Stefano Stabellini ---- - xen/arch/arm/README.LinuxPrimitives | 1 - - xen/arch/arm/arm64/lib/bitops.S | 67 --------------------- - xen/arch/arm/arm64/lib/bitops.c | 90 +++++++++++++++++++++++++++++ - 3 files changed, 90 insertions(+), 68 deletions(-) - delete mode 100644 xen/arch/arm/arm64/lib/bitops.S - create mode 100644 xen/arch/arm/arm64/lib/bitops.c - -diff --git a/xen/arch/arm/README.LinuxPrimitives b/xen/arch/arm/README.LinuxPrimitives -index 028e8721f9..891667a5da 100644 ---- a/xen/arch/arm/README.LinuxPrimitives -+++ b/xen/arch/arm/README.LinuxPrimitives -@@ -8,7 +8,6 @@ arm64: - - bitops: last sync @ v3.16-rc6 (last commit: 8715466b6027) - --linux/arch/arm64/lib/bitops.S xen/arch/arm/arm64/lib/bitops.S - linux/arch/arm64/include/asm/bitops.h xen/include/asm-arm/arm64/bitops.h - - --------------------------------------------------------------------- -diff --git a/xen/arch/arm/arm64/lib/bitops.S b/xen/arch/arm/arm64/lib/bitops.S -deleted file mode 100644 -index 6471dd1875..0000000000 ---- a/xen/arch/arm/arm64/lib/bitops.S -+++ /dev/null -@@ -1,67 +0,0 @@ --/* -- * Based on linux/arch/arm64/lib/bitops.h which in turn is -- * Based on arch/arm/lib/bitops.h -- * -- * Copyright (C) 2013 ARM Ltd. -- * -- * 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 distributed in the hope that it will be useful, -- * but WITHOUT ANY WARRANTY; without even the implied warranty of -- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- * GNU General Public License for more details. -- * -- * You should have received a copy of the GNU General Public License -- * along with this program. If not, see . -- */ -- --/* -- * x0: bits 4:0 bit offset -- * bits 31:5 word offset -- * x1: address -- */ -- .macro bitop, name, instr --ENTRY( \name ) -- and w3, w0, #31 // Get bit offset -- eor w0, w0, w3 // Clear low bits -- mov x2, #1 -- add x1, x1, x0, lsr #3 // Get word offset -- lsl x3, x2, x3 // Create mask --1: ldxr w2, [x1] -- \instr w2, w2, w3 -- stxr w0, w2, [x1] -- cbnz w0, 1b -- ret --ENDPROC(\name ) -- .endm -- -- .macro testop, name, instr --ENTRY( \name ) -- and w3, w0, #31 // Get bit offset -- eor w0, w0, w3 // Clear low bits -- mov x2, #1 -- add x1, x1, x0, lsr #3 // Get word offset -- lsl x4, x2, x3 // Create mask --1: ldxr w2, [x1] -- lsr w0, w2, w3 // Save old value of bit -- \instr w2, w2, w4 // toggle bit -- stlxr w5, w2, [x1] -- cbnz w5, 1b -- dmb ish -- and w0, w0, #1 --3: ret --ENDPROC(\name ) -- .endm -- --/* -- * Atomic bit operations. -- */ -- bitop change_bit, eor -- bitop clear_bit, bic -- bitop set_bit, orr -- -- testop test_and_change_bit, eor -- testop test_and_clear_bit, bic -- testop test_and_set_bit, orr -diff --git a/xen/arch/arm/arm64/lib/bitops.c b/xen/arch/arm/arm64/lib/bitops.c -new file mode 100644 -index 0000000000..b1c681c642 ---- /dev/null -+++ b/xen/arch/arm/arm64/lib/bitops.c -@@ -0,0 +1,90 @@ -+/* -+ * Copyright (C) 2018 ARM Ltd. -+ * -+ * 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 distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+ -+#include -+#include -+ -+/* -+ * The atomic bit operations pass the number of bit in a signed number -+ * (not sure why). This has the drawback to increase the complexity of -+ * the resulting assembly. -+ * -+ * To generate simpler code, the number of bit (nr) will be cast to -+ * unsigned int. -+ * -+ * XXX: Rework the interface to use unsigned int. -+ */ -+ -+#define bitop(name, instr) \ -+void name(int nr, volatile void *p) \ -+{ \ -+ volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ -+ const uint32_t mask = BIT_MASK((unsigned int)nr); \ -+ unsigned long res, tmp; \ -+ \ -+ do \ -+ { \ -+ asm volatile ("// " __stringify(name) "\n" \ -+ " ldxr %w2, %1\n" \ -+ " " __stringify(instr) " %w2, %w2, %w3\n" \ -+ " stxr %w0, %w2, %1\n" \ -+ : "=&r" (res), "+Q" (*ptr), "=&r" (tmp) \ -+ : "r" (mask)); \ -+ } while ( res ); \ -+} \ -+ -+#define testop(name, instr) \ -+int name(int nr, volatile void *p) \ -+{ \ -+ volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ -+ unsigned int bit = (unsigned int)nr % BITS_PER_WORD; \ -+ const uint32_t mask = BIT_MASK(bit); \ -+ unsigned long res, tmp; \ -+ unsigned long oldbit; \ -+ \ -+ do \ -+ { \ -+ asm volatile ("// " __stringify(name) "\n" \ -+ " ldxr %w3, %2\n" \ -+ " lsr %w1, %w3, %w5 // Save old value of bit\n" \ -+ " " __stringify(instr) " %w3, %w3, %w4 // Toggle bit\n" \ -+ " stlxr %w0, %w3, %2\n" \ -+ : "=&r" (res), "=&r" (oldbit), "+Q" (*ptr), "=&r" (tmp) \ -+ : "r" (mask), "r" (bit) \ -+ : "memory"); \ -+ } while ( res ); \ -+ \ -+ dmb(ish); \ -+ \ -+ return oldbit & 1; \ -+} -+ -+bitop(change_bit, eor) -+bitop(clear_bit, bic) -+bitop(set_bit, orr) -+ -+testop(test_and_change_bit, eor) -+testop(test_and_clear_bit, bic) -+testop(test_and_set_bit, orr) -+ -+/* -+ * Local variables: -+ * mode: C -+ * c-file-style: "BSD" -+ * c-basic-offset: 4 -+ * indent-tabs-mode: nil -+ * End: -+ */ --- -2.17.1 - diff --git a/xsa295-4.12-06.patch b/xsa295-4.12-06.patch deleted file mode 100644 index 7fb835f..0000000 --- a/xsa295-4.12-06.patch +++ /dev/null @@ -1,442 +0,0 @@ -From b919e89a3cc3295ee56b1392d42ba7eea898f9d4 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:19 +0100 -Subject: [PATCH v2 4.12 06/17] xen/arm32: bitops: Rewrite bitop helpers in C - -This is part of XSA-295. - -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini -Signed-off-by: Stefano Stabellini ---- - xen/arch/arm/README.LinuxPrimitives | 14 +--- - xen/arch/arm/arm32/lib/Makefile | 5 +- - xen/arch/arm/arm32/lib/bitops.c | 98 +++++++++++++++++++++++ - xen/arch/arm/arm32/lib/bitops.h | 104 ------------------------- - xen/arch/arm/arm32/lib/changebit.S | 14 ---- - xen/arch/arm/arm32/lib/clearbit.S | 14 ---- - xen/arch/arm/arm32/lib/setbit.S | 15 ---- - xen/arch/arm/arm32/lib/testchangebit.S | 15 ---- - xen/arch/arm/arm32/lib/testclearbit.S | 15 ---- - xen/arch/arm/arm32/lib/testsetbit.S | 15 ---- - xen/include/asm-arm/arm32/bitops.h | 19 ++--- - 11 files changed, 108 insertions(+), 220 deletions(-) - create mode 100644 xen/arch/arm/arm32/lib/bitops.c - delete mode 100644 xen/arch/arm/arm32/lib/bitops.h - delete mode 100644 xen/arch/arm/arm32/lib/changebit.S - delete mode 100644 xen/arch/arm/arm32/lib/clearbit.S - delete mode 100644 xen/arch/arm/arm32/lib/setbit.S - delete mode 100644 xen/arch/arm/arm32/lib/testchangebit.S - delete mode 100644 xen/arch/arm/arm32/lib/testclearbit.S - delete mode 100644 xen/arch/arm/arm32/lib/testsetbit.S - -diff --git a/xen/arch/arm/README.LinuxPrimitives b/xen/arch/arm/README.LinuxPrimitives -index 891667a5da..664a9f89ed 100644 ---- a/xen/arch/arm/README.LinuxPrimitives -+++ b/xen/arch/arm/README.LinuxPrimitives -@@ -68,19 +68,9 @@ arm32 - - bitops: last sync @ v3.16-rc6 (last commit: c32ffce0f66e) - --linux/arch/arm/lib/bitops.h xen/arch/arm/arm32/lib/bitops.h --linux/arch/arm/lib/changebit.S xen/arch/arm/arm32/lib/changebit.S --linux/arch/arm/lib/clearbit.S xen/arch/arm/arm32/lib/clearbit.S - linux/arch/arm/lib/findbit.S xen/arch/arm/arm32/lib/findbit.S --linux/arch/arm/lib/setbit.S xen/arch/arm/arm32/lib/setbit.S --linux/arch/arm/lib/testchangebit.S xen/arch/arm/arm32/lib/testchangebit.S --linux/arch/arm/lib/testclearbit.S xen/arch/arm/arm32/lib/testclearbit.S --linux/arch/arm/lib/testsetbit.S xen/arch/arm/arm32/lib/testsetbit.S -- --for i in bitops.h changebit.S clearbit.S findbit.S setbit.S testchangebit.S \ -- testclearbit.S testsetbit.S; do -- diff -u ../linux/arch/arm/lib/$i xen/arch/arm/arm32/lib/$i; --done -+ -+diff -u ../linux/arch/arm/lib/findbit.S xen/arch/arm/arm32/lib/findbit.S - - --------------------------------------------------------------------- - -diff --git a/xen/arch/arm/arm32/lib/Makefile b/xen/arch/arm/arm32/lib/Makefile -index e9fbc595b9..b1457c89dc 100644 ---- a/xen/arch/arm/arm32/lib/Makefile -+++ b/xen/arch/arm/arm32/lib/Makefile -@@ -1,6 +1,5 @@ - obj-y += memcpy.o memmove.o memset.o memchr.o memzero.o --obj-y += findbit.o setbit.o --obj-y += setbit.o clearbit.o changebit.o --obj-y += testsetbit.o testclearbit.o testchangebit.o -+obj-y += findbit.o -+obj-y += bitops.o - obj-y += strchr.o strrchr.o - obj-y += lib1funcs.o lshrdi3.o div64.o -diff --git a/xen/arch/arm/arm32/lib/bitops.c b/xen/arch/arm/arm32/lib/bitops.c -new file mode 100644 -index 0000000000..c69bb53037 ---- /dev/null -+++ b/xen/arch/arm/arm32/lib/bitops.c -@@ -0,0 +1,98 @@ -+/* -+ * Copyright (C) 2018 ARM Ltd. -+ * -+ * 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 distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+ -+#include -+#include -+#include -+ -+/* -+ * The atomic bit operations pass the number of bit in a signed number -+ * (not sure why). This has the drawback to increase the complexity of -+ * the resulting assembly. -+ * -+ * To generate simpler code, the number of bit (nr) will be cast to -+ * unsigned int. -+ * -+ * XXX: Rework the interface to use unsigned int. -+ */ -+ -+#define bitop(name, instr) \ -+void name(int nr, volatile void *p) \ -+{ \ -+ volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ -+ const uint32_t mask = BIT_MASK((unsigned int)nr); \ -+ unsigned long res, tmp; \ -+ \ -+ ASSERT(((vaddr_t)p & 0x3) == 0); \ -+ prefetchw((const void *)ptr); \ -+ \ -+ do \ -+ { \ -+ asm volatile ("// " __stringify(name) "\n" \ -+ " ldrex %2, %1\n" \ -+ " " __stringify(instr) " %2, %2, %3\n" \ -+ " strex %0, %2, %1\n" \ -+ : "=&r" (res), "+Qo" (*ptr), "=&r" (tmp) \ -+ : "r" (mask)); \ -+ } while ( res ); \ -+} -+ -+#define testop(name, instr) \ -+int name(int nr, volatile void *p) \ -+{ \ -+ volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ -+ unsigned int bit = (unsigned int)nr % BITS_PER_WORD; \ -+ const uint32_t mask = BIT_MASK(bit); \ -+ unsigned long res, tmp; \ -+ int oldbit; \ -+ \ -+ ASSERT(((vaddr_t)p & 0x3) == 0); \ -+ smp_mb(); \ -+ \ -+ prefetchw((const void *)ptr); \ -+ \ -+ do \ -+ { \ -+ asm volatile ("// " __stringify(name) "\n" \ -+ " ldrex %3, %2\n" \ -+ " lsr %1, %3, %5 // Save old value of bit\n" \ -+ " " __stringify(instr) " %3, %3, %4 // Toggle bit\n" \ -+ " strex %0, %3, %2\n" \ -+ : "=&r" (res), "=&r" (oldbit), "+Qo" (*ptr), "=&r" (tmp) \ -+ : "r" (mask), "r" (bit)); \ -+ } while ( res ); \ -+ \ -+ smp_mb(); \ -+ \ -+ return oldbit & 1; \ -+} \ -+ -+bitop(change_bit, eor) -+bitop(clear_bit, bic) -+bitop(set_bit, orr) -+ -+testop(test_and_change_bit, eor) -+testop(test_and_clear_bit, bic) -+testop(test_and_set_bit, orr) -+ -+/* -+ * Local variables: -+ * mode: C -+ * c-file-style: "BSD" -+ * c-basic-offset: 4 -+ * indent-tabs-mode: nil -+ * End: -+ */ -diff --git a/xen/arch/arm/arm32/lib/bitops.h b/xen/arch/arm/arm32/lib/bitops.h -deleted file mode 100644 -index d5e13476f4..0000000000 ---- a/xen/arch/arm/arm32/lib/bitops.h -+++ /dev/null -@@ -1,104 +0,0 @@ -- --#if __LINUX_ARM_ARCH__ >= 6 -- .macro bitop, name, instr --ENTRY( \name ) --UNWIND( .fnstart ) -- ands ip, r1, #3 -- strneb r1, [ip] @ assert word-aligned -- mov r2, #1 -- and r3, r0, #31 @ Get bit offset -- mov r0, r0, lsr #5 -- add r1, r1, r0, lsl #2 @ Get word offset --#if __LINUX_ARM_ARCH__ >= 7 && defined(CONFIG_SMP) -- .arch_extension mp -- ALT_SMP(W(pldw) [r1]) -- ALT_UP(W(nop)) --#endif -- mov r3, r2, lsl r3 --1: ldrex r2, [r1] -- \instr r2, r2, r3 -- strex r0, r2, [r1] -- cmp r0, #0 -- bne 1b -- bx lr --UNWIND( .fnend ) --ENDPROC(\name ) -- .endm -- -- .macro testop, name, instr, store --ENTRY( \name ) --UNWIND( .fnstart ) -- ands ip, r1, #3 -- strneb r1, [ip] @ assert word-aligned -- mov r2, #1 -- and r3, r0, #31 @ Get bit offset -- mov r0, r0, lsr #5 -- add r1, r1, r0, lsl #2 @ Get word offset -- mov r3, r2, lsl r3 @ create mask -- smp_dmb --#if __LINUX_ARM_ARCH__ >= 7 && defined(CONFIG_SMP) -- .arch_extension mp -- ALT_SMP(W(pldw) [r1]) -- ALT_UP(W(nop)) --#endif --1: ldrex r2, [r1] -- ands r0, r2, r3 @ save old value of bit -- \instr r2, r2, r3 @ toggle bit -- strex ip, r2, [r1] -- cmp ip, #0 -- bne 1b -- smp_dmb -- cmp r0, #0 -- movne r0, #1 --2: bx lr --UNWIND( .fnend ) --ENDPROC(\name ) -- .endm --#else -- .macro bitop, name, instr --ENTRY( \name ) --UNWIND( .fnstart ) -- ands ip, r1, #3 -- strneb r1, [ip] @ assert word-aligned -- and r2, r0, #31 -- mov r0, r0, lsr #5 -- mov r3, #1 -- mov r3, r3, lsl r2 -- save_and_disable_irqs ip -- ldr r2, [r1, r0, lsl #2] -- \instr r2, r2, r3 -- str r2, [r1, r0, lsl #2] -- restore_irqs ip -- mov pc, lr --UNWIND( .fnend ) --ENDPROC(\name ) -- .endm -- --/** -- * testop - implement a test_and_xxx_bit operation. -- * @instr: operational instruction -- * @store: store instruction -- * -- * Note: we can trivially conditionalise the store instruction -- * to avoid dirtying the data cache. -- */ -- .macro testop, name, instr, store --ENTRY( \name ) --UNWIND( .fnstart ) -- ands ip, r1, #3 -- strneb r1, [ip] @ assert word-aligned -- and r3, r0, #31 -- mov r0, r0, lsr #5 -- save_and_disable_irqs ip -- ldr r2, [r1, r0, lsl #2]! -- mov r0, #1 -- tst r2, r0, lsl r3 -- \instr r2, r2, r0, lsl r3 -- \store r2, [r1] -- moveq r0, #0 -- restore_irqs ip -- mov pc, lr --UNWIND( .fnend ) --ENDPROC(\name ) -- .endm --#endif -diff --git a/xen/arch/arm/arm32/lib/changebit.S b/xen/arch/arm/arm32/lib/changebit.S -deleted file mode 100644 -index 6e4ae7594a..0000000000 ---- a/xen/arch/arm/arm32/lib/changebit.S -+++ /dev/null -@@ -1,14 +0,0 @@ --/* -- * linux/arch/arm/lib/changebit.S -- * -- * Copyright (C) 1995-1996 Russell King -- * -- * 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. -- */ --#include "assembler.h" --#include "bitops.h" -- .text -- --bitop _change_bit, eor -diff --git a/xen/arch/arm/arm32/lib/clearbit.S b/xen/arch/arm/arm32/lib/clearbit.S -deleted file mode 100644 -index fda553f246..0000000000 ---- a/xen/arch/arm/arm32/lib/clearbit.S -+++ /dev/null -@@ -1,14 +0,0 @@ --/* -- * linux/arch/arm/lib/clearbit.S -- * -- * Copyright (C) 1995-1996 Russell King -- * -- * 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. -- */ --#include "assembler.h" --#include "bitops.h" -- .text -- --bitop _clear_bit, bic -diff --git a/xen/arch/arm/arm32/lib/setbit.S b/xen/arch/arm/arm32/lib/setbit.S -deleted file mode 100644 -index d52f0ab65c..0000000000 ---- a/xen/arch/arm/arm32/lib/setbit.S -+++ /dev/null -@@ -1,15 +0,0 @@ --/* -- * linux/arch/arm/lib/setbit.S -- * -- * Copyright (C) 1995-1996 Russell King -- * -- * 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. -- */ -- --#include "assembler.h" --#include "bitops.h" -- .text -- --bitop _set_bit, orr -diff --git a/xen/arch/arm/arm32/lib/testchangebit.S b/xen/arch/arm/arm32/lib/testchangebit.S -deleted file mode 100644 -index d83b04c057..0000000000 ---- a/xen/arch/arm/arm32/lib/testchangebit.S -+++ /dev/null -@@ -1,15 +0,0 @@ --/* -- * linux/arch/arm/lib/testchangebit.S -- * -- * Copyright (C) 1995-1996 Russell King -- * -- * 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. -- */ -- --#include "assembler.h" --#include "bitops.h" -- .text -- --testop _test_and_change_bit, eor, str -diff --git a/xen/arch/arm/arm32/lib/testclearbit.S b/xen/arch/arm/arm32/lib/testclearbit.S -deleted file mode 100644 -index 6f5b7b92d1..0000000000 ---- a/xen/arch/arm/arm32/lib/testclearbit.S -+++ /dev/null -@@ -1,15 +0,0 @@ --/* -- * linux/arch/arm/lib/testclearbit.S -- * -- * Copyright (C) 1995-1996 Russell King -- * -- * 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. -- */ -- --#include "assembler.h" --#include "bitops.h" -- .text -- --testop _test_and_clear_bit, bicne, strne -diff --git a/xen/arch/arm/arm32/lib/testsetbit.S b/xen/arch/arm/arm32/lib/testsetbit.S -deleted file mode 100644 -index 30425a842a..0000000000 ---- a/xen/arch/arm/arm32/lib/testsetbit.S -+++ /dev/null -@@ -1,15 +0,0 @@ --/* -- * linux/arch/arm/lib/testsetbit.S -- * -- * Copyright (C) 1995-1996 Russell King -- * -- * 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. -- */ -- --#include "assembler.h" --#include "bitops.h" -- .text -- --testop _test_and_set_bit, orreq, streq -diff --git a/xen/include/asm-arm/arm32/bitops.h b/xen/include/asm-arm/arm32/bitops.h -index 8be3564540..67c4c3f55c 100644 ---- a/xen/include/asm-arm/arm32/bitops.h -+++ b/xen/include/asm-arm/arm32/bitops.h -@@ -1,19 +1,12 @@ - #ifndef _ARM_ARM32_BITOPS_H - #define _ARM_ARM32_BITOPS_H - --extern void _set_bit(int nr, volatile void * p); --extern void _clear_bit(int nr, volatile void * p); --extern void _change_bit(int nr, volatile void * p); --extern int _test_and_set_bit(int nr, volatile void * p); --extern int _test_and_clear_bit(int nr, volatile void * p); --extern int _test_and_change_bit(int nr, volatile void * p); -- --#define set_bit(n,p) _set_bit(n,p) --#define clear_bit(n,p) _clear_bit(n,p) --#define change_bit(n,p) _change_bit(n,p) --#define test_and_set_bit(n,p) _test_and_set_bit(n,p) --#define test_and_clear_bit(n,p) _test_and_clear_bit(n,p) --#define test_and_change_bit(n,p) _test_and_change_bit(n,p) -+extern void set_bit(int nr, volatile void * p); -+extern void clear_bit(int nr, volatile void * p); -+extern void change_bit(int nr, volatile void * p); -+extern int test_and_set_bit(int nr, volatile void * p); -+extern int test_and_clear_bit(int nr, volatile void * p); -+extern int test_and_change_bit(int nr, volatile void * p); - - #define flsl fls - --- -2.17.1 - diff --git a/xsa295-4.12-07.patch b/xsa295-4.12-07.patch deleted file mode 100644 index 017a0c7..0000000 --- a/xsa295-4.12-07.patch +++ /dev/null @@ -1,83 +0,0 @@ -From 244df788936cb47ee5479617ebf1d1e8b656266b Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:20 +0100 -Subject: [PATCH v2 4.12 07/17] xen/arm: bitops: Consolidate prototypes in one - place - -The prototype are the same between arm32 and arm64. Consolidate them in -asm-arm/bitops.h. - -This change will help the introductions of new helpers in a follow-up -patch. - -This is part of XSA-295. - -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini ---- - xen/include/asm-arm/arm32/bitops.h | 7 ------- - xen/include/asm-arm/arm64/bitops.h | 10 ---------- - xen/include/asm-arm/bitops.h | 8 ++++++++ - 3 files changed, 8 insertions(+), 17 deletions(-) - -diff --git a/xen/include/asm-arm/arm32/bitops.h b/xen/include/asm-arm/arm32/bitops.h -index 67c4c3f55c..57938a5874 100644 ---- a/xen/include/asm-arm/arm32/bitops.h -+++ b/xen/include/asm-arm/arm32/bitops.h -@@ -1,13 +1,6 @@ - #ifndef _ARM_ARM32_BITOPS_H - #define _ARM_ARM32_BITOPS_H - --extern void set_bit(int nr, volatile void * p); --extern void clear_bit(int nr, volatile void * p); --extern void change_bit(int nr, volatile void * p); --extern int test_and_set_bit(int nr, volatile void * p); --extern int test_and_clear_bit(int nr, volatile void * p); --extern int test_and_change_bit(int nr, volatile void * p); -- - #define flsl fls - - /* -diff --git a/xen/include/asm-arm/arm64/bitops.h b/xen/include/asm-arm/arm64/bitops.h -index 6bf1922680..6cc224ad13 100644 ---- a/xen/include/asm-arm/arm64/bitops.h -+++ b/xen/include/asm-arm/arm64/bitops.h -@@ -1,16 +1,6 @@ - #ifndef _ARM_ARM64_BITOPS_H - #define _ARM_ARM64_BITOPS_H - --/* -- * Little endian assembly atomic bitops. -- */ --extern void set_bit(int nr, volatile void *p); --extern void clear_bit(int nr, volatile void *p); --extern void change_bit(int nr, volatile void *p); --extern int test_and_set_bit(int nr, volatile void *p); --extern int test_and_clear_bit(int nr, volatile void *p); --extern int test_and_change_bit(int nr, volatile void *p); -- - /* Based on linux/include/asm-generic/bitops/builtin-__ffs.h */ - /** - * __ffs - find first bit in word. -diff --git a/xen/include/asm-arm/bitops.h b/xen/include/asm-arm/bitops.h -index 1cbfb9edb2..c69b08adf6 100644 ---- a/xen/include/asm-arm/bitops.h -+++ b/xen/include/asm-arm/bitops.h -@@ -38,6 +38,14 @@ - # error "unknown ARM variant" - #endif - -+/* Atomics bitops */ -+void set_bit(int nr, volatile void *p); -+void clear_bit(int nr, volatile void *p); -+void change_bit(int nr, volatile void *p); -+int test_and_set_bit(int nr, volatile void *p); -+int test_and_clear_bit(int nr, volatile void *p); -+int test_and_change_bit(int nr, volatile void *p); -+ - /** - * __test_and_set_bit - Set a bit and return its old value - * @nr: Bit to set --- -2.17.1 - diff --git a/xsa295-4.12-08.patch b/xsa295-4.12-08.patch deleted file mode 100644 index b5b2327..0000000 --- a/xsa295-4.12-08.patch +++ /dev/null @@ -1,145 +0,0 @@ -From 25f650e4dbf25f1aff1a4c9d509ca7f0ed8da104 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Wed, 22 May 2019 13:37:53 -0700 -Subject: [PATCH v2 4.12 08/17] xen/arm64: cmpxchg: Simplify the cmpxchg - implementation - -The only difference between each case of the cmpxchg is the size of -used. Rather than duplicating the code, provide a macro to generate each -cases. - -This makes the code easier to read and modify. - -This is part of XSA-295. - -Signed-off-by; Julien Grall -Reviewed-by: Stefano Stabellini -Signed-off-by: Stefano Stabellini ---- - xen/include/asm-arm/arm64/cmpxchg.h | 98 +++++++++++------------------ - 1 file changed, 36 insertions(+), 62 deletions(-) - -diff --git a/xen/include/asm-arm/arm64/cmpxchg.h b/xen/include/asm-arm/arm64/cmpxchg.h -index ae42b2f5ff..393fbca6a5 100644 ---- a/xen/include/asm-arm/arm64/cmpxchg.h -+++ b/xen/include/asm-arm/arm64/cmpxchg.h -@@ -61,80 +61,54 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size - __ret; \ - }) - --extern void __bad_cmpxchg(volatile void *ptr, int size); -+extern unsigned long __bad_cmpxchg(volatile void *ptr, int size); -+ -+#define __CMPXCHG_CASE(w, sz, name) \ -+static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ -+ unsigned long old, \ -+ unsigned long new) \ -+{ \ -+ unsigned long res, oldval; \ -+ \ -+ do { \ -+ asm volatile("// __cmpxchg_case_" #name "\n" \ -+ " ldxr" #sz " %" #w "1, %2\n" \ -+ " mov %w0, #0\n" \ -+ " cmp %" #w "1, %" #w "3\n" \ -+ " b.ne 1f\n" \ -+ " stxr" #sz " %w0, %" #w "4, %2\n" \ -+ "1:\n" \ -+ : "=&r" (res), "=&r" (oldval), \ -+ "+Q" (*(unsigned long *)ptr) \ -+ : "Ir" (old), "r" (new) \ -+ : "cc"); \ -+ } while (res); \ -+ \ -+ return oldval; \ -+} -+ -+__CMPXCHG_CASE(w, b, 1) -+__CMPXCHG_CASE(w, h, 2) -+__CMPXCHG_CASE(w, , 4) -+__CMPXCHG_CASE( , , 8) - - static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, - unsigned long new, int size) - { -- unsigned long oldval = 0, res; -- - switch (size) { - case 1: -- do { -- asm volatile("// __cmpxchg1\n" -- " ldxrb %w1, %2\n" -- " mov %w0, #0\n" -- " cmp %w1, %w3\n" -- " b.ne 1f\n" -- " stxrb %w0, %w4, %2\n" -- "1:\n" -- : "=&r" (res), "=&r" (oldval), "+Q" (*(u8 *)ptr) -- : "Ir" (old), "r" (new) -- : "cc"); -- } while (res); -- break; -- -+ return __cmpxchg_case_1(ptr, old, new); - case 2: -- do { -- asm volatile("// __cmpxchg2\n" -- " ldxrh %w1, %2\n" -- " mov %w0, #0\n" -- " cmp %w1, %w3\n" -- " b.ne 1f\n" -- " stxrh %w0, %w4, %2\n" -- "1:\n" -- : "=&r" (res), "=&r" (oldval), "+Q" (*(u16 *)ptr) -- : "Ir" (old), "r" (new) -- : "cc"); -- } while (res); -- break; -- -+ return __cmpxchg_case_2(ptr, old, new); - case 4: -- do { -- asm volatile("// __cmpxchg4\n" -- " ldxr %w1, %2\n" -- " mov %w0, #0\n" -- " cmp %w1, %w3\n" -- " b.ne 1f\n" -- " stxr %w0, %w4, %2\n" -- "1:\n" -- : "=&r" (res), "=&r" (oldval), "+Q" (*(u32 *)ptr) -- : "Ir" (old), "r" (new) -- : "cc"); -- } while (res); -- break; -- -+ return __cmpxchg_case_4(ptr, old, new); - case 8: -- do { -- asm volatile("// __cmpxchg8\n" -- " ldxr %1, %2\n" -- " mov %w0, #0\n" -- " cmp %1, %3\n" -- " b.ne 1f\n" -- " stxr %w0, %4, %2\n" -- "1:\n" -- : "=&r" (res), "=&r" (oldval), "+Q" (*(u64 *)ptr) -- : "Ir" (old), "r" (new) -- : "cc"); -- } while (res); -- break; -- -+ return __cmpxchg_case_8(ptr, old, new); - default: -- __bad_cmpxchg(ptr, size); -- oldval = 0; -+ return __bad_cmpxchg(ptr, size); - } - -- return oldval; -+ ASSERT_UNREACHABLE(); - } - - static inline unsigned long __cmpxchg_mb(volatile void *ptr, unsigned long old, --- -2.17.1 - diff --git a/xsa295-4.12-09.patch b/xsa295-4.12-09.patch deleted file mode 100644 index 1e2b9d1..0000000 --- a/xsa295-4.12-09.patch +++ /dev/null @@ -1,135 +0,0 @@ -From f33610438bb6586eb665922d9f32bb2889220b2b Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:22 +0100 -Subject: [PATCH v2 4.12 09/17] xen/arm32: cmpxchg: Simplify the cmpxchg - implementation - -The only difference between each case of the cmpxchg is the size of -used. Rather than duplicating the code, provide a macro to generate each -cases. - -This makes the code easier to read and modify. - -While doing the rework, the case for 64-bit cmpxchg is removed. This is -unused today (already commented) and it would not be possible to use -it directly. - -This is part of XSA-295. - -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini ---- - xen/include/asm-arm/arm32/cmpxchg.h | 84 +++++++++++------------------ - 1 file changed, 31 insertions(+), 53 deletions(-) - -diff --git a/xen/include/asm-arm/arm32/cmpxchg.h b/xen/include/asm-arm/arm32/cmpxchg.h -index 03e0bed3a6..471a9e3a3f 100644 ---- a/xen/include/asm-arm/arm32/cmpxchg.h -+++ b/xen/include/asm-arm/arm32/cmpxchg.h -@@ -52,72 +52,50 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size - * indicated by comparing RETURN with OLD. - */ - --extern void __bad_cmpxchg(volatile void *ptr, int size); -+extern unsigned long __bad_cmpxchg(volatile void *ptr, int size); -+ -+#define __CMPXCHG_CASE(sz, name) \ -+static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ -+ unsigned long old, \ -+ unsigned long new) \ -+{ \ -+ unsigned long oldval, res; \ -+ \ -+ do { \ -+ asm volatile("@ __cmpxchg_case_" #name "\n" \ -+ " ldrex" #sz " %1, [%2]\n" \ -+ " mov %0, #0\n" \ -+ " teq %1, %3\n" \ -+ " strex" #sz "eq %0, %4, [%2]\n" \ -+ : "=&r" (res), "=&r" (oldval) \ -+ : "r" (ptr), "Ir" (old), "r" (new) \ -+ : "memory", "cc"); \ -+ } while (res); \ -+ \ -+ return oldval; \ -+} -+ -+__CMPXCHG_CASE(b, 1) -+__CMPXCHG_CASE(h, 2) -+__CMPXCHG_CASE( , 4) - - static always_inline unsigned long __cmpxchg( - volatile void *ptr, unsigned long old, unsigned long new, int size) - { -- unsigned long oldval, res; -- - prefetchw((const void *)ptr); - - switch (size) { - case 1: -- do { -- asm volatile("@ __cmpxchg1\n" -- " ldrexb %1, [%2]\n" -- " mov %0, #0\n" -- " teq %1, %3\n" -- " strexbeq %0, %4, [%2]\n" -- : "=&r" (res), "=&r" (oldval) -- : "r" (ptr), "Ir" (old), "r" (new) -- : "memory", "cc"); -- } while (res); -- break; -+ return __cmpxchg_case_1(ptr, old, new); - case 2: -- do { -- asm volatile("@ __cmpxchg2\n" -- " ldrexh %1, [%2]\n" -- " mov %0, #0\n" -- " teq %1, %3\n" -- " strexheq %0, %4, [%2]\n" -- : "=&r" (res), "=&r" (oldval) -- : "r" (ptr), "Ir" (old), "r" (new) -- : "memory", "cc"); -- } while (res); -- break; -+ return __cmpxchg_case_2(ptr, old, new); - case 4: -- do { -- asm volatile("@ __cmpxchg4\n" -- " ldrex %1, [%2]\n" -- " mov %0, #0\n" -- " teq %1, %3\n" -- " strexeq %0, %4, [%2]\n" -- : "=&r" (res), "=&r" (oldval) -- : "r" (ptr), "Ir" (old), "r" (new) -- : "memory", "cc"); -- } while (res); -- break; --#if 0 -- case 8: -- do { -- asm volatile("@ __cmpxchg8\n" -- " ldrexd %1, [%2]\n" -- " mov %0, #0\n" -- " teq %1, %3\n" -- " strexdeq %0, %4, [%2]\n" -- : "=&r" (res), "=&r" (oldval) -- : "r" (ptr), "Ir" (old), "r" (new) -- : "memory", "cc"); -- } while (res); -- break; --#endif -+ return __cmpxchg_case_4(ptr, old, new); - default: -- __bad_cmpxchg(ptr, size); -- oldval = 0; -+ return __bad_cmpxchg(ptr, size); - } - -- return oldval; -+ ASSERT_UNREACHABLE(); - } - - static always_inline unsigned long __cmpxchg_mb(volatile void *ptr, --- -2.17.1 - diff --git a/xsa295-4.12-10.patch b/xsa295-4.12-10.patch deleted file mode 100644 index 9c2d235..0000000 --- a/xsa295-4.12-10.patch +++ /dev/null @@ -1,255 +0,0 @@ -From 7cd0aeaa3ef96180ed251d66a4aff8ffad8c3dfe Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:23 +0100 -Subject: [PATCH v2 4.12 10/17] xen/arm: bitops: Implement a new set of helpers - that can timeout - -Exclusive load-store atomics should only be used between trusted -threads. As not all the guests are trusted, it may be possible to DoS -Xen when updating shared memory with guest atomically. - -To prevent the infinite loop, we introduce a new set of helpers that can -timeout. The timeout is based on the maximum number of iterations. - -They will be used in follow-up patch to make atomic operations -on shared memory safe. - -This is part of XSA-295. - -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini ---- - xen/arch/arm/arm32/lib/bitops.c | 52 ++++++++++++++++++++++++++++----- - xen/arch/arm/arm64/lib/bitops.c | 52 ++++++++++++++++++++++++++++----- - xen/include/asm-arm/bitops.h | 28 +++++++++++++++++- - 3 files changed, 117 insertions(+), 15 deletions(-) - -diff --git a/xen/arch/arm/arm32/lib/bitops.c b/xen/arch/arm/arm32/lib/bitops.c -index c69bb53037..08750314fc 100644 ---- a/xen/arch/arm/arm32/lib/bitops.c -+++ b/xen/arch/arm/arm32/lib/bitops.c -@@ -30,7 +30,8 @@ - */ - - #define bitop(name, instr) \ --void name(int nr, volatile void *p) \ -+static always_inline bool int_##name(int nr, volatile void *p, bool timeout,\ -+ unsigned int max_try) \ - { \ - volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ - const uint32_t mask = BIT_MASK((unsigned int)nr); \ -@@ -47,17 +48,33 @@ void name(int nr, volatile void *p) \ - " strex %0, %2, %1\n" \ - : "=&r" (res), "+Qo" (*ptr), "=&r" (tmp) \ - : "r" (mask)); \ -- } while ( res ); \ -+ \ -+ if ( !res ) \ -+ break; \ -+ } while ( !timeout || ((--max_try) > 0) ); \ -+ \ -+ return !res; \ -+} \ -+ \ -+void name(int nr, volatile void *p) \ -+{ \ -+ if ( !int_##name(nr, p, false, 0) ) \ -+ ASSERT_UNREACHABLE(); \ -+} \ -+ \ -+bool name##_timeout(int nr, volatile void *p, unsigned int max_try) \ -+{ \ -+ return int_##name(nr, p, true, max_try); \ - } - - #define testop(name, instr) \ --int name(int nr, volatile void *p) \ -+static always_inline bool int_##name(int nr, volatile void *p, int *oldbit, \ -+ bool timeout, unsigned int max_try) \ - { \ - volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ - unsigned int bit = (unsigned int)nr % BITS_PER_WORD; \ - const uint32_t mask = BIT_MASK(bit); \ - unsigned long res, tmp; \ -- int oldbit; \ - \ - ASSERT(((vaddr_t)p & 0x3) == 0); \ - smp_mb(); \ -@@ -71,14 +88,35 @@ int name(int nr, volatile void *p) \ - " lsr %1, %3, %5 // Save old value of bit\n" \ - " " __stringify(instr) " %3, %3, %4 // Toggle bit\n" \ - " strex %0, %3, %2\n" \ -- : "=&r" (res), "=&r" (oldbit), "+Qo" (*ptr), "=&r" (tmp) \ -+ : "=&r" (res), "=&r" (*oldbit), "+Qo" (*ptr), "=&r" (tmp) \ - : "r" (mask), "r" (bit)); \ -- } while ( res ); \ -+ \ -+ if ( !res ) \ -+ break; \ -+ } while ( !timeout || ((--max_try) > 0) ); \ - \ - smp_mb(); \ - \ -- return oldbit & 1; \ -+ *oldbit &= 1; \ -+ \ -+ return !res; \ -+} \ -+ \ -+int name(int nr, volatile void *p) \ -+{ \ -+ int oldbit; \ -+ \ -+ if ( !int_##name(nr, p, &oldbit, false, 0) ) \ -+ ASSERT_UNREACHABLE(); \ -+ \ -+ return oldbit; \ - } \ -+ \ -+bool name##_timeout(int nr, volatile void *p, \ -+ int *oldbit, unsigned int max_try) \ -+{ \ -+ return int_##name(nr, p, oldbit, true, max_try); \ -+} - - bitop(change_bit, eor) - bitop(clear_bit, bic) -diff --git a/xen/arch/arm/arm64/lib/bitops.c b/xen/arch/arm/arm64/lib/bitops.c -index b1c681c642..78bf4ed8c5 100644 ---- a/xen/arch/arm/arm64/lib/bitops.c -+++ b/xen/arch/arm/arm64/lib/bitops.c -@@ -29,7 +29,8 @@ - */ - - #define bitop(name, instr) \ --void name(int nr, volatile void *p) \ -+static always_inline bool int_##name(int nr, volatile void *p, bool timeout,\ -+ unsigned int max_try) \ - { \ - volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ - const uint32_t mask = BIT_MASK((unsigned int)nr); \ -@@ -43,17 +44,33 @@ void name(int nr, volatile void *p) \ - " stxr %w0, %w2, %1\n" \ - : "=&r" (res), "+Q" (*ptr), "=&r" (tmp) \ - : "r" (mask)); \ -- } while ( res ); \ -+ \ -+ if ( !res ) \ -+ break; \ -+ } while ( !timeout || ((--max_try) > 0) ); \ -+ \ -+ return !res; \ - } \ -+ \ -+void name(int nr, volatile void *p) \ -+{ \ -+ if ( !int_##name(nr, p, false, 0) ) \ -+ ASSERT_UNREACHABLE(); \ -+} \ -+ \ -+bool name##_timeout(int nr, volatile void *p, unsigned int max_try) \ -+{ \ -+ return int_##name(nr, p, true, max_try); \ -+} - - #define testop(name, instr) \ --int name(int nr, volatile void *p) \ -+static always_inline bool int_##name(int nr, volatile void *p, int *oldbit, \ -+ bool timeout, unsigned int max_try) \ - { \ - volatile uint32_t *ptr = (uint32_t *)p + BIT_WORD((unsigned int)nr); \ - unsigned int bit = (unsigned int)nr % BITS_PER_WORD; \ - const uint32_t mask = BIT_MASK(bit); \ - unsigned long res, tmp; \ -- unsigned long oldbit; \ - \ - do \ - { \ -@@ -62,14 +79,35 @@ int name(int nr, volatile void *p) \ - " lsr %w1, %w3, %w5 // Save old value of bit\n" \ - " " __stringify(instr) " %w3, %w3, %w4 // Toggle bit\n" \ - " stlxr %w0, %w3, %2\n" \ -- : "=&r" (res), "=&r" (oldbit), "+Q" (*ptr), "=&r" (tmp) \ -+ : "=&r" (res), "=&r" (*oldbit), "+Q" (*ptr), "=&r" (tmp) \ - : "r" (mask), "r" (bit) \ - : "memory"); \ -- } while ( res ); \ -+ \ -+ if ( !res ) \ -+ break; \ -+ } while ( !timeout || ((--max_try) > 0) ); \ - \ - dmb(ish); \ - \ -- return oldbit & 1; \ -+ *oldbit &= 1; \ -+ \ -+ return !res; \ -+} \ -+ \ -+int name(int nr, volatile void *p) \ -+{ \ -+ int oldbit; \ -+ \ -+ if ( !int_##name(nr, p, &oldbit, false, 0) ) \ -+ ASSERT_UNREACHABLE(); \ -+ \ -+ return oldbit; \ -+} \ -+ \ -+bool name##_timeout(int nr, volatile void *p, \ -+ int *oldbit, unsigned int max_try) \ -+{ \ -+ return int_##name(nr, p, oldbit, true, max_try); \ - } - - bitop(change_bit, eor) -diff --git a/xen/include/asm-arm/bitops.h b/xen/include/asm-arm/bitops.h -index c69b08adf6..f6782b33be 100644 ---- a/xen/include/asm-arm/bitops.h -+++ b/xen/include/asm-arm/bitops.h -@@ -38,7 +38,14 @@ - # error "unknown ARM variant" - #endif - --/* Atomics bitops */ -+/* -+ * Atomic bitops -+ * -+ * The helpers below *should* only be used on memory shared between -+ * trusted threads or we know the memory cannot be accessed by another -+ * thread. -+ */ -+ - void set_bit(int nr, volatile void *p); - void clear_bit(int nr, volatile void *p); - void change_bit(int nr, volatile void *p); -@@ -46,6 +53,25 @@ int test_and_set_bit(int nr, volatile void *p); - int test_and_clear_bit(int nr, volatile void *p); - int test_and_change_bit(int nr, volatile void *p); - -+/* -+ * The helpers below may fail to update the memory if the action takes -+ * too long. -+ * -+ * @max_try: Maximum number of iterations -+ * -+ * The helpers will return true when the update has succeeded (i.e no -+ * timeout) and false if the update has failed. -+ */ -+bool set_bit_timeout(int nr, volatile void *p, unsigned int max_try); -+bool clear_bit_timeout(int nr, volatile void *p, unsigned int max_try); -+bool change_bit_timeout(int nr, volatile void *p, unsigned int max_try); -+bool test_and_set_bit_timeout(int nr, volatile void *p, -+ int *oldbit, unsigned int max_try); -+bool test_and_clear_bit_timeout(int nr, volatile void *p, -+ int *oldbit, unsigned int max_try); -+bool test_and_change_bit_timeout(int nr, volatile void *p, -+ int *oldbit, unsigned int max_try); -+ - /** - * __test_and_set_bit - Set a bit and return its old value - * @nr: Bit to set --- -2.17.1 - diff --git a/xsa295-4.12-11.patch b/xsa295-4.12-11.patch deleted file mode 100644 index b915abc..0000000 --- a/xsa295-4.12-11.patch +++ /dev/null @@ -1,258 +0,0 @@ -From 9a6ac9945d1d742185189a7d751d3ad10c8bed3f Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Wed, 22 May 2019 13:39:17 -0700 -Subject: [PATCH v2 4.12 11/17] xen/arm: cmpxchg: Provide a new helper that can - timeout - -Exclusive load-store atomics should only be used between trusted -threads. As not all the guests are trusted, it may be possible to DoS -Xen when updating shared memory with guest atomically. - -To prevent the infinite loop, we introduce a new helper that can timeout. -The timeout is based on the maximum number of iterations. - -It will be used in follow-up patch to make atomic operations on shared -memory safe. - -This is part of XSA-295. - -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini -Signed-off-by: Stefano Stabellini ---- - xen/include/asm-arm/arm32/cmpxchg.h | 63 +++++++++++++++++++++----- - xen/include/asm-arm/arm64/cmpxchg.h | 70 ++++++++++++++++++++++------- - 2 files changed, 106 insertions(+), 27 deletions(-) - -diff --git a/xen/include/asm-arm/arm32/cmpxchg.h b/xen/include/asm-arm/arm32/cmpxchg.h -index 471a9e3a3f..49ca2a0d7a 100644 ---- a/xen/include/asm-arm/arm32/cmpxchg.h -+++ b/xen/include/asm-arm/arm32/cmpxchg.h -@@ -55,11 +55,14 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size - extern unsigned long __bad_cmpxchg(volatile void *ptr, int size); - - #define __CMPXCHG_CASE(sz, name) \ --static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ -- unsigned long old, \ -- unsigned long new) \ -+static inline bool __cmpxchg_case_##name(volatile void *ptr, \ -+ unsigned long *old, \ -+ unsigned long new, \ -+ bool timeout, \ -+ unsigned int max_try) \ - { \ -- unsigned long oldval, res; \ -+ unsigned long oldval; \ -+ unsigned long res; \ - \ - do { \ - asm volatile("@ __cmpxchg_case_" #name "\n" \ -@@ -68,29 +71,35 @@ static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ - " teq %1, %3\n" \ - " strex" #sz "eq %0, %4, [%2]\n" \ - : "=&r" (res), "=&r" (oldval) \ -- : "r" (ptr), "Ir" (old), "r" (new) \ -+ : "r" (ptr), "Ir" (*old), "r" (new) \ - : "memory", "cc"); \ -- } while (res); \ - \ -- return oldval; \ -+ if (!res) \ -+ break; \ -+ } while (!timeout || ((--max_try) > 0)); \ -+ \ -+ *old = oldval; \ -+ \ -+ return !res; \ - } - - __CMPXCHG_CASE(b, 1) - __CMPXCHG_CASE(h, 2) - __CMPXCHG_CASE( , 4) - --static always_inline unsigned long __cmpxchg( -- volatile void *ptr, unsigned long old, unsigned long new, int size) -+static always_inline bool __int_cmpxchg(volatile void *ptr, unsigned long *old, -+ unsigned long new, int size, -+ bool timeout, unsigned int max_try) - { - prefetchw((const void *)ptr); - - switch (size) { - case 1: -- return __cmpxchg_case_1(ptr, old, new); -+ return __cmpxchg_case_1(ptr, old, new, timeout, max_try); - case 2: -- return __cmpxchg_case_2(ptr, old, new); -+ return __cmpxchg_case_2(ptr, old, new, timeout, max_try); - case 4: -- return __cmpxchg_case_4(ptr, old, new); -+ return __cmpxchg_case_4(ptr, old, new, timeout, max_try); - default: - return __bad_cmpxchg(ptr, size); - } -@@ -98,6 +107,17 @@ static always_inline unsigned long __cmpxchg( - ASSERT_UNREACHABLE(); - } - -+static always_inline unsigned long __cmpxchg(volatile void *ptr, -+ unsigned long old, -+ unsigned long new, -+ int size) -+{ -+ if (!__int_cmpxchg(ptr, &old, new, size, false, 0)) -+ ASSERT_UNREACHABLE(); -+ -+ return old; -+} -+ - static always_inline unsigned long __cmpxchg_mb(volatile void *ptr, - unsigned long old, - unsigned long new, int size) -@@ -111,6 +131,25 @@ static always_inline unsigned long __cmpxchg_mb(volatile void *ptr, - return ret; - } - -+/* -+ * The helper may fail to update the memory if the action takes too long. -+ * -+ * @old: On call the value pointed contains the expected old value. It will be -+ * updated to the actual old value. -+ * @max_try: Maximum number of iterations -+ * -+ * The helper will return true when the update has succeeded (i.e no -+ * timeout) and false if the update has failed. -+ */ -+static always_inline bool __cmpxchg_mb_timeout(volatile void *ptr, -+ unsigned long *old, -+ unsigned long new, -+ int size, -+ unsigned int max_try) -+{ -+ return __int_cmpxchg(ptr, old, new, size, true, max_try); -+} -+ - #define cmpxchg(ptr,o,n) \ - ((__typeof__(*(ptr)))__cmpxchg_mb((ptr), \ - (unsigned long)(o), \ -diff --git a/xen/include/asm-arm/arm64/cmpxchg.h b/xen/include/asm-arm/arm64/cmpxchg.h -index 393fbca6a5..5bc2e1f786 100644 ---- a/xen/include/asm-arm/arm64/cmpxchg.h -+++ b/xen/include/asm-arm/arm64/cmpxchg.h -@@ -64,11 +64,14 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size - extern unsigned long __bad_cmpxchg(volatile void *ptr, int size); - - #define __CMPXCHG_CASE(w, sz, name) \ --static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ -- unsigned long old, \ -- unsigned long new) \ -+static inline bool __cmpxchg_case_##name(volatile void *ptr, \ -+ unsigned long *old, \ -+ unsigned long new, \ -+ bool timeout, \ -+ unsigned int max_try) \ - { \ -- unsigned long res, oldval; \ -+ unsigned long oldval; \ -+ unsigned long res; \ - \ - do { \ - asm volatile("// __cmpxchg_case_" #name "\n" \ -@@ -80,11 +83,16 @@ static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ - "1:\n" \ - : "=&r" (res), "=&r" (oldval), \ - "+Q" (*(unsigned long *)ptr) \ -- : "Ir" (old), "r" (new) \ -+ : "Ir" (*old), "r" (new) \ - : "cc"); \ -- } while (res); \ - \ -- return oldval; \ -+ if (!res) \ -+ break; \ -+ } while (!timeout || ((--max_try) > 0)); \ -+ \ -+ *old = oldval; \ -+ \ -+ return !res; \ - } - - __CMPXCHG_CASE(w, b, 1) -@@ -92,18 +100,19 @@ __CMPXCHG_CASE(w, h, 2) - __CMPXCHG_CASE(w, , 4) - __CMPXCHG_CASE( , , 8) - --static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, -- unsigned long new, int size) -+static always_inline bool __int_cmpxchg(volatile void *ptr, unsigned long *old, -+ unsigned long new, int size, -+ bool timeout, unsigned int max_try) - { - switch (size) { - case 1: -- return __cmpxchg_case_1(ptr, old, new); -+ return __cmpxchg_case_1(ptr, old, new, timeout, max_try); - case 2: -- return __cmpxchg_case_2(ptr, old, new); -+ return __cmpxchg_case_2(ptr, old, new, timeout, max_try); - case 4: -- return __cmpxchg_case_4(ptr, old, new); -+ return __cmpxchg_case_4(ptr, old, new, timeout, max_try); - case 8: -- return __cmpxchg_case_8(ptr, old, new); -+ return __cmpxchg_case_8(ptr, old, new, timeout, max_try); - default: - return __bad_cmpxchg(ptr, size); - } -@@ -111,8 +120,20 @@ static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, - ASSERT_UNREACHABLE(); - } - --static inline unsigned long __cmpxchg_mb(volatile void *ptr, unsigned long old, -- unsigned long new, int size) -+static always_inline unsigned long __cmpxchg(volatile void *ptr, -+ unsigned long old, -+ unsigned long new, -+ int size) -+{ -+ if (!__int_cmpxchg(ptr, &old, new, size, false, 0)) -+ ASSERT_UNREACHABLE(); -+ -+ return old; -+} -+ -+static always_inline unsigned long __cmpxchg_mb(volatile void *ptr, -+ unsigned long old, -+ unsigned long new, int size) - { - unsigned long ret; - -@@ -123,6 +144,25 @@ static inline unsigned long __cmpxchg_mb(volatile void *ptr, unsigned long old, - return ret; - } - -+/* -+ * The helper may fail to update the memory if the action takes too long. -+ * -+ * @old: On call the value pointed contains the expected old value. It will be -+ * updated to the actual old value. -+ * @max_try: Maximum number of iterations -+ * -+ * The helper will return true when the update has succeeded (i.e no -+ * timeout) and false if the update has failed. -+ */ -+static always_inline bool __cmpxchg_mb_timeout(volatile void *ptr, -+ unsigned long *old, -+ unsigned long new, -+ int size, -+ unsigned int max_try) -+{ -+ return __int_cmpxchg(ptr, old, new, size, true, max_try); -+} -+ - #define cmpxchg(ptr, o, n) \ - ({ \ - __typeof__(*(ptr)) __ret; \ --- -2.17.1 - diff --git a/xsa295-4.12-12.patch b/xsa295-4.12-12.patch deleted file mode 100644 index 3ff0716..0000000 --- a/xsa295-4.12-12.patch +++ /dev/null @@ -1,112 +0,0 @@ -From 07c47ec0a6aa0c76e6822127ea5ec5122c9282a0 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:25 +0100 -Subject: [PATCH v2 4.12 12/17] xen/arm: Turn on SILO mode by default on Arm - -On Arm, exclusive load-store atomics should only be used between trusted -thread. As not all the guests are trusted, it may be possible to DoS Xen -when updating shared memory with guest atomically. - -Recent patches introduced new helpers to update shared memory with guest -atomically. Those helpers relies on a memory region to be be shared with -Xen and a single guest. - -At the moment, nothing prevent a guest sharing a page with Xen and as -well with another guest (e.g via grant table). - -For the scope of the XSA, the quickest way is to deny communications -between unprivileged guest. So this patch is enabling and using SILO -mode by default on Arm. - -Users wanted finer graine policy could wrote their own Flask policy. - -This is part of XSA-295. - -Signed-off-by: Julien Grall -Acked-by: Jan Beulich ---- - xen/arch/arm/setup.c | 8 ++++++-- - xen/common/Kconfig | 3 ++- - xen/include/xsm/xsm.h | 5 +++++ - xen/xsm/xsm_core.c | 2 +- - 4 files changed, 14 insertions(+), 4 deletions(-) - -diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c -index 444857a967..a2aedc43e2 100644 ---- a/xen/arch/arm/setup.c -+++ b/xen/arch/arm/setup.c -@@ -39,6 +39,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -834,8 +835,11 @@ void __init start_xen(unsigned long boot_phys_offset, - - tasklet_subsys_init(); - -- -- xsm_dt_init(); -+ if ( xsm_dt_init() != 1 ) -+ warning_add("WARNING: SILO mode is not enabled.\n" -+ "It has implications on the security of the system,\n" -+ "unless the communications have been forbidden between\n" -+ "untrusted domains.\n"); - - init_maintenance_interrupt(); - init_timer_interrupt(); -diff --git a/xen/common/Kconfig b/xen/common/Kconfig -index 04384628bb..e9b1006852 100644 ---- a/xen/common/Kconfig -+++ b/xen/common/Kconfig -@@ -106,7 +106,7 @@ config XENOPROF - - config XSM - bool "Xen Security Modules support" -- default n -+ default ARM - ---help--- - Enables the security framework known as Xen Security Modules which - allows administrators fine-grained control over a Xen domain and -@@ -171,6 +171,7 @@ config XSM_SILO - choice - prompt "Default XSM implementation" - depends on XSM -+ default XSM_SILO_DEFAULT if XSM_SILO && ARM - default XSM_FLASK_DEFAULT if XSM_FLASK - default XSM_SILO_DEFAULT if XSM_SILO - default XSM_DUMMY_DEFAULT -diff --git a/xen/include/xsm/xsm.h b/xen/include/xsm/xsm.h -index 8a78d8abd3..fc9d6b5bf0 100644 ---- a/xen/include/xsm/xsm.h -+++ b/xen/include/xsm/xsm.h -@@ -741,6 +741,11 @@ extern int xsm_multiboot_policy_init(unsigned long *module_map, - #endif - - #ifdef CONFIG_HAS_DEVICE_TREE -+/* -+ * Initialize XSM -+ * -+ * On success, return 1 if using SILO mode else 0. -+ */ - extern int xsm_dt_init(void); - extern int xsm_dt_policy_init(void **policy_buffer, size_t *policy_size); - extern bool has_xsm_magic(paddr_t); -diff --git a/xen/xsm/xsm_core.c b/xen/xsm/xsm_core.c -index 201c354390..a319df253d 100644 ---- a/xen/xsm/xsm_core.c -+++ b/xen/xsm/xsm_core.c -@@ -167,7 +167,7 @@ int __init xsm_dt_init(void) - - xfree(policy_buffer); - -- return ret; -+ return ret ?: (xsm_bootparam == XSM_BOOTPARAM_SILO); - } - - /** --- -2.17.1 - diff --git a/xsa295-4.12-13.patch b/xsa295-4.12-13.patch deleted file mode 100644 index 68f5ae0..0000000 --- a/xsa295-4.12-13.patch +++ /dev/null @@ -1,277 +0,0 @@ -From 0566038dc84d91b962e5338a9c12e29c2d7d2e9b Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:26 +0100 -Subject: [PATCH v2 4.12 13/17] xen/bitops: Provide helpers to safely modify - guest memory atomically - -On Arm, exclusive load-store atomics should only be used between trusted -thread. As not all the guests are trusted, it may be possible to DoS Xen -when updating shared memory with guest atomically. - -This patch adds a new set of helper that will update the guest memory -safely. For x86, it is already possible to use the current helpers -safely. So just wrap them. - -For Arm, we will first attempt to update the guest memory with the loop -bounded by a maximum number of iterations. If it fails, we will pause the -domain and try again. - -Note that this heuristics assumes that a page can only be shared between -Xen and one domain. Not Xen and multiple domain. - -The maximum number of iterations is based on how many times a simple -load-store atomic operation can be executed in 1uS. The maximum value is -per-CPU to cater big.LITTLE and calculated when the CPU is booting. The -heuristic was randomly chosen and can be modified if impact too much -good-behaving guest. - -Note, while test_bit does not requires to use atomic operation, a -wrapper for test_bit was added for completeness. In this case, the -domain stays constified to avoid major rework in the caller for the -time-being. - -This is part of XSA-295. - -Signed-of-by: Julien Grall -Acked-by: Jan Beulich -Reviewed-by: Stefano Stabellini ---- - xen/arch/arm/Makefile | 1 + - xen/arch/arm/guest_atomics.c | 91 +++++++++++++++++++++++++++++ - xen/include/asm-arm/guest_atomics.h | 76 ++++++++++++++++++++++++ - xen/include/asm-x86/guest_atomics.h | 30 ++++++++++ - 4 files changed, 198 insertions(+) - create mode 100644 xen/arch/arm/guest_atomics.c - create mode 100644 xen/include/asm-arm/guest_atomics.h - create mode 100644 xen/include/asm-x86/guest_atomics.h - -diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile -index cb902cb6fe..872a155b60 100644 ---- a/xen/arch/arm/Makefile -+++ b/xen/arch/arm/Makefile -@@ -22,6 +22,7 @@ obj-$(CONFIG_GICV3) += gic-v3.o - obj-$(CONFIG_HAS_ITS) += gic-v3-its.o - obj-$(CONFIG_HAS_ITS) += gic-v3-lpi.o - obj-y += guestcopy.o -+obj-y += guest_atomics.o - obj-y += guest_walk.o - obj-y += hvm.o - obj-y += io.o -diff --git a/xen/arch/arm/guest_atomics.c b/xen/arch/arm/guest_atomics.c -new file mode 100644 -index 0000000000..1b78a062f0 ---- /dev/null -+++ b/xen/arch/arm/guest_atomics.c -@@ -0,0 +1,91 @@ -+/* -+ * arch/arm/guest_atomics.c -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License along with -+ * this program; If not, see . -+ */ -+#include -+ -+#include -+ -+DEFINE_PER_CPU_READ_MOSTLY(unsigned int, guest_safe_atomic_max); -+ -+/* -+ * Heuristic to find a safe upper-limit for load-store exclusive -+ * operations on memory shared with guest. -+ * -+ * At the moment, we calculate the number of iterations of a simple -+ * load-store atomic loop in 1uS. -+ */ -+static void calibrate_safe_atomic(void) -+{ -+ s_time_t deadline = NOW() + MICROSECS(1); -+ unsigned int counter = 0; -+ unsigned long mem = 0; -+ -+ do -+ { -+ unsigned long res, tmp; -+ -+#ifdef CONFIG_ARM_32 -+ asm volatile (" ldrex %2, %1\n" -+ " add %2, %2, #1\n" -+ " strex %0, %2, %1\n" -+ : "=&r" (res), "+Q" (mem), "=&r" (tmp)); -+#else -+ asm volatile (" ldxr %w2, %1\n" -+ " add %w2, %w2, #1\n" -+ " stxr %w0, %w2, %1\n" -+ : "=&r" (res), "+Q" (mem), "=&r" (tmp)); -+#endif -+ counter++; -+ } while (NOW() < deadline); -+ -+ this_cpu(guest_safe_atomic_max) = counter; -+ -+ printk(XENLOG_DEBUG -+ "CPU%u: Guest atomics will try %u times before pausing the domain\n", -+ smp_processor_id(), counter); -+} -+ -+static int cpu_guest_safe_atomic_callback(struct notifier_block *nfb, -+ unsigned long action, -+ void *hcpu) -+{ -+ if ( action == CPU_STARTING ) -+ calibrate_safe_atomic(); -+ -+ return NOTIFY_DONE; -+} -+ -+static struct notifier_block cpu_guest_safe_atomic_nfb = { -+ .notifier_call = cpu_guest_safe_atomic_callback, -+}; -+ -+static int __init guest_safe_atomic_init(void) -+{ -+ register_cpu_notifier(&cpu_guest_safe_atomic_nfb); -+ -+ calibrate_safe_atomic(); -+ -+ return 0; -+} -+presmp_initcall(guest_safe_atomic_init); -+ -+/* -+ * Local variables: -+ * mode: C -+ * c-file-style: "BSD" -+ * c-basic-offset: 4 -+ * indent-tabs-mode: nil -+ * End: -+ */ -diff --git a/xen/include/asm-arm/guest_atomics.h b/xen/include/asm-arm/guest_atomics.h -new file mode 100644 -index 0000000000..4f127fda41 ---- /dev/null -+++ b/xen/include/asm-arm/guest_atomics.h -@@ -0,0 +1,76 @@ -+#ifndef _ARM_GUEST_ATOMICS_H -+#define _ARM_GUEST_ATOMICS_H -+ -+#include -+#include -+ -+/* -+ * The guest atomics helpers shares the same logic. We first try to use -+ * the *_timeout version of the operation. If it didn't timeout, then we -+ * successfully updated the memory. Nothing else to do. -+ * -+ * If it did timeout, then it means we didn't manage to update the -+ * memory. This is possibly because the guest is misbehaving (i.e tight -+ * store loop) but can also happen for other reasons (i.e nested Xen). -+ * In that case pause the domain and retry the operation, this time -+ * without a timeout. -+ * -+ * Note, those helpers rely on other part of the code to prevent sharing -+ * a page between Xen and multiple domain. -+ */ -+ -+DECLARE_PER_CPU(unsigned int, guest_safe_atomic_max); -+ -+#define guest_bitop(name) \ -+static inline void guest_##name(struct domain *d, int nr, volatile void *p) \ -+{ \ -+ if ( name##_timeout(nr, p, this_cpu(guest_safe_atomic_max)) ) \ -+ return; \ -+ \ -+ domain_pause_nosync(d); \ -+ name(nr, p); \ -+ domain_unpause(d); \ -+} -+ -+#define guest_testop(name) \ -+static inline int guest_##name(struct domain *d, int nr, volatile void *p) \ -+{ \ -+ bool succeed; \ -+ int oldbit; \ -+ \ -+ succeed = name##_timeout(nr, p, &oldbit, \ -+ this_cpu(guest_safe_atomic_max)); \ -+ if ( succeed ) \ -+ return oldbit; \ -+ \ -+ domain_pause_nosync(d); \ -+ oldbit = name(nr, p); \ -+ domain_unpause(d); \ -+ \ -+ return oldbit; \ -+} -+ -+guest_bitop(set_bit) -+guest_bitop(clear_bit) -+guest_bitop(change_bit) -+ -+#undef guest_bitop -+ -+/* test_bit does not use load-store atomic operations */ -+#define guest_test_bit(d, nr, p) ((void)(d), test_bit(nr, p)) -+ -+guest_testop(test_and_set_bit) -+guest_testop(test_and_clear_bit) -+guest_testop(test_and_change_bit) -+ -+#undef guest_testop -+ -+#endif /* _ARM_GUEST_ATOMICS_H */ -+/* -+ * Local variables: -+ * mode: C -+ * c-file-style: "BSD" -+ * c-basic-offset: 4 -+ * indent-tabs-mode: nil -+ * End: -+ */ -diff --git a/xen/include/asm-x86/guest_atomics.h b/xen/include/asm-x86/guest_atomics.h -new file mode 100644 -index 0000000000..0c71d2d278 ---- /dev/null -+++ b/xen/include/asm-x86/guest_atomics.h -@@ -0,0 +1,30 @@ -+#ifndef _X86_GUEST_ATOMICS_H -+#define _X86_GUEST_ATOMICS_H -+ -+#include -+ -+/* -+ * It is safe to use the atomics helpers on x86 on memory shared with -+ * the guests. -+ */ -+#define guest_set_bit(d, nr, p) ((void)(d), set_bit(nr, p)) -+#define guest_clear_bit(d, nr, p) ((void)(d), clear_bit(nr, p)) -+#define guest_change_bit(d, nr, p) ((void)(d), change_bit(nr, p)) -+#define guest_test_bit(d, nr, p) ((void)(d), test_bit(nr, p)) -+ -+#define guest_test_and_set_bit(d, nr, p) \ -+ ((void)(d), test_and_set_bit(nr, p)) -+#define guest_test_and_clear_bit(d, nr, p) \ -+ ((void)(d), test_and_clear_bit(nr, p)) -+#define guest_test_and_change_bit(d, nr, p) \ -+ ((void)(d), test_and_change_bit(nr, p)) -+ -+#endif /* _X86_GUEST_ATOMICS_H */ -+/* -+ * Local variables: -+ * mode: C -+ * c-file-style: "BSD" -+ * c-basic-offset: 4 -+ * indent-tabs-mode: nil -+ * End: -+ */ --- -2.17.1 - diff --git a/xsa295-4.12-14.patch b/xsa295-4.12-14.patch deleted file mode 100644 index 381a9c8..0000000 --- a/xsa295-4.12-14.patch +++ /dev/null @@ -1,93 +0,0 @@ -From fefcb78a412f99ea2781595448d60abb690e3246 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:27 +0100 -Subject: [PATCH v2 4.12 14/17] xen/cmpxchg: Provide helper to safely modify - guest memory atomically - -On Arm, exclusive load-store atomics should only be used between trusted -thread. As not all the guests are trusted, it may be possible to DoS Xen -when updating shared memory with guest atomically. - -This patch adds a new helper that will update the guest memory safely. -For x86, it is already possible to use the current helper safely. So -just wrap it. - -For Arm, we will first attempt to update the guest memory with the -loop bounded by a maximum number of iterations. If it fails, we will -pause the domain and try again. - -Note that this heuristics assumes that a page can only -be shared between Xen and one domain. Not Xen and multiple domain. - -The maximum number of iterations is based on how many times atomic_inc() -can be executed in 1uS. The maximum value is per-CPU to cater big.LITTLE -and calculated when the CPU is booting. - -The maximum number of iterations is based on how many times a simple -load-store atomic operation can be executed in 1uS. The maximum -value is per-CPU to cater big.LITTLE and calculated when the CPU is -booting. The heuristic was randomly chosen and can be modified if -impact too much good-behaving guest. - -This is part of XSA-295. - -Signed-of-by: Julien Grall -Reviewed-by: Stefano Stabellini -Acked-by: Jan Beulich ---- - xen/include/asm-arm/guest_atomics.h | 25 +++++++++++++++++++++++++ - xen/include/asm-x86/guest_atomics.h | 2 ++ - 2 files changed, 27 insertions(+) - -diff --git a/xen/include/asm-arm/guest_atomics.h b/xen/include/asm-arm/guest_atomics.h -index 4f127fda41..61925d313d 100644 ---- a/xen/include/asm-arm/guest_atomics.h -+++ b/xen/include/asm-arm/guest_atomics.h -@@ -65,6 +65,31 @@ guest_testop(test_and_change_bit) - - #undef guest_testop - -+static inline unsigned long __guest_cmpxchg(struct domain *d, -+ volatile void *ptr, -+ unsigned long old, -+ unsigned long new, -+ unsigned int size) -+{ -+ unsigned long oldval = old; -+ -+ if ( __cmpxchg_mb_timeout(ptr, &oldval, new, size, -+ this_cpu(guest_safe_atomic_max)) ) -+ return oldval; -+ -+ domain_pause_nosync(d); -+ oldval = __cmpxchg_mb(ptr, old, new, size); -+ domain_unpause(d); -+ -+ return oldval; -+} -+ -+#define guest_cmpxchg(d, ptr, o, n) \ -+ ((__typeof__(*(ptr)))__guest_cmpxchg(d, ptr, \ -+ (unsigned long)(o),\ -+ (unsigned long)(n),\ -+ sizeof (*(ptr)))) -+ - #endif /* _ARM_GUEST_ATOMICS_H */ - /* - * Local variables: -diff --git a/xen/include/asm-x86/guest_atomics.h b/xen/include/asm-x86/guest_atomics.h -index 0c71d2d278..029417c8ff 100644 ---- a/xen/include/asm-x86/guest_atomics.h -+++ b/xen/include/asm-x86/guest_atomics.h -@@ -19,6 +19,8 @@ - #define guest_test_and_change_bit(d, nr, p) \ - ((void)(d), test_and_change_bit(nr, p)) - -+#define guest_cmpxchg(d, ptr, o, n) ((void)(d), cmpxchg(ptr, o, n)) -+ - #endif /* _X86_GUEST_ATOMICS_H */ - /* - * Local variables: --- -2.17.1 - diff --git a/xsa295-4.12-15.patch b/xsa295-4.12-15.patch deleted file mode 100644 index 976344b..0000000 --- a/xsa295-4.12-15.patch +++ /dev/null @@ -1,577 +0,0 @@ -From d7bf1476465d084d062459ff437396a517facd37 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:28 +0100 -Subject: [PATCH v2 4.12 15/17] xen: Use guest atomics helpers when modifying - atomically guest memory - -On Arm, exclusive load-store atomics should only be used between trusted -thread. As not all the guests are trusted, it may be possible to DoS Xen -when updating shared memory with guest atomically. - -This patch replaces all the atomics operations on shared memory with -a guest by the new guest atomics helpers. The x86 code was not audited -to know where guest atomics helpers could be used. I will leave that -to the x86 folks. - -Note that some rework was required in order to plumb use the new guest -atomics in event channel and grant-table. - -Because guest_test_bit is ignoring the parameter "d" for now, it -means there a lot of places do not need to drop the const. We may want -to revisit this in the future if the parameter "d" becomes necessary. - -This is part of XSA-295. - -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini ---- - xen/arch/arm/domain.c | 3 +- - xen/arch/arm/mm.c | 6 ++-- - xen/common/event_2l.c | 26 ++++++++------ - xen/common/event_fifo.c | 44 ++++++++++++----------- - xen/common/grant_table.c | 59 +++++++++++++++++-------------- - xen/include/asm-arm/grant_table.h | 2 +- - xen/include/asm-x86/grant_table.h | 3 +- - 7 files changed, 79 insertions(+), 64 deletions(-) - -diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c -index 6dc633ed50..c3d9d42ada 100644 ---- a/xen/arch/arm/domain.c -+++ b/xen/arch/arm/domain.c -@@ -27,6 +27,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -1017,7 +1018,7 @@ void arch_dump_vcpu_info(struct vcpu *v) - - void vcpu_mark_events_pending(struct vcpu *v) - { -- int already_pending = test_and_set_bit( -+ bool already_pending = guest_test_and_set_bit(v->domain, - 0, (unsigned long *)&vcpu_info(v, evtchn_upcall_pending)); - - if ( already_pending ) -diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c -index 01ae2cccc0..3a7cfb1b50 100644 ---- a/xen/arch/arm/mm.c -+++ b/xen/arch/arm/mm.c -@@ -40,6 +40,8 @@ - #include - #include - #include -+ -+#include - #include - - struct domain *dom_xen, *dom_io, *dom_cow; -@@ -1380,7 +1382,7 @@ void put_page_type(struct page_info *page) - return; - } - --void gnttab_clear_flag(unsigned long nr, uint16_t *addr) -+void gnttab_clear_flag(struct domain *d, unsigned long nr, uint16_t *addr) - { - /* - * Note that this cannot be clear_bit(), as the access must be -@@ -1390,7 +1392,7 @@ void gnttab_clear_flag(unsigned long nr, uint16_t *addr) - - do { - old = *addr; -- } while (cmpxchg(addr, old, old & mask) != old); -+ } while (guest_cmpxchg(d, addr, old, old & mask) != old); - } - - void gnttab_mark_dirty(struct domain *d, mfn_t mfn) -diff --git a/xen/common/event_2l.c b/xen/common/event_2l.c -index 8ca90899ab..e1dbb860f4 100644 ---- a/xen/common/event_2l.c -+++ b/xen/common/event_2l.c -@@ -13,6 +13,8 @@ - #include - #include - -+#include -+ - static void evtchn_2l_set_pending(struct vcpu *v, struct evtchn *evtchn) - { - struct domain *d = v->domain; -@@ -25,12 +27,12 @@ static void evtchn_2l_set_pending(struct vcpu *v, struct evtchn *evtchn) - * others may require explicit memory barriers. - */ - -- if ( test_and_set_bit(port, &shared_info(d, evtchn_pending)) ) -+ if ( guest_test_and_set_bit(d, port, &shared_info(d, evtchn_pending)) ) - return; - -- if ( !test_bit (port, &shared_info(d, evtchn_mask)) && -- !test_and_set_bit(port / BITS_PER_EVTCHN_WORD(d), -- &vcpu_info(v, evtchn_pending_sel)) ) -+ if ( !guest_test_bit(d, port, &shared_info(d, evtchn_mask)) && -+ !guest_test_and_set_bit(d, port / BITS_PER_EVTCHN_WORD(d), -+ &vcpu_info(v, evtchn_pending_sel)) ) - { - vcpu_mark_events_pending(v); - } -@@ -40,7 +42,7 @@ static void evtchn_2l_set_pending(struct vcpu *v, struct evtchn *evtchn) - - static void evtchn_2l_clear_pending(struct domain *d, struct evtchn *evtchn) - { -- clear_bit(evtchn->port, &shared_info(d, evtchn_pending)); -+ guest_clear_bit(d, evtchn->port, &shared_info(d, evtchn_pending)); - } - - static void evtchn_2l_unmask(struct domain *d, struct evtchn *evtchn) -@@ -52,10 +54,10 @@ static void evtchn_2l_unmask(struct domain *d, struct evtchn *evtchn) - * These operations must happen in strict order. Based on - * evtchn_2l_set_pending() above. - */ -- if ( test_and_clear_bit(port, &shared_info(d, evtchn_mask)) && -- test_bit (port, &shared_info(d, evtchn_pending)) && -- !test_and_set_bit (port / BITS_PER_EVTCHN_WORD(d), -- &vcpu_info(v, evtchn_pending_sel)) ) -+ if ( guest_test_and_clear_bit(d, port, &shared_info(d, evtchn_mask)) && -+ guest_test_bit(d, port, &shared_info(d, evtchn_pending)) && -+ !guest_test_and_set_bit(d, port / BITS_PER_EVTCHN_WORD(d), -+ &vcpu_info(v, evtchn_pending_sel)) ) - { - vcpu_mark_events_pending(v); - } -@@ -66,7 +68,8 @@ static bool evtchn_2l_is_pending(const struct domain *d, evtchn_port_t port) - unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); - - ASSERT(port < max_ports); -- return port < max_ports && test_bit(port, &shared_info(d, evtchn_pending)); -+ return (port < max_ports && -+ guest_test_bit(d, port, &shared_info(d, evtchn_pending))); - } - - static bool evtchn_2l_is_masked(const struct domain *d, evtchn_port_t port) -@@ -74,7 +77,8 @@ static bool evtchn_2l_is_masked(const struct domain *d, evtchn_port_t port) - unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); - - ASSERT(port < max_ports); -- return port >= max_ports || test_bit(port, &shared_info(d, evtchn_mask)); -+ return (port >= max_ports || -+ guest_test_bit(d, port, &shared_info(d, evtchn_mask))); - } - - static void evtchn_2l_print_state(struct domain *d, -diff --git a/xen/common/event_fifo.c b/xen/common/event_fifo.c -index 3eecab3f22..230f440f14 100644 ---- a/xen/common/event_fifo.c -+++ b/xen/common/event_fifo.c -@@ -17,6 +17,8 @@ - #include - #include - -+#include -+ - #include - - static inline event_word_t *evtchn_fifo_word_from_port(const struct domain *d, -@@ -51,7 +53,7 @@ static void evtchn_fifo_init(struct domain *d, struct evtchn *evtchn) - * on the wrong VCPU or with an unexpected priority. - */ - word = evtchn_fifo_word_from_port(d, evtchn->port); -- if ( word && test_bit(EVTCHN_FIFO_LINKED, word) ) -+ if ( word && guest_test_bit(d, EVTCHN_FIFO_LINKED, word) ) - gdprintk(XENLOG_WARNING, "domain %d, port %d already on a queue\n", - d->domain_id, evtchn->port); - } -@@ -116,7 +118,7 @@ static int try_set_link(event_word_t *word, event_word_t *w, uint32_t link) - * We block unmasking by the guest by marking the tail word as BUSY, - * therefore, the cmpxchg() may fail at most 4 times. - */ --static bool_t evtchn_fifo_set_link(const struct domain *d, event_word_t *word, -+static bool_t evtchn_fifo_set_link(struct domain *d, event_word_t *word, - uint32_t link) - { - event_word_t w; -@@ -130,7 +132,7 @@ static bool_t evtchn_fifo_set_link(const struct domain *d, event_word_t *word, - return ret; - - /* Lock the word to prevent guest unmasking. */ -- set_bit(EVTCHN_FIFO_BUSY, word); -+ guest_set_bit(d, EVTCHN_FIFO_BUSY, word); - - w = read_atomic(word); - -@@ -140,13 +142,13 @@ static bool_t evtchn_fifo_set_link(const struct domain *d, event_word_t *word, - if ( ret >= 0 ) - { - if ( ret == 0 ) -- clear_bit(EVTCHN_FIFO_BUSY, word); -+ guest_clear_bit(d, EVTCHN_FIFO_BUSY, word); - return ret; - } - } - gdprintk(XENLOG_WARNING, "domain %d, port %d not linked\n", - d->domain_id, link); -- clear_bit(EVTCHN_FIFO_BUSY, word); -+ guest_clear_bit(d, EVTCHN_FIFO_BUSY, word); - return 1; - } - -@@ -171,13 +173,13 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) - return; - } - -- was_pending = test_and_set_bit(EVTCHN_FIFO_PENDING, word); -+ was_pending = guest_test_and_set_bit(d, EVTCHN_FIFO_PENDING, word); - - /* - * Link the event if it unmasked and not already linked. - */ -- if ( !test_bit(EVTCHN_FIFO_MASKED, word) -- && !test_bit(EVTCHN_FIFO_LINKED, word) ) -+ if ( !guest_test_bit(d, EVTCHN_FIFO_MASKED, word) && -+ !guest_test_bit(d, EVTCHN_FIFO_LINKED, word) ) - { - struct evtchn_fifo_queue *q, *old_q; - event_word_t *tail_word; -@@ -206,7 +208,7 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) - if ( !old_q ) - goto done; - -- if ( test_and_set_bit(EVTCHN_FIFO_LINKED, word) ) -+ if ( guest_test_and_set_bit(d, EVTCHN_FIFO_LINKED, word) ) - { - spin_unlock_irqrestore(&old_q->lock, flags); - goto done; -@@ -252,8 +254,8 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) - spin_unlock_irqrestore(&q->lock, flags); - - if ( !linked -- && !test_and_set_bit(q->priority, -- &v->evtchn_fifo->control_block->ready) ) -+ && !guest_test_and_set_bit(d, q->priority, -+ &v->evtchn_fifo->control_block->ready) ) - vcpu_mark_events_pending(v); - } - done: -@@ -275,7 +277,7 @@ static void evtchn_fifo_clear_pending(struct domain *d, struct evtchn *evtchn) - * No need to unlink as the guest will unlink and ignore - * non-pending events. - */ -- clear_bit(EVTCHN_FIFO_PENDING, word); -+ guest_clear_bit(d, EVTCHN_FIFO_PENDING, word); - } - - static void evtchn_fifo_unmask(struct domain *d, struct evtchn *evtchn) -@@ -287,10 +289,10 @@ static void evtchn_fifo_unmask(struct domain *d, struct evtchn *evtchn) - if ( unlikely(!word) ) - return; - -- clear_bit(EVTCHN_FIFO_MASKED, word); -+ guest_clear_bit(d, EVTCHN_FIFO_MASKED, word); - - /* Relink if pending. */ -- if ( test_bit(EVTCHN_FIFO_PENDING, word) ) -+ if ( guest_test_bit(d, EVTCHN_FIFO_PENDING, word) ) - evtchn_fifo_set_pending(v, evtchn); - } - -@@ -298,21 +300,21 @@ static bool evtchn_fifo_is_pending(const struct domain *d, evtchn_port_t port) - { - const event_word_t *word = evtchn_fifo_word_from_port(d, port); - -- return word && test_bit(EVTCHN_FIFO_PENDING, word); -+ return word && guest_test_bit(d, EVTCHN_FIFO_PENDING, word); - } - - static bool_t evtchn_fifo_is_masked(const struct domain *d, evtchn_port_t port) - { - const event_word_t *word = evtchn_fifo_word_from_port(d, port); - -- return !word || test_bit(EVTCHN_FIFO_MASKED, word); -+ return !word || guest_test_bit(d, EVTCHN_FIFO_MASKED, word); - } - - static bool_t evtchn_fifo_is_busy(const struct domain *d, evtchn_port_t port) - { - const event_word_t *word = evtchn_fifo_word_from_port(d, port); - -- return word && test_bit(EVTCHN_FIFO_LINKED, word); -+ return word && guest_test_bit(d, EVTCHN_FIFO_LINKED, word); - } - - static int evtchn_fifo_set_priority(struct domain *d, struct evtchn *evtchn, -@@ -339,11 +341,11 @@ static void evtchn_fifo_print_state(struct domain *d, - word = evtchn_fifo_word_from_port(d, evtchn->port); - if ( !word ) - printk("? "); -- else if ( test_bit(EVTCHN_FIFO_LINKED, word) ) -- printk("%c %-4u", test_bit(EVTCHN_FIFO_BUSY, word) ? 'B' : ' ', -+ else if ( guest_test_bit(d, EVTCHN_FIFO_LINKED, word) ) -+ printk("%c %-4u", guest_test_bit(d, EVTCHN_FIFO_BUSY, word) ? 'B' : ' ', - *word & EVTCHN_FIFO_LINK_MASK); - else -- printk("%c - ", test_bit(EVTCHN_FIFO_BUSY, word) ? 'B' : ' '); -+ printk("%c - ", guest_test_bit(d, EVTCHN_FIFO_BUSY, word) ? 'B' : ' '); - } - - static const struct evtchn_port_ops evtchn_port_ops_fifo = -@@ -495,7 +497,7 @@ static void setup_ports(struct domain *d) - - evtchn = evtchn_from_port(d, port); - -- if ( test_bit(port, &shared_info(d, evtchn_pending)) ) -+ if ( guest_test_bit(d, port, &shared_info(d, evtchn_pending)) ) - evtchn->pending = 1; - - evtchn_fifo_set_priority(d, evtchn, EVTCHN_FIFO_PRIORITY_DEFAULT); -diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c -index 1b82d534a3..e9ce0ac473 100644 ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -39,6 +39,7 @@ - #include - #include - #include -+#include - - /* Per-domain grant information. */ - struct grant_table { -@@ -646,6 +647,7 @@ static unsigned int nr_grant_entries(struct grant_table *gt) - } - - static int _set_status_v1(const grant_entry_header_t *shah, -+ struct domain *rd, - struct active_grant_entry *act, - int readonly, - int mapflag, -@@ -701,8 +703,8 @@ static int _set_status_v1(const grant_entry_header_t *shah, - "Attempt to write-pin a r/o grant entry\n"); - } - -- prev_scombo.word = cmpxchg((u32 *)shah, -- scombo.word, new_scombo.word); -+ prev_scombo.word = guest_cmpxchg(rd, (u32 *)shah, -+ scombo.word, new_scombo.word); - if ( likely(prev_scombo.word == scombo.word) ) - break; - -@@ -719,6 +721,7 @@ done: - - static int _set_status_v2(const grant_entry_header_t *shah, - grant_status_t *status, -+ struct domain *rd, - struct active_grant_entry *act, - int readonly, - int mapflag, -@@ -781,8 +784,8 @@ static int _set_status_v2(const grant_entry_header_t *shah, - (id != ldomid) || - (!readonly && (flags & GTF_readonly)) ) - { -- gnttab_clear_flag(_GTF_writing, status); -- gnttab_clear_flag(_GTF_reading, status); -+ gnttab_clear_flag(rd, _GTF_writing, status); -+ gnttab_clear_flag(rd, _GTF_reading, status); - PIN_FAIL(done, GNTST_general_error, - "Unstable flags (%x) or dom (%d); expected d%d (r/w: %d)\n", - flags, id, ldomid, !readonly); -@@ -792,7 +795,7 @@ static int _set_status_v2(const grant_entry_header_t *shah, - { - if ( unlikely(flags & GTF_readonly) ) - { -- gnttab_clear_flag(_GTF_writing, status); -+ gnttab_clear_flag(rd, _GTF_writing, status); - PIN_FAIL(done, GNTST_general_error, - "Unstable grant readonly flag\n"); - } -@@ -805,6 +808,7 @@ done: - - static int _set_status(const grant_entry_header_t *shah, - grant_status_t *status, -+ struct domain *rd, - unsigned rgt_version, - struct active_grant_entry *act, - int readonly, -@@ -813,9 +817,9 @@ static int _set_status(const grant_entry_header_t *shah, - { - - if ( rgt_version == 1 ) -- return _set_status_v1(shah, act, readonly, mapflag, ldomid); -+ return _set_status_v1(shah, rd, act, readonly, mapflag, ldomid); - else -- return _set_status_v2(shah, status, act, readonly, mapflag, ldomid); -+ return _set_status_v2(shah, status, rd, act, readonly, mapflag, ldomid); - } - - static struct active_grant_entry *grant_map_exists(const struct domain *ld, -@@ -980,7 +984,7 @@ map_grant_ref( - (!(op->flags & GNTMAP_readonly) && - !(act->pin & (GNTPIN_hstw_mask|GNTPIN_devw_mask))) ) - { -- if ( (rc = _set_status(shah, status, rgt->gt_version, act, -+ if ( (rc = _set_status(shah, status, rd, rgt->gt_version, act, - op->flags & GNTMAP_readonly, 1, - ld->domain_id) != GNTST_okay) ) - goto act_release_out; -@@ -1204,10 +1208,10 @@ map_grant_ref( - unlock_out_clear: - if ( !(op->flags & GNTMAP_readonly) && - !(act->pin & (GNTPIN_hstw_mask|GNTPIN_devw_mask)) ) -- gnttab_clear_flag(_GTF_writing, status); -+ gnttab_clear_flag(rd, _GTF_writing, status); - - if ( !act->pin ) -- gnttab_clear_flag(_GTF_reading, status); -+ gnttab_clear_flag(rd, _GTF_reading, status); - - act_release_out: - active_entry_release(act); -@@ -1477,10 +1481,10 @@ unmap_common_complete(struct gnttab_unmap_common *op) - - if ( ((act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)) == 0) && - !(op->done & GNTMAP_readonly) ) -- gnttab_clear_flag(_GTF_writing, status); -+ gnttab_clear_flag(rd, _GTF_writing, status); - - if ( act->pin == 0 ) -- gnttab_clear_flag(_GTF_reading, status); -+ gnttab_clear_flag(rd, _GTF_reading, status); - - active_entry_release(act); - grant_read_unlock(rgt); -@@ -2045,8 +2049,8 @@ gnttab_prepare_for_transfer( - new_scombo = scombo; - new_scombo.shorts.flags |= GTF_transfer_committed; - -- prev_scombo.word = cmpxchg((u32 *)&sha->flags, -- scombo.word, new_scombo.word); -+ prev_scombo.word = guest_cmpxchg(rd, (u32 *)&sha->flags, -+ scombo.word, new_scombo.word); - if ( likely(prev_scombo.word == scombo.word) ) - break; - -@@ -2339,11 +2343,11 @@ release_grant_for_copy( - - act->pin -= GNTPIN_hstw_inc; - if ( !(act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)) ) -- gnttab_clear_flag(_GTF_writing, status); -+ gnttab_clear_flag(rd, _GTF_writing, status); - } - - if ( !act->pin ) -- gnttab_clear_flag(_GTF_reading, status); -+ gnttab_clear_flag(rd, _GTF_reading, status); - - active_entry_release(act); - grant_read_unlock(rgt); -@@ -2365,14 +2369,15 @@ release_grant_for_copy( - under the domain's grant table lock. */ - /* Only safe on transitive grants. Even then, note that we don't - attempt to drop any pin on the referent grant. */ --static void fixup_status_for_copy_pin(const struct active_grant_entry *act, -+static void fixup_status_for_copy_pin(struct domain *rd, -+ const struct active_grant_entry *act, - uint16_t *status) - { - if ( !(act->pin & (GNTPIN_hstw_mask | GNTPIN_devw_mask)) ) -- gnttab_clear_flag(_GTF_writing, status); -+ gnttab_clear_flag(rd, _GTF_writing, status); - - if ( !act->pin ) -- gnttab_clear_flag(_GTF_reading, status); -+ gnttab_clear_flag(rd, _GTF_reading, status); - } - - /* -@@ -2434,7 +2439,7 @@ acquire_grant_for_copy( - { - if ( (!old_pin || (!readonly && - !(old_pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)))) && -- (rc = _set_status_v2(shah, status, act, readonly, 0, -+ (rc = _set_status_v2(shah, status, rd, act, readonly, 0, - ldom)) != GNTST_okay ) - goto unlock_out; - -@@ -2483,7 +2488,7 @@ acquire_grant_for_copy( - - if ( rc != GNTST_okay ) - { -- fixup_status_for_copy_pin(act, status); -+ fixup_status_for_copy_pin(rd, act, status); - rcu_unlock_domain(td); - active_entry_release(act); - grant_read_unlock(rgt); -@@ -2506,7 +2511,7 @@ acquire_grant_for_copy( - !act->is_sub_page)) ) - { - release_grant_for_copy(td, trans_gref, readonly); -- fixup_status_for_copy_pin(act, status); -+ fixup_status_for_copy_pin(rd, act, status); - rcu_unlock_domain(td); - active_entry_release(act); - grant_read_unlock(rgt); -@@ -2535,7 +2540,7 @@ acquire_grant_for_copy( - else if ( !old_pin || - (!readonly && !(old_pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask))) ) - { -- if ( (rc = _set_status(shah, status, rgt->gt_version, act, -+ if ( (rc = _set_status(shah, status, rd, rgt->gt_version, act, - readonly, 0, ldom)) != GNTST_okay ) - goto unlock_out; - -@@ -2623,10 +2628,10 @@ acquire_grant_for_copy( - unlock_out_clear: - if ( !(readonly) && - !(act->pin & (GNTPIN_hstw_mask | GNTPIN_devw_mask)) ) -- gnttab_clear_flag(_GTF_writing, status); -+ gnttab_clear_flag(rd, _GTF_writing, status); - - if ( !act->pin ) -- gnttab_clear_flag(_GTF_reading, status); -+ gnttab_clear_flag(rd, _GTF_reading, status); - - unlock_out: - active_entry_release(act); -@@ -3661,11 +3666,11 @@ gnttab_release_mappings( - } - - if ( (act->pin & (GNTPIN_devw_mask|GNTPIN_hstw_mask)) == 0 ) -- gnttab_clear_flag(_GTF_writing, status); -+ gnttab_clear_flag(rd, _GTF_writing, status); - } - - if ( act->pin == 0 ) -- gnttab_clear_flag(_GTF_reading, status); -+ gnttab_clear_flag(rd, _GTF_reading, status); - - active_entry_release(act); - grant_read_unlock(rgt); -diff --git a/xen/include/asm-arm/grant_table.h b/xen/include/asm-arm/grant_table.h -index 816e3c6d68..5e9aa53814 100644 ---- a/xen/include/asm-arm/grant_table.h -+++ b/xen/include/asm-arm/grant_table.h -@@ -14,7 +14,7 @@ struct grant_table_arch { - gfn_t *status_gfn; - }; - --void gnttab_clear_flag(unsigned long nr, uint16_t *addr); -+void gnttab_clear_flag(struct domain *d, unsigned long nr, uint16_t *addr); - int create_grant_host_mapping(unsigned long gpaddr, mfn_t mfn, - unsigned int flags, unsigned int cache_flags); - #define gnttab_host_mapping_get_page_type(ro, ld, rd) (0) -diff --git a/xen/include/asm-x86/grant_table.h b/xen/include/asm-x86/grant_table.h -index 4b8c4f9160..11f061aa2d 100644 ---- a/xen/include/asm-x86/grant_table.h -+++ b/xen/include/asm-x86/grant_table.h -@@ -64,7 +64,8 @@ static inline int replace_grant_host_mapping(uint64_t addr, mfn_t frame, - - #define gnttab_mark_dirty(d, f) paging_mark_dirty((d), f) - --static inline void gnttab_clear_flag(unsigned int nr, uint16_t *st) -+static inline void gnttab_clear_flag(struct domain *d, unsigned int nr, -+ uint16_t *st) - { - /* - * Note that this cannot be clear_bit(), as the access must be --- -2.17.1 - diff --git a/xsa295-4.12-16.patch b/xsa295-4.12-16.patch deleted file mode 100644 index fa9263f..0000000 --- a/xsa295-4.12-16.patch +++ /dev/null @@ -1,84 +0,0 @@ -From b5142d29a96d6c5bbd505d2ba299b1ef0416391a Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:29 +0100 -Subject: [PATCH v2 4.12 16/17] xen/arm: Add performance counters in guest - atomic helpers - -Add performance counters in guest atomic helpers to be able to detect -whether a guest is often paused during the operations. - -This is part of XSA-295. - -Signed-off-by: Julien Grall -Acked-by: Stefano Stabellini ---- - xen/include/asm-arm/guest_atomics.h | 12 ++++++++++++ - xen/include/asm-arm/perfc_defn.h | 3 +++ - 2 files changed, 15 insertions(+) - -diff --git a/xen/include/asm-arm/guest_atomics.h b/xen/include/asm-arm/guest_atomics.h -index 61925d313d..698508bf87 100644 ---- a/xen/include/asm-arm/guest_atomics.h -+++ b/xen/include/asm-arm/guest_atomics.h -@@ -24,9 +24,13 @@ DECLARE_PER_CPU(unsigned int, guest_safe_atomic_max); - #define guest_bitop(name) \ - static inline void guest_##name(struct domain *d, int nr, volatile void *p) \ - { \ -+ perfc_incr(atomics_guest); \ -+ \ - if ( name##_timeout(nr, p, this_cpu(guest_safe_atomic_max)) ) \ - return; \ - \ -+ perfc_incr(atomics_guest_paused); \ -+ \ - domain_pause_nosync(d); \ - name(nr, p); \ - domain_unpause(d); \ -@@ -38,11 +42,15 @@ static inline int guest_##name(struct domain *d, int nr, volatile void *p) \ - bool succeed; \ - int oldbit; \ - \ -+ perfc_incr(atomics_guest); \ -+ \ - succeed = name##_timeout(nr, p, &oldbit, \ - this_cpu(guest_safe_atomic_max)); \ - if ( succeed ) \ - return oldbit; \ - \ -+ perfc_incr(atomics_guest_paused); \ -+ \ - domain_pause_nosync(d); \ - oldbit = name(nr, p); \ - domain_unpause(d); \ -@@ -73,10 +81,14 @@ static inline unsigned long __guest_cmpxchg(struct domain *d, - { - unsigned long oldval = old; - -+ perfc_incr(atomics_guest); -+ - if ( __cmpxchg_mb_timeout(ptr, &oldval, new, size, - this_cpu(guest_safe_atomic_max)) ) - return oldval; - -+ perfc_incr(atomics_guest_paused); -+ - domain_pause_nosync(d); - oldval = __cmpxchg_mb(ptr, old, new, size); - domain_unpause(d); -diff --git a/xen/include/asm-arm/perfc_defn.h b/xen/include/asm-arm/perfc_defn.h -index 8922e9525a..6a83185163 100644 ---- a/xen/include/asm-arm/perfc_defn.h -+++ b/xen/include/asm-arm/perfc_defn.h -@@ -73,6 +73,9 @@ PERFCOUNTER(phys_timer_irqs, "Physical timer interrupts") - PERFCOUNTER(virt_timer_irqs, "Virtual timer interrupts") - PERFCOUNTER(maintenance_irqs, "Maintenance interrupts") - -+PERFCOUNTER(atomics_guest, "atomics: guest access") -+PERFCOUNTER(atomics_guest_paused, "atomics: guest paused") -+ - /*#endif*/ /* __XEN_PERFC_DEFN_H__ */ - - /* --- -2.17.1 - diff --git a/xsa295-4.12-17.patch b/xsa295-4.12-17.patch deleted file mode 100644 index d8884db..0000000 --- a/xsa295-4.12-17.patch +++ /dev/null @@ -1,199 +0,0 @@ -From c338acb8b71b8aad258f4c3c0fda28ef294ed95c Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 29 Apr 2019 15:05:30 +0100 -Subject: [PATCH v2 4.12 17/17] xen/arm: grant-table: Protect gnttab_clear_flag - against guest misbehavior - -The function gnttab_clear_flag is used to clear the access flags. On -Arm, it is implemented using a loop and guest_cmpxchg. - -It is possible that guest_cmpxchg will always return a different value -than old. This can happen if the guest updated the memory before Xen has -time to do the exchange. Because of that, there are no way for to -promise the loop will end. - -It is possible to make the current code safe by re-using the same -principle as applied on the guest atomic helper. However this patch -takes a different approach that should lead to more efficient code in -the default case. - -A new helper is introduced to clear a set of bits on a 16-bits word. -This should avoid a an extra loop to check cmpxchg succeeded. - -Note that a mask is used instead of a bit, so the helper can be re-used -later on for clearing multiple flags at the same time. - -This is part of XSA-295. - -Reported-by: Andrew Cooper -Signed-off-by: Julien Grall -Signed-off-by: Stefano Stabellini -Reviewed-by: Stefano Stabellini ---- - xen/arch/arm/arm32/lib/bitops.c | 35 +++++++++++++++++++++++++++++ - xen/arch/arm/arm64/lib/bitops.c | 33 +++++++++++++++++++++++++++ - xen/arch/arm/mm.c | 10 +-------- - xen/include/asm-arm/bitops.h | 4 ++++ - xen/include/asm-arm/guest_atomics.h | 13 +++++++++++ - 5 files changed, 86 insertions(+), 9 deletions(-) - -diff --git a/xen/arch/arm/arm32/lib/bitops.c b/xen/arch/arm/arm32/lib/bitops.c -index 08750314fc..3dca769bf0 100644 ---- a/xen/arch/arm/arm32/lib/bitops.c -+++ b/xen/arch/arm/arm32/lib/bitops.c -@@ -126,6 +126,41 @@ testop(test_and_change_bit, eor) - testop(test_and_clear_bit, bic) - testop(test_and_set_bit, orr) - -+static always_inline bool int_clear_mask16(uint16_t mask, volatile uint16_t *p, -+ bool timeout, unsigned int max_try) -+{ -+ unsigned long res, tmp; -+ -+ prefetchw((const uint16_t *)p); -+ -+ do -+ { -+ asm volatile ("// int_clear_mask16\n" -+ " ldrexh %2, %1\n" -+ " bic %2, %2, %3\n" -+ " strexh %0, %2, %1\n" -+ : "=&r" (res), "+Qo" (*p), "=&r" (tmp) -+ : "r" (mask)); -+ -+ if ( !res ) -+ break; -+ } while ( !timeout || ((--max_try) > 0) ); -+ -+ return !res; -+} -+ -+void clear_mask16(uint16_t mask, volatile void *p) -+{ -+ if ( !int_clear_mask16(mask, p, false, 0) ) -+ ASSERT_UNREACHABLE(); -+} -+ -+bool clear_mask16_timeout(uint16_t mask, volatile void *p, -+ unsigned int max_try) -+{ -+ return int_clear_mask16(mask, p, true, max_try); -+} -+ - /* - * Local variables: - * mode: C -diff --git a/xen/arch/arm/arm64/lib/bitops.c b/xen/arch/arm/arm64/lib/bitops.c -index 78bf4ed8c5..27688e5418 100644 ---- a/xen/arch/arm/arm64/lib/bitops.c -+++ b/xen/arch/arm/arm64/lib/bitops.c -@@ -118,6 +118,39 @@ testop(test_and_change_bit, eor) - testop(test_and_clear_bit, bic) - testop(test_and_set_bit, orr) - -+static always_inline bool int_clear_mask16(uint16_t mask, volatile uint16_t *p, -+ bool timeout, unsigned int max_try) -+{ -+ unsigned long res, tmp; -+ -+ do -+ { -+ asm volatile ("// int_clear_mask16\n" -+ " ldxrh %w2, %1\n" -+ " bic %w2, %w2, %w3\n" -+ " stxrh %w0, %w2, %1\n" -+ : "=&r" (res), "+Q" (*p), "=&r" (tmp) -+ : "r" (mask)); -+ -+ if ( !res ) -+ break; -+ } while ( !timeout || ((--max_try) > 0) ); -+ -+ return !res; -+} -+ -+void clear_mask16(uint16_t mask, volatile void *p) -+{ -+ if ( !int_clear_mask16(mask, p, false, 0) ) -+ ASSERT_UNREACHABLE(); -+} -+ -+bool clear_mask16_timeout(uint16_t mask, volatile void *p, -+ unsigned int max_try) -+{ -+ return int_clear_mask16(mask, p, true, max_try); -+} -+ - /* - * Local variables: - * mode: C -diff --git a/xen/arch/arm/mm.c b/xen/arch/arm/mm.c -index 3a7cfb1b50..8a53544975 100644 ---- a/xen/arch/arm/mm.c -+++ b/xen/arch/arm/mm.c -@@ -1384,15 +1384,7 @@ void put_page_type(struct page_info *page) - - void gnttab_clear_flag(struct domain *d, unsigned long nr, uint16_t *addr) - { -- /* -- * Note that this cannot be clear_bit(), as the access must be -- * confined to the specified 2 bytes. -- */ -- uint16_t mask = ~(1 << nr), old; -- -- do { -- old = *addr; -- } while (guest_cmpxchg(d, addr, old, old & mask) != old); -+ guest_clear_mask16(d, BIT(nr), addr); - } - - void gnttab_mark_dirty(struct domain *d, mfn_t mfn) -diff --git a/xen/include/asm-arm/bitops.h b/xen/include/asm-arm/bitops.h -index f6782b33be..f989bc726c 100644 ---- a/xen/include/asm-arm/bitops.h -+++ b/xen/include/asm-arm/bitops.h -@@ -53,6 +53,8 @@ int test_and_set_bit(int nr, volatile void *p); - int test_and_clear_bit(int nr, volatile void *p); - int test_and_change_bit(int nr, volatile void *p); - -+void clear_mask16(uint16_t mask, volatile void *p); -+ - /* - * The helpers below may fail to update the memory if the action takes - * too long. -@@ -71,6 +73,8 @@ bool test_and_clear_bit_timeout(int nr, volatile void *p, - int *oldbit, unsigned int max_try); - bool test_and_change_bit_timeout(int nr, volatile void *p, - int *oldbit, unsigned int max_try); -+bool clear_mask16_timeout(uint16_t mask, volatile void *p, -+ unsigned int max_try); - - /** - * __test_and_set_bit - Set a bit and return its old value -diff --git a/xen/include/asm-arm/guest_atomics.h b/xen/include/asm-arm/guest_atomics.h -index 698508bf87..af27cc627b 100644 ---- a/xen/include/asm-arm/guest_atomics.h -+++ b/xen/include/asm-arm/guest_atomics.h -@@ -73,6 +73,19 @@ guest_testop(test_and_change_bit) - - #undef guest_testop - -+static inline void guest_clear_mask16(struct domain *d, uint16_t mask, -+ volatile uint16_t *p) -+{ -+ perfc_incr(atomics_guest); -+ -+ if ( clear_mask16_timeout(mask, p, this_cpu(guest_safe_atomic_max)) ) -+ return; -+ -+ domain_pause_nosync(d); -+ clear_mask16(mask, p); -+ domain_unpause(d); -+} -+ - static inline unsigned long __guest_cmpxchg(struct domain *d, - volatile void *ptr, - unsigned long old, --- -2.17.1 - diff --git a/xsa297-4.12-1.patch b/xsa297-4.12-1.patch deleted file mode 100644 index 51da965..0000000 --- a/xsa297-4.12-1.patch +++ /dev/null @@ -1,163 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Reposition the XPTI command line parsing logic - -It has ended up in the middle of the mitigation calculation logic. Move it to -be beside the other command line parsing. - -No functional change. - -Signed-off-by: Andrew Cooper -Acked-by: Jan Beulich - -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 1171c02..99310c8 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -167,6 +167,73 @@ static int __init parse_spec_ctrl(const char *s) - } - custom_param("spec-ctrl", parse_spec_ctrl); - -+int8_t __read_mostly opt_xpti_hwdom = -1; -+int8_t __read_mostly opt_xpti_domu = -1; -+ -+static __init void xpti_init_default(uint64_t caps) -+{ -+ if ( boot_cpu_data.x86_vendor == X86_VENDOR_AMD ) -+ caps = ARCH_CAPS_RDCL_NO; -+ -+ if ( caps & ARCH_CAPS_RDCL_NO ) -+ { -+ if ( opt_xpti_hwdom < 0 ) -+ opt_xpti_hwdom = 0; -+ if ( opt_xpti_domu < 0 ) -+ opt_xpti_domu = 0; -+ } -+ else -+ { -+ if ( opt_xpti_hwdom < 0 ) -+ opt_xpti_hwdom = 1; -+ if ( opt_xpti_domu < 0 ) -+ opt_xpti_domu = 1; -+ } -+} -+ -+static __init int parse_xpti(const char *s) -+{ -+ const char *ss; -+ int val, rc = 0; -+ -+ /* Interpret 'xpti' alone in its positive boolean form. */ -+ if ( *s == '\0' ) -+ opt_xpti_hwdom = opt_xpti_domu = 1; -+ -+ do { -+ ss = strchr(s, ','); -+ if ( !ss ) -+ ss = strchr(s, '\0'); -+ -+ switch ( parse_bool(s, ss) ) -+ { -+ case 0: -+ opt_xpti_hwdom = opt_xpti_domu = 0; -+ break; -+ -+ case 1: -+ opt_xpti_hwdom = opt_xpti_domu = 1; -+ break; -+ -+ default: -+ if ( !strcmp(s, "default") ) -+ opt_xpti_hwdom = opt_xpti_domu = -1; -+ else if ( (val = parse_boolean("dom0", s, ss)) >= 0 ) -+ opt_xpti_hwdom = val; -+ else if ( (val = parse_boolean("domu", s, ss)) >= 0 ) -+ opt_xpti_domu = val; -+ else if ( *s ) -+ rc = -EINVAL; -+ break; -+ } -+ -+ s = ss + 1; -+ } while ( *ss ); -+ -+ return rc; -+} -+custom_param("xpti", parse_xpti); -+ - int8_t __read_mostly opt_pv_l1tf_hwdom = -1; - int8_t __read_mostly opt_pv_l1tf_domu = -1; - -@@ -627,73 +694,6 @@ static __init void l1tf_calculations(uint64_t caps) - : (3ul << (paddr_bits - 2)))); - } - --int8_t __read_mostly opt_xpti_hwdom = -1; --int8_t __read_mostly opt_xpti_domu = -1; -- --static __init void xpti_init_default(uint64_t caps) --{ -- if ( boot_cpu_data.x86_vendor == X86_VENDOR_AMD ) -- caps = ARCH_CAPS_RDCL_NO; -- -- if ( caps & ARCH_CAPS_RDCL_NO ) -- { -- if ( opt_xpti_hwdom < 0 ) -- opt_xpti_hwdom = 0; -- if ( opt_xpti_domu < 0 ) -- opt_xpti_domu = 0; -- } -- else -- { -- if ( opt_xpti_hwdom < 0 ) -- opt_xpti_hwdom = 1; -- if ( opt_xpti_domu < 0 ) -- opt_xpti_domu = 1; -- } --} -- --static __init int parse_xpti(const char *s) --{ -- const char *ss; -- int val, rc = 0; -- -- /* Interpret 'xpti' alone in its positive boolean form. */ -- if ( *s == '\0' ) -- opt_xpti_hwdom = opt_xpti_domu = 1; -- -- do { -- ss = strchr(s, ','); -- if ( !ss ) -- ss = strchr(s, '\0'); -- -- switch ( parse_bool(s, ss) ) -- { -- case 0: -- opt_xpti_hwdom = opt_xpti_domu = 0; -- break; -- -- case 1: -- opt_xpti_hwdom = opt_xpti_domu = 1; -- break; -- -- default: -- if ( !strcmp(s, "default") ) -- opt_xpti_hwdom = opt_xpti_domu = -1; -- else if ( (val = parse_boolean("dom0", s, ss)) >= 0 ) -- opt_xpti_hwdom = val; -- else if ( (val = parse_boolean("domu", s, ss)) >= 0 ) -- opt_xpti_domu = val; -- else if ( *s ) -- rc = -EINVAL; -- break; -- } -- -- s = ss + 1; -- } while ( *ss ); -- -- return rc; --} --custom_param("xpti", parse_xpti); -- - void __init init_speculation_mitigations(void) - { - enum ind_thunk thunk = THUNK_DEFAULT; diff --git a/xsa297-4.12-2.patch b/xsa297-4.12-2.patch deleted file mode 100644 index d03279a..0000000 --- a/xsa297-4.12-2.patch +++ /dev/null @@ -1,54 +0,0 @@ -From: Andrew Cooper -Subject: x86/msr: Definitions for MSR_INTEL_CORE_THREAD_COUNT - -This is a model specific register which details the current configuration -cores and threads in the package. Because of how Hyperthread and Core -configuration works works in firmware, the MSR it is de-facto constant and -will remain unchanged until the next system reset. - -It is a read only MSR (so unilaterally reject writes), but for now retain its -leaky-on-read properties. Further CPUID/MSR work is required before we can -start virtualising a consistent topology to the guest, and retaining the old -behaviour is the safest course of action. - -Signed-off-by: Andrew Cooper -Acked-by: Jan Beulich - -diff --git a/xen/arch/x86/msr.c b/xen/arch/x86/msr.c -index 4df4a59..a7f67d9 100644 ---- a/xen/arch/x86/msr.c -+++ b/xen/arch/x86/msr.c -@@ -200,6 +200,10 @@ int guest_rdmsr(const struct vcpu *v, uint32_t msr, uint64_t *val) - ARRAY_SIZE(msrs->dr_mask))]; - break; - -+ /* -+ * TODO: Implement when we have better topology representation. -+ case MSR_INTEL_CORE_THREAD_COUNT: -+ */ - default: - return X86EMUL_UNHANDLEABLE; - } -@@ -229,6 +233,7 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) - { - uint64_t rsvd; - -+ case MSR_INTEL_CORE_THREAD_COUNT: - case MSR_INTEL_PLATFORM_INFO: - case MSR_ARCH_CAPABILITIES: - /* Read-only */ -diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h -index 11512d4..389f95f 100644 ---- a/xen/include/asm-x86/msr-index.h -+++ b/xen/include/asm-x86/msr-index.h -@@ -32,6 +32,10 @@ - #define EFER_KNOWN_MASK (EFER_SCE | EFER_LME | EFER_LMA | EFER_NX | \ - EFER_SVME | EFER_FFXSE) - -+#define MSR_INTEL_CORE_THREAD_COUNT 0x00000035 -+#define MSR_CTC_THREAD_MASK 0x0000ffff -+#define MSR_CTC_CORE_MASK 0xffff0000 -+ - /* Speculation Controls. */ - #define MSR_SPEC_CTRL 0x00000048 - #define SPEC_CTRL_IBRS (_AC(1, ULL) << 0) diff --git a/xsa297-4.12-3.patch b/xsa297-4.12-3.patch deleted file mode 100644 index 7b43266..0000000 --- a/xsa297-4.12-3.patch +++ /dev/null @@ -1,109 +0,0 @@ -From: Andrew Cooper -Subject: x86/boot: Detect the firmware SMT setting correctly on Intel hardware - -While boot_cpu_data.x86_num_siblings is an accurate value to use on AMD -hardware, it isn't on Intel when the user has disabled Hyperthreading in the -firmware. As a result, a user which has chosen to disable HT still gets -nagged on L1TF-vulnerable hardware when they haven't chosen an explicit -smt= setting. - -Make use of the largely-undocumented MSR_INTEL_CORE_THREAD_COUNT which in -practice exists since Nehalem, when booting on real hardware. Fall back to -using the ACPI table APIC IDs. - -While adjusting this logic, fix a latent bug in amd_get_topology(). The -thread count field in CPUID.0x8000001e.ebx is documented as 8 bits wide, -rather than 2 bits wide. - -Signed-off-by: Andrew Cooper -Acked-by: Jan Beulich - -diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c -index c790416..b1debac 100644 ---- a/xen/arch/x86/cpu/amd.c -+++ b/xen/arch/x86/cpu/amd.c -@@ -507,7 +507,7 @@ static void amd_get_topology(struct cpuinfo_x86 *c) - u32 eax, ebx, ecx, edx; - - cpuid(0x8000001e, &eax, &ebx, &ecx, &edx); -- c->x86_num_siblings = ((ebx >> 8) & 0x3) + 1; -+ c->x86_num_siblings = ((ebx >> 8) & 0xff) + 1; - - if (c->x86 < 0x17) - c->compute_unit_id = ebx & 0xFF; -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 99310c8..e49ab3f 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -368,6 +368,45 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - #endif - } - -+static bool __init check_smt_enabled(void) -+{ -+ uint64_t val; -+ unsigned int cpu; -+ -+ /* -+ * x86_num_siblings defaults to 1 in the absence of other information, and -+ * is adjusted based on other topology information found in CPUID leaves. -+ * -+ * On AMD hardware, it will be the current SMT configuration. On Intel -+ * hardware, it will represent the maximum capability, rather than the -+ * current configuration. -+ */ -+ if ( boot_cpu_data.x86_num_siblings < 2 ) -+ return false; -+ -+ /* -+ * Intel Nehalem and later hardware does have an MSR which reports the -+ * current count of cores/threads in the package. -+ * -+ * At the time of writing, it is almost completely undocumented, so isn't -+ * virtualised reliably. -+ */ -+ if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && !cpu_has_hypervisor && -+ !rdmsr_safe(MSR_INTEL_CORE_THREAD_COUNT, val) ) -+ return (MASK_EXTR(val, MSR_CTC_CORE_MASK) != -+ MASK_EXTR(val, MSR_CTC_THREAD_MASK)); -+ -+ /* -+ * Search over the CPUs reported in the ACPI tables. Any whose APIC ID -+ * has a non-zero thread id component indicates that SMT is active. -+ */ -+ for_each_present_cpu ( cpu ) -+ if ( x86_cpu_to_apicid[cpu] & (boot_cpu_data.x86_num_siblings - 1) ) -+ return true; -+ -+ return false; -+} -+ - /* Calculate whether Retpoline is known-safe on this CPU. */ - static bool __init retpoline_safe(uint64_t caps) - { -@@ -697,12 +736,14 @@ static __init void l1tf_calculations(uint64_t caps) - void __init init_speculation_mitigations(void) - { - enum ind_thunk thunk = THUNK_DEFAULT; -- bool use_spec_ctrl = false, ibrs = false; -+ bool use_spec_ctrl = false, ibrs = false, hw_smt_enabled; - uint64_t caps = 0; - - if ( boot_cpu_has(X86_FEATURE_ARCH_CAPS) ) - rdmsrl(MSR_ARCH_CAPABILITIES, caps); - -+ hw_smt_enabled = check_smt_enabled(); -+ - /* - * Has the user specified any custom BTI mitigations? If so, follow their - * instructions exactly and disable all heuristics. -@@ -873,8 +914,7 @@ void __init init_speculation_mitigations(void) - * However, if we are on affected hardware, with HT enabled, and the user - * hasn't explicitly chosen whether to use HT or not, nag them to do so. - */ -- if ( opt_smt == -1 && cpu_has_bug_l1tf && !pv_shim && -- boot_cpu_data.x86_num_siblings > 1 ) -+ if ( opt_smt == -1 && cpu_has_bug_l1tf && !pv_shim && hw_smt_enabled ) - warning_add( - "Booted on L1TF-vulnerable hardware with SMT/Hyperthreading\n" - "enabled. Please assess your configuration and choose an\n" diff --git a/xsa297-4.12-4.patch b/xsa297-4.12-4.patch deleted file mode 100644 index a7e2fe2..0000000 --- a/xsa297-4.12-4.patch +++ /dev/null @@ -1,55 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Misc non-functional cleanup - - * Identify BTI in the spec_ctrl_{enter,exit}_idle() comments, as other - mitigations will shortly appear. - * Use alternative_input() and cover the lack of memory cobber with a further - barrier. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h -index 779da2b..20ee112 100644 ---- a/xen/include/asm-x86/spec_ctrl.h -+++ b/xen/include/asm-x86/spec_ctrl.h -@@ -68,6 +68,8 @@ static always_inline void spec_ctrl_enter_idle(struct cpu_info *info) - uint32_t val = 0; - - /* -+ * Branch Target Injection: -+ * - * Latch the new shadow value, then enable shadowing, then update the MSR. - * There are no SMP issues here; only local processor ordering concerns. - */ -@@ -75,8 +77,9 @@ static always_inline void spec_ctrl_enter_idle(struct cpu_info *info) - barrier(); - info->spec_ctrl_flags |= SCF_use_shadow; - barrier(); -- asm volatile ( ALTERNATIVE("", "wrmsr", X86_FEATURE_SC_MSR_IDLE) -- :: "a" (val), "c" (MSR_SPEC_CTRL), "d" (0) : "memory" ); -+ alternative_input("", "wrmsr", X86_FEATURE_SC_MSR_IDLE, -+ "a" (val), "c" (MSR_SPEC_CTRL), "d" (0)); -+ barrier(); - } - - /* WARNING! `ret`, `call *`, `jmp *` not safe before this call. */ -@@ -85,13 +88,16 @@ static always_inline void spec_ctrl_exit_idle(struct cpu_info *info) - uint32_t val = info->xen_spec_ctrl; - - /* -+ * Branch Target Injection: -+ * - * Disable shadowing before updating the MSR. There are no SMP issues - * here; only local processor ordering concerns. - */ - info->spec_ctrl_flags &= ~SCF_use_shadow; - barrier(); -- asm volatile ( ALTERNATIVE("", "wrmsr", X86_FEATURE_SC_MSR_IDLE) -- :: "a" (val), "c" (MSR_SPEC_CTRL), "d" (0) : "memory" ); -+ alternative_input("", "wrmsr", X86_FEATURE_SC_MSR_IDLE, -+ "a" (val), "c" (MSR_SPEC_CTRL), "d" (0)); -+ barrier(); - } - - #endif /* __ASSEMBLY__ */ diff --git a/xsa297-4.12-5.patch b/xsa297-4.12-5.patch deleted file mode 100644 index 172a992..0000000 --- a/xsa297-4.12-5.patch +++ /dev/null @@ -1,147 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: CPUID/MSR definitions for Microarchitectural Data - Sampling - -The MD_CLEAR feature can be automatically offered to guests. No -infrastructure is needed in Xen to support the guest making use of it. - -This is part of XSA-297, CVE-2018-12126, CVE-2018-12127, CVE-2018-12130, CVE-2019-11091. - -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 6db82f3..f80d8d8 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -483,7 +483,7 @@ accounting for hardware capabilities as enumerated via CPUID. - - Currently accepted: - --The Speculation Control hardware features `ibrsb`, `stibp`, `ibpb`, -+The Speculation Control hardware features `md-clear`, `ibrsb`, `stibp`, `ibpb`, - `l1d-flush` and `ssbd` are used by default if available and applicable. They can - be ignored, e.g. `no-ibrsb`, at which point Xen won't use them itself, and - won't offer them to guests. -diff --git a/tools/libxl/libxl_cpuid.c b/tools/libxl/libxl_cpuid.c -index 52e16c2..5a1702d 100644 ---- a/tools/libxl/libxl_cpuid.c -+++ b/tools/libxl/libxl_cpuid.c -@@ -202,6 +202,7 @@ int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str) - - {"avx512-4vnniw",0x00000007, 0, CPUID_REG_EDX, 2, 1}, - {"avx512-4fmaps",0x00000007, 0, CPUID_REG_EDX, 3, 1}, -+ {"md-clear", 0x00000007, 0, CPUID_REG_EDX, 10, 1}, - {"ibrsb", 0x00000007, 0, CPUID_REG_EDX, 26, 1}, - {"stibp", 0x00000007, 0, CPUID_REG_EDX, 27, 1}, - {"l1d-flush", 0x00000007, 0, CPUID_REG_EDX, 28, 1}, -diff --git a/tools/misc/xen-cpuid.c b/tools/misc/xen-cpuid.c -index d87a72e..f67ecd3 100644 ---- a/tools/misc/xen-cpuid.c -+++ b/tools/misc/xen-cpuid.c -@@ -146,6 +146,7 @@ static const char *str_7d0[32] = - { - [ 2] = "avx512_4vnniw", [ 3] = "avx512_4fmaps", - -+ [10] = "md-clear", - /* 12 */ [13] = "tsx-force-abort", - - [26] = "ibrsb", [27] = "stibp", -diff --git a/xen/arch/x86/cpuid.c b/xen/arch/x86/cpuid.c -index ab0aab6..3efad9c 100644 ---- a/xen/arch/x86/cpuid.c -+++ b/xen/arch/x86/cpuid.c -@@ -29,7 +29,12 @@ static int __init parse_xen_cpuid(const char *s) - if ( !ss ) - ss = strchr(s, '\0'); - -- if ( (val = parse_boolean("ibpb", s, ss)) >= 0 ) -+ if ( (val = parse_boolean("md-clear", s, ss)) >= 0 ) -+ { -+ if ( !val ) -+ setup_clear_cpu_cap(X86_FEATURE_MD_CLEAR); -+ } -+ else if ( (val = parse_boolean("ibpb", s, ss)) >= 0 ) - { - if ( !val ) - setup_clear_cpu_cap(X86_FEATURE_IBPB); -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index e49ab3f..a573b02 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -291,17 +291,19 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - printk("Speculative mitigation facilities:\n"); - - /* Hardware features which pertain to speculative mitigations. */ -- 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", - (_7d0 & cpufeat_mask(X86_FEATURE_IBRSB)) ? " IBRS/IBPB" : "", - (_7d0 & cpufeat_mask(X86_FEATURE_STIBP)) ? " STIBP" : "", - (_7d0 & cpufeat_mask(X86_FEATURE_L1D_FLUSH)) ? " L1D_FLUSH" : "", - (_7d0 & cpufeat_mask(X86_FEATURE_SSBD)) ? " SSBD" : "", -+ (_7d0 & cpufeat_mask(X86_FEATURE_MD_CLEAR)) ? " MD_CLEAR" : "", - (e8b & cpufeat_mask(X86_FEATURE_IBPB)) ? " IBPB" : "", - (caps & ARCH_CAPS_IBRS_ALL) ? " IBRS_ALL" : "", - (caps & ARCH_CAPS_RDCL_NO) ? " RDCL_NO" : "", - (caps & ARCH_CAPS_RSBA) ? " RSBA" : "", - (caps & ARCH_CAPS_SKIP_L1DFL) ? " SKIP_L1DFL": "", -- (caps & ARCH_CAPS_SSB_NO) ? " SSB_NO" : ""); -+ (caps & ARCH_CAPS_SSB_NO) ? " SSB_NO" : "", -+ (caps & ARCH_CAPS_MDS_NO) ? " MDS_NO" : ""); - - /* Compiled-in support which pertains to mitigations. */ - if ( IS_ENABLED(CONFIG_INDIRECT_THUNK) || IS_ENABLED(CONFIG_SHADOW_PAGING) ) -@@ -339,23 +341,25 @@ 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\n", -+ printk(" Support for HVM VMs:%s%s%s%s%s\n", - (boot_cpu_has(X86_FEATURE_SC_MSR_HVM) || - boot_cpu_has(X86_FEATURE_SC_RSB_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" : ""); -+ opt_eager_fpu ? " EAGER_FPU" : "", -+ boot_cpu_has(X86_FEATURE_MD_CLEAR) ? " MD_CLEAR" : ""); - - #endif - #ifdef CONFIG_PV -- printk(" Support for PV VMs:%s%s%s%s\n", -+ printk(" Support for PV VMs:%s%s%s%s%s\n", - (boot_cpu_has(X86_FEATURE_SC_MSR_PV) || - boot_cpu_has(X86_FEATURE_SC_RSB_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" : ""); -+ opt_eager_fpu ? " EAGER_FPU" : "", -+ boot_cpu_has(X86_FEATURE_MD_CLEAR) ? " MD_CLEAR" : ""); - - printk(" XPTI (64-bit PV only): Dom0 %s, DomU %s (with%s PCID)\n", - opt_xpti_hwdom ? "enabled" : "disabled", -diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h -index 389f95f..637259b 100644 ---- a/xen/include/asm-x86/msr-index.h -+++ b/xen/include/asm-x86/msr-index.h -@@ -51,6 +51,7 @@ - #define ARCH_CAPS_RSBA (_AC(1, ULL) << 2) - #define ARCH_CAPS_SKIP_L1DFL (_AC(1, ULL) << 3) - #define ARCH_CAPS_SSB_NO (_AC(1, ULL) << 4) -+#define ARCH_CAPS_MDS_NO (_AC(1, ULL) << 5) - - #define MSR_FLUSH_CMD 0x0000010b - #define FLUSH_CMD_L1D (_AC(1, ULL) << 0) -diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h -index 2bcc548..55231d4 100644 ---- a/xen/include/public/arch-x86/cpufeatureset.h -+++ b/xen/include/public/arch-x86/cpufeatureset.h -@@ -242,6 +242,7 @@ XEN_CPUFEATURE(IBPB, 8*32+12) /*A IBPB support only (no IBRS, used by - /* Intel-defined CPU features, CPUID level 0x00000007:0.edx, word 9 */ - XEN_CPUFEATURE(AVX512_4VNNIW, 9*32+ 2) /*A AVX512 Neural Network Instructions */ - XEN_CPUFEATURE(AVX512_4FMAPS, 9*32+ 3) /*A AVX512 Multiply Accumulation Single Precision */ -+XEN_CPUFEATURE(MD_CLEAR, 9*32+10) /*A VERW clears microarchitectural buffers */ - XEN_CPUFEATURE(TSX_FORCE_ABORT, 9*32+13) /* MSR_TSX_FORCE_ABORT.RTM_ABORT */ - XEN_CPUFEATURE(IBRSB, 9*32+26) /*A IBRS and IBPB support (used by Intel) */ - XEN_CPUFEATURE(STIBP, 9*32+27) /*A STIBP */ diff --git a/xsa297-4.12-6.patch b/xsa297-4.12-6.patch deleted file mode 100644 index 48023a8..0000000 --- a/xsa297-4.12-6.patch +++ /dev/null @@ -1,134 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Infrastructure to use VERW to flush pipeline buffers - -Three synthetic features are introduced, as we need individual control of -each, depending on circumstances. A later change will enable them at -appropriate points. - -The verw_sel field doesn't strictly need to live in struct cpu_info. It lives -there because there is a convenient hole it can fill, and it reduces the -complexity of the SPEC_CTRL_EXIT_TO_{PV,HVM} assembly by avoiding the need for -any temporary stack maintenance. - -This is part of XSA-297, CVE-2018-12126, CVE-2018-12127, CVE-2018-12130, CVE-2019-11091. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/x86_64/asm-offsets.c b/xen/arch/x86/x86_64/asm-offsets.c -index 052228c..33930ce 100644 ---- a/xen/arch/x86/x86_64/asm-offsets.c -+++ b/xen/arch/x86/x86_64/asm-offsets.c -@@ -110,6 +110,7 @@ void __dummy__(void) - BLANK(); - - OFFSET(CPUINFO_guest_cpu_user_regs, struct cpu_info, guest_cpu_user_regs); -+ OFFSET(CPUINFO_verw_sel, struct cpu_info, verw_sel); - OFFSET(CPUINFO_current_vcpu, struct cpu_info, current_vcpu); - OFFSET(CPUINFO_cr4, struct cpu_info, cr4); - OFFSET(CPUINFO_xen_cr3, struct cpu_info, xen_cr3); -diff --git a/xen/include/asm-x86/cpufeatures.h b/xen/include/asm-x86/cpufeatures.h -index 0c06274..ba55245 100644 ---- a/xen/include/asm-x86/cpufeatures.h -+++ b/xen/include/asm-x86/cpufeatures.h -@@ -31,3 +31,6 @@ XEN_CPUFEATURE(SC_RSB_PV, (FSCAPINTS+0)*32+18) /* RSB overwrite needed for - XEN_CPUFEATURE(SC_RSB_HVM, (FSCAPINTS+0)*32+19) /* RSB overwrite needed for HVM */ - XEN_CPUFEATURE(SC_MSR_IDLE, (FSCAPINTS+0)*32+21) /* (SC_MSR_PV || SC_MSR_HVM) && default_xen_spec_ctrl */ - XEN_CPUFEATURE(XEN_LBR, (FSCAPINTS+0)*32+22) /* Xen uses MSR_DEBUGCTL.LBR */ -+XEN_CPUFEATURE(SC_VERW_PV, (FSCAPINTS+0)*32+23) /* VERW used by Xen for PV */ -+XEN_CPUFEATURE(SC_VERW_HVM, (FSCAPINTS+0)*32+24) /* VERW used by Xen for HVM */ -+XEN_CPUFEATURE(SC_VERW_IDLE, (FSCAPINTS+0)*32+25) /* VERW used by Xen for idle */ -diff --git a/xen/include/asm-x86/current.h b/xen/include/asm-x86/current.h -index 5bd64b2..f3508c3 100644 ---- a/xen/include/asm-x86/current.h -+++ b/xen/include/asm-x86/current.h -@@ -38,6 +38,7 @@ struct vcpu; - struct cpu_info { - struct cpu_user_regs guest_cpu_user_regs; - unsigned int processor_id; -+ unsigned int verw_sel; - struct vcpu *current_vcpu; - unsigned long per_cpu_offset; - unsigned long cr4; -diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h -index 20ee112..ba03bb4 100644 ---- a/xen/include/asm-x86/spec_ctrl.h -+++ b/xen/include/asm-x86/spec_ctrl.h -@@ -60,6 +60,13 @@ static inline void init_shadow_spec_ctrl_state(void) - info->shadow_spec_ctrl = 0; - info->xen_spec_ctrl = default_xen_spec_ctrl; - info->spec_ctrl_flags = default_spec_ctrl_flags; -+ -+ /* -+ * For least latency, the VERW selector should be a writeable data -+ * descriptor resident in the cache. __HYPERVISOR_DS32 shares a cache -+ * line with __HYPERVISOR_CS, so is expected to be very cache-hot. -+ */ -+ info->verw_sel = __HYPERVISOR_DS32; - } - - /* WARNING! `ret`, `call *`, `jmp *` not safe after this call. */ -@@ -80,6 +87,22 @@ static always_inline void spec_ctrl_enter_idle(struct cpu_info *info) - alternative_input("", "wrmsr", X86_FEATURE_SC_MSR_IDLE, - "a" (val), "c" (MSR_SPEC_CTRL), "d" (0)); - barrier(); -+ -+ /* -+ * Microarchitectural Store Buffer Data Sampling: -+ * -+ * On vulnerable systems, store buffer entries are statically partitioned -+ * between active threads. When entering idle, our store buffer entries -+ * are re-partitioned to allow the other threads to use them. -+ * -+ * Flush the buffers to ensure that no sensitive data of ours can be -+ * leaked by a sibling after it gets our store buffer entries. -+ * -+ * Note: VERW must be encoded with a memory operand, as it is only that -+ * form which causes a flush. -+ */ -+ alternative_input("", "verw %[sel]", X86_FEATURE_SC_VERW_IDLE, -+ [sel] "m" (info->verw_sel)); - } - - /* WARNING! `ret`, `call *`, `jmp *` not safe before this call. */ -@@ -98,6 +121,17 @@ static always_inline void spec_ctrl_exit_idle(struct cpu_info *info) - alternative_input("", "wrmsr", X86_FEATURE_SC_MSR_IDLE, - "a" (val), "c" (MSR_SPEC_CTRL), "d" (0)); - barrier(); -+ -+ /* -+ * Microarchitectural Store Buffer Data Sampling: -+ * -+ * On vulnerable systems, store buffer entries are statically partitioned -+ * between active threads. When exiting idle, the other threads store -+ * buffer entries are re-partitioned to give us some. -+ * -+ * We now have store buffer entries with stale data from sibling threads. -+ * A flush if necessary will be performed on the return to guest path. -+ */ - } - - #endif /* __ASSEMBLY__ */ -diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h -index 803f7ce..c60093b 100644 ---- a/xen/include/asm-x86/spec_ctrl_asm.h -+++ b/xen/include/asm-x86/spec_ctrl_asm.h -@@ -241,12 +241,16 @@ - /* 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 -+ DO_SPEC_CTRL_EXIT_TO_GUEST, X86_FEATURE_SC_MSR_PV; \ -+ 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 -+ 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. diff --git a/xsa297-4.12-7.patch b/xsa297-4.12-7.patch deleted file mode 100644 index 8a70687..0000000 --- a/xsa297-4.12-7.patch +++ /dev/null @@ -1,316 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Introduce options to control VERW flushing - -The Microarchitectural Data Sampling vulnerability is split into categories -with subtly different properties: - - MLPDS - Microarchitectural Load Port Data Sampling - MSBDS - Microarchitectural Store Buffer Data Sampling - MFBDS - Microarchitectural Fill Buffer Data Sampling - MDSUM - Microarchitectural Data Sampling Uncacheable Memory - -MDSUM is a special case of the other three, and isn't distinguished further. - -These issues pertain to three microarchitectural buffers. The Load Ports, the -Store Buffers and the Fill Buffers. Each of these structures are flushed by -the new enhanced VERW functionality, but the conditions under which flushing -is necessary vary. - -For this concise overview of the issues and default logic, the abbreviations -SP (Store Port), FB (Fill Buffer), LP (Load Port) and HT (Hyperthreading) are -used for brevity: - - * Vulnerable hardware is divided into two categories - parts which suffer - from SP only, and parts with any other combination of vulnerabilities. - - * SP only has an HT interaction when the thread goes idle, due to the static - partitioning of resources. LP and FB have HT interactions at all points, - due to the competitive sharing of resources. All issues potentially leak - data across the return-to-guest transition. - - * The microcode which implements VERW flushing also extends MSR_FLUSH_CMD, so - we don't need to do both on the HVM return-to-guest path. However, some - parts are not vulnerable to L1TF (therefore have no MSR_FLUSH_CMD), but are - vulnerable to MDS, so do require VERW on the HVM path. - -Note that we deliberately support mds=1 even without MD_CLEAR in case the -microcode has been updated but the feature bit not exposed. - -This is part of XSA-297, CVE-2018-12126, CVE-2018-12127, CVE-2018-12130, CVE-2019-11091. - -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 f80d8d8..85081fd 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -1895,7 +1895,7 @@ 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}=, -+> `= List of [ , xen=, {pv,hvm,msr-sc,rsb,md-clear}=, - > bti-thunk=retpoline|lfence|jmp, {ibrs,ibpb,ssbd,eager-fpu, - > l1d-flush}= ]` - -@@ -1919,9 +1919,10 @@ 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=` and `rsb=` 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. -+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. - - * `pv=` and `hvm=` offer control over all suboptions for PV and HVM guests - respectively. -@@ -1930,6 +1931,11 @@ protect itself, and Xen's ability to virtualise support for 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. -+* `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.* - - 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 a573b02..0509ac8 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -35,6 +35,8 @@ static bool __initdata opt_msr_sc_pv = true; - static bool __initdata opt_msr_sc_hvm = true; - static bool __initdata opt_rsb_pv = true; - 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; - - /* Cmdline controls for Xen's speculative settings. */ - static enum ind_thunk { -@@ -59,6 +61,9 @@ paddr_t __read_mostly l1tf_addr_mask, __read_mostly l1tf_safe_maddr; - static bool __initdata cpu_has_bug_l1tf; - static unsigned int __initdata l1d_maxphysaddr; - -+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 int __init parse_spec_ctrl(const char *s) - { - const char *ss; -@@ -94,6 +99,8 @@ static int __init 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_thunk = THUNK_JMP; - opt_ibrs = 0; -@@ -116,11 +123,13 @@ static int __init parse_spec_ctrl(const char *s) - { - opt_msr_sc_pv = val; - opt_rsb_pv = val; -+ opt_md_clear_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; - } - else if ( (val = parse_boolean("msr-sc", s, ss)) >= 0 ) - { -@@ -132,6 +141,12 @@ static int __init parse_spec_ctrl(const char *s) - opt_rsb_pv = val; - opt_rsb_hvm = val; - } -+ else if ( (val = parse_boolean("md-clear", s, ss)) >= 0 || -+ (val = parse_boolean("mds", s, ss)) >= 0 ) -+ { -+ opt_md_clear_pv = val; -+ opt_md_clear_hvm = val; -+ } - - /* Xen's speculative sidechannel mitigation settings. */ - else if ( !strncmp(s, "bti-thunk=", 10) ) -@@ -317,7 +332,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, Other:%s%s\n", -+ printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s, Other:%s%s%s\n", - thunk == THUNK_NONE ? "N/A" : - thunk == THUNK_RETPOLINE ? "RETPOLINE" : - thunk == THUNK_LFENCE ? "LFENCE" : -@@ -327,7 +342,8 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - !boot_cpu_has(X86_FEATURE_SSBD) ? "" : - (default_xen_spec_ctrl & SPEC_CTRL_SSBD) ? " SSBD+" : " SSBD-", - opt_ibpb ? " IBPB" : "", -- opt_l1d_flush ? " L1D_FLUSH" : ""); -+ opt_l1d_flush ? " L1D_FLUSH" : "", -+ opt_md_clear_pv || opt_md_clear_hvm ? " VERW" : ""); - - /* L1TF diagnostics, printed if vulnerable or PV shadowing is in use. */ - if ( cpu_has_bug_l1tf || opt_pv_l1tf_hwdom || opt_pv_l1tf_domu ) -@@ -737,6 +753,107 @@ static __init void l1tf_calculations(uint64_t caps) - : (3ul << (paddr_bits - 2)))); - } - -+/* Calculate whether this CPU is vulnerable to MDS. */ -+static __init void mds_calculations(uint64_t caps) -+{ -+ /* MDS 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; -+ -+ /* Any processor advertising MDS_NO should be not vulnerable to MDS. */ -+ if ( caps & ARCH_CAPS_MDS_NO ) -+ return; -+ -+ switch ( boot_cpu_data.x86_model ) -+ { -+ /* -+ * Core processors since at least Nehalem are vulnerable. -+ */ -+ case 0x1f: /* Auburndale / Havendale */ -+ case 0x1e: /* Nehalem */ -+ case 0x1a: /* Nehalem EP */ -+ case 0x2e: /* Nehalem EX */ -+ case 0x25: /* Westmere */ -+ case 0x2c: /* Westmere EP */ -+ case 0x2f: /* Westmere EX */ -+ case 0x2a: /* SandyBridge */ -+ case 0x2d: /* SandyBridge EP/EX */ -+ case 0x3a: /* IvyBridge */ -+ case 0x3e: /* IvyBridge EP/EX */ -+ case 0x3c: /* Haswell */ -+ case 0x3f: /* Haswell EX/EP */ -+ case 0x45: /* Haswell D */ -+ case 0x46: /* Haswell H */ -+ case 0x3d: /* Broadwell */ -+ case 0x47: /* Broadwell H */ -+ case 0x4f: /* Broadwell EP/EX */ -+ case 0x56: /* Broadwell D */ -+ case 0x4e: /* Skylake M */ -+ case 0x5e: /* Skylake D */ -+ cpu_has_bug_mds = true; -+ break; -+ -+ /* -+ * Some Core processors have per-stepping vulnerability. -+ */ -+ case 0x55: /* Skylake-X / Cascade Lake */ -+ if ( boot_cpu_data.x86_mask <= 5 ) -+ cpu_has_bug_mds = true; -+ break; -+ -+ case 0x8e: /* Kaby / Coffee / Whiskey Lake M */ -+ if ( boot_cpu_data.x86_mask <= 0xb ) -+ cpu_has_bug_mds = true; -+ break; -+ -+ case 0x9e: /* Kaby / Coffee / Whiskey Lake D */ -+ if ( boot_cpu_data.x86_mask <= 0xc ) -+ cpu_has_bug_mds = true; -+ break; -+ -+ /* -+ * Very old and very new Atom processors are not vulnerable. -+ */ -+ case 0x1c: /* Pineview */ -+ case 0x26: /* Lincroft */ -+ case 0x27: /* Penwell */ -+ case 0x35: /* Cloverview */ -+ case 0x36: /* Cedarview */ -+ case 0x7a: /* Goldmont */ -+ break; -+ -+ /* -+ * Middling Atom processors are vulnerable to just the Store Buffer -+ * aspect. -+ */ -+ case 0x37: /* Baytrail / Valleyview (Silvermont) */ -+ case 0x4a: /* Merrifield */ -+ case 0x4c: /* Cherrytrail / Brasswell */ -+ case 0x4d: /* Avaton / Rangely (Silvermont) */ -+ case 0x5a: /* Moorefield */ -+ case 0x5d: -+ case 0x65: -+ case 0x6e: -+ case 0x75: -+ /* -+ * Knights processors (which are based on the Silvermont/Airmont -+ * microarchitecture) are similarly only affected by the Store Buffer -+ * aspect. -+ */ -+ case 0x57: /* Knights Landing */ -+ case 0x85: /* Knights Mill */ -+ cpu_has_bug_msbds_only = true; -+ break; -+ -+ default: -+ printk("Unrecognised CPU model %#x - assuming vulnerable to MDS\n", -+ boot_cpu_data.x86_model); -+ cpu_has_bug_mds = true; -+ break; -+ } -+} -+ - void __init init_speculation_mitigations(void) - { - enum ind_thunk thunk = THUNK_DEFAULT; -@@ -924,6 +1041,47 @@ void __init init_speculation_mitigations(void) - "enabled. Please assess your configuration and choose an\n" - "explicit 'smt=' setting. See XSA-273.\n"); - -+ mds_calculations(caps); -+ -+ /* -+ * 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)); -+ -+ /* -+ * 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. -+ * -+ * 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. -+ */ -+ 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); -+ -+ /* -+ * Warn the user if they are on MLPDS/MFBDS-vulnerable hardware with HT -+ * active and no explicit SMT choice. -+ */ -+ if ( opt_smt == -1 && cpu_has_bug_mds && hw_smt_enabled ) -+ warning_add( -+ "Booted on MLPDS/MFBDS-vulnerable hardware with SMT/Hyperthreading\n" -+ "enabled. Mitigations will not be fully effective. Please\n" -+ "choose an explicit smt= setting. See XSA-297.\n"); -+ - print_details(thunk, caps); - - /* From a5572496fb13f642e05376fdda0801046f266df5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Mon, 19 Aug 2019 11:09:49 +0200 Subject: [PATCH 016/194] Rebuilt for Python 3.8 --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 3d6eddc..2b5c33c 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.12.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 @@ -875,6 +875,9 @@ fi %endif %changelog +* Mon Aug 19 2019 Miro Hrončok - 4.12.1-2 +- Rebuilt for Python 3.8 + * Fri Aug 09 2019 Michael Young - 4.12.1-1 - update to 4.12.1 remove patches for issues now fixed upstream From 9e7881994e688a57d2fbb10e333b4362649b023c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Thu, 3 Oct 2019 14:41:52 +0200 Subject: [PATCH 017/194] Rebuilt for Python 3.8.0rc1 (#1748018) --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 2b5c33c..d4fba3f 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.12.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 @@ -875,6 +875,9 @@ fi %endif %changelog +* Thu Oct 03 2019 Miro Hrončok - 4.12.1-3 +- Rebuilt for Python 3.8.0rc1 (#1748018) + * Mon Aug 19 2019 Miro Hrončok - 4.12.1-2 - Rebuilt for Python 3.8 From ed166e2fa92d49f414aa1b960117dbad443d7d7c Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 31 Oct 2019 20:47:20 +0000 Subject: [PATCH 018/194] 6 security fixes VCPUOP_initialise DoS [XSA-296, CVE-2019-18420] missing descriptor table limit checking in x86 PV emulation [XSA-298, CVE-2019-18425] Issues with restartable PV type change operations [XSA-299, CVE-2019-18421] add-to-physmap can be abused to DoS Arm hosts [XSA-301, CVE-2019-18423] passed through PCI devices may corrupt host memory after deassignment [XSA-302, CVE-2019-18424] ARM: Interrupts are unconditionally unmasked in exception handlers [XSA-303, CVE-2019-18422] --- xen.spec | 39 +- xsa296.patch | 195 +++++++ xsa298.patch | 89 ++++ ...F-checks-don-t-leave-a-partial-entry.patch | 94 ++++ xsa301-master-1.patch | 80 +++ xsa301-master-2.patch | 92 ++++ xsa301-master-3.patch | 67 +++ ....12-0001-IOMMU-add-missing-HVM-check.patch | 37 ++ ...2-passthrough-quarantine-PCI-devices.patch | 499 ++++++++++++++++++ ...try-Split-__DEFINE_ENTRY_TRAP-in-two.patch | 74 +++ ...Fold-the-macro-SAVE_ALL-in-the-macro.patch | 97 ++++ ...blindly-unmask-interrupts-on-trap-wi.patch | 226 ++++++++ ...blindly-unmask-interrupts-on-trap-wi.patch | 114 ++++ 13 files changed, 1702 insertions(+), 1 deletion(-) create mode 100644 xsa296.patch create mode 100644 xsa298.patch create mode 100644 xsa299-4.12-0001-x86-mm-L1TF-checks-don-t-leave-a-partial-entry.patch create mode 100644 xsa301-master-1.patch create mode 100644 xsa301-master-2.patch create mode 100644 xsa301-master-3.patch create mode 100644 xsa302-4.12-0001-IOMMU-add-missing-HVM-check.patch create mode 100644 xsa302-4.12-0002-passthrough-quarantine-PCI-devices.patch create mode 100644 xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch create mode 100644 xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch create mode 100644 xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch create mode 100644 xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch diff --git a/xen.spec b/xen.spec index d4fba3f..cafbc00 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.12.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,18 @@ Patch41: xen.python.env.patch Patch42: xen.gcc9.fixes.patch Patch43: xen.python3.patch Patch54: xen.python38.patch +Patch55: xsa296.patch +Patch56: xsa298.patch +Patch57: xsa299-4.12-0001-x86-mm-L1TF-checks-don-t-leave-a-partial-entry.patch +Patch58: xsa301-master-1.patch +Patch59: xsa301-master-2.patch +Patch60: xsa301-master-3.patch +Patch61: xsa302-4.12-0001-IOMMU-add-missing-HVM-check.patch +Patch62: xsa302-4.12-0002-passthrough-quarantine-PCI-devices.patch +Patch63: xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch +Patch64: xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch +Patch65: xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch +Patch66: xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch %if %build_qemutrad @@ -312,6 +324,20 @@ manage Xen virtual machines. %patch42 -p1 %patch43 -p1 %patch54 -p1 +%patch55 -p1 +%patch56 -p1 +%patch57 -p1 +%patch58 -p1 +%patch59 -p1 +%patch60 -p1 +%ifarch %{ix86} x86_64 +%patch61 -p1 +%patch62 -p1 +%endif +%patch63 -p1 +%patch64 -p1 +%patch65 -p1 +%patch66 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -875,6 +901,17 @@ fi %endif %changelog +* Thu Oct 31 2019 Michael Young - 4.12.1-4 +- VCPUOP_initialise DoS [XSA-296, CVE-2019-18420] +- missing descriptor table limit checking in x86 PV emulation [XSA-298, + CVE-2019-18425] +- Issues with restartable PV type change operations [XSA-299, CVE-2019-18421] +- add-to-physmap can be abused to DoS Arm hosts [XSA-301, CVE-2019-18423] +- passed through PCI devices may corrupt host memory after deassignment + [XSA-302, CVE-2019-18424] +- ARM: Interrupts are unconditionally unmasked in exception handlers + [XSA-303, CVE-2019-18422] + * Thu Oct 03 2019 Miro Hrončok - 4.12.1-3 - Rebuilt for Python 3.8.0rc1 (#1748018) diff --git a/xsa296.patch b/xsa296.patch new file mode 100644 index 0000000..e71ea7f --- /dev/null +++ b/xsa296.patch @@ -0,0 +1,195 @@ +From: Andrew Cooper +Subject: xen/hypercall: Don't use BUG() for parameter checking in hypercall_create_continuation() + +Since c/s 1d429034 "hypercall: update vcpu_op to take an unsigned vcpuid", +which incorrectly swapped 'i' for 'u' in the parameter type list, guests have +been able to hit the BUG() in next_args()'s default case. + +Correct these back to 'i'. + +In addition, make adjustments to prevent this class of issue from occurring in +the future - crashing Xen is not an appropriate form of parameter checking. + +Capitalise NEXT_ARG() to catch all uses, to highlight that it is a macro doing +non-function-like things behind the scenes, and undef it when appropriate. +Implement a bad_fmt: block which prints an error, asserts unreachable, and +crashes the guest. + +On the ARM side, drop all parameter checking of p. It is asymmetric with the +x86 side, and akin to expecting memcpy() or sprintf() to check their src/fmt +parameter before use. A caller passing "" or something other than a string +literal will be obvious during code review. + +This is XSA-296. + +Signed-off-by: Andrew Cooper +Acked-by: Julien Grall + +diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c +index 941bbff4fe..a3da8e9c08 100644 +--- a/xen/arch/arm/domain.c ++++ b/xen/arch/arm/domain.c +@@ -383,14 +383,15 @@ void sync_vcpu_execstate(struct vcpu *v) + /* Nothing to do -- no lazy switching */ + } + +-#define next_arg(fmt, args) ({ \ ++#define NEXT_ARG(fmt, args) \ ++({ \ + unsigned long __arg; \ + switch ( *(fmt)++ ) \ + { \ + case 'i': __arg = (unsigned long)va_arg(args, unsigned int); break; \ + case 'l': __arg = (unsigned long)va_arg(args, unsigned long); break; \ + case 'h': __arg = (unsigned long)va_arg(args, void *); break; \ +- default: __arg = 0; BUG(); \ ++ default: goto bad_fmt; \ + } \ + __arg; \ + }) +@@ -405,9 +406,6 @@ unsigned long hypercall_create_continuation( + unsigned int i; + va_list args; + +- /* All hypercalls take at least one argument */ +- BUG_ON( !p || *p == '\0' ); +- + current->hcall_preempted = true; + + va_start(args, format); +@@ -415,7 +413,7 @@ unsigned long hypercall_create_continuation( + if ( mcs->flags & MCSF_in_multicall ) + { + for ( i = 0; *p != '\0'; i++ ) +- mcs->call.args[i] = next_arg(p, args); ++ mcs->call.args[i] = NEXT_ARG(p, args); + + /* Return value gets written back to mcs->call.result */ + rc = mcs->call.result; +@@ -431,7 +429,7 @@ unsigned long hypercall_create_continuation( + + for ( i = 0; *p != '\0'; i++ ) + { +- arg = next_arg(p, args); ++ arg = NEXT_ARG(p, args); + + switch ( i ) + { +@@ -454,7 +452,7 @@ unsigned long hypercall_create_continuation( + + for ( i = 0; *p != '\0'; i++ ) + { +- arg = next_arg(p, args); ++ arg = NEXT_ARG(p, args); + + switch ( i ) + { +@@ -475,8 +473,16 @@ unsigned long hypercall_create_continuation( + va_end(args); + + return rc; ++ ++ bad_fmt: ++ gprintk(XENLOG_ERR, "Bad hypercall continuation format '%c'\n", *p); ++ ASSERT_UNREACHABLE(); ++ domain_crash(current->domain); ++ return 0; + } + ++#undef NEXT_ARG ++ + void startup_cpu_idle_loop(void) + { + struct vcpu *v = current; +diff --git a/xen/arch/x86/hypercall.c b/xen/arch/x86/hypercall.c +index d483dbaa6b..4643e5eb43 100644 +--- a/xen/arch/x86/hypercall.c ++++ b/xen/arch/x86/hypercall.c +@@ -80,14 +80,15 @@ const hypercall_args_t hypercall_args_table[NR_hypercalls] = + #undef COMP + #undef ARGS + +-#define next_arg(fmt, args) ({ \ ++#define NEXT_ARG(fmt, args) \ ++({ \ + unsigned long __arg; \ + switch ( *(fmt)++ ) \ + { \ + case 'i': __arg = (unsigned long)va_arg(args, unsigned int); break; \ + case 'l': __arg = (unsigned long)va_arg(args, unsigned long); break; \ + case 'h': __arg = (unsigned long)va_arg(args, void *); break; \ +- default: __arg = 0; BUG(); \ ++ default: goto bad_fmt; \ + } \ + __arg; \ + }) +@@ -109,7 +110,7 @@ unsigned long hypercall_create_continuation( + if ( mcs->flags & MCSF_in_multicall ) + { + for ( i = 0; *p != '\0'; i++ ) +- mcs->call.args[i] = next_arg(p, args); ++ mcs->call.args[i] = NEXT_ARG(p, args); + } + else + { +@@ -121,7 +122,7 @@ unsigned long hypercall_create_continuation( + { + for ( i = 0; *p != '\0'; i++ ) + { +- arg = next_arg(p, args); ++ arg = NEXT_ARG(p, args); + switch ( i ) + { + case 0: regs->rdi = arg; break; +@@ -137,7 +138,7 @@ unsigned long hypercall_create_continuation( + { + for ( i = 0; *p != '\0'; i++ ) + { +- arg = next_arg(p, args); ++ arg = NEXT_ARG(p, args); + switch ( i ) + { + case 0: regs->rbx = arg; break; +@@ -154,8 +155,16 @@ unsigned long hypercall_create_continuation( + va_end(args); + + return op; ++ ++ bad_fmt: ++ gprintk(XENLOG_ERR, "Bad hypercall continuation format '%c'\n", *p); ++ ASSERT_UNREACHABLE(); ++ domain_crash(curr->domain); ++ return 0; + } + ++#undef NEXT_ARG ++ + int hypercall_xlat_continuation(unsigned int *id, unsigned int nr, + unsigned int mask, ...) + { +diff --git a/xen/common/compat/domain.c b/xen/common/compat/domain.c +index 39877b3ab2..2531fa7421 100644 +--- a/xen/common/compat/domain.c ++++ b/xen/common/compat/domain.c +@@ -81,7 +81,7 @@ int compat_vcpu_op(int cmd, unsigned int vcpuid, XEN_GUEST_HANDLE_PARAM(void) ar + } + + if ( rc == -ERESTART ) +- rc = hypercall_create_continuation(__HYPERVISOR_vcpu_op, "iuh", ++ rc = hypercall_create_continuation(__HYPERVISOR_vcpu_op, "iih", + cmd, vcpuid, arg); + + break; +diff --git a/xen/common/domain.c b/xen/common/domain.c +index 2308588052..65bcd85e34 100644 +--- a/xen/common/domain.c ++++ b/xen/common/domain.c +@@ -1411,7 +1411,7 @@ long do_vcpu_op(int cmd, unsigned int vcpuid, XEN_GUEST_HANDLE_PARAM(void) arg) + + rc = arch_initialise_vcpu(v, arg); + if ( rc == -ERESTART ) +- rc = hypercall_create_continuation(__HYPERVISOR_vcpu_op, "iuh", ++ rc = hypercall_create_continuation(__HYPERVISOR_vcpu_op, "iih", + cmd, vcpuid, arg); + + break; diff --git a/xsa298.patch b/xsa298.patch new file mode 100644 index 0000000..aa39042 --- /dev/null +++ b/xsa298.patch @@ -0,0 +1,89 @@ +From: Jan Beulich +Subject: x86/PV: check GDT/LDT limits during emulation + +Accesses beyond the LDT limit originating from emulation would trigger +the ASSERT() in pv_map_ldt_shadow_page(). On production builds such +accesses would cause an attempt to promote the touched page (offset from +the present LDT base address) to a segment descriptor one. If this +happens to succeed, guest user mode would be able to elevate its +privileges to that of the guest kernel. This is particularly easy when +there's no LDT at all, in which case the LDT base stored internally to +Xen is simply zero. + +Also adjust the ASSERT() that was triggering: It was off by one to +begin with, and for production builds we also better use +ASSERT_UNREACHABLE() instead with suitable recovery code afterwards. + +This is XSA-298. + +Reported-by: Andrew Cooper +Signed-off-by: Jan Beulich +Reviewed-by: Andrew Cooper +--- +v2: Correct 64-bit-only limit check (by folding into the common one). + +--- a/xen/arch/x86/pv/emul-gate-op.c ++++ b/xen/arch/x86/pv/emul-gate-op.c +@@ -51,7 +51,13 @@ static int read_gate_descriptor(unsigned + const seg_desc_t *pdesc = gdt_ldt_desc_ptr(gate_sel); + + if ( (gate_sel < 4) || +- ((gate_sel >= FIRST_RESERVED_GDT_BYTE) && !(gate_sel & 4)) || ++ /* ++ * We're interested in call gates only, which occupy a single ++ * seg_desc_t for 32-bit and a consecutive pair of them for 64-bit. ++ */ ++ ((gate_sel >> 3) + !is_pv_32bit_vcpu(v) >= ++ (gate_sel & 4 ? v->arch.pv.ldt_ents ++ : v->arch.pv.gdt_ents)) || + __get_user(desc, pdesc) ) + return 0; + +@@ -70,7 +76,7 @@ static int read_gate_descriptor(unsigned + if ( !is_pv_32bit_vcpu(v) ) + { + if ( (*ar & 0x1f00) != 0x0c00 || +- (gate_sel >= FIRST_RESERVED_GDT_BYTE - 8 && !(gate_sel & 4)) || ++ /* Limit check done above already. */ + __get_user(desc, pdesc + 1) || + (desc.b & 0x1f00) ) + return 0; +--- a/xen/arch/x86/pv/emulate.c ++++ b/xen/arch/x86/pv/emulate.c +@@ -31,7 +31,14 @@ int pv_emul_read_descriptor(unsigned int + { + seg_desc_t desc; + +- if ( sel < 4) ++ if ( sel < 4 || ++ /* ++ * Don't apply the GDT limit here, as the selector may be a Xen ++ * provided one. __get_user() will fail (without taking further ++ * action) for ones falling in the gap between guest populated ++ * and Xen ones. ++ */ ++ ((sel & 4) && (sel >> 3) >= v->arch.pv.ldt_ents) ) + desc.b = desc.a = 0; + else if ( __get_user(desc, gdt_ldt_desc_ptr(sel)) ) + return 0; +--- a/xen/arch/x86/pv/mm.c ++++ b/xen/arch/x86/pv/mm.c +@@ -92,12 +92,16 @@ bool pv_map_ldt_shadow_page(unsigned int + BUG_ON(unlikely(in_irq())); + + /* +- * Hardware limit checking should guarantee this property. NB. This is ++ * Prior limit checking should guarantee this property. NB. This is + * safe as updates to the LDT can only be made by MMUEXT_SET_LDT to the + * current vcpu, and vcpu_reset() will block until this vcpu has been + * descheduled before continuing. + */ +- ASSERT((offset >> 3) <= curr->arch.pv.ldt_ents); ++ if ( unlikely((offset >> 3) >= curr->arch.pv.ldt_ents) ) ++ { ++ ASSERT_UNREACHABLE(); ++ return false; ++ } + + if ( is_pv_32bit_domain(currd) ) + linear = (uint32_t)linear; diff --git a/xsa299-4.12-0001-x86-mm-L1TF-checks-don-t-leave-a-partial-entry.patch b/xsa299-4.12-0001-x86-mm-L1TF-checks-don-t-leave-a-partial-entry.patch new file mode 100644 index 0000000..fbb9d80 --- /dev/null +++ b/xsa299-4.12-0001-x86-mm-L1TF-checks-don-t-leave-a-partial-entry.patch @@ -0,0 +1,94 @@ +From 33d051917d5ef38f678b507a3c832afde48b9b49 Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Thu, 10 Oct 2019 17:57:49 +0100 +Subject: [PATCH 01/11] x86/mm: L1TF checks don't leave a partial entry + +On detection of a potential L1TF issue, most validation code returns +-ERESTART to allow the switch to shadow mode to happen and cause the +original operation to be restarted. + +However, in the validation code, the return value -ERESTART has been +repurposed to indicate 1) the function has partially completed +something which needs to be undone, and 2) calling put_page_type() +should cleanly undo it. This causes problems in several places. + +For L1 tables, on receiving an -ERESTART return from alloc_l1_table(), +alloc_page_type() will set PGT_partial on the page. If for some +reason the original operation never restarts, then on domain +destruction, relinquish_memory() will call free_page_type() on the +page. + +Unfortunately, alloc_ and free_l1_table() aren't set up to deal with +PGT_partial. When returning a failure, alloc_l1_table() always +de-validates whatever it's validated so far, and free_l1_table() +always devalidates the whole page. This means that if +relinquish_memory() calls free_page_type() on an L1 that didn't +complete due to an L1TF, it will call put_page_from_l1e() on "page +entries" that have never been validated. + +For L2+ tables, setting rc to ERESTART causes the rest of the +alloc_lN_table() function to *think* that the entry in question will +have PGT_partial set. This will cause it to set partial_pte = 1. If +relinqush_memory() then calls free_page_type() on one of those pages, +then free_lN_table() will call put_page_from_lNe() on the entry when +it shouldn't. + +Rather than indicating -ERESTART, indicate -EINTR. This is the code +to indicate that nothing has changed from when you started the call +(which is effectively how alloc_l1_table() handles errors). + +mod_lN_entry() shouldn't have any of these types of problems, so leave +potential changes there for a clean-up patch later. + +This is part of XSA-299. + +Reported-by: George Dunlap +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +--- + xen/arch/x86/mm.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 3557cd1178..a1b55c10ff 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -1409,7 +1409,7 @@ static int alloc_l1_table(struct page_info *page) + { + if ( !(l1e_get_flags(pl1e[i]) & _PAGE_PRESENT) ) + { +- ret = pv_l1tf_check_l1e(d, pl1e[i]) ? -ERESTART : 0; ++ ret = pv_l1tf_check_l1e(d, pl1e[i]) ? -EINTR : 0; + if ( ret ) + goto out; + } +@@ -1517,7 +1517,7 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) + { + if ( !pv_l1tf_check_l2e(d, l2e) ) + continue; +- rc = -ERESTART; ++ rc = -EINTR; + } + else + rc = get_page_from_l2e(l2e, pfn, d, partial); +@@ -1603,7 +1603,7 @@ static int alloc_l3_table(struct page_info *page) + { + if ( !pv_l1tf_check_l3e(d, l3e) ) + continue; +- rc = -ERESTART; ++ rc = -EINTR; + } + else + rc = get_page_from_l3e(l3e, pfn, d, partial); +@@ -1783,7 +1783,7 @@ static int alloc_l4_table(struct page_info *page) + { + if ( !pv_l1tf_check_l4e(d, l4e) ) + continue; +- rc = -ERESTART; ++ rc = -EINTR; + } + else + rc = get_page_from_l4e(l4e, pfn, d, partial); +-- +2.23.0 + diff --git a/xsa301-master-1.patch b/xsa301-master-1.patch new file mode 100644 index 0000000..54cce2c --- /dev/null +++ b/xsa301-master-1.patch @@ -0,0 +1,80 @@ +From 19d6330f142cb941b6340a88592e8a294de0ff8c Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Tue, 15 Oct 2019 17:10:40 +0100 +Subject: [PATCH 1/3] xen/arm: p2m: Avoid aliasing guest physical frame + +The P2M helpers implementation is quite lax and will end up to ignore +the unused top bits of a guest physical frame. + +This effectively means that p2m_set_entry() will create a mapping for a +different frame (it is always equal to gfn & (mask unused bits)). Yet +p2m->max_mapped_gfn will be updated using the original frame. + +At the moment, p2m_get_entry() and p2m_resolve_translation_fault() +assume that p2m_get_root_pointer() will always return a non-NULL pointer +when the GFN is smaller than p2m->max_mapped_gfn. + +Unfortunately, because of the aliasing described above, it would be +possible to set p2m->max_mapped_gfn high enough so it covers frame that +would lead p2m_get_root_pointer() to return NULL. + +As we don't sanity check the guest physical frame provided by a guest, a +malicious guest could craft a series of hypercalls that will hit the +BUG_ON() and therefore DoS Xen. + +To prevent aliasing, the function p2m_get_root_pointer() is now reworked +to return NULL If any of the unused top bits are not zero. The caller +can then decide what's the appropriate action to do. Since the two paths +(i.e. P2M_ROOT_PAGES == 1 and P2M_ROOT_PAGES != 1) are now very +similarly, take the opportunity to consolidate them making the code a +bit simpler. + +With this change, p2m_get_entry() will not try to insert a mapping as +the root pointer is invalid. + +Note that root_table is now switch to unsigned long as unsigned int is +not enough to hold part of a GFN. + +This is part of XSA-301. + +Reported-by: Julien Grall +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +--- + xen/arch/arm/p2m.c | 17 +++++------------ + 1 file changed, 5 insertions(+), 12 deletions(-) + +diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c +index a2749d9b6f..d0045a8b28 100644 +--- a/xen/arch/arm/p2m.c ++++ b/xen/arch/arm/p2m.c +@@ -229,21 +229,14 @@ void p2m_tlb_flush_sync(struct p2m_domain *p2m) + static lpae_t *p2m_get_root_pointer(struct p2m_domain *p2m, + gfn_t gfn) + { +- unsigned int root_table; +- +- if ( P2M_ROOT_PAGES == 1 ) +- return __map_domain_page(p2m->root); ++ unsigned long root_table; + + /* +- * Concatenated root-level tables. The table number will be the +- * offset at the previous level. It is not possible to +- * concatenate a level-0 root. ++ * While the root table index is the offset from the previous level, ++ * we can't use (P2M_ROOT_LEVEL - 1) because the root level might be ++ * 0. Yet we still want to check if all the unused bits are zeroed. + */ +- ASSERT(P2M_ROOT_LEVEL > 0); +- +- root_table = gfn_x(gfn) >> (level_orders[P2M_ROOT_LEVEL - 1]); +- root_table &= LPAE_ENTRY_MASK; +- ++ root_table = gfn_x(gfn) >> (level_orders[P2M_ROOT_LEVEL] + LPAE_SHIFT); + if ( root_table >= P2M_ROOT_PAGES ) + return NULL; + +-- +2.23.0 + diff --git a/xsa301-master-2.patch b/xsa301-master-2.patch new file mode 100644 index 0000000..baedc9c --- /dev/null +++ b/xsa301-master-2.patch @@ -0,0 +1,92 @@ +From 3b896936f7505e929dd869d14afcb185d0ee75f8 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Tue, 15 Oct 2019 17:10:41 +0100 +Subject: [PATCH 2/3] xen/arm: p2m: Avoid off-by-one check on + p2m->max_mapped_gfn + +The code base is using inconsistently the field p2m->max_mapped_gfn. +Some of the useres expect that p2m->max_guest_gfn contain the highest +mapped GFN while others expect highest + 1. + +p2m->max_guest_gfn is set as highest + 1, because of that the sanity +check on the GFN in p2m_resolved_translation_fault() and +p2m_get_entry() can be bypassed when GFN == p2m->max_guest_gfn. + +p2m_get_root_pointer(p2m->max_guest_gfn) may return NULL if it is +outside of address range supported and therefore the BUG_ON() could be +hit. + +The current value hold in p2m->max_mapped_gfn is inconsistent with the +expectation of the common code (see domain_get_maximum_gpfn()) and also +the documentation of the field. + +Rather than changing the check in p2m_translation_fault() and +p2m_get_entry(), p2m->max_mapped_gfn is now containing the highest +mapped GFN and the callers assuming "highest + 1" are now adjusted. + +Take the opportunity to use 1UL rather than 1 as page_order could +theoritically big enough to overflow a 32-bit integer. + +Lastly, the documentation of the field max_guest_gfn to reflect how it +is computed. + +This is part of XSA-301. + +Reported-by: Julien Grall +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +--- + xen/arch/arm/p2m.c | 6 +++--- + xen/include/asm-arm/p2m.h | 5 +---- + 2 files changed, 4 insertions(+), 7 deletions(-) + +diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c +index d0045a8b28..8d20d27961 100644 +--- a/xen/arch/arm/p2m.c ++++ b/xen/arch/arm/p2m.c +@@ -1041,7 +1041,7 @@ static int __p2m_set_entry(struct p2m_domain *p2m, + p2m_write_pte(entry, pte, p2m->clean_pte); + + p2m->max_mapped_gfn = gfn_max(p2m->max_mapped_gfn, +- gfn_add(sgfn, 1 << page_order)); ++ gfn_add(sgfn, (1UL << page_order) - 1)); + p2m->lowest_mapped_gfn = gfn_min(p2m->lowest_mapped_gfn, sgfn); + } + +@@ -1572,7 +1572,7 @@ int relinquish_p2m_mapping(struct domain *d) + p2m_write_lock(p2m); + + start = p2m->lowest_mapped_gfn; +- end = p2m->max_mapped_gfn; ++ end = gfn_add(p2m->max_mapped_gfn, 1); + + for ( ; gfn_x(start) < gfn_x(end); + start = gfn_next_boundary(start, order) ) +@@ -1641,7 +1641,7 @@ int p2m_cache_flush_range(struct domain *d, gfn_t *pstart, gfn_t end) + p2m_read_lock(p2m); + + start = gfn_max(start, p2m->lowest_mapped_gfn); +- end = gfn_min(end, p2m->max_mapped_gfn); ++ end = gfn_min(end, gfn_add(p2m->max_mapped_gfn, 1)); + + next_block_gfn = start; + +diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h +index 89f82df380..5fdb6e8183 100644 +--- a/xen/include/asm-arm/p2m.h ++++ b/xen/include/asm-arm/p2m.h +@@ -36,10 +36,7 @@ struct p2m_domain { + /* Current Translation Table Base Register for the p2m */ + uint64_t vttbr; + +- /* +- * Highest guest frame that's ever been mapped in the p2m +- * Only takes into account ram and foreign mapping +- */ ++ /* Highest guest frame that's ever been mapped in the p2m */ + gfn_t max_mapped_gfn; + + /* +-- +2.23.0 + diff --git a/xsa301-master-3.patch b/xsa301-master-3.patch new file mode 100644 index 0000000..9f137b8 --- /dev/null +++ b/xsa301-master-3.patch @@ -0,0 +1,67 @@ +From 060c2dd3b7c2674a019d94afb2b4ebf3663f6c6e Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Tue, 15 Oct 2019 17:10:42 +0100 +Subject: [PATCH 3/3] xen/arm: p2m: Don't check the return of + p2m_get_root_pointer() with BUG_ON() + +It turns out that the BUG_ON() was actually reachable with well-crafted +hypercalls. The BUG_ON() is here to prevent catch logical error, so +crashing Xen is a bit over the top. + +While all the holes should now be fixed, it would be better to downgrade +the BUG_ON() to something less fatal to prevent any more DoS. + +The BUG_ON() in p2m_get_entry() is now replaced by ASSERT_UNREACHABLE() +to catch mistake in debug build and return INVALID_MFN for production +build. The interface also requires to set page_order to give an idea of +the size of "hole". So 'level' is now set so we report a hole of size of +the an entry of the root page-table. This stays inline with what happen +when the GFN is higher than p2m->max_mapped_gfn. + +The BUG_ON() in p2m_resolve_translation_fault() is now replaced by +ASSERT_UNREACHABLE() to catch mistake in debug build and just report a +fault for producion build. + +This is part of XSA-301. + +Reported-by: Julien Grall +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +--- + xen/arch/arm/p2m.c | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c +index 8d20d27961..ce59f2b503 100644 +--- a/xen/arch/arm/p2m.c ++++ b/xen/arch/arm/p2m.c +@@ -395,7 +395,12 @@ mfn_t p2m_get_entry(struct p2m_domain *p2m, gfn_t gfn, + * the table should always be non-NULL because the gfn is below + * p2m->max_mapped_gfn and the root table pages are always present. + */ +- BUG_ON(table == NULL); ++ if ( !table ) ++ { ++ ASSERT_UNREACHABLE(); ++ level = P2M_ROOT_LEVEL; ++ goto out; ++ } + + for ( level = P2M_ROOT_LEVEL; level < 3; level++ ) + { +@@ -1196,7 +1201,11 @@ bool p2m_resolve_translation_fault(struct domain *d, gfn_t gfn) + * The table should always be non-NULL because the gfn is below + * p2m->max_mapped_gfn and the root table pages are always present. + */ +- BUG_ON(table == NULL); ++ if ( !table ) ++ { ++ ASSERT_UNREACHABLE(); ++ goto out; ++ } + + /* + * Go down the page-tables until an entry has the valid bit unset or +-- +2.23.0 + diff --git a/xsa302-4.12-0001-IOMMU-add-missing-HVM-check.patch b/xsa302-4.12-0001-IOMMU-add-missing-HVM-check.patch new file mode 100644 index 0000000..5d52163 --- /dev/null +++ b/xsa302-4.12-0001-IOMMU-add-missing-HVM-check.patch @@ -0,0 +1,37 @@ +From 0c9c0fbb356e3210cb77b3d738be50981b26058a Mon Sep 17 00:00:00 2001 +From: Jan Beulich +Date: Wed, 2 Oct 2019 13:36:59 +0200 +Subject: [PATCH 1/2] IOMMU: add missing HVM check +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Fix an unguarded d->arch.hvm access in assign_device(). + +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné +Acked-by: Andrew Cooper + +(cherry picked from commit 41fd1009cd7416b73d745a77c24b4e8d1a296fe6) +Signed-off-by: Ian Jackson +--- + xen/drivers/passthrough/pci.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c +index 8108ed5f9a..d7420bd8bf 100644 +--- a/xen/drivers/passthrough/pci.c ++++ b/xen/drivers/passthrough/pci.c +@@ -1452,7 +1452,8 @@ static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) + + /* Prevent device assign if mem paging or mem sharing have been + * enabled for this domain */ +- if ( unlikely(d->arch.hvm.mem_sharing_enabled || ++ if ( unlikely((is_hvm_domain(d) && ++ d->arch.hvm.mem_sharing_enabled) || + vm_event_check_ring(d->vm_event_paging) || + p2m_get_hostp2m(d)->global_logdirty) ) + return -EXDEV; +-- +2.11.0 + diff --git a/xsa302-4.12-0002-passthrough-quarantine-PCI-devices.patch b/xsa302-4.12-0002-passthrough-quarantine-PCI-devices.patch new file mode 100644 index 0000000..181ece3 --- /dev/null +++ b/xsa302-4.12-0002-passthrough-quarantine-PCI-devices.patch @@ -0,0 +1,499 @@ +From 278d8e585a9f110a1af0bd92a9fc43733c9c7227 Mon Sep 17 00:00:00 2001 +From: Paul Durrant +Date: Mon, 14 Oct 2019 17:52:59 +0100 +Subject: [PATCH 2/2] passthrough: quarantine PCI devices + +When a PCI device is assigned to an untrusted domain, it is possible for +that domain to program the device to DMA to an arbitrary address. The +IOMMU is used to protect the host from malicious DMA by making sure that +the device addresses can only target memory assigned to the guest. However, +when the guest domain is torn down the device is assigned back to dom0, +thus allowing any in-flight DMA to potentially target critical host data. + +This patch introduces a 'quarantine' for PCI devices using dom_io. When +the toolstack makes a device assignable (by binding it to pciback), it +will now also assign it to DOMID_IO and the device will only be assigned +back to dom0 when the device is made unassignable again. Whilst device is +assignable it will only ever transfer between dom_io and guest domains. +dom_io is actually only used as a sentinel domain for quarantining purposes; +it is not configured with any IOMMU mappings. Assignment to dom_io simply +means that the device's initiator (requestor) identifier is not present in +the IOMMU's device table and thus any DMA transactions issued will be +terminated with a fault condition. + +In addition, a fix to assignment handling is made for VT-d. Failure +during the assignment step should not lead to a device still being +associated with its prior owner. Hand the device to DomIO temporarily, +until the assignment step has completed successfully. Remove the PI +hooks from the source domain then earlier as well. + +Failure of the recovery reassign_device_ownership() may not go silent: +There e.g. may still be left over RMRR mappings in the domain assignment +to which has failed, and hence we can't allow that domain to continue +executing. + +NOTE: This patch also includes one printk() cleanup; the + "XEN_DOMCTL_assign_device: " tag is dropped in iommu_do_pci_domctl(), + since similar printk()-s elsewhere also don't log such a tag. + +This is XSA-302. + +Signed-off-by: Paul Durrant +Signed-off-by: Jan Beulich +Signed-off-by: Ian Jackson +(cherry picked from commit ec99857f59f7f06236f11ca8b0b2303e5e745cc4) +--- + tools/libxl/libxl_pci.c | 25 +++++++++++- + xen/arch/x86/mm.c | 2 + + xen/common/domctl.c | 14 ++++++- + xen/drivers/passthrough/amd/pci_amd_iommu.c | 10 ++++- + xen/drivers/passthrough/iommu.c | 9 +++++ + xen/drivers/passthrough/pci.c | 59 ++++++++++++++++++++++------- + xen/drivers/passthrough/vtd/iommu.c | 40 ++++++++++++++++--- + xen/include/xen/pci.h | 3 ++ + 8 files changed, 138 insertions(+), 24 deletions(-) + +diff --git a/tools/libxl/libxl_pci.c b/tools/libxl/libxl_pci.c +index 88c324ea23..d6a23fb5f8 100644 +--- a/tools/libxl/libxl_pci.c ++++ b/tools/libxl/libxl_pci.c +@@ -754,6 +754,7 @@ static int libxl__device_pci_assignable_add(libxl__gc *gc, + libxl_device_pci *pcidev, + int rebind) + { ++ libxl_ctx *ctx = libxl__gc_owner(gc); + unsigned dom, bus, dev, func; + char *spath, *driver_path = NULL; + int rc; +@@ -779,7 +780,7 @@ static int libxl__device_pci_assignable_add(libxl__gc *gc, + } + if ( rc ) { + LOG(WARN, PCI_BDF" already assigned to pciback", dom, bus, dev, func); +- return 0; ++ goto quarantine; + } + + /* Check to see if there's already a driver that we need to unbind from */ +@@ -810,6 +811,19 @@ static int libxl__device_pci_assignable_add(libxl__gc *gc, + return ERROR_FAIL; + } + ++quarantine: ++ /* ++ * DOMID_IO is just a sentinel domain, without any actual mappings, ++ * so always pass XEN_DOMCTL_DEV_RDM_RELAXED to avoid assignment being ++ * unnecessarily denied. ++ */ ++ rc = xc_assign_device(ctx->xch, DOMID_IO, pcidev_encode_bdf(pcidev), ++ XEN_DOMCTL_DEV_RDM_RELAXED); ++ if ( rc < 0 ) { ++ LOG(ERROR, "failed to quarantine "PCI_BDF, dom, bus, dev, func); ++ return ERROR_FAIL; ++ } ++ + return 0; + } + +@@ -817,9 +831,18 @@ static int libxl__device_pci_assignable_remove(libxl__gc *gc, + libxl_device_pci *pcidev, + int rebind) + { ++ libxl_ctx *ctx = libxl__gc_owner(gc); + int rc; + char *driver_path; + ++ /* De-quarantine */ ++ rc = xc_deassign_device(ctx->xch, DOMID_IO, pcidev_encode_bdf(pcidev)); ++ if ( rc < 0 ) { ++ LOG(ERROR, "failed to de-quarantine "PCI_BDF, pcidev->domain, pcidev->bus, ++ pcidev->dev, pcidev->func); ++ return ERROR_FAIL; ++ } ++ + /* Unbind from pciback */ + if ( (rc=pciback_dev_is_assigned(gc, pcidev)) < 0 ) { + return ERROR_FAIL; +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 3557cd1178..11d753d8d2 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -295,9 +295,11 @@ void __init arch_init_memory(void) + * Initialise our DOMID_IO domain. + * This domain owns I/O pages that are within the range of the page_info + * array. Mappings occur at the priv of the caller. ++ * Quarantined PCI devices will be associated with this domain. + */ + dom_io = domain_create(DOMID_IO, NULL, false); + BUG_ON(IS_ERR(dom_io)); ++ INIT_LIST_HEAD(&dom_io->arch.pdev_list); + + /* + * Initialise our COW domain. +diff --git a/xen/common/domctl.c b/xen/common/domctl.c +index d08b6274e2..e3c4be2b48 100644 +--- a/xen/common/domctl.c ++++ b/xen/common/domctl.c +@@ -391,6 +391,16 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) + + switch ( op->cmd ) + { ++ case XEN_DOMCTL_assign_device: ++ case XEN_DOMCTL_deassign_device: ++ if ( op->domain == DOMID_IO ) ++ { ++ d = dom_io; ++ break; ++ } ++ else if ( op->domain == DOMID_INVALID ) ++ return -ESRCH; ++ /* fall through */ + case XEN_DOMCTL_test_assign_device: + if ( op->domain == DOMID_INVALID ) + { +@@ -412,7 +422,7 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) + + if ( !domctl_lock_acquire() ) + { +- if ( d ) ++ if ( d && d != dom_io ) + rcu_unlock_domain(d); + return hypercall_create_continuation( + __HYPERVISOR_domctl, "h", u_domctl); +@@ -1074,7 +1084,7 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) + domctl_lock_release(); + + domctl_out_unlock_domonly: +- if ( d ) ++ if ( d && d != dom_io ) + rcu_unlock_domain(d); + + if ( copyback && __copy_to_guest(u_domctl, op, 1) ) +diff --git a/xen/drivers/passthrough/amd/pci_amd_iommu.c b/xen/drivers/passthrough/amd/pci_amd_iommu.c +index 33a3798f36..15c13e1163 100644 +--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c ++++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c +@@ -120,6 +120,10 @@ static void amd_iommu_setup_domain_device( + u8 bus = pdev->bus; + const struct domain_iommu *hd = dom_iommu(domain); + ++ /* dom_io is used as a sentinel for quarantined devices */ ++ if ( domain == dom_io ) ++ return; ++ + BUG_ON( !hd->arch.root_table || !hd->arch.paging_mode || + !iommu->dev_table.buffer ); + +@@ -277,6 +281,10 @@ void amd_iommu_disable_domain_device(struct domain *domain, + int req_id; + u8 bus = pdev->bus; + ++ /* dom_io is used as a sentinel for quarantined devices */ ++ if ( domain == dom_io ) ++ return; ++ + BUG_ON ( iommu->dev_table.buffer == NULL ); + req_id = get_dma_requestor_id(iommu->seg, PCI_BDF2(bus, devfn)); + dte = iommu->dev_table.buffer + (req_id * IOMMU_DEV_TABLE_ENTRY_SIZE); +@@ -363,7 +371,7 @@ static int amd_iommu_assign_device(struct domain *d, u8 devfn, + ivrs_mappings[req_id].read_permission); + } + +- return reassign_device(hardware_domain, d, devfn, pdev); ++ return reassign_device(pdev->domain, d, devfn, pdev); + } + + static void deallocate_next_page_table(struct page_info *pg, int level) +diff --git a/xen/drivers/passthrough/iommu.c b/xen/drivers/passthrough/iommu.c +index a6697d58fb..2762e1342f 100644 +--- a/xen/drivers/passthrough/iommu.c ++++ b/xen/drivers/passthrough/iommu.c +@@ -232,6 +232,9 @@ void iommu_teardown(struct domain *d) + { + struct domain_iommu *hd = dom_iommu(d); + ++ if ( d == dom_io ) ++ return; ++ + hd->status = IOMMU_STATUS_disabled; + hd->platform_ops->teardown(d); + tasklet_schedule(&iommu_pt_cleanup_tasklet); +@@ -241,6 +244,9 @@ int iommu_construct(struct domain *d) + { + struct domain_iommu *hd = dom_iommu(d); + ++ if ( d == dom_io ) ++ return 0; ++ + if ( hd->status == IOMMU_STATUS_initialized ) + return 0; + +@@ -521,6 +527,9 @@ int __init iommu_setup(void) + printk("I/O virtualisation %sabled\n", iommu_enabled ? "en" : "dis"); + if ( iommu_enabled ) + { ++ if ( iommu_domain_init(dom_io) ) ++ panic("Could not set up quarantine\n"); ++ + printk(" - Dom0 mode: %s\n", + iommu_hwdom_passthrough ? "Passthrough" : + iommu_hwdom_strict ? "Strict" : "Relaxed"); +diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c +index d7420bd8bf..d66a8a1daf 100644 +--- a/xen/drivers/passthrough/pci.c ++++ b/xen/drivers/passthrough/pci.c +@@ -1426,19 +1426,29 @@ static int iommu_remove_device(struct pci_dev *pdev) + return hd->platform_ops->remove_device(pdev->devfn, pci_to_dev(pdev)); + } + +-/* +- * If the device isn't owned by the hardware domain, it means it already +- * has been assigned to other domain, or it doesn't exist. +- */ + static int device_assigned(u16 seg, u8 bus, u8 devfn) + { + struct pci_dev *pdev; ++ int rc = 0; + + pcidevs_lock(); +- pdev = pci_get_pdev_by_domain(hardware_domain, seg, bus, devfn); ++ ++ pdev = pci_get_pdev(seg, bus, devfn); ++ ++ if ( !pdev ) ++ rc = -ENODEV; ++ /* ++ * If the device exists and it is not owned by either the hardware ++ * domain or dom_io then it must be assigned to a guest, or be ++ * hidden (owned by dom_xen). ++ */ ++ else if ( pdev->domain != hardware_domain && ++ pdev->domain != dom_io ) ++ rc = -EBUSY; ++ + pcidevs_unlock(); + +- return pdev ? 0 : -EBUSY; ++ return rc; + } + + static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) +@@ -1452,7 +1462,8 @@ static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) + + /* Prevent device assign if mem paging or mem sharing have been + * enabled for this domain */ +- if ( unlikely((is_hvm_domain(d) && ++ if ( d != dom_io && ++ unlikely((is_hvm_domain(d) && + d->arch.hvm.mem_sharing_enabled) || + vm_event_check_ring(d->vm_event_paging) || + p2m_get_hostp2m(d)->global_logdirty) ) +@@ -1468,12 +1479,20 @@ static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) + return rc; + } + +- pdev = pci_get_pdev_by_domain(hardware_domain, seg, bus, devfn); ++ pdev = pci_get_pdev(seg, bus, devfn); ++ ++ rc = -ENODEV; + if ( !pdev ) +- { +- rc = pci_get_pdev(seg, bus, devfn) ? -EBUSY : -ENODEV; + goto done; +- } ++ ++ rc = 0; ++ if ( d == pdev->domain ) ++ goto done; ++ ++ rc = -EBUSY; ++ if ( pdev->domain != hardware_domain && ++ pdev->domain != dom_io ) ++ goto done; + + if ( pdev->msix ) + msixtbl_init(d); +@@ -1496,6 +1515,10 @@ static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) + } + + done: ++ /* The device is assigned to dom_io so mark it as quarantined */ ++ if ( !rc && d == dom_io ) ++ pdev->quarantine = true; ++ + if ( !has_arch_pdevs(d) && has_iommu_pt(d) ) + iommu_teardown(d); + pcidevs_unlock(); +@@ -1508,6 +1531,7 @@ int deassign_device(struct domain *d, u16 seg, u8 bus, u8 devfn) + { + const struct domain_iommu *hd = dom_iommu(d); + struct pci_dev *pdev = NULL; ++ struct domain *target; + int ret = 0; + + if ( !iommu_enabled || !hd->platform_ops ) +@@ -1518,12 +1542,16 @@ int deassign_device(struct domain *d, u16 seg, u8 bus, u8 devfn) + if ( !pdev ) + return -ENODEV; + ++ /* De-assignment from dom_io should de-quarantine the device */ ++ target = (pdev->quarantine && pdev->domain != dom_io) ? ++ dom_io : hardware_domain; ++ + while ( pdev->phantom_stride ) + { + devfn += pdev->phantom_stride; + if ( PCI_SLOT(devfn) != PCI_SLOT(pdev->devfn) ) + break; +- ret = hd->platform_ops->reassign_device(d, hardware_domain, devfn, ++ ret = hd->platform_ops->reassign_device(d, target, devfn, + pci_to_dev(pdev)); + if ( !ret ) + continue; +@@ -1534,7 +1562,7 @@ int deassign_device(struct domain *d, u16 seg, u8 bus, u8 devfn) + } + + devfn = pdev->devfn; +- ret = hd->platform_ops->reassign_device(d, hardware_domain, devfn, ++ ret = hd->platform_ops->reassign_device(d, target, devfn, + pci_to_dev(pdev)); + if ( ret ) + { +@@ -1544,6 +1572,9 @@ int deassign_device(struct domain *d, u16 seg, u8 bus, u8 devfn) + return ret; + } + ++ if ( pdev->domain == hardware_domain ) ++ pdev->quarantine = false; ++ + pdev->fault.count = 0; + + if ( !has_arch_pdevs(d) && has_iommu_pt(d) ) +@@ -1722,7 +1753,7 @@ int iommu_do_pci_domctl( + ret = hypercall_create_continuation(__HYPERVISOR_domctl, + "h", u_domctl); + else if ( ret ) +- printk(XENLOG_G_ERR "XEN_DOMCTL_assign_device: " ++ printk(XENLOG_G_ERR + "assign %04x:%02x:%02x.%u to dom%d failed (%d)\n", + seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), + d->domain_id, ret); +diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c +index 1db1cd9f2d..a8d1baa064 100644 +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -1338,6 +1338,10 @@ int domain_context_mapping_one( + int agaw, rc, ret; + bool_t flush_dev_iotlb; + ++ /* dom_io is used as a sentinel for quarantined devices */ ++ if ( domain == dom_io ) ++ return 0; ++ + ASSERT(pcidevs_locked()); + spin_lock(&iommu->lock); + maddr = bus_to_context_maddr(iommu, bus); +@@ -1573,6 +1577,10 @@ int domain_context_unmap_one( + int iommu_domid, rc, ret; + bool_t flush_dev_iotlb; + ++ /* dom_io is used as a sentinel for quarantined devices */ ++ if ( domain == dom_io ) ++ return 0; ++ + ASSERT(pcidevs_locked()); + spin_lock(&iommu->lock); + +@@ -1705,6 +1713,10 @@ static int domain_context_unmap(struct domain *domain, u8 devfn, + goto out; + } + ++ /* dom_io is used as a sentinel for quarantined devices */ ++ if ( domain == dom_io ) ++ 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 +@@ -2441,6 +2453,15 @@ static int reassign_device_ownership( + if ( ret ) + return ret; + ++ if ( devfn == pdev->devfn ) ++ { ++ list_move(&pdev->domain_list, &dom_io->arch.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); + +@@ -2459,15 +2480,13 @@ static int reassign_device_ownership( + pdev->domain = target; + } + +- if ( !has_arch_pdevs(source) ) +- vmx_pi_hooks_deassign(source); +- + return ret; + } + + static int intel_iommu_assign_device( + struct domain *d, u8 devfn, struct pci_dev *pdev, u32 flag) + { ++ struct domain *s = pdev->domain; + struct acpi_rmrr_unit *rmrr; + int ret = 0, i; + u16 bdf, seg; +@@ -2510,8 +2529,8 @@ static int intel_iommu_assign_device( + } + } + +- ret = reassign_device_ownership(hardware_domain, d, devfn, pdev); +- if ( ret ) ++ ret = reassign_device_ownership(s, d, devfn, pdev); ++ if ( ret || d == dom_io ) + return ret; + + /* Setup rmrr identity mapping */ +@@ -2524,11 +2543,20 @@ static int intel_iommu_assign_device( + ret = rmrr_identity_mapping(d, 1, rmrr, flag); + if ( ret ) + { +- reassign_device_ownership(d, hardware_domain, devfn, pdev); ++ 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 %04x:%02x:%02x.%u from %pd (%d)\n", ++ seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), d, rc); ++ domain_crash(d); ++ } + break; + } + } +diff --git a/xen/include/xen/pci.h b/xen/include/xen/pci.h +index 8b21e8dc84..a031fd6020 100644 +--- a/xen/include/xen/pci.h ++++ b/xen/include/xen/pci.h +@@ -88,6 +88,9 @@ struct pci_dev { + + nodeid_t node; /* NUMA node */ + ++ /* Device to be quarantined, don't automatically re-assign to dom0 */ ++ bool quarantine; ++ + /* Device with errata, ignore the BARs. */ + bool ignore_bars; + +-- +2.11.0 + diff --git a/xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch b/xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch new file mode 100644 index 0000000..afb1096 --- /dev/null +++ b/xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch @@ -0,0 +1,74 @@ +From c8cb33fa64c9ccbfa2a494a9dad2e0a763c09176 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Tue, 1 Oct 2019 13:07:53 +0100 +Subject: [PATCH 1/4] xen/arm32: entry: Split __DEFINE_ENTRY_TRAP in two + +The preprocessing macro __DEFINE_ENTRY_TRAP is used to generate trap +entry function. While the macro is fairly small today, follow-up patches +will increase the size signicantly. + +In general, assembly macros are more readable as they allow you to name +parameters and avoid '\'. So the actual implementation of the trap is +now switched to an assembly macro. + +This is part of XSA-303. + +Reported-by: Julien Grall +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +Reviewed-by: Andre Przywara +--- + xen/arch/arm/arm32/entry.S | 34 +++++++++++++++++++--------------- + 1 file changed, 19 insertions(+), 15 deletions(-) + +diff --git a/xen/arch/arm/arm32/entry.S b/xen/arch/arm/arm32/entry.S +index 0b4cd19abd..4a762e04f1 100644 +--- a/xen/arch/arm/arm32/entry.S ++++ b/xen/arch/arm/arm32/entry.S +@@ -126,24 +126,28 @@ abort_guest_exit_end: + skip_check: + mov pc, lr + +-/* +- * Macro to define trap entry. The iflags corresponds to the list of +- * interrupts (Asynchronous Abort, IRQ, FIQ) to unmask. +- */ ++ /* ++ * Macro to define trap entry. The iflags corresponds to the list of ++ * interrupts (Asynchronous Abort, IRQ, FIQ) to unmask. ++ */ ++ .macro vector trap, iflags ++ SAVE_ALL ++ cpsie \iflags ++ adr lr, return_from_trap ++ mov r0, sp ++ /* ++ * Save the stack pointer in r11. It will be restored after the ++ * trap has been handled (see return_from_trap). ++ */ ++ mov r11, sp ++ bic sp, #7 /* Align the stack pointer (noop on guest trap) */ ++ b do_trap_\trap ++ .endm ++ + #define __DEFINE_TRAP_ENTRY(trap, iflags) \ + ALIGN; \ + trap_##trap: \ +- SAVE_ALL; \ +- cpsie iflags; \ +- adr lr, return_from_trap; \ +- mov r0, sp; \ +- /* \ +- * Save the stack pointer in r11. It will be restored after the \ +- * trap has been handled (see return_from_trap). \ +- */ \ +- mov r11, sp; \ +- bic sp, #7; /* Align the stack pointer (noop on guest trap) */ \ +- b do_trap_##trap ++ vector trap, iflags + + /* Trap handler which unmask IRQ/Abort, keep FIQ masked */ + #define DEFINE_TRAP_ENTRY(trap) __DEFINE_TRAP_ENTRY(trap, ai) +-- +2.11.0 + diff --git a/xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch b/xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch new file mode 100644 index 0000000..35f9c04 --- /dev/null +++ b/xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch @@ -0,0 +1,97 @@ +From be7379207c83fa74f8a6c22a8ea213f02714776f Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Tue, 1 Oct 2019 13:15:48 +0100 +Subject: [PATCH 2/4] xen/arm32: entry: Fold the macro SAVE_ALL in the macro + vector + +Follow-up rework will require the macro vector to distinguish between +a trap from a guest vs while in the hypervisor. + +The macro SAVE_ALL already has code to distinguish between the two and +it is only called by the vector macro. So fold the former into the +latter. This will help to avoid duplicating the check. + +This is part of XSA-303. + +Reported-by: Julien Grall +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +Reviewed-by: Andre Przywara +--- + xen/arch/arm/arm32/entry.S | 46 +++++++++++++++++++++++----------------------- + 1 file changed, 23 insertions(+), 23 deletions(-) + +diff --git a/xen/arch/arm/arm32/entry.S b/xen/arch/arm/arm32/entry.S +index 4a762e04f1..150cbc0b4b 100644 +--- a/xen/arch/arm/arm32/entry.S ++++ b/xen/arch/arm/arm32/entry.S +@@ -13,27 +13,6 @@ + #define RESTORE_BANKED(mode) \ + RESTORE_ONE_BANKED(SP_##mode) ; RESTORE_ONE_BANKED(LR_##mode) ; RESTORE_ONE_BANKED(SPSR_##mode) + +-#define SAVE_ALL \ +- sub sp, #(UREGS_SP_usr - UREGS_sp); /* SP, LR, SPSR, PC */ \ +- push {r0-r12}; /* Save R0-R12 */ \ +- \ +- mrs r11, ELR_hyp; /* ELR_hyp is return address. */\ +- str r11, [sp, #UREGS_pc]; \ +- \ +- str lr, [sp, #UREGS_lr]; \ +- \ +- add r11, sp, #UREGS_kernel_sizeof+4; \ +- str r11, [sp, #UREGS_sp]; \ +- \ +- mrc CP32(r11, HSR); /* Save exception syndrome */ \ +- str r11, [sp, #UREGS_hsr]; \ +- \ +- mrs r11, SPSR_hyp; \ +- str r11, [sp, #UREGS_cpsr]; \ +- and r11, #PSR_MODE_MASK; \ +- cmp r11, #PSR_MODE_HYP; \ +- blne save_guest_regs +- + save_guest_regs: + #ifdef CONFIG_ARM32_HARDEN_BRANCH_PREDICTOR + /* +@@ -52,7 +31,7 @@ save_guest_regs: + ldr r11, =0xffffffff /* Clobber SP which is only valid for hypervisor frames. */ + str r11, [sp, #UREGS_sp] + SAVE_ONE_BANKED(SP_usr) +- /* LR_usr is the same physical register as lr and is saved in SAVE_ALL */ ++ /* LR_usr is the same physical register as lr and is saved by the caller */ + SAVE_BANKED(svc) + SAVE_BANKED(abt) + SAVE_BANKED(und) +@@ -131,7 +110,28 @@ skip_check: + * interrupts (Asynchronous Abort, IRQ, FIQ) to unmask. + */ + .macro vector trap, iflags +- SAVE_ALL ++ /* Save registers in the stack */ ++ sub sp, #(UREGS_SP_usr - UREGS_sp) /* SP, LR, SPSR, PC */ ++ push {r0-r12} /* Save R0-R12 */ ++ mrs r11, ELR_hyp /* ELR_hyp is return address */ ++ str r11, [sp, #UREGS_pc] ++ ++ str lr, [sp, #UREGS_lr] ++ ++ add r11, sp, #(UREGS_kernel_sizeof + 4) ++ ++ str r11, [sp, #UREGS_sp] ++ ++ mrc CP32(r11, HSR) /* Save exception syndrome */ ++ str r11, [sp, #UREGS_hsr] ++ ++ mrs r11, SPSR_hyp ++ str r11, [sp, #UREGS_cpsr] ++ and r11, #PSR_MODE_MASK ++ cmp r11, #PSR_MODE_HYP ++ blne save_guest_regs ++ ++ /* We are ready to handle the trap, setup the registers and jump. */ + cpsie \iflags + adr lr, return_from_trap + mov r0, sp +-- +2.11.0 + diff --git a/xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch b/xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch new file mode 100644 index 0000000..5168452 --- /dev/null +++ b/xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch @@ -0,0 +1,226 @@ +From 098fe877967870ffda2dfd9629a5fd272f6aacdc Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Fri, 11 Oct 2019 17:49:28 +0100 +Subject: [PATCH 3/4] xen/arm32: Don't blindly unmask interrupts on trap + without a change of level + +Exception vectors will unmask interrupts regardless the state of them in +the interrupted context. + +One of the consequences is IRQ will be unmasked when receiving an +undefined instruction exception (used by WARN*) from the hypervisor. +This could result to unexpected behavior such as deadlock (if a lock was +shared with interrupts). + +In a nutshell, interrupts should only be unmasked when it is safe to do. +Xen only unmask IRQ and Abort interrupts, so the logic can stay simple. + +As vectors exceptions may be shared between guest and hypervisor, we now +need to have a different policy for the interrupts. + +On exception from hypervisor, each vector will select the list of +interrupts to inherit from the interrupted context. Any interrupts not +listed will be kept masked. + +On exception from the guest, the Abort and IRQ will be unmasked +depending on the exact vector. + +The interrupts will be kept unmasked when the vector cannot used by +either guest or hypervisor. + +Note that each vector is not anymore preceded by ALIGN. This is fine +because the alignment is already bigger than what we need. + +This is part of XSA-303. + +Reported-by: Julien Grall +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +Reviewed-by: Andre Przywara +--- + xen/arch/arm/arm32/entry.S | 138 +++++++++++++++++++++++++++++++++++---------- + 1 file changed, 109 insertions(+), 29 deletions(-) + +diff --git a/xen/arch/arm/arm32/entry.S b/xen/arch/arm/arm32/entry.S +index 150cbc0b4b..ec90cca093 100644 +--- a/xen/arch/arm/arm32/entry.S ++++ b/xen/arch/arm/arm32/entry.S +@@ -4,6 +4,17 @@ + #include + #include + ++/* ++ * Short-hands to defined the interrupts (A, I, F) ++ * ++ * _ means the interrupt state will not change ++ * X means the state of interrupt X will change ++ * ++ * To be used with msr cpsr_* only ++ */ ++#define IFLAGS_AIF PSR_ABT_MASK | PSR_IRQ_MASK | PSR_FIQ_MASK ++#define IFLAGS_A_F PSR_ABT_MASK | PSR_FIQ_MASK ++ + #define SAVE_ONE_BANKED(reg) mrs r11, reg; str r11, [sp, #UREGS_##reg] + #define RESTORE_ONE_BANKED(reg) ldr r11, [sp, #UREGS_##reg]; msr reg, r11 + +@@ -106,10 +117,18 @@ skip_check: + mov pc, lr + + /* +- * Macro to define trap entry. The iflags corresponds to the list of +- * interrupts (Asynchronous Abort, IRQ, FIQ) to unmask. ++ * Macro to define a trap entry. ++ * ++ * @guest_iflags: Optional list of interrupts to unmask when ++ * entering from guest context. As this is used with cpsie, ++ * the letter (a, i, f) should be used. ++ * ++ * @hyp_iflags: Optional list of interrupts to inherit when ++ * entering from hypervisor context. Any interrupts not ++ * listed will be kept unchanged. As this is used with cpsr_*, ++ * IFLAGS_* short-hands should be used. + */ +- .macro vector trap, iflags ++ .macro vector trap, guest_iflags=n, hyp_iflags=0 + /* Save registers in the stack */ + sub sp, #(UREGS_SP_usr - UREGS_sp) /* SP, LR, SPSR, PC */ + push {r0-r12} /* Save R0-R12 */ +@@ -127,12 +146,39 @@ skip_check: + + mrs r11, SPSR_hyp + str r11, [sp, #UREGS_cpsr] +- and r11, #PSR_MODE_MASK +- cmp r11, #PSR_MODE_HYP +- blne save_guest_regs + ++ /* ++ * We need to distinguish whether we came from guest or ++ * hypervisor context. ++ */ ++ and r0, r11, #PSR_MODE_MASK ++ cmp r0, #PSR_MODE_HYP ++ ++ bne 1f ++ /* ++ * Trap from the hypervisor ++ * ++ * Inherit the state of the interrupts from the hypervisor ++ * context. For that we need to use SPSR (stored in r11) and ++ * modify CPSR accordingly. ++ * ++ * CPSR = (CPSR & ~hyp_iflags) | (SPSR & hyp_iflags) ++ */ ++ mrs r10, cpsr ++ bic r10, r10, #\hyp_iflags ++ and r11, r11, #\hyp_iflags ++ orr r10, r10, r11 ++ msr cpsr_cx, r10 ++ b 2f ++ ++1: ++ /* Trap from the guest */ ++ bl save_guest_regs ++ .if \guest_iflags != n ++ cpsie \guest_iflags ++ .endif ++2: + /* We are ready to handle the trap, setup the registers and jump. */ +- cpsie \iflags + adr lr, return_from_trap + mov r0, sp + /* +@@ -144,20 +190,6 @@ skip_check: + b do_trap_\trap + .endm + +-#define __DEFINE_TRAP_ENTRY(trap, iflags) \ +- ALIGN; \ +-trap_##trap: \ +- vector trap, iflags +- +-/* Trap handler which unmask IRQ/Abort, keep FIQ masked */ +-#define DEFINE_TRAP_ENTRY(trap) __DEFINE_TRAP_ENTRY(trap, ai) +- +-/* Trap handler which unmask Abort, keep IRQ/FIQ masked */ +-#define DEFINE_TRAP_ENTRY_NOIRQ(trap) __DEFINE_TRAP_ENTRY(trap, a) +- +-/* Trap handler which unmask IRQ, keep Abort/FIQ masked */ +-#define DEFINE_TRAP_ENTRY_NOABORT(trap) __DEFINE_TRAP_ENTRY(trap, i) +- + .align 5 + GLOBAL(hyp_traps_vector) + b trap_reset /* 0x00 - Reset */ +@@ -228,14 +260,62 @@ decode_vectors: + + #endif /* CONFIG_HARDEN_BRANCH_PREDICTOR */ + +-DEFINE_TRAP_ENTRY(reset) +-DEFINE_TRAP_ENTRY(undefined_instruction) +-DEFINE_TRAP_ENTRY(hypervisor_call) +-DEFINE_TRAP_ENTRY(prefetch_abort) +-DEFINE_TRAP_ENTRY(guest_sync) +-DEFINE_TRAP_ENTRY_NOIRQ(irq) +-DEFINE_TRAP_ENTRY_NOIRQ(fiq) +-DEFINE_TRAP_ENTRY_NOABORT(data_abort) ++/* Vector not used by the Hypervisor. */ ++trap_reset: ++ vector reset ++ ++/* ++ * Vector only used by the Hypervisor. ++ * ++ * While the exception can be executed with all the interrupts (e.g. ++ * IRQ) unmasked, the interrupted context may have purposefully masked ++ * some of them. So we want to inherit the state from the interrupted ++ * context. ++ */ ++trap_undefined_instruction: ++ vector undefined_instruction, hyp_iflags=IFLAGS_AIF ++ ++/* We should never reach this trap */ ++trap_hypervisor_call: ++ vector hypervisor_call ++ ++/* ++ * Vector only used by the hypervisor. ++ * ++ * While the exception can be executed with all the interrupts (e.g. ++ * IRQ) unmasked, the interrupted context may have purposefully masked ++ * some of them. So we want to inherit the state from the interrupted ++ * context. ++ */ ++trap_prefetch_abort: ++ vector prefetch_abort, hyp_iflags=IFLAGS_AIF ++ ++/* ++ * Vector only used by the hypervisor. ++ * ++ * Data Abort should be rare and most likely fatal. It is best to not ++ * unmask any interrupts to limit the amount of code that can run before ++ * the Data Abort is treated. ++ */ ++trap_data_abort: ++ vector data_abort ++ ++/* Vector only used by the guest. We can unmask Abort/IRQ. */ ++trap_guest_sync: ++ vector guest_sync, guest_iflags=ai ++ ++ ++/* Vector used by the hypervisor and the guest. */ ++trap_irq: ++ vector irq, guest_iflags=a, hyp_iflags=IFLAGS_A_F ++ ++/* ++ * Vector used by the hypervisor and the guest. ++ * ++ * FIQ are not meant to happen, so we don't unmask any interrupts. ++ */ ++trap_fiq: ++ vector fiq + + return_from_trap: + /* +-- +2.11.0 + diff --git a/xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch b/xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch new file mode 100644 index 0000000..106cbf9 --- /dev/null +++ b/xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch @@ -0,0 +1,114 @@ +From c6d290ce157a044dec417fdda8db71e41a37d744 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 7 Oct 2019 18:10:56 +0100 +Subject: [PATCH 4/4] xen/arm64: Don't blindly unmask interrupts on trap + without a change of level + +Some of the traps without a change of the level (i.e. hypervisor -> +hypervisor) will unmask interrupts regardless the state of them in the +interrupted context. + +One of the consequences is IRQ will be unmasked when receiving a +synchronous exception (used by WARN*()). This could result to unexpected +behavior such as deadlock (if a lock was shared with interrupts). + +In a nutshell, interrupts should only be unmasked when it is safe to +do. Xen only unmask IRQ and Abort interrupts, so the logic can stay +simple: + - hyp_error: All the interrupts are now kept masked. SError should + be pretty rare and if ever happen then we most likely want to + avoid any other interrupts to be generated. The potential main + "caller" is during virtual SError synchronization on the exit + path from the guest (see check_pending_vserror). + + - hyp_sync: The interrupts state is inherited from the interrupted + context. + + - hyp_irq: All the interrupts but IRQ state are inherited from the + interrupted context. IRQ is kept masked. + +This is part of XSA-303. + +Reported-by: Julien Grall +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +Reviewed-by: Andre Przywara +--- + xen/arch/arm/arm64/entry.S | 47 ++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 43 insertions(+), 4 deletions(-) + +diff --git a/xen/arch/arm/arm64/entry.S b/xen/arch/arm/arm64/entry.S +index 2d9a2713a1..3e41ba65b6 100644 +--- a/xen/arch/arm/arm64/entry.S ++++ b/xen/arch/arm/arm64/entry.S +@@ -188,24 +188,63 @@ hyp_error_invalid: + entry hyp=1 + invalid BAD_ERROR + ++/* ++ * SError received while running in the hypervisor mode. ++ * ++ * Technically, we could unmask the IRQ if it were unmasked in the ++ * interrupted context. However, this require to check the PSTATE. For ++ * simplicity, as SError should be rare and potentially fatal, ++ * all interrupts are kept masked. ++ */ + hyp_error: + entry hyp=1 +- msr daifclr, #2 + mov x0, sp + bl do_trap_hyp_serror + exit hyp=1 + +-/* Traps taken in Current EL with SP_ELx */ ++/* ++ * Synchronous exception received while running in the hypervisor mode. ++ * ++ * While the exception could be executed with all the interrupts (e.g. ++ * IRQ) unmasked, the interrupted context may have purposefully masked ++ * some of them. So we want to inherit the state from the interrupted ++ * context. ++ */ + hyp_sync: + entry hyp=1 +- msr daifclr, #6 ++ ++ /* Inherit interrupts */ ++ mrs x0, SPSR_el2 ++ and x0, x0, #(PSR_DBG_MASK | PSR_ABT_MASK | PSR_IRQ_MASK | PSR_FIQ_MASK) ++ msr daif, x0 ++ + mov x0, sp + bl do_trap_hyp_sync + exit hyp=1 + ++/* ++ * IRQ received while running in the hypervisor mode. ++ * ++ * While the exception could be executed with all the interrupts but IRQ ++ * unmasked, the interrupted context may have purposefully masked some ++ * of them. So we want to inherit the state from the interrupt context ++ * and keep IRQ masked. ++ * ++ * XXX: We may want to consider an ordering between interrupts (e.g. if ++ * SError are masked, then IRQ should be masked too). However, this ++ * would require some rework in some paths (e.g. panic, livepatch) to ++ * ensure the ordering is enforced everywhere. ++ */ + hyp_irq: + entry hyp=1 +- msr daifclr, #4 ++ ++ /* Inherit D, A, F interrupts and keep I masked */ ++ mrs x0, SPSR_el2 ++ mov x1, #(PSR_DBG_MASK | PSR_ABT_MASK | PSR_FIQ_MASK) ++ and x0, x0, x1 ++ orr x0, x0, #PSR_IRQ_MASK ++ msr daif, x0 ++ + mov x0, sp + bl do_trap_irq + exit hyp=1 +-- +2.11.0 + From 0b616dfcac2bd2e6c0e1475256921a8578143784 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Fri, 1 Nov 2019 18:33:39 +0000 Subject: [PATCH 019/194] add CVE references --- xen.spec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index cafbc00..e687f4a 100644 --- a/xen.spec +++ b/xen.spec @@ -906,9 +906,10 @@ fi - missing descriptor table limit checking in x86 PV emulation [XSA-298, CVE-2019-18425] - Issues with restartable PV type change operations [XSA-299, CVE-2019-18421] + (#1767726) - add-to-physmap can be abused to DoS Arm hosts [XSA-301, CVE-2019-18423] - passed through PCI devices may corrupt host memory after deassignment - [XSA-302, CVE-2019-18424] + [XSA-302, CVE-2019-18424] (#1767731) - ARM: Interrupts are unconditionally unmasked in exception handlers [XSA-303, CVE-2019-18422] From f522170871e162c6ff048e961bd241078f1f473d Mon Sep 17 00:00:00 2001 From: Michael Young Date: Fri, 1 Nov 2019 19:28:17 +0000 Subject: [PATCH 020/194] 6 security fixes VCPUOP_initialise DoS [XSA-296, CVE-2019-18420] missing descriptor table limit checking in x86 PV emulation [XSA-298, CVE-2019-18425] Issues with restartable PV type change operations [XSA-299, CVE-2019-18421] (#1767726) add-to-physmap can be abused to DoS Arm hosts [XSA-301, CVE-2019-18423] passed through PCI devices may corrupt host memory after deassignment [XSA-302, CVE-2019-18424] (#1767731) ARM: Interrupts are unconditionally unmasked in exception handlers [XSA-303, CVE-2019-18422] --- xen.spec | 40 +- xsa296.patch | 195 ++++++ xsa298-4.11.patch | 87 +++ ...F-checks-don-t-leave-a-partial-entry.patch | 94 +++ ...set-PGT_pinned-on-a-partially-de-val.patch | 99 +++ ...out-partial_pte-tristate-into-indivi.patch | 609 ++++++++++++++++++ ...-for-_put_page_type-rather-than-a-bo.patch | 140 ++++ ...t_page_and_type_from_mfn-conditional.patch | 79 +++ ...c_l-23-_table-clear-partial_flags-wh.patch | 101 +++ ...ways-retain-a-general-ref-on-partial.patch | 374 +++++++++++ ...PTF_partial_set-and-PTF_partial_gene.patch | 227 +++++++ ...handle-linear-pagetable-promotion-fa.patch | 106 +++ ...mm-Fix-nested-de-validation-on-error.patch | 169 +++++ ...p-a-type-ref-unless-you-held-a-ref-t.patch | 413 ++++++++++++ xsa301-4.11-1.patch | 80 +++ xsa301-4.11-2.patch | 92 +++ xsa301-4.11-3.patch | 49 ++ ....11-0001-IOMMU-add-missing-HVM-check.patch | 37 ++ ...2-passthrough-quarantine-PCI-devices.patch | 498 ++++++++++++++ ...try-Split-__DEFINE_ENTRY_TRAP-in-two.patch | 74 +++ ...Fold-the-macro-SAVE_ALL-in-the-macro.patch | 97 +++ ...blindly-unmask-interrupts-on-trap-wi.patch | 226 +++++++ ...blindly-unmask-interrupts-on-trap-wi.patch | 114 ++++ 23 files changed, 3999 insertions(+), 1 deletion(-) create mode 100644 xsa296.patch create mode 100644 xsa298-4.11.patch create mode 100644 xsa299-4.11-0001-x86-mm-L1TF-checks-don-t-leave-a-partial-entry.patch create mode 100644 xsa299-4.11-0002-x86-mm-Don-t-re-set-PGT_pinned-on-a-partially-de-val.patch create mode 100644 xsa299-4.11-0003-x86-mm-Separate-out-partial_pte-tristate-into-indivi.patch create mode 100644 xsa299-4.11-0004-x86-mm-Use-flags-for-_put_page_type-rather-than-a-bo.patch create mode 100644 xsa299-4.11-0005-x86-mm-Rework-get_page_and_type_from_mfn-conditional.patch create mode 100644 xsa299-4.11-0006-x86-mm-Have-alloc_l-23-_table-clear-partial_flags-wh.patch create mode 100644 xsa299-4.11-0007-x86-mm-Always-retain-a-general-ref-on-partial.patch create mode 100644 xsa299-4.11-0008-x86-mm-Collapse-PTF_partial_set-and-PTF_partial_gene.patch create mode 100644 xsa299-4.11-0009-x86-mm-Properly-handle-linear-pagetable-promotion-fa.patch create mode 100644 xsa299-4.11-0010-x86-mm-Fix-nested-de-validation-on-error.patch create mode 100644 xsa299-4.11-0011-x86-mm-Don-t-drop-a-type-ref-unless-you-held-a-ref-t.patch create mode 100644 xsa301-4.11-1.patch create mode 100644 xsa301-4.11-2.patch create mode 100644 xsa301-4.11-3.patch create mode 100644 xsa302-4.11-0001-IOMMU-add-missing-HVM-check.patch create mode 100644 xsa302-4.11-0002-passthrough-quarantine-PCI-devices.patch create mode 100644 xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch create mode 100644 xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch create mode 100644 xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch create mode 100644 xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch diff --git a/xen.spec b/xen.spec index dd90da2..3566ea4 100644 --- a/xen.spec +++ b/xen.spec @@ -67,7 +67,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.11.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 @@ -126,6 +126,18 @@ Patch42: xen.stubdom.build.patch Patch44: xen.vwprintw.fix.patch Patch45: xen.python.env.patch Patch46: xen.gcc9.fixes.patch +Patch47: xsa296.patch +Patch48: xsa298-4.11.patch +Patch49: xsa299-4.11-0001-x86-mm-L1TF-checks-don-t-leave-a-partial-entry.patch +Patch50: xsa301-4.11-1.patch +Patch51: xsa301-4.11-2.patch +Patch52: xsa301-4.11-3.patch +Patch53: xsa302-4.11-0001-IOMMU-add-missing-HVM-check.patch +Patch54: xsa302-4.11-0002-passthrough-quarantine-PCI-devices.patch +Patch55: xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch +Patch56: xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch +Patch57: xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch +Patch58: xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch %if %build_qemutrad @@ -332,6 +344,20 @@ manage Xen virtual machines. %patch44 -p1 %patch45 -p1 %patch46 -p1 +%patch47 -p1 +%patch48 -p1 +%patch49 -p1 +%patch50 -p1 +%patch51 -p1 +%patch52 -p1 +%ifarch %{ix86} x86_64 +%patch53 -p1 +%patch54 -p1 +%endif +%patch55 -p1 +%patch56 -p1 +%patch57 -p1 +%patch58 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -931,6 +957,18 @@ fi %endif %changelog +* Fri Nov 01 2019 Michael Young - 4.11.2-2 +- VCPUOP_initialise DoS [XSA-296, CVE-2019-18420] +- missing descriptor table limit checking in x86 PV emulation [XSA-298, + CVE-2019-18425] +- Issues with restartable PV type change operations [XSA-299, CVE-2019-18421] + (#1767726) +- add-to-physmap can be abused to DoS Arm hosts [XSA-301, CVE-2019-18423] +- passed through PCI devices may corrupt host memory after deassignment + [XSA-302, CVE-2019-18424] (#1767731) +- ARM: Interrupts are unconditionally unmasked in exception handlers + [XSA-303, CVE-2019-18422] + * Mon Jul 01 2019 Michael Young - 4.11.2-1 - update to 4.11.2 remove patches now fixed upstream diff --git a/xsa296.patch b/xsa296.patch new file mode 100644 index 0000000..e71ea7f --- /dev/null +++ b/xsa296.patch @@ -0,0 +1,195 @@ +From: Andrew Cooper +Subject: xen/hypercall: Don't use BUG() for parameter checking in hypercall_create_continuation() + +Since c/s 1d429034 "hypercall: update vcpu_op to take an unsigned vcpuid", +which incorrectly swapped 'i' for 'u' in the parameter type list, guests have +been able to hit the BUG() in next_args()'s default case. + +Correct these back to 'i'. + +In addition, make adjustments to prevent this class of issue from occurring in +the future - crashing Xen is not an appropriate form of parameter checking. + +Capitalise NEXT_ARG() to catch all uses, to highlight that it is a macro doing +non-function-like things behind the scenes, and undef it when appropriate. +Implement a bad_fmt: block which prints an error, asserts unreachable, and +crashes the guest. + +On the ARM side, drop all parameter checking of p. It is asymmetric with the +x86 side, and akin to expecting memcpy() or sprintf() to check their src/fmt +parameter before use. A caller passing "" or something other than a string +literal will be obvious during code review. + +This is XSA-296. + +Signed-off-by: Andrew Cooper +Acked-by: Julien Grall + +diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c +index 941bbff4fe..a3da8e9c08 100644 +--- a/xen/arch/arm/domain.c ++++ b/xen/arch/arm/domain.c +@@ -383,14 +383,15 @@ void sync_vcpu_execstate(struct vcpu *v) + /* Nothing to do -- no lazy switching */ + } + +-#define next_arg(fmt, args) ({ \ ++#define NEXT_ARG(fmt, args) \ ++({ \ + unsigned long __arg; \ + switch ( *(fmt)++ ) \ + { \ + case 'i': __arg = (unsigned long)va_arg(args, unsigned int); break; \ + case 'l': __arg = (unsigned long)va_arg(args, unsigned long); break; \ + case 'h': __arg = (unsigned long)va_arg(args, void *); break; \ +- default: __arg = 0; BUG(); \ ++ default: goto bad_fmt; \ + } \ + __arg; \ + }) +@@ -405,9 +406,6 @@ unsigned long hypercall_create_continuation( + unsigned int i; + va_list args; + +- /* All hypercalls take at least one argument */ +- BUG_ON( !p || *p == '\0' ); +- + current->hcall_preempted = true; + + va_start(args, format); +@@ -415,7 +413,7 @@ unsigned long hypercall_create_continuation( + if ( mcs->flags & MCSF_in_multicall ) + { + for ( i = 0; *p != '\0'; i++ ) +- mcs->call.args[i] = next_arg(p, args); ++ mcs->call.args[i] = NEXT_ARG(p, args); + + /* Return value gets written back to mcs->call.result */ + rc = mcs->call.result; +@@ -431,7 +429,7 @@ unsigned long hypercall_create_continuation( + + for ( i = 0; *p != '\0'; i++ ) + { +- arg = next_arg(p, args); ++ arg = NEXT_ARG(p, args); + + switch ( i ) + { +@@ -454,7 +452,7 @@ unsigned long hypercall_create_continuation( + + for ( i = 0; *p != '\0'; i++ ) + { +- arg = next_arg(p, args); ++ arg = NEXT_ARG(p, args); + + switch ( i ) + { +@@ -475,8 +473,16 @@ unsigned long hypercall_create_continuation( + va_end(args); + + return rc; ++ ++ bad_fmt: ++ gprintk(XENLOG_ERR, "Bad hypercall continuation format '%c'\n", *p); ++ ASSERT_UNREACHABLE(); ++ domain_crash(current->domain); ++ return 0; + } + ++#undef NEXT_ARG ++ + void startup_cpu_idle_loop(void) + { + struct vcpu *v = current; +diff --git a/xen/arch/x86/hypercall.c b/xen/arch/x86/hypercall.c +index d483dbaa6b..4643e5eb43 100644 +--- a/xen/arch/x86/hypercall.c ++++ b/xen/arch/x86/hypercall.c +@@ -80,14 +80,15 @@ const hypercall_args_t hypercall_args_table[NR_hypercalls] = + #undef COMP + #undef ARGS + +-#define next_arg(fmt, args) ({ \ ++#define NEXT_ARG(fmt, args) \ ++({ \ + unsigned long __arg; \ + switch ( *(fmt)++ ) \ + { \ + case 'i': __arg = (unsigned long)va_arg(args, unsigned int); break; \ + case 'l': __arg = (unsigned long)va_arg(args, unsigned long); break; \ + case 'h': __arg = (unsigned long)va_arg(args, void *); break; \ +- default: __arg = 0; BUG(); \ ++ default: goto bad_fmt; \ + } \ + __arg; \ + }) +@@ -109,7 +110,7 @@ unsigned long hypercall_create_continuation( + if ( mcs->flags & MCSF_in_multicall ) + { + for ( i = 0; *p != '\0'; i++ ) +- mcs->call.args[i] = next_arg(p, args); ++ mcs->call.args[i] = NEXT_ARG(p, args); + } + else + { +@@ -121,7 +122,7 @@ unsigned long hypercall_create_continuation( + { + for ( i = 0; *p != '\0'; i++ ) + { +- arg = next_arg(p, args); ++ arg = NEXT_ARG(p, args); + switch ( i ) + { + case 0: regs->rdi = arg; break; +@@ -137,7 +138,7 @@ unsigned long hypercall_create_continuation( + { + for ( i = 0; *p != '\0'; i++ ) + { +- arg = next_arg(p, args); ++ arg = NEXT_ARG(p, args); + switch ( i ) + { + case 0: regs->rbx = arg; break; +@@ -154,8 +155,16 @@ unsigned long hypercall_create_continuation( + va_end(args); + + return op; ++ ++ bad_fmt: ++ gprintk(XENLOG_ERR, "Bad hypercall continuation format '%c'\n", *p); ++ ASSERT_UNREACHABLE(); ++ domain_crash(curr->domain); ++ return 0; + } + ++#undef NEXT_ARG ++ + int hypercall_xlat_continuation(unsigned int *id, unsigned int nr, + unsigned int mask, ...) + { +diff --git a/xen/common/compat/domain.c b/xen/common/compat/domain.c +index 39877b3ab2..2531fa7421 100644 +--- a/xen/common/compat/domain.c ++++ b/xen/common/compat/domain.c +@@ -81,7 +81,7 @@ int compat_vcpu_op(int cmd, unsigned int vcpuid, XEN_GUEST_HANDLE_PARAM(void) ar + } + + if ( rc == -ERESTART ) +- rc = hypercall_create_continuation(__HYPERVISOR_vcpu_op, "iuh", ++ rc = hypercall_create_continuation(__HYPERVISOR_vcpu_op, "iih", + cmd, vcpuid, arg); + + break; +diff --git a/xen/common/domain.c b/xen/common/domain.c +index 2308588052..65bcd85e34 100644 +--- a/xen/common/domain.c ++++ b/xen/common/domain.c +@@ -1411,7 +1411,7 @@ long do_vcpu_op(int cmd, unsigned int vcpuid, XEN_GUEST_HANDLE_PARAM(void) arg) + + rc = arch_initialise_vcpu(v, arg); + if ( rc == -ERESTART ) +- rc = hypercall_create_continuation(__HYPERVISOR_vcpu_op, "iuh", ++ rc = hypercall_create_continuation(__HYPERVISOR_vcpu_op, "iih", + cmd, vcpuid, arg); + + break; diff --git a/xsa298-4.11.patch b/xsa298-4.11.patch new file mode 100644 index 0000000..9f649e3 --- /dev/null +++ b/xsa298-4.11.patch @@ -0,0 +1,87 @@ +From: Jan Beulich +Subject: x86/PV: check GDT/LDT limits during emulation + +Accesses beyond the LDT limit originating from emulation would trigger +the ASSERT() in pv_map_ldt_shadow_page(). On production builds such +accesses would cause an attempt to promote the touched page (offset from +the present LDT base address) to a segment descriptor one. If this +happens to succeed, guest user mode would be able to elevate its +privileges to that of the guest kernel. This is particularly easy when +there's no LDT at all, in which case the LDT base stored internally to +Xen is simply zero. + +Also adjust the ASSERT() that was triggering: It was off by one to +begin with, and for production builds we also better use +ASSERT_UNREACHABLE() instead with suitable recovery code afterwards. + +This is XSA-298. + +Reported-by: Andrew Cooper +Signed-off-by: Jan Beulich +Reviewed-by: Andrew Cooper + +--- a/xen/arch/x86/pv/emul-gate-op.c ++++ b/xen/arch/x86/pv/emul-gate-op.c +@@ -51,7 +51,13 @@ static int read_gate_descriptor(unsigned + const struct desc_struct *pdesc = gdt_ldt_desc_ptr(gate_sel); + + if ( (gate_sel < 4) || +- ((gate_sel >= FIRST_RESERVED_GDT_BYTE) && !(gate_sel & 4)) || ++ /* ++ * We're interested in call gates only, which occupy a single ++ * seg_desc_t for 32-bit and a consecutive pair of them for 64-bit. ++ */ ++ ((gate_sel >> 3) + !is_pv_32bit_vcpu(v) >= ++ (gate_sel & 4 ? v->arch.pv_vcpu.ldt_ents ++ : v->arch.pv_vcpu.gdt_ents)) || + __get_user(desc, pdesc) ) + return 0; + +@@ -70,7 +76,7 @@ static int read_gate_descriptor(unsigned + if ( !is_pv_32bit_vcpu(v) ) + { + if ( (*ar & 0x1f00) != 0x0c00 || +- (gate_sel >= FIRST_RESERVED_GDT_BYTE - 8 && !(gate_sel & 4)) || ++ /* Limit check done above already. */ + __get_user(desc, pdesc + 1) || + (desc.b & 0x1f00) ) + return 0; +--- a/xen/arch/x86/pv/emulate.c ++++ b/xen/arch/x86/pv/emulate.c +@@ -31,7 +31,14 @@ int pv_emul_read_descriptor(unsigned int + { + struct desc_struct desc; + +- if ( sel < 4) ++ if ( sel < 4 || ++ /* ++ * Don't apply the GDT limit here, as the selector may be a Xen ++ * provided one. __get_user() will fail (without taking further ++ * action) for ones falling in the gap between guest populated ++ * and Xen ones. ++ */ ++ ((sel & 4) && (sel >> 3) >= v->arch.pv_vcpu.ldt_ents) ) + desc.b = desc.a = 0; + else if ( __get_user(desc, gdt_ldt_desc_ptr(sel)) ) + return 0; +--- a/xen/arch/x86/pv/mm.c ++++ b/xen/arch/x86/pv/mm.c +@@ -92,12 +92,16 @@ bool pv_map_ldt_shadow_page(unsigned int + BUG_ON(unlikely(in_irq())); + + /* +- * Hardware limit checking should guarantee this property. NB. This is ++ * Prior limit checking should guarantee this property. NB. This is + * safe as updates to the LDT can only be made by MMUEXT_SET_LDT to the + * current vcpu, and vcpu_reset() will block until this vcpu has been + * descheduled before continuing. + */ +- ASSERT((offset >> 3) <= curr->arch.pv_vcpu.ldt_ents); ++ if ( unlikely((offset >> 3) >= curr->arch.pv_vcpu.ldt_ents) ) ++ { ++ ASSERT_UNREACHABLE(); ++ return false; ++ } + + if ( is_pv_32bit_domain(currd) ) + linear = (uint32_t)linear; diff --git a/xsa299-4.11-0001-x86-mm-L1TF-checks-don-t-leave-a-partial-entry.patch b/xsa299-4.11-0001-x86-mm-L1TF-checks-don-t-leave-a-partial-entry.patch new file mode 100644 index 0000000..6475328 --- /dev/null +++ b/xsa299-4.11-0001-x86-mm-L1TF-checks-don-t-leave-a-partial-entry.patch @@ -0,0 +1,94 @@ +From 852df269d247e177d5f2e9b8f3a4301a6fdd76bd Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Thu, 10 Oct 2019 17:57:49 +0100 +Subject: [PATCH 01/11] x86/mm: L1TF checks don't leave a partial entry + +On detection of a potential L1TF issue, most validation code returns +-ERESTART to allow the switch to shadow mode to happen and cause the +original operation to be restarted. + +However, in the validation code, the return value -ERESTART has been +repurposed to indicate 1) the function has partially completed +something which needs to be undone, and 2) calling put_page_type() +should cleanly undo it. This causes problems in several places. + +For L1 tables, on receiving an -ERESTART return from alloc_l1_table(), +alloc_page_type() will set PGT_partial on the page. If for some +reason the original operation never restarts, then on domain +destruction, relinquish_memory() will call free_page_type() on the +page. + +Unfortunately, alloc_ and free_l1_table() aren't set up to deal with +PGT_partial. When returning a failure, alloc_l1_table() always +de-validates whatever it's validated so far, and free_l1_table() +always devalidates the whole page. This means that if +relinquish_memory() calls free_page_type() on an L1 that didn't +complete due to an L1TF, it will call put_page_from_l1e() on "page +entries" that have never been validated. + +For L2+ tables, setting rc to ERESTART causes the rest of the +alloc_lN_table() function to *think* that the entry in question will +have PGT_partial set. This will cause it to set partial_pte = 1. If +relinqush_memory() then calls free_page_type() on one of those pages, +then free_lN_table() will call put_page_from_lNe() on the entry when +it shouldn't. + +Rather than indicating -ERESTART, indicate -EINTR. This is the code +to indicate that nothing has changed from when you started the call +(which is effectively how alloc_l1_table() handles errors). + +mod_lN_entry() shouldn't have any of these types of problems, so leave +potential changes there for a clean-up patch later. + +This is part of XSA-299. + +Reported-by: George Dunlap +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +--- + xen/arch/x86/mm.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index e6a4cb28f8..8ced185b49 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -1110,7 +1110,7 @@ get_page_from_l2e( + int rc; + + if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) ) +- return pv_l1tf_check_l2e(d, l2e) ? -ERESTART : 1; ++ return pv_l1tf_check_l2e(d, l2e) ? -EINTR : 1; + + if ( unlikely((l2e_get_flags(l2e) & L2_DISALLOW_MASK)) ) + { +@@ -1142,7 +1142,7 @@ get_page_from_l3e( + int rc; + + if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) ) +- return pv_l1tf_check_l3e(d, l3e) ? -ERESTART : 1; ++ return pv_l1tf_check_l3e(d, l3e) ? -EINTR : 1; + + if ( unlikely((l3e_get_flags(l3e) & l3_disallow_mask(d))) ) + { +@@ -1175,7 +1175,7 @@ get_page_from_l4e( + int rc; + + if ( !(l4e_get_flags(l4e) & _PAGE_PRESENT) ) +- return pv_l1tf_check_l4e(d, l4e) ? -ERESTART : 1; ++ return pv_l1tf_check_l4e(d, l4e) ? -EINTR : 1; + + if ( unlikely((l4e_get_flags(l4e) & L4_DISALLOW_MASK)) ) + { +@@ -1404,7 +1404,7 @@ static int alloc_l1_table(struct page_info *page) + { + if ( !(l1e_get_flags(pl1e[i]) & _PAGE_PRESENT) ) + { +- ret = pv_l1tf_check_l1e(d, pl1e[i]) ? -ERESTART : 0; ++ ret = pv_l1tf_check_l1e(d, pl1e[i]) ? -EINTR : 0; + if ( ret ) + goto out; + } +-- +2.23.0 + diff --git a/xsa299-4.11-0002-x86-mm-Don-t-re-set-PGT_pinned-on-a-partially-de-val.patch b/xsa299-4.11-0002-x86-mm-Don-t-re-set-PGT_pinned-on-a-partially-de-val.patch new file mode 100644 index 0000000..a369f93 --- /dev/null +++ b/xsa299-4.11-0002-x86-mm-Don-t-re-set-PGT_pinned-on-a-partially-de-val.patch @@ -0,0 +1,99 @@ +From 6bdddd7980eac0cc883945d823986f24682ca47a Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Thu, 10 Oct 2019 17:57:49 +0100 +Subject: [PATCH 02/11] x86/mm: Don't re-set PGT_pinned on a partially + de-validated page + +When unpinning pagetables, if an operation is interrupted, +relinquish_memory() re-sets PGT_pinned so that the un-pin will +pickedup again when the hypercall restarts. + +This is appropriate when put_page_and_type_preemptible() returns +-EINTR, which indicates that the page is back in its initial state +(i.e., completely validated). However, for -ERESTART, this leads to a +state where a page has both PGT_pinned and PGT_partial set. + +This happens to work at the moment, although it's not really a +"canonical" state; but in subsequent patches, where we need to make a +distinction in handling between PGT_validated and PGT_partial pages, +this causes issues. + +Move to a "canonical" state by: +- Only re-setting PGT_pinned on -EINTR +- Re-dropping the refcount held by PGT_pinned on -ERESTART + +In the latter case, the PGT_partial bit will be cleared further down +with the rest of the other PGT_partial pages. + +While here, clean up some trainling whitespace. + +This is part of XSA-299. + +Reported-by: George Dunlap +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +--- + xen/arch/x86/domain.c | 31 ++++++++++++++++++++++++++++--- + 1 file changed, 28 insertions(+), 3 deletions(-) + +diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c +index 29f892c04c..8fbecbb169 100644 +--- a/xen/arch/x86/domain.c ++++ b/xen/arch/x86/domain.c +@@ -112,7 +112,7 @@ static void play_dead(void) + * this case, heap corruption or #PF can occur (when heap debugging is + * enabled). For example, even printk() can involve tasklet scheduling, + * which touches per-cpu vars. +- * ++ * + * Consider very carefully when adding code to *dead_idle. Most hypervisor + * subsystems are unsafe to call. + */ +@@ -1838,9 +1838,34 @@ static int relinquish_memory( + break; + case -ERESTART: + case -EINTR: ++ /* ++ * -EINTR means PGT_validated has been re-set; re-set ++ * PGT_pinned again so that it gets picked up next time ++ * around. ++ * ++ * -ERESTART, OTOH, means PGT_partial is set instead. Put ++ * it back on the list, but don't set PGT_pinned; the ++ * section below will finish off de-validation. But we do ++ * need to drop the general ref associated with ++ * PGT_pinned, since put_page_and_type_preemptible() ++ * didn't do it. ++ * ++ * NB we can do an ASSERT for PGT_validated, since we ++ * "own" the type ref; but theoretically, the PGT_partial ++ * could be cleared by someone else. ++ */ ++ if ( ret == -EINTR ) ++ { ++ ASSERT(page->u.inuse.type_info & PGT_validated); ++ set_bit(_PGT_pinned, &page->u.inuse.type_info); ++ } ++ else ++ put_page(page); ++ + ret = -ERESTART; ++ ++ /* Put the page back on the list and drop the ref we grabbed above */ + page_list_add(page, list); +- set_bit(_PGT_pinned, &page->u.inuse.type_info); + put_page(page); + goto out; + default: +@@ -2062,7 +2087,7 @@ void vcpu_kick(struct vcpu *v) + * pending flag. These values may fluctuate (after all, we hold no + * locks) but the key insight is that each change will cause + * evtchn_upcall_pending to be polled. +- * ++ * + * NB2. We save the running flag across the unblock to avoid a needless + * IPI for domains that we IPI'd to unblock. + */ +-- +2.23.0 + diff --git a/xsa299-4.11-0003-x86-mm-Separate-out-partial_pte-tristate-into-indivi.patch b/xsa299-4.11-0003-x86-mm-Separate-out-partial_pte-tristate-into-indivi.patch new file mode 100644 index 0000000..fa6914b --- /dev/null +++ b/xsa299-4.11-0003-x86-mm-Separate-out-partial_pte-tristate-into-indivi.patch @@ -0,0 +1,609 @@ +From 7c0a37005f52d10903ce22851b52ae9b6f4f0ee2 Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Thu, 10 Oct 2019 17:57:49 +0100 +Subject: [PATCH 03/11] x86/mm: Separate out partial_pte tristate into + individual flags + +At the moment, partial_pte is a tri-state that contains two distinct bits +of information: + +1. If zero, the pte at index [nr_validated_ptes] is un-validated. If + non-zero, the pte was last seen with PGT_partial set. + +2. If positive, the pte at index [nr_validated_ptes] does not hold a + general reference count. If negative, it does. + +To make future patches more clear, separate out this functionality +into two distinct, named bits: PTF_partial_set (for #1) and +PTF_partial_general_ref (for #2). + +Additionally, a number of functions which need this information also +take other flags to control behavior (such as `preemptible` and +`defer`). These are hard to read in the caller (since you only see +'true' or 'false'), and ugly when many are added together. In +preparation for adding yet another flag in a future patch, collapse +all of these into a single `flag` variable. + +NB that this does mean checking for what was previously the '-1' +condition a bit more ugly in the put_page_from_lNe functions (since +you have to check for both partial_set and general ref); but this +clause will go away in a future patch. + +Also note that the original comment had an off-by-one error: +partial_flags (like partial_pte before it) concerns +plNe[nr_validated_ptes], not plNe[nr_validated_ptes+1]. + +No functional change intended. + +This is part of XSA-299. + +Reported-by: George Dunlap +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +--- + xen/arch/x86/mm.c | 164 +++++++++++++++++++++++---------------- + xen/include/asm-x86/mm.h | 41 ++++++---- + 2 files changed, 127 insertions(+), 78 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 8ced185b49..1c4f54e328 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -610,20 +610,34 @@ static int alloc_segdesc_page(struct page_info *page) + static int _get_page_type(struct page_info *page, unsigned long type, + bool preemptible); + ++/* ++ * The following flags are used to specify behavior of various get and ++ * put commands. The first two are also stored in page->partial_flags ++ * to indicate the state of the page pointed to by ++ * page->pte[page->nr_validated_entries]. See the comment in mm.h for ++ * more information. ++ */ ++#define PTF_partial_set (1 << 0) ++#define PTF_partial_general_ref (1 << 1) ++#define PTF_preemptible (1 << 2) ++#define PTF_defer (1 << 3) ++ + static int get_page_and_type_from_mfn( + mfn_t mfn, unsigned long type, struct domain *d, +- int partial, int preemptible) ++ unsigned int flags) + { + struct page_info *page = mfn_to_page(mfn); + int rc; ++ bool preemptible = flags & PTF_preemptible, ++ partial_ref = flags & PTF_partial_general_ref; + +- if ( likely(partial >= 0) && ++ if ( likely(!partial_ref) && + unlikely(!get_page_from_mfn(mfn, d)) ) + return -EINVAL; + + rc = _get_page_type(page, type, preemptible); + +- if ( unlikely(rc) && partial >= 0 && ++ if ( unlikely(rc) && !partial_ref && + (!preemptible || page != current->arch.old_guest_table) ) + put_page(page); + +@@ -1104,7 +1118,7 @@ get_page_from_l1e( + define_get_linear_pagetable(l2); + static int + get_page_from_l2e( +- l2_pgentry_t l2e, unsigned long pfn, struct domain *d, int partial) ++ l2_pgentry_t l2e, unsigned long pfn, struct domain *d, unsigned int flags) + { + unsigned long mfn = l2e_get_pfn(l2e); + int rc; +@@ -1119,8 +1133,9 @@ get_page_from_l2e( + return -EINVAL; + } + +- rc = get_page_and_type_from_mfn(_mfn(mfn), PGT_l1_page_table, d, +- partial, false); ++ ASSERT(!(flags & PTF_preemptible)); ++ ++ rc = get_page_and_type_from_mfn(_mfn(mfn), PGT_l1_page_table, d, flags); + if ( unlikely(rc == -EINVAL) && get_l2_linear_pagetable(l2e, pfn, d) ) + rc = 0; + +@@ -1137,7 +1152,7 @@ get_page_from_l2e( + define_get_linear_pagetable(l3); + static int + get_page_from_l3e( +- l3_pgentry_t l3e, unsigned long pfn, struct domain *d, int partial) ++ l3_pgentry_t l3e, unsigned long pfn, struct domain *d, unsigned int flags) + { + int rc; + +@@ -1152,7 +1167,7 @@ get_page_from_l3e( + } + + rc = get_page_and_type_from_mfn( +- l3e_get_mfn(l3e), PGT_l2_page_table, d, partial, 1); ++ l3e_get_mfn(l3e), PGT_l2_page_table, d, flags | PTF_preemptible); + if ( unlikely(rc == -EINVAL) && + !is_pv_32bit_domain(d) && + get_l3_linear_pagetable(l3e, pfn, d) ) +@@ -1170,7 +1185,7 @@ get_page_from_l3e( + define_get_linear_pagetable(l4); + static int + get_page_from_l4e( +- l4_pgentry_t l4e, unsigned long pfn, struct domain *d, int partial) ++ l4_pgentry_t l4e, unsigned long pfn, struct domain *d, unsigned int flags) + { + int rc; + +@@ -1185,7 +1200,7 @@ get_page_from_l4e( + } + + rc = get_page_and_type_from_mfn( +- l4e_get_mfn(l4e), PGT_l3_page_table, d, partial, 1); ++ l4e_get_mfn(l4e), PGT_l3_page_table, d, flags | PTF_preemptible); + if ( unlikely(rc == -EINVAL) && get_l4_linear_pagetable(l4e, pfn, d) ) + rc = 0; + +@@ -1275,7 +1290,7 @@ void put_page_from_l1e(l1_pgentry_t l1e, struct domain *l1e_owner) + * Note also that this automatically deals correctly with linear p.t.'s. + */ + static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, +- int partial, bool defer) ++ unsigned int flags) + { + int rc = 0; + +@@ -1295,12 +1310,13 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, + struct page_info *pg = l2e_get_page(l2e); + struct page_info *ptpg = mfn_to_page(_mfn(pfn)); + +- if ( unlikely(partial > 0) ) ++ if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == ++ PTF_partial_set ) + { +- ASSERT(!defer); ++ ASSERT(!(flags & PTF_defer)); + rc = _put_page_type(pg, true, ptpg); + } +- else if ( defer ) ++ else if ( flags & PTF_defer ) + { + current->arch.old_guest_ptpg = ptpg; + current->arch.old_guest_table = pg; +@@ -1317,7 +1333,7 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, + } + + static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, +- int partial, bool defer) ++ unsigned int flags) + { + struct page_info *pg; + int rc; +@@ -1340,13 +1356,14 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, + + pg = l3e_get_page(l3e); + +- if ( unlikely(partial > 0) ) ++ if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == ++ PTF_partial_set ) + { +- ASSERT(!defer); ++ ASSERT(!(flags & PTF_defer)); + return _put_page_type(pg, true, mfn_to_page(_mfn(pfn))); + } + +- if ( defer ) ++ if ( flags & PTF_defer ) + { + current->arch.old_guest_ptpg = mfn_to_page(_mfn(pfn)); + current->arch.old_guest_table = pg; +@@ -1361,7 +1378,7 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, + } + + static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, +- int partial, bool defer) ++ unsigned int flags) + { + int rc = 1; + +@@ -1370,13 +1387,14 @@ static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, + { + struct page_info *pg = l4e_get_page(l4e); + +- if ( unlikely(partial > 0) ) ++ if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == ++ PTF_partial_set ) + { +- ASSERT(!defer); ++ ASSERT(!(flags & PTF_defer)); + return _put_page_type(pg, true, mfn_to_page(_mfn(pfn))); + } + +- if ( defer ) ++ if ( flags & PTF_defer ) + { + current->arch.old_guest_ptpg = mfn_to_page(_mfn(pfn)); + current->arch.old_guest_table = pg; +@@ -1483,12 +1501,13 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) + unsigned long pfn = mfn_x(page_to_mfn(page)); + l2_pgentry_t *pl2e; + unsigned int i; +- int rc = 0, partial = page->partial_pte; ++ int rc = 0; ++ unsigned int partial_flags = page->partial_flags; + + pl2e = map_domain_page(_mfn(pfn)); + + for ( i = page->nr_validated_ptes; i < L2_PAGETABLE_ENTRIES; +- i++, partial = 0 ) ++ i++, partial_flags = 0 ) + { + if ( i > page->nr_validated_ptes && hypercall_preempt_check() ) + { +@@ -1498,18 +1517,19 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) + } + + if ( !is_guest_l2_slot(d, type, i) || +- (rc = get_page_from_l2e(pl2e[i], pfn, d, partial)) > 0 ) ++ (rc = get_page_from_l2e(pl2e[i], pfn, d, partial_flags)) > 0 ) + continue; + + if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; +- page->partial_pte = partial ?: 1; ++ /* Set 'set', retain 'general ref' */ ++ page->partial_flags = partial_flags | PTF_partial_set; + } + else if ( rc == -EINTR && i ) + { + page->nr_validated_ptes = i; +- page->partial_pte = 0; ++ page->partial_flags = 0; + rc = -ERESTART; + } + else if ( rc < 0 && rc != -EINTR ) +@@ -1518,7 +1538,7 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) + if ( i ) + { + page->nr_validated_ptes = i; +- page->partial_pte = 0; ++ page->partial_flags = 0; + current->arch.old_guest_ptpg = NULL; + current->arch.old_guest_table = page; + } +@@ -1542,7 +1562,8 @@ static int alloc_l3_table(struct page_info *page) + unsigned long pfn = mfn_x(page_to_mfn(page)); + l3_pgentry_t *pl3e; + unsigned int i; +- int rc = 0, partial = page->partial_pte; ++ int rc = 0; ++ unsigned int partial_flags = page->partial_flags; + + pl3e = map_domain_page(_mfn(pfn)); + +@@ -1557,7 +1578,7 @@ static int alloc_l3_table(struct page_info *page) + memset(pl3e + 4, 0, (L3_PAGETABLE_ENTRIES - 4) * sizeof(*pl3e)); + + for ( i = page->nr_validated_ptes; i < L3_PAGETABLE_ENTRIES; +- i++, partial = 0 ) ++ i++, partial_flags = 0 ) + { + if ( i > page->nr_validated_ptes && hypercall_preempt_check() ) + { +@@ -1574,20 +1595,22 @@ static int alloc_l3_table(struct page_info *page) + else + rc = get_page_and_type_from_mfn( + l3e_get_mfn(pl3e[i]), +- PGT_l2_page_table | PGT_pae_xen_l2, d, partial, 1); ++ PGT_l2_page_table | PGT_pae_xen_l2, d, ++ partial_flags | PTF_preemptible); + } +- else if ( (rc = get_page_from_l3e(pl3e[i], pfn, d, partial)) > 0 ) ++ else if ( (rc = get_page_from_l3e(pl3e[i], pfn, d, partial_flags)) > 0 ) + continue; + + if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; +- page->partial_pte = partial ?: 1; ++ /* Set 'set', leave 'general ref' set if this entry was set */ ++ page->partial_flags = partial_flags | PTF_partial_set; + } + else if ( rc == -EINTR && i ) + { + page->nr_validated_ptes = i; +- page->partial_pte = 0; ++ page->partial_flags = 0; + rc = -ERESTART; + } + if ( rc < 0 ) +@@ -1604,7 +1627,7 @@ static int alloc_l3_table(struct page_info *page) + if ( i ) + { + page->nr_validated_ptes = i; +- page->partial_pte = 0; ++ page->partial_flags = 0; + current->arch.old_guest_ptpg = NULL; + current->arch.old_guest_table = page; + } +@@ -1736,19 +1759,21 @@ static int alloc_l4_table(struct page_info *page) + unsigned long pfn = mfn_x(page_to_mfn(page)); + l4_pgentry_t *pl4e = map_domain_page(_mfn(pfn)); + unsigned int i; +- int rc = 0, partial = page->partial_pte; ++ int rc = 0; ++ unsigned int partial_flags = page->partial_flags; + + for ( i = page->nr_validated_ptes; i < L4_PAGETABLE_ENTRIES; +- i++, partial = 0 ) ++ i++, partial_flags = 0 ) + { + if ( !is_guest_l4_slot(d, i) || +- (rc = get_page_from_l4e(pl4e[i], pfn, d, partial)) > 0 ) ++ (rc = get_page_from_l4e(pl4e[i], pfn, d, partial_flags)) > 0 ) + continue; + + if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; +- page->partial_pte = partial ?: 1; ++ /* Set 'set', leave 'general ref' set if this entry was set */ ++ page->partial_flags = partial_flags | PTF_partial_set; + } + else if ( rc < 0 ) + { +@@ -1758,7 +1783,7 @@ static int alloc_l4_table(struct page_info *page) + if ( i ) + { + page->nr_validated_ptes = i; +- page->partial_pte = 0; ++ page->partial_flags = 0; + if ( rc == -EINTR ) + rc = -ERESTART; + else +@@ -1811,19 +1836,20 @@ static int free_l2_table(struct page_info *page) + struct domain *d = page_get_owner(page); + unsigned long pfn = mfn_x(page_to_mfn(page)); + l2_pgentry_t *pl2e; +- int rc = 0, partial = page->partial_pte; +- unsigned int i = page->nr_validated_ptes - !partial; ++ int rc = 0; ++ unsigned int partial_flags = page->partial_flags, ++ i = page->nr_validated_ptes - !(partial_flags & PTF_partial_set); + + pl2e = map_domain_page(_mfn(pfn)); + + for ( ; ; ) + { + if ( is_guest_l2_slot(d, page->u.inuse.type_info, i) ) +- rc = put_page_from_l2e(pl2e[i], pfn, partial, false); ++ rc = put_page_from_l2e(pl2e[i], pfn, partial_flags); + if ( rc < 0 ) + break; + +- partial = 0; ++ partial_flags = 0; + + if ( !i-- ) + break; +@@ -1845,12 +1871,14 @@ static int free_l2_table(struct page_info *page) + else if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; +- page->partial_pte = partial ?: -1; ++ page->partial_flags = (partial_flags & PTF_partial_set) ? ++ partial_flags : ++ (PTF_partial_set | PTF_partial_general_ref); + } + else if ( rc == -EINTR && i < L2_PAGETABLE_ENTRIES - 1 ) + { + page->nr_validated_ptes = i + 1; +- page->partial_pte = 0; ++ page->partial_flags = 0; + rc = -ERESTART; + } + +@@ -1862,18 +1890,19 @@ static int free_l3_table(struct page_info *page) + struct domain *d = page_get_owner(page); + unsigned long pfn = mfn_x(page_to_mfn(page)); + l3_pgentry_t *pl3e; +- int rc = 0, partial = page->partial_pte; +- unsigned int i = page->nr_validated_ptes - !partial; ++ int rc = 0; ++ unsigned int partial_flags = page->partial_flags, ++ i = page->nr_validated_ptes - !(partial_flags & PTF_partial_set); + + pl3e = map_domain_page(_mfn(pfn)); + + for ( ; ; ) + { +- rc = put_page_from_l3e(pl3e[i], pfn, partial, 0); ++ rc = put_page_from_l3e(pl3e[i], pfn, partial_flags); + if ( rc < 0 ) + break; + +- partial = 0; ++ partial_flags = 0; + if ( rc == 0 ) + pl3e[i] = unadjust_guest_l3e(pl3e[i], d); + +@@ -1892,12 +1921,14 @@ static int free_l3_table(struct page_info *page) + if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; +- page->partial_pte = partial ?: -1; ++ page->partial_flags = (partial_flags & PTF_partial_set) ? ++ partial_flags : ++ (PTF_partial_set | PTF_partial_general_ref); + } + else if ( rc == -EINTR && i < L3_PAGETABLE_ENTRIES - 1 ) + { + page->nr_validated_ptes = i + 1; +- page->partial_pte = 0; ++ page->partial_flags = 0; + rc = -ERESTART; + } + return rc > 0 ? 0 : rc; +@@ -1908,26 +1939,29 @@ static int free_l4_table(struct page_info *page) + struct domain *d = page_get_owner(page); + unsigned long pfn = mfn_x(page_to_mfn(page)); + l4_pgentry_t *pl4e = map_domain_page(_mfn(pfn)); +- int rc = 0, partial = page->partial_pte; +- unsigned int i = page->nr_validated_ptes - !partial; ++ int rc = 0; ++ unsigned partial_flags = page->partial_flags, ++ i = page->nr_validated_ptes - !(partial_flags & PTF_partial_set); + + do { + if ( is_guest_l4_slot(d, i) ) +- rc = put_page_from_l4e(pl4e[i], pfn, partial, 0); ++ rc = put_page_from_l4e(pl4e[i], pfn, partial_flags); + if ( rc < 0 ) + break; +- partial = 0; ++ partial_flags = 0; + } while ( i-- ); + + if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; +- page->partial_pte = partial ?: -1; ++ page->partial_flags = (partial_flags & PTF_partial_set) ? ++ partial_flags : ++ (PTF_partial_set | PTF_partial_general_ref); + } + else if ( rc == -EINTR && i < L4_PAGETABLE_ENTRIES - 1 ) + { + page->nr_validated_ptes = i + 1; +- page->partial_pte = 0; ++ page->partial_flags = 0; + rc = -ERESTART; + } + +@@ -2203,7 +2237,7 @@ static int mod_l2_entry(l2_pgentry_t *pl2e, + return -EBUSY; + } + +- put_page_from_l2e(ol2e, pfn, 0, true); ++ put_page_from_l2e(ol2e, pfn, PTF_defer); + + return rc; + } +@@ -2271,7 +2305,7 @@ static int mod_l3_entry(l3_pgentry_t *pl3e, + if ( !create_pae_xen_mappings(d, pl3e) ) + BUG(); + +- put_page_from_l3e(ol3e, pfn, 0, 1); ++ put_page_from_l3e(ol3e, pfn, PTF_defer); + return rc; + } + +@@ -2334,7 +2368,7 @@ static int mod_l4_entry(l4_pgentry_t *pl4e, + return -EFAULT; + } + +- put_page_from_l4e(ol4e, pfn, 0, 1); ++ put_page_from_l4e(ol4e, pfn, PTF_defer); + return rc; + } + +@@ -2598,7 +2632,7 @@ int free_page_type(struct page_info *page, unsigned long type, + if ( !(type & PGT_partial) ) + { + page->nr_validated_ptes = 1U << PAGETABLE_ORDER; +- page->partial_pte = 0; ++ page->partial_flags = 0; + } + + switch ( type & PGT_type_mask ) +@@ -2889,7 +2923,7 @@ static int _get_page_type(struct page_info *page, unsigned long type, + if ( !(x & PGT_partial) ) + { + page->nr_validated_ptes = 0; +- page->partial_pte = 0; ++ page->partial_flags = 0; + } + page->linear_pt_count = 0; + rc = alloc_page_type(page, type, preemptible); +@@ -3064,7 +3098,7 @@ int new_guest_cr3(mfn_t mfn) + return 0; + } + +- rc = get_page_and_type_from_mfn(mfn, PGT_root_page_table, d, 0, 1); ++ rc = get_page_and_type_from_mfn(mfn, PGT_root_page_table, d, PTF_preemptible); + switch ( rc ) + { + case 0: +@@ -3452,7 +3486,7 @@ long do_mmuext_op( + if ( op.arg1.mfn != 0 ) + { + rc = get_page_and_type_from_mfn( +- _mfn(op.arg1.mfn), PGT_root_page_table, currd, 0, 1); ++ _mfn(op.arg1.mfn), PGT_root_page_table, currd, PTF_preemptible); + + if ( unlikely(rc) ) + { +diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h +index 1ea173c555..46cba52941 100644 +--- a/xen/include/asm-x86/mm.h ++++ b/xen/include/asm-x86/mm.h +@@ -228,19 +228,34 @@ struct page_info + * setting the flag must not drop that reference, whereas the instance + * clearing it will have to. + * +- * If @partial_pte is positive then PTE at @nr_validated_ptes+1 has +- * been partially validated. This implies that the general reference +- * to the page (acquired from get_page_from_lNe()) would be dropped +- * (again due to the apparent failure) and hence must be re-acquired +- * when resuming the validation, but must not be dropped when picking +- * up the page for invalidation. ++ * If partial_flags & PTF_partial_set is set, then the page at ++ * at @nr_validated_ptes had PGT_partial set as a result of an ++ * operation on the current page. (That page may or may not ++ * still have PGT_partial set.) + * +- * If @partial_pte is negative then PTE at @nr_validated_ptes+1 has +- * been partially invalidated. This is basically the opposite case of +- * above, i.e. the general reference to the page was not dropped in +- * put_page_from_lNe() (due to the apparent failure), and hence it +- * must be dropped when the put operation is resumed (and completes), +- * but it must not be acquired if picking up the page for validation. ++ * If PTF_partial_general_ref is set, then the PTE at ++ * @nr_validated_ptef holds a general reference count for the ++ * page. ++ * ++ * This happens: ++ * - During de-validation, if de-validation of the page was ++ * interrupted ++ * - During validation, if an invalid entry is encountered and ++ * validation is preemptible ++ * - During validation, if PTF_partial_general_ref was set on ++ * this entry to begin with (perhaps because we're picking ++ * up from a partial de-validation). ++ * ++ * When resuming validation, if PTF_partial_general_ref is clear, ++ * then a general reference must be re-acquired; if it is set, no ++ * reference should be acquired. ++ * ++ * When resuming de-validation, if PTF_partial_general_ref is ++ * clear, no reference should be dropped; if it is set, a ++ * reference should be dropped. ++ * ++ * NB that PTF_partial_set and PTF_partial_general_ref are ++ * defined in mm.c, the only place where they are used. + * + * The 3rd field, @linear_pt_count, indicates + * - by a positive value, how many same-level page table entries a page +@@ -251,7 +266,7 @@ struct page_info + struct { + u16 nr_validated_ptes:PAGETABLE_ORDER + 1; + u16 :16 - PAGETABLE_ORDER - 1 - 2; +- s16 partial_pte:2; ++ u16 partial_flags:2; + s16 linear_pt_count; + }; + +-- +2.23.0 + diff --git a/xsa299-4.11-0004-x86-mm-Use-flags-for-_put_page_type-rather-than-a-bo.patch b/xsa299-4.11-0004-x86-mm-Use-flags-for-_put_page_type-rather-than-a-bo.patch new file mode 100644 index 0000000..767ebc1 --- /dev/null +++ b/xsa299-4.11-0004-x86-mm-Use-flags-for-_put_page_type-rather-than-a-bo.patch @@ -0,0 +1,140 @@ +From 20b8a6702c6839bafd252789396b443d4b5c5474 Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Thu, 10 Oct 2019 17:57:49 +0100 +Subject: [PATCH 04/11] x86/mm: Use flags for _put_page_type rather than a + boolean + +This is in mainly in preparation for _put_page_type taking the +partial_flags value in the future. It also makes it easier to read in +the caller (since you see a flag name rather than `true` or `false`). + +No functional change intended. + +This is part of XSA-299. + +Reported-by: George Dunlap +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +--- + xen/arch/x86/mm.c | 25 +++++++++++++------------ + 1 file changed, 13 insertions(+), 12 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 1c4f54e328..e2fba15d86 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -1207,7 +1207,7 @@ get_page_from_l4e( + return rc; + } + +-static int _put_page_type(struct page_info *page, bool preemptible, ++static int _put_page_type(struct page_info *page, unsigned int flags, + struct page_info *ptpg); + + void put_page_from_l1e(l1_pgentry_t l1e, struct domain *l1e_owner) +@@ -1314,7 +1314,7 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, + PTF_partial_set ) + { + ASSERT(!(flags & PTF_defer)); +- rc = _put_page_type(pg, true, ptpg); ++ rc = _put_page_type(pg, PTF_preemptible, ptpg); + } + else if ( flags & PTF_defer ) + { +@@ -1323,7 +1323,7 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, + } + else + { +- rc = _put_page_type(pg, true, ptpg); ++ rc = _put_page_type(pg, PTF_preemptible, ptpg); + if ( likely(!rc) ) + put_page(pg); + } +@@ -1360,7 +1360,7 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, + PTF_partial_set ) + { + ASSERT(!(flags & PTF_defer)); +- return _put_page_type(pg, true, mfn_to_page(_mfn(pfn))); ++ return _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); + } + + if ( flags & PTF_defer ) +@@ -1370,7 +1370,7 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, + return 0; + } + +- rc = _put_page_type(pg, true, mfn_to_page(_mfn(pfn))); ++ rc = _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); + if ( likely(!rc) ) + put_page(pg); + +@@ -1391,7 +1391,7 @@ static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, + PTF_partial_set ) + { + ASSERT(!(flags & PTF_defer)); +- return _put_page_type(pg, true, mfn_to_page(_mfn(pfn))); ++ return _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); + } + + if ( flags & PTF_defer ) +@@ -1401,7 +1401,7 @@ static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, + return 0; + } + +- rc = _put_page_type(pg, true, mfn_to_page(_mfn(pfn))); ++ rc = _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); + if ( likely(!rc) ) + put_page(pg); + } +@@ -2701,10 +2701,11 @@ static int _put_final_page_type(struct page_info *page, unsigned long type, + } + + +-static int _put_page_type(struct page_info *page, bool preemptible, ++static int _put_page_type(struct page_info *page, unsigned int flags, + struct page_info *ptpg) + { + unsigned long nx, x, y = page->u.inuse.type_info; ++ bool preemptible = flags & PTF_preemptible; + + ASSERT(current_locked_page_ne_check(page)); + +@@ -2911,7 +2912,7 @@ static int _get_page_type(struct page_info *page, unsigned long type, + + if ( unlikely(iommu_ret) ) + { +- _put_page_type(page, false, NULL); ++ _put_page_type(page, 0, NULL); + rc = iommu_ret; + goto out; + } +@@ -2938,7 +2939,7 @@ static int _get_page_type(struct page_info *page, unsigned long type, + + void put_page_type(struct page_info *page) + { +- int rc = _put_page_type(page, false, NULL); ++ int rc = _put_page_type(page, 0, NULL); + ASSERT(rc == 0); + (void)rc; + } +@@ -2955,7 +2956,7 @@ int get_page_type(struct page_info *page, unsigned long type) + + int put_page_type_preemptible(struct page_info *page) + { +- return _put_page_type(page, true, NULL); ++ return _put_page_type(page, PTF_preemptible, NULL); + } + + int get_page_type_preemptible(struct page_info *page, unsigned long type) +@@ -2972,7 +2973,7 @@ int put_old_guest_table(struct vcpu *v) + if ( !v->arch.old_guest_table ) + return 0; + +- switch ( rc = _put_page_type(v->arch.old_guest_table, true, ++ switch ( rc = _put_page_type(v->arch.old_guest_table, PTF_preemptible, + v->arch.old_guest_ptpg) ) + { + case -EINTR: +-- +2.23.0 + diff --git a/xsa299-4.11-0005-x86-mm-Rework-get_page_and_type_from_mfn-conditional.patch b/xsa299-4.11-0005-x86-mm-Rework-get_page_and_type_from_mfn-conditional.patch new file mode 100644 index 0000000..523ea25 --- /dev/null +++ b/xsa299-4.11-0005-x86-mm-Rework-get_page_and_type_from_mfn-conditional.patch @@ -0,0 +1,79 @@ +From 7b3f9f9a797459902bebba962e31be5cbfe7b515 Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Thu, 10 Oct 2019 17:57:49 +0100 +Subject: [PATCH 05/11] x86/mm: Rework get_page_and_type_from_mfn conditional + +Make it easier to read by declaring the conditions in which we will +retain the ref, rather than the conditions under which we release it. + +The only way (page == current->arch.old_guest_table) can be true is if +preemptible is true; so remove this from the query itself, and add an +ASSERT() to that effect on the opposite path. + +No functional change intended. + +NB that alloc_lN_table() mishandle the "linear pt failure" situation +described in the comment; this will be addressed in a future patch. + +This is part of XSA-299. + +Reported-by: George Dunlap +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +--- + xen/arch/x86/mm.c | 39 +++++++++++++++++++++++++++++++++++++-- + 1 file changed, 37 insertions(+), 2 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index e2fba15d86..eaf7b14245 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -637,8 +637,43 @@ static int get_page_and_type_from_mfn( + + rc = _get_page_type(page, type, preemptible); + +- if ( unlikely(rc) && !partial_ref && +- (!preemptible || page != current->arch.old_guest_table) ) ++ /* ++ * Retain the refcount if: ++ * - page is fully validated (rc == 0) ++ * - page is not validated (rc < 0) but: ++ * - We came in with a reference (partial_ref) ++ * - page is partially validated but there's been an error ++ * (page == current->arch.old_guest_table) ++ * ++ * The partial_ref-on-error clause is worth an explanation. There ++ * are two scenarios where partial_ref might be true coming in: ++ * - mfn has been partially demoted as type `type`; i.e. has ++ * PGT_partial set ++ * - mfn has been partially demoted as L(type+1) (i.e., a linear ++ * page; e.g. we're being called from get_page_from_l2e with ++ * type == PGT_l1_table, but the mfn is PGT_l2_table) ++ * ++ * If there's an error, in the first case, _get_page_type will ++ * either return -ERESTART, in which case we want to retain the ++ * ref (as the caller will consider it retained), or -EINVAL, in ++ * which case old_guest_table will be set; in both cases, we need ++ * to retain the ref. ++ * ++ * In the second case, if there's an error, _get_page_type() can ++ * *only* return -EINVAL, and *never* set old_guest_table. In ++ * that case we also want to retain the reference, to allow the ++ * page to continue to be torn down (i.e., PGT_partial cleared) ++ * safely. ++ * ++ * Also note that we shouldn't be able to leave with the reference ++ * count retained unless we succeeded, or the operation was ++ * preemptible. ++ */ ++ if ( likely(!rc) || partial_ref ) ++ /* nothing */; ++ else if ( page == current->arch.old_guest_table ) ++ ASSERT(preemptible); ++ else + put_page(page); + + return rc; +-- +2.23.0 + diff --git a/xsa299-4.11-0006-x86-mm-Have-alloc_l-23-_table-clear-partial_flags-wh.patch b/xsa299-4.11-0006-x86-mm-Have-alloc_l-23-_table-clear-partial_flags-wh.patch new file mode 100644 index 0000000..c611801 --- /dev/null +++ b/xsa299-4.11-0006-x86-mm-Have-alloc_l-23-_table-clear-partial_flags-wh.patch @@ -0,0 +1,101 @@ +From d28893777be56ef51562ed32502377974f738fd3 Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Thu, 10 Oct 2019 17:57:49 +0100 +Subject: [PATCH 06/11] x86/mm: Have alloc_l[23]_table clear partial_flags when + preempting + +In order to allow recursive pagetable promotions and demotions to be +interrupted, Xen must keep track of the state of the sub-pages +promoted or demoted. This is stored in two elements in the page +struct: nr_entries_validated and partial_flags. + +The rule is that entries [0, nr_entries_validated) should always be +validated and hold a general reference count. If partial_flags is +zero, then [nr_entries_validated] is not validated and no reference +count is held. If PTF_partial_set is set, then [nr_entries_validated] +is partially validated. + +At the moment, a distinction is made between promotion and demotion +with regard to whether the entry itself "holds" a general reference +count: when entry promotion is interrupted (i.e., returns -ERESTART), +the entry is not considered to hold a reference; when entry demotion +is interrupted, the entry is still considered to hold a general +reference. + +PTF_partial_general_ref is used to distinguish between these cases. +If clear, it's a partial promotion => no general reference count held +by the entry; if set, it's partial demotion, so a general reference +count held. Because promotions and demotions can be interleaved, this +value is passed to get_page_and_type_from_mfn and put_page_from_l*e, +to be able to properly handle reference counts. + +Unfortunately, when alloc_l[23]_table check hypercall_preempt_check() +and return -ERESTART, they set nr_entries_validated, but don't clear +partial_flags. + +If we were picking up from a previously-interrupted promotion, that +means that PTF_partial_set would be set even though +[nr_entries_validated] was not partially validated. This means that +if the page in this state were de-validated, put_page_type() would +erroneously be called on that entry. + +Perhaps worse, if we were racing with a de-validation, then we might +leave both PTF_partial_set and PTF_partial_general_ref; and when +de-validation picked up again, both the type and the general ref would +be erroneously dropped from [nr_entries_validated]. + +In a sense, the real issue here is code duplication. Rather than +duplicate the interruption code, set rc to -EINTR and fall through to +the code which already handles that case correctly. + +Given the logic at this point, it should be impossible for +partial_flags to be non-zero; add an ASSERT() to catch any changes. + +This is part of XSA-299. + +Reported-by: George Dunlap +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +--- + xen/arch/x86/mm.c | 18 ++++-------------- + 1 file changed, 4 insertions(+), 14 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index eaf7b14245..053465cb7c 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -1545,13 +1545,8 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) + i++, partial_flags = 0 ) + { + if ( i > page->nr_validated_ptes && hypercall_preempt_check() ) +- { +- page->nr_validated_ptes = i; +- rc = -ERESTART; +- break; +- } +- +- if ( !is_guest_l2_slot(d, type, i) || ++ rc = -EINTR; ++ else if ( !is_guest_l2_slot(d, type, i) || + (rc = get_page_from_l2e(pl2e[i], pfn, d, partial_flags)) > 0 ) + continue; + +@@ -1616,13 +1611,8 @@ static int alloc_l3_table(struct page_info *page) + i++, partial_flags = 0 ) + { + if ( i > page->nr_validated_ptes && hypercall_preempt_check() ) +- { +- page->nr_validated_ptes = i; +- rc = -ERESTART; +- break; +- } +- +- if ( is_pv_32bit_domain(d) && (i == 3) ) ++ rc = -EINTR; ++ else if ( is_pv_32bit_domain(d) && (i == 3) ) + { + if ( !(l3e_get_flags(pl3e[i]) & _PAGE_PRESENT) || + (l3e_get_flags(pl3e[i]) & l3_disallow_mask(d)) ) +-- +2.23.0 + diff --git a/xsa299-4.11-0007-x86-mm-Always-retain-a-general-ref-on-partial.patch b/xsa299-4.11-0007-x86-mm-Always-retain-a-general-ref-on-partial.patch new file mode 100644 index 0000000..0d54cc5 --- /dev/null +++ b/xsa299-4.11-0007-x86-mm-Always-retain-a-general-ref-on-partial.patch @@ -0,0 +1,374 @@ +From f608a53c25806a7a4318cbe225bc5f5bbf154d69 Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Thu, 10 Oct 2019 17:57:49 +0100 +Subject: [PATCH 07/11] x86/mm: Always retain a general ref on partial + +In order to allow recursive pagetable promotions and demotions to be +interrupted, Xen must keep track of the state of the sub-pages +promoted or demoted. This is stored in two elements in the page struct: +nr_entries_validated and partial_flags. + +The rule is that entries [0, nr_entries_validated) should always be +validated and hold a general reference count. If partial_flags is +zero, then [nr_entries_validated] is not validated and no reference +count is held. If PTF_partial_set is set, then [nr_entries_validated] +is partially validated. + +At the moment, a distinction is made between promotion and demotion +with regard to whether the entry itself "holds" a general reference +count: when entry promotion is interrupted (i.e., returns -ERESTART), +the entry is not considered to hold a reference; when entry demotion +is interrupted, the entry is still considered to hold a general +reference. + +PTF_partial_general_ref is used to distinguish between these cases. +If clear, it's a partial promotion => no general reference count held +by the entry; if set, it's partial demotion, so a general reference +count held. Because promotions and demotions can be interleaved, this +value is passed to get_page_and_type_from_mfn and put_page_from_l*e, +to be able to properly handle reference counts. + +Unfortunately, because a refcount is not held, it is possible to +engineer a situation where PFT_partial_set is set but the page in +question has been assigned to another domain. A sketch is provided in +the appendix. + +Fix this by having the parent page table entry hold a general +reference count whenever PFT_partial_set is set. (For clarity of +change, keep two separate flags. These will be collapsed in a +subsequent changeset.) + +This has two basic implications. On the put_page_from_lNe() side, +this mean that the (partial_set && !partial_ref) case can never happen, +and no longer needs to be special-cased. + +Secondly, because both flags are set together, there's no need to carry over +existing bits from partial_pte. + +(NB there is still another issue with calling _put_page_type() on a +page which had PGT_partial set; that will be handled in a subsequent +patch.) + +On the get_page_and_type_from_mfn() side, we need to distinguish +between callers which hold a reference on partial (i.e., +alloc_lN_table()), and those which do not (new_cr3, PIN_LN_TABLE, and +so on): pass a flag if the type should be retained on interruption. + +NB that since l1 promotion can't be preempted, that get_page_from_l2e +can't return -ERESTART. + +This is part of XSA-299. + +Reported-by: George Dunlap +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +----- +* Appendix: Engineering PTF_partial_set while a page belongs to a + foreign domain + +Suppose A is a page which can be promoted to an l3, and B is a page +which can be promoted to an l2, and A[x] points to B. B has +PGC_allocated set but no other general references. + +V1: PIN_L3 A. + A is validated, B is validated. + A.type_count = 1 | PGT_validated | PGT_pinned + B.type_count = 1 | PGT_validated + B.count = 2 | PGC_allocated (A[x] holds a general ref) + +V1: UNPIN A. + A begins de-validation. + Arrange to be interrupted when i < x + V1->old_guest_table = A + V1->old_guest_table_ref_held = false + A.type_count = 1 | PGT_partial + A.nr_validated_entries = i < x + B.type_count = 0 + B.count = 1 | PGC_allocated + +V2: MOD_L4_ENTRY to point some l4e to A. + Picks up re-validation of A. + Arrange to be interrupted halfway through B's validation + B.type_count = 1 | PGT_partial + B.count = 2 | PGC_allocated (PGT_partial holds a general ref) + A.type_count = 1 | PGT_partial + A.nr_validated_entries = x + A.partial_pte = PTF_partial_set + +V3: MOD_L3_ENTRY to point some other l3e (not in A) to B. + Validates B. + B.type_count = 1 | PGT_validated + B.count = 2 | PGC_allocated ("other l3e" holds a general ref) + +V3: MOD_L3_ENTRY to clear l3e pointing to B. + Devalidates B. + B.type_count = 0 + B.count = 1 | PGC_allocated + +V3: decrease_reservation(B) + Clears PGC_allocated + B.count = 0 => B is freed + +B gets assigned to a different domain + +V1: Restarts UNPIN of A + put_old_guest_table(A) + ... + free_l3_table(A) + +Now since A.partial_flags has PTF_partial_set, free_l3_table() will +call put_page_from_l3e() on A[x], which points to B, while B is owned +by another domain. + +If A[x] held a general refcount for B on partial validation, as it does +for partial de-validation, then B would still have a reference count of +1 after PGC_allocated was freed; so B wouldn't be freed until after +put_page_from_l3e() had happend on A[x]. +--- + xen/arch/x86/mm.c | 84 +++++++++++++++++++++++----------------- + xen/include/asm-x86/mm.h | 15 ++++--- + 2 files changed, 58 insertions(+), 41 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 053465cb7c..68a9e74002 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -617,10 +617,11 @@ static int _get_page_type(struct page_info *page, unsigned long type, + * page->pte[page->nr_validated_entries]. See the comment in mm.h for + * more information. + */ +-#define PTF_partial_set (1 << 0) +-#define PTF_partial_general_ref (1 << 1) +-#define PTF_preemptible (1 << 2) +-#define PTF_defer (1 << 3) ++#define PTF_partial_set (1 << 0) ++#define PTF_partial_general_ref (1 << 1) ++#define PTF_preemptible (1 << 2) ++#define PTF_defer (1 << 3) ++#define PTF_retain_ref_on_restart (1 << 4) + + static int get_page_and_type_from_mfn( + mfn_t mfn, unsigned long type, struct domain *d, +@@ -629,7 +630,11 @@ static int get_page_and_type_from_mfn( + struct page_info *page = mfn_to_page(mfn); + int rc; + bool preemptible = flags & PTF_preemptible, +- partial_ref = flags & PTF_partial_general_ref; ++ partial_ref = flags & PTF_partial_general_ref, ++ partial_set = flags & PTF_partial_set, ++ retain_ref = flags & PTF_retain_ref_on_restart; ++ ++ ASSERT(partial_ref == partial_set); + + if ( likely(!partial_ref) && + unlikely(!get_page_from_mfn(mfn, d)) ) +@@ -642,13 +647,15 @@ static int get_page_and_type_from_mfn( + * - page is fully validated (rc == 0) + * - page is not validated (rc < 0) but: + * - We came in with a reference (partial_ref) ++ * - page is partially validated (rc == -ERESTART), and the ++ * caller has asked the ref to be retained in that case + * - page is partially validated but there's been an error + * (page == current->arch.old_guest_table) + * + * The partial_ref-on-error clause is worth an explanation. There + * are two scenarios where partial_ref might be true coming in: +- * - mfn has been partially demoted as type `type`; i.e. has +- * PGT_partial set ++ * - mfn has been partially promoted / demoted as type `type`; ++ * i.e. has PGT_partial set + * - mfn has been partially demoted as L(type+1) (i.e., a linear + * page; e.g. we're being called from get_page_from_l2e with + * type == PGT_l1_table, but the mfn is PGT_l2_table) +@@ -671,7 +678,8 @@ static int get_page_and_type_from_mfn( + */ + if ( likely(!rc) || partial_ref ) + /* nothing */; +- else if ( page == current->arch.old_guest_table ) ++ else if ( page == current->arch.old_guest_table || ++ (retain_ref && rc == -ERESTART) ) + ASSERT(preemptible); + else + put_page(page); +@@ -1348,8 +1356,8 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, + if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == + PTF_partial_set ) + { +- ASSERT(!(flags & PTF_defer)); +- rc = _put_page_type(pg, PTF_preemptible, ptpg); ++ /* partial_set should always imply partial_ref */ ++ BUG(); + } + else if ( flags & PTF_defer ) + { +@@ -1394,8 +1402,8 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, + if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == + PTF_partial_set ) + { +- ASSERT(!(flags & PTF_defer)); +- return _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); ++ /* partial_set should always imply partial_ref */ ++ BUG(); + } + + if ( flags & PTF_defer ) +@@ -1425,8 +1433,8 @@ static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, + if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == + PTF_partial_set ) + { +- ASSERT(!(flags & PTF_defer)); +- return _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); ++ /* partial_set should always imply partial_ref */ ++ BUG(); + } + + if ( flags & PTF_defer ) +@@ -1550,13 +1558,22 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) + (rc = get_page_from_l2e(pl2e[i], pfn, d, partial_flags)) > 0 ) + continue; + +- if ( rc == -ERESTART ) +- { +- page->nr_validated_ptes = i; +- /* Set 'set', retain 'general ref' */ +- page->partial_flags = partial_flags | PTF_partial_set; +- } +- else if ( rc == -EINTR && i ) ++ /* ++ * It shouldn't be possible for get_page_from_l2e to return ++ * -ERESTART, since we never call this with PTF_preemptible. ++ * (alloc_l1_table may return -EINTR on an L1TF-vulnerable ++ * entry.) ++ * ++ * NB that while on a "clean" promotion, we can never get ++ * PGT_partial. It is possible to arrange for an l2e to ++ * contain a partially-devalidated l2; but in that case, both ++ * of the following functions will fail anyway (the first ++ * because the page in question is not an l1; the second ++ * because the page is not fully validated). ++ */ ++ ASSERT(rc != -ERESTART); ++ ++ if ( rc == -EINTR && i ) + { + page->nr_validated_ptes = i; + page->partial_flags = 0; +@@ -1565,6 +1582,7 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) + else if ( rc < 0 && rc != -EINTR ) + { + gdprintk(XENLOG_WARNING, "Failure in alloc_l2_table: slot %#x\n", i); ++ ASSERT(current->arch.old_guest_table == NULL); + if ( i ) + { + page->nr_validated_ptes = i; +@@ -1621,16 +1639,17 @@ static int alloc_l3_table(struct page_info *page) + rc = get_page_and_type_from_mfn( + l3e_get_mfn(pl3e[i]), + PGT_l2_page_table | PGT_pae_xen_l2, d, +- partial_flags | PTF_preemptible); ++ partial_flags | PTF_preemptible | PTF_retain_ref_on_restart); + } +- else if ( (rc = get_page_from_l3e(pl3e[i], pfn, d, partial_flags)) > 0 ) ++ else if ( (rc = get_page_from_l3e(pl3e[i], pfn, d, ++ partial_flags | PTF_retain_ref_on_restart)) > 0 ) + continue; + + if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; + /* Set 'set', leave 'general ref' set if this entry was set */ +- page->partial_flags = partial_flags | PTF_partial_set; ++ page->partial_flags = PTF_partial_set | PTF_partial_general_ref; + } + else if ( rc == -EINTR && i ) + { +@@ -1791,14 +1810,15 @@ static int alloc_l4_table(struct page_info *page) + i++, partial_flags = 0 ) + { + if ( !is_guest_l4_slot(d, i) || +- (rc = get_page_from_l4e(pl4e[i], pfn, d, partial_flags)) > 0 ) ++ (rc = get_page_from_l4e(pl4e[i], pfn, d, ++ partial_flags | PTF_retain_ref_on_restart)) > 0 ) + continue; + + if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; + /* Set 'set', leave 'general ref' set if this entry was set */ +- page->partial_flags = partial_flags | PTF_partial_set; ++ page->partial_flags = PTF_partial_set | PTF_partial_general_ref; + } + else if ( rc < 0 ) + { +@@ -1896,9 +1916,7 @@ static int free_l2_table(struct page_info *page) + else if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; +- page->partial_flags = (partial_flags & PTF_partial_set) ? +- partial_flags : +- (PTF_partial_set | PTF_partial_general_ref); ++ page->partial_flags = PTF_partial_set | PTF_partial_general_ref; + } + else if ( rc == -EINTR && i < L2_PAGETABLE_ENTRIES - 1 ) + { +@@ -1946,9 +1964,7 @@ static int free_l3_table(struct page_info *page) + if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; +- page->partial_flags = (partial_flags & PTF_partial_set) ? +- partial_flags : +- (PTF_partial_set | PTF_partial_general_ref); ++ page->partial_flags = PTF_partial_set | PTF_partial_general_ref; + } + else if ( rc == -EINTR && i < L3_PAGETABLE_ENTRIES - 1 ) + { +@@ -1979,9 +1995,7 @@ static int free_l4_table(struct page_info *page) + if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; +- page->partial_flags = (partial_flags & PTF_partial_set) ? +- partial_flags : +- (PTF_partial_set | PTF_partial_general_ref); ++ page->partial_flags = PTF_partial_set | PTF_partial_general_ref; + } + else if ( rc == -EINTR && i < L4_PAGETABLE_ENTRIES - 1 ) + { +diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h +index 46cba52941..dc9cb869dd 100644 +--- a/xen/include/asm-x86/mm.h ++++ b/xen/include/asm-x86/mm.h +@@ -238,22 +238,25 @@ struct page_info + * page. + * + * This happens: +- * - During de-validation, if de-validation of the page was ++ * - During validation or de-validation, if the operation was + * interrupted + * - During validation, if an invalid entry is encountered and + * validation is preemptible + * - During validation, if PTF_partial_general_ref was set on +- * this entry to begin with (perhaps because we're picking +- * up from a partial de-validation). ++ * this entry to begin with (perhaps because it picked up a ++ * previous operation) + * +- * When resuming validation, if PTF_partial_general_ref is clear, +- * then a general reference must be re-acquired; if it is set, no +- * reference should be acquired. ++ * When resuming validation, if PTF_partial_general_ref is ++ * clear, then a general reference must be re-acquired; if it ++ * is set, no reference should be acquired. + * + * When resuming de-validation, if PTF_partial_general_ref is + * clear, no reference should be dropped; if it is set, a + * reference should be dropped. + * ++ * NB at the moment, PTF_partial_set should be set if and only if ++ * PTF_partial_general_ref is set. ++ * + * NB that PTF_partial_set and PTF_partial_general_ref are + * defined in mm.c, the only place where they are used. + * +-- +2.23.0 + diff --git a/xsa299-4.11-0008-x86-mm-Collapse-PTF_partial_set-and-PTF_partial_gene.patch b/xsa299-4.11-0008-x86-mm-Collapse-PTF_partial_set-and-PTF_partial_gene.patch new file mode 100644 index 0000000..dd847e8 --- /dev/null +++ b/xsa299-4.11-0008-x86-mm-Collapse-PTF_partial_set-and-PTF_partial_gene.patch @@ -0,0 +1,227 @@ +From 6811df7fb7a1d4bb5a75fec9cf41519b5c86c605 Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Thu, 10 Oct 2019 17:57:49 +0100 +Subject: [PATCH 08/11] x86/mm: Collapse PTF_partial_set and + PTF_partial_general_ref into one + +...now that they are equivalent. No functional change intended. + +Reported-by: George Dunlap +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +--- + xen/arch/x86/mm.c | 50 +++++++++++----------------------------- + xen/include/asm-x86/mm.h | 29 +++++++++++------------ + 2 files changed, 26 insertions(+), 53 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 68a9e74002..4970b19aff 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -612,13 +612,12 @@ static int _get_page_type(struct page_info *page, unsigned long type, + + /* + * The following flags are used to specify behavior of various get and +- * put commands. The first two are also stored in page->partial_flags +- * to indicate the state of the page pointed to by ++ * put commands. The first is also stored in page->partial_flags to ++ * indicate the state of the page pointed to by + * page->pte[page->nr_validated_entries]. See the comment in mm.h for + * more information. + */ + #define PTF_partial_set (1 << 0) +-#define PTF_partial_general_ref (1 << 1) + #define PTF_preemptible (1 << 2) + #define PTF_defer (1 << 3) + #define PTF_retain_ref_on_restart (1 << 4) +@@ -630,13 +629,10 @@ static int get_page_and_type_from_mfn( + struct page_info *page = mfn_to_page(mfn); + int rc; + bool preemptible = flags & PTF_preemptible, +- partial_ref = flags & PTF_partial_general_ref, + partial_set = flags & PTF_partial_set, + retain_ref = flags & PTF_retain_ref_on_restart; + +- ASSERT(partial_ref == partial_set); +- +- if ( likely(!partial_ref) && ++ if ( likely(!partial_set) && + unlikely(!get_page_from_mfn(mfn, d)) ) + return -EINVAL; + +@@ -646,14 +642,14 @@ static int get_page_and_type_from_mfn( + * Retain the refcount if: + * - page is fully validated (rc == 0) + * - page is not validated (rc < 0) but: +- * - We came in with a reference (partial_ref) ++ * - We came in with a reference (partial_set) + * - page is partially validated (rc == -ERESTART), and the + * caller has asked the ref to be retained in that case + * - page is partially validated but there's been an error + * (page == current->arch.old_guest_table) + * +- * The partial_ref-on-error clause is worth an explanation. There +- * are two scenarios where partial_ref might be true coming in: ++ * The partial_set-on-error clause is worth an explanation. There ++ * are two scenarios where partial_set might be true coming in: + * - mfn has been partially promoted / demoted as type `type`; + * i.e. has PGT_partial set + * - mfn has been partially demoted as L(type+1) (i.e., a linear +@@ -676,7 +672,7 @@ static int get_page_and_type_from_mfn( + * count retained unless we succeeded, or the operation was + * preemptible. + */ +- if ( likely(!rc) || partial_ref ) ++ if ( likely(!rc) || partial_set ) + /* nothing */; + else if ( page == current->arch.old_guest_table || + (retain_ref && rc == -ERESTART) ) +@@ -1353,13 +1349,7 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, + struct page_info *pg = l2e_get_page(l2e); + struct page_info *ptpg = mfn_to_page(_mfn(pfn)); + +- if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == +- PTF_partial_set ) +- { +- /* partial_set should always imply partial_ref */ +- BUG(); +- } +- else if ( flags & PTF_defer ) ++ if ( flags & PTF_defer ) + { + current->arch.old_guest_ptpg = ptpg; + current->arch.old_guest_table = pg; +@@ -1399,13 +1389,6 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, + + pg = l3e_get_page(l3e); + +- if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == +- PTF_partial_set ) +- { +- /* partial_set should always imply partial_ref */ +- BUG(); +- } +- + if ( flags & PTF_defer ) + { + current->arch.old_guest_ptpg = mfn_to_page(_mfn(pfn)); +@@ -1430,13 +1413,6 @@ static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, + { + struct page_info *pg = l4e_get_page(l4e); + +- if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == +- PTF_partial_set ) +- { +- /* partial_set should always imply partial_ref */ +- BUG(); +- } +- + if ( flags & PTF_defer ) + { + current->arch.old_guest_ptpg = mfn_to_page(_mfn(pfn)); +@@ -1649,7 +1625,7 @@ static int alloc_l3_table(struct page_info *page) + { + page->nr_validated_ptes = i; + /* Set 'set', leave 'general ref' set if this entry was set */ +- page->partial_flags = PTF_partial_set | PTF_partial_general_ref; ++ page->partial_flags = PTF_partial_set; + } + else if ( rc == -EINTR && i ) + { +@@ -1818,7 +1794,7 @@ static int alloc_l4_table(struct page_info *page) + { + page->nr_validated_ptes = i; + /* Set 'set', leave 'general ref' set if this entry was set */ +- page->partial_flags = PTF_partial_set | PTF_partial_general_ref; ++ page->partial_flags = PTF_partial_set; + } + else if ( rc < 0 ) + { +@@ -1916,7 +1892,7 @@ static int free_l2_table(struct page_info *page) + else if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; +- page->partial_flags = PTF_partial_set | PTF_partial_general_ref; ++ page->partial_flags = PTF_partial_set; + } + else if ( rc == -EINTR && i < L2_PAGETABLE_ENTRIES - 1 ) + { +@@ -1964,7 +1940,7 @@ static int free_l3_table(struct page_info *page) + if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; +- page->partial_flags = PTF_partial_set | PTF_partial_general_ref; ++ page->partial_flags = PTF_partial_set; + } + else if ( rc == -EINTR && i < L3_PAGETABLE_ENTRIES - 1 ) + { +@@ -1995,7 +1971,7 @@ static int free_l4_table(struct page_info *page) + if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; +- page->partial_flags = PTF_partial_set | PTF_partial_general_ref; ++ page->partial_flags = PTF_partial_set; + } + else if ( rc == -EINTR && i < L4_PAGETABLE_ENTRIES - 1 ) + { +diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h +index dc9cb869dd..c6ba9e4d73 100644 +--- a/xen/include/asm-x86/mm.h ++++ b/xen/include/asm-x86/mm.h +@@ -233,7 +233,7 @@ struct page_info + * operation on the current page. (That page may or may not + * still have PGT_partial set.) + * +- * If PTF_partial_general_ref is set, then the PTE at ++ * Additionally, if PTF_partial_set is set, then the PTE at + * @nr_validated_ptef holds a general reference count for the + * page. + * +@@ -242,23 +242,20 @@ struct page_info + * interrupted + * - During validation, if an invalid entry is encountered and + * validation is preemptible +- * - During validation, if PTF_partial_general_ref was set on +- * this entry to begin with (perhaps because it picked up a ++ * - During validation, if PTF_partial_set was set on this ++ * entry to begin with (perhaps because it picked up a + * previous operation) + * +- * When resuming validation, if PTF_partial_general_ref is +- * clear, then a general reference must be re-acquired; if it +- * is set, no reference should be acquired. ++ * When resuming validation, if PTF_partial_set is clear, then ++ * a general reference must be re-acquired; if it is set, no ++ * reference should be acquired. + * +- * When resuming de-validation, if PTF_partial_general_ref is +- * clear, no reference should be dropped; if it is set, a +- * reference should be dropped. ++ * When resuming de-validation, if PTF_partial_set is clear, ++ * no reference should be dropped; if it is set, a reference ++ * should be dropped. + * +- * NB at the moment, PTF_partial_set should be set if and only if +- * PTF_partial_general_ref is set. +- * +- * NB that PTF_partial_set and PTF_partial_general_ref are +- * defined in mm.c, the only place where they are used. ++ * NB that PTF_partial_set is defined in mm.c, the only place ++ * where it is used. + * + * The 3rd field, @linear_pt_count, indicates + * - by a positive value, how many same-level page table entries a page +@@ -268,8 +265,8 @@ struct page_info + */ + struct { + u16 nr_validated_ptes:PAGETABLE_ORDER + 1; +- u16 :16 - PAGETABLE_ORDER - 1 - 2; +- u16 partial_flags:2; ++ u16 :16 - PAGETABLE_ORDER - 1 - 1; ++ u16 partial_flags:1; + s16 linear_pt_count; + }; + +-- +2.23.0 + diff --git a/xsa299-4.11-0009-x86-mm-Properly-handle-linear-pagetable-promotion-fa.patch b/xsa299-4.11-0009-x86-mm-Properly-handle-linear-pagetable-promotion-fa.patch new file mode 100644 index 0000000..f62d774 --- /dev/null +++ b/xsa299-4.11-0009-x86-mm-Properly-handle-linear-pagetable-promotion-fa.patch @@ -0,0 +1,106 @@ +From a6098b8920b02149220641cb13358e9012b5fc4d Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Thu, 10 Oct 2019 17:57:49 +0100 +Subject: [PATCH 09/11] x86/mm: Properly handle linear pagetable promotion + failures + +In order to allow recursive pagetable promotions and demotions to be +interrupted, Xen must keep track of the state of the sub-pages +promoted or demoted. This is stored in two elements in the page +struct: nr_entries_validated and partial_flags. + +The rule is that entries [0, nr_entries_validated) should always be +validated and hold a general reference count. If partial_flags is +zero, then [nr_entries_validated] is not validated and no reference +count is held. If PTF_partial_set is set, then [nr_entries_validated] +is partially validated, and a general reference count is held. + +Unfortunately, in cases where an entry began with PTF_partial_set set, +and get_page_from_lNe() returns -EINVAL, the PTF_partial_set bit is +erroneously dropped. (This scenario can be engineered mainly by the +use of interleaving of promoting and demoting a page which has "linear +pagetable" entries; see the appendix for a sketch.) This means that +we will "leak" a general reference count on the page in question, +preventing the page from being freed. + +Fix this by setting page->partial_flags to the partial_flags local +variable. + +This is part of XSA-299. + +Reported-by: George Dunlap +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +----- +Appendix + +Suppose A and B can both be promoted to L2 pages, and A[x] points to B. + +V1: PIN_L2 B. + B.type_count = 1 | PGT_validated + B.count = 2 | PGC_allocated + +V1: MOD_L3_ENTRY pointing something to A. + In the process of validating A[x], grab an extra type / ref on B: + B.type_count = 2 | PGT_validated + B.count = 3 | PGC_allocated + A.type_count = 1 | PGT_validated + A.count = 2 | PGC_allocated + +V1: UNPIN B. + B.type_count = 1 | PGT_validate + B.count = 2 | PGC_allocated + +V1: MOD_L3_ENTRY removing the reference to A. + De-validate A, down to A[x], which points to B. + Drop the final type on B. Arrange to be interrupted. + B.type_count = 1 | PGT_partial + B.count = 2 | PGC_allocated + A.type_count = 1 | PGT_partial + A.nr_validated_entries = x + A.partial_pte = -1 + +V2: MOD_L3_ENTRY adds a reference to A. + +At this point, get_page_from_l2e(A[x]) tries +get_page_and_type_from_mfn(), which fails because it's the wrong type; +and get_l2_linear_pagetable() also fails, because B isn't validated as +an l2 anymore. +--- + xen/arch/x86/mm.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 4970b19aff..cfb7538403 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -1562,7 +1562,7 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) + if ( i ) + { + page->nr_validated_ptes = i; +- page->partial_flags = 0; ++ page->partial_flags = partial_flags; + current->arch.old_guest_ptpg = NULL; + current->arch.old_guest_table = page; + } +@@ -1647,7 +1647,7 @@ static int alloc_l3_table(struct page_info *page) + if ( i ) + { + page->nr_validated_ptes = i; +- page->partial_flags = 0; ++ page->partial_flags = partial_flags; + current->arch.old_guest_ptpg = NULL; + current->arch.old_guest_table = page; + } +@@ -1804,7 +1804,7 @@ static int alloc_l4_table(struct page_info *page) + if ( i ) + { + page->nr_validated_ptes = i; +- page->partial_flags = 0; ++ page->partial_flags = partial_flags; + if ( rc == -EINTR ) + rc = -ERESTART; + else +-- +2.23.0 + diff --git a/xsa299-4.11-0010-x86-mm-Fix-nested-de-validation-on-error.patch b/xsa299-4.11-0010-x86-mm-Fix-nested-de-validation-on-error.patch new file mode 100644 index 0000000..643ef53 --- /dev/null +++ b/xsa299-4.11-0010-x86-mm-Fix-nested-de-validation-on-error.patch @@ -0,0 +1,169 @@ +From eabd77b59f4006128501d6e15f9e620dfb349420 Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Thu, 10 Oct 2019 17:57:49 +0100 +Subject: [PATCH 10/11] x86/mm: Fix nested de-validation on error + +If an invalid entry is discovered when validating a page-table tree, +the entire tree which has so far been validated must be de-validated. +Since this may take a long time, alloc_l[2-4]_table() set current +vcpu's old_guest_table immediately; put_old_guest_table() will make +sure that put_page_type() will be called to finish off the +de-validation before any other MMU operations can happen on the vcpu. + +The invariant for partial pages should be: + +* Entries [0, nr_validated_ptes) should be completely validated; + put_page_type() will de-validate these. + +* If [nr_validated_ptes] is partially validated, partial_flags should + set PTF_partiaL_set. put_page_type() will be called on this page to + finish off devalidation, and the appropriate refcount adjustments + will be done. + +alloc_l[2-3]_table() indicates partial validation to its callers by +setting current->old_guest_table. + +Unfortunately, this is mishandled. + +Take the case where validating lNe[x] returns an error. + +First, alloc_l3_table() doesn't check old_guest_table at all; as a +result, partial_flags is not set when it should be. nr_validated_ptes +is set to x; and since PFT_partial_set clear, de-validation resumes at +nr_validated_ptes-1. This means that the l2 page at pl3e[x] will not +have put_page_type() called on it when de-validating the rest of the +l3: it will be stuck in the PGT_partial state until the domain is +destroyed, or until it is re-used as an l2. (Any other page type will +fail.) + +Worse, alloc_l4_table(), rather than setting PTF_partial_set as it +should, sets nr_validated_ptes to x+1. When de-validating, since +partial is 0, this will correctly resume calling put_page_type at [x]; +but, if the put_page_type() is never called, but instead +get_page_type() is called, validation will pick up at [x+1], +neglecting to validate [x]. If the rest of the validation succeeds, +the l4 will be validated even though [x] is invalid. + +Fix this in both cases by setting PTF_partial_set if old_guest_table +is set. + +While here, add some safety catches: +- old_guest_table must point to the page contained in + [nr_validated_ptes]. +- alloc_l1_page shouldn't set old_guest_table + +If we experience one of these situations in production builds, it's +safer to avoid calling put_page_type for the pages in question. If +they have PGT_partial set, they will be cleaned up on domain +destruction; if not, we have no idea whether a type count is safe to +drop. Retaining an extra type ref that should have been dropped may +trigger a BUG() on the free_domain_page() path, but dropping a type +count that shouldn't be dropped may cause a privilege escalation. + +This is part of XSA-299. + +Reported-by: George Dunlap +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +--- + xen/arch/x86/mm.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 54 insertions(+), 1 deletion(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index cfb7538403..aa03cb8b40 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -1561,6 +1561,20 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) + ASSERT(current->arch.old_guest_table == NULL); + if ( i ) + { ++ /* ++ * alloc_l1_table() doesn't set old_guest_table; it does ++ * its own tear-down immediately on failure. If it ++ * did we'd need to check it and set partial_flags as we ++ * do in alloc_l[34]_table(). ++ * ++ * Note on the use of ASSERT: if it's non-null and ++ * hasn't been cleaned up yet, it should have ++ * PGT_partial set; and so the type will be cleaned up ++ * on domain destruction. Unfortunately, we would ++ * leak the general ref held by old_guest_table; but ++ * leaking a page is less bad than a host crash. ++ */ ++ ASSERT(current->arch.old_guest_table == NULL); + page->nr_validated_ptes = i; + page->partial_flags = partial_flags; + current->arch.old_guest_ptpg = NULL; +@@ -1588,6 +1602,7 @@ static int alloc_l3_table(struct page_info *page) + unsigned int i; + int rc = 0; + unsigned int partial_flags = page->partial_flags; ++ l3_pgentry_t l3e = l3e_empty(); + + pl3e = map_domain_page(_mfn(pfn)); + +@@ -1634,7 +1649,11 @@ static int alloc_l3_table(struct page_info *page) + rc = -ERESTART; + } + if ( rc < 0 ) ++ { ++ /* XSA-299 Backport: Copy l3e for checking */ ++ l3e = pl3e[i]; + break; ++ } + + pl3e[i] = adjust_guest_l3e(pl3e[i], d); + } +@@ -1648,6 +1667,24 @@ static int alloc_l3_table(struct page_info *page) + { + page->nr_validated_ptes = i; + page->partial_flags = partial_flags; ++ if ( current->arch.old_guest_table ) ++ { ++ /* ++ * We've experienced a validation failure. If ++ * old_guest_table is set, "transfer" the general ++ * reference count to pl3e[nr_validated_ptes] by ++ * setting PTF_partial_set. ++ * ++ * As a precaution, check that old_guest_table is the ++ * page pointed to by pl3e[nr_validated_ptes]. If ++ * not, it's safer to leak a type ref on production ++ * builds. ++ */ ++ if ( current->arch.old_guest_table == l3e_get_page(l3e) ) ++ page->partial_flags = PTF_partial_set; ++ else ++ ASSERT_UNREACHABLE(); ++ } + current->arch.old_guest_ptpg = NULL; + current->arch.old_guest_table = page; + } +@@ -1810,7 +1847,23 @@ static int alloc_l4_table(struct page_info *page) + else + { + if ( current->arch.old_guest_table ) +- page->nr_validated_ptes++; ++ { ++ /* ++ * We've experienced a validation failure. If ++ * old_guest_table is set, "transfer" the general ++ * reference count to pl3e[nr_validated_ptes] by ++ * setting PTF_partial_set. ++ * ++ * As a precaution, check that old_guest_table is the ++ * page pointed to by pl4e[nr_validated_ptes]. If ++ * not, it's safer to leak a type ref on production ++ * builds. ++ */ ++ if ( current->arch.old_guest_table == l4e_get_page(pl4e[i]) ) ++ page->partial_flags = PTF_partial_set; ++ else ++ ASSERT_UNREACHABLE(); ++ } + current->arch.old_guest_ptpg = NULL; + current->arch.old_guest_table = page; + } +-- +2.23.0 + diff --git a/xsa299-4.11-0011-x86-mm-Don-t-drop-a-type-ref-unless-you-held-a-ref-t.patch b/xsa299-4.11-0011-x86-mm-Don-t-drop-a-type-ref-unless-you-held-a-ref-t.patch new file mode 100644 index 0000000..24970da --- /dev/null +++ b/xsa299-4.11-0011-x86-mm-Don-t-drop-a-type-ref-unless-you-held-a-ref-t.patch @@ -0,0 +1,413 @@ +From f0086e3ac65c8bcabb84c1c29ab00b0c8a187555 Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Thu, 10 Oct 2019 17:57:50 +0100 +Subject: [PATCH 11/11] x86/mm: Don't drop a type ref unless you held a ref to + begin with + +Validation and de-validation of pagetable trees may take arbitrarily +large amounts of time, and so must be preemptible. This is indicated +by setting the PGT_partial bit in the type_info, and setting +nr_validated_entries and partial_flags appropriately. Specifically, +if the entry at [nr_validated_entries] is partially validated, +partial_flags should have the PGT_partial_set bit set, and the entry +should hold a general reference count. During de-validation, +put_page_type() is called on partially validated entries. + +Unfortunately, there are a number of issues with the current algorithm. + +First, doing a "normal" put_page_type() is not safe when no type ref +is held: there is nothing to stop another vcpu from coming along and +picking up validation again: at which point the put_page_type may drop +the only page ref on an in-use page. Some examples are listed in the +appendix. + +The core issue is that put_page_type() is being called both to clean +up PGT_partial, and to drop a type count; and has no way of knowing +which is which; and so if in between, PGT_partial is cleared, +put_page_type() will drop the type ref erroneously. + +What is needed is to distinguish between two states: +- Dropping a type ref which is held +- Cleaning up a page which has been partially de/validated + +Fix this by telling put_page_type() which of the two activities you +intend. + +When cleaning up a partial de/validation, take no action unless you +find a page partially validated. + +If put_page_type() is called without PTF_partial_set, and finds the +page in a PGT_partial state anyway, then there's certainly been a +misaccounting somewhere, and carrying on would almost certainly cause +a security issue, so crash the host instead. + +In put_page_from_lNe, pass partial_flags on to _put_page_type(). + +old_guest_table may be set either with a fully validated page (when +using the "deferred put" pattern), or with a partially validated page +(when a normal "de-validation" is interrupted, or when a validation +fails part-way through due to invalid entries). Add a flag, +old_guest_table_partial, to indicate which of these it is, and use +that to pass the appropriate flag to _put_page_type(). + +While here, delete stray trailing whitespace. + +This is part of XSA-299. + +Reported-by: George Dunlap +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +----- +Appendix: + +Suppose page A, when interpreted as an l3 pagetable, contains all +valid entries; and suppose A[x] points to page B, which when +interpreted as an l2 pagetable, contains all valid entries. + +P1: PIN_L3_TABLE + A -> PGT_l3_table | 1 | valid + B -> PGT_l2_table | 1 | valid + +P1: UNPIN_TABLE + > Arrange to interrupt after B has been de-validated + B: + type_info -> PGT_l2_table | 0 + A: + type_info -> PGT_l3_table | 1 | partial + nr_validated_enties -> (less than x) + +P2: mod_l4_entry to point to A + > Arrange for this to be interrupted while B is being validated + B: + type_info -> PGT_l2_table | 1 | partial + (nr_validated_entires &c set as appropriate) + A: + type_info -> PGT_l3_table | 1 | partial + nr_validated_entries -> x + partial_pte = 1 + +P3: mod_l3_entry some other unrelated l3 to point to B: + B: + type_info -> PGT_l2_table | 1 + +P1: Restart UNPIN_TABLE + +At this point, since A.nr_validate_entries == x and A.partial_pte != +0, free_l3_table() will call put_page_from_l3e() on pl3e[x], dropping +its type count to 0 while it's still being pointed to by some other l3 + +A similar issue arises with old_guest_table. Consider the following +scenario: + +Suppose A is a page which, when interpreted as an l2, has valid entries +until entry x, which is invalid. + +V1: PIN_L2_TABLE(A) + + A -> PGT_l2_table | 1 | PGT_partial + V1 -> old_guest_table = A + + +V2: PIN_L2_TABLE(A) + + A -> PGT_l2_table | 1 | PGT_partial + V2 -> old_guest_table = A + + put_old_guest_table() + _put_page_type(A) + A -> PGT_l2_table | 0 + +V1: + put_old_guest_table() + _put_page_type(A) # UNDERFLOW + +Indeed, it is possible to engineer for old_guest_table for every vcpu +a guest has to point to the same page. +--- + xen/arch/x86/domain.c | 6 +++ + xen/arch/x86/mm.c | 99 +++++++++++++++++++++++++++++++----- + xen/include/asm-x86/domain.h | 4 +- + 3 files changed, 95 insertions(+), 14 deletions(-) + +diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c +index 8fbecbb169..c880568dd4 100644 +--- a/xen/arch/x86/domain.c ++++ b/xen/arch/x86/domain.c +@@ -1074,9 +1074,15 @@ int arch_set_info_guest( + rc = -ERESTART; + /* Fallthrough */ + case -ERESTART: ++ /* ++ * NB that we're putting the kernel-mode table ++ * here, which we've already successfully ++ * validated above; hence partial = false; ++ */ + v->arch.old_guest_ptpg = NULL; + v->arch.old_guest_table = + pagetable_get_page(v->arch.guest_table); ++ v->arch.old_guest_table_partial = false; + v->arch.guest_table = pagetable_null(); + break; + default: +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index aa03cb8b40..c701c7ef14 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -1353,10 +1353,11 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, + { + current->arch.old_guest_ptpg = ptpg; + current->arch.old_guest_table = pg; ++ current->arch.old_guest_table_partial = false; + } + else + { +- rc = _put_page_type(pg, PTF_preemptible, ptpg); ++ rc = _put_page_type(pg, flags | PTF_preemptible, ptpg); + if ( likely(!rc) ) + put_page(pg); + } +@@ -1379,6 +1380,7 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, + unsigned long mfn = l3e_get_pfn(l3e); + int writeable = l3e_get_flags(l3e) & _PAGE_RW; + ++ ASSERT(!(flags & PTF_partial_set)); + ASSERT(!(mfn & ((1UL << (L3_PAGETABLE_SHIFT - PAGE_SHIFT)) - 1))); + do { + put_data_page(mfn_to_page(_mfn(mfn)), writeable); +@@ -1391,12 +1393,14 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, + + if ( flags & PTF_defer ) + { ++ ASSERT(!(flags & PTF_partial_set)); + current->arch.old_guest_ptpg = mfn_to_page(_mfn(pfn)); + current->arch.old_guest_table = pg; ++ current->arch.old_guest_table_partial = false; + return 0; + } + +- rc = _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); ++ rc = _put_page_type(pg, flags | PTF_preemptible, mfn_to_page(_mfn(pfn))); + if ( likely(!rc) ) + put_page(pg); + +@@ -1415,12 +1419,15 @@ static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, + + if ( flags & PTF_defer ) + { ++ ASSERT(!(flags & PTF_partial_set)); + current->arch.old_guest_ptpg = mfn_to_page(_mfn(pfn)); + current->arch.old_guest_table = pg; ++ current->arch.old_guest_table_partial = false; + return 0; + } + +- rc = _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); ++ rc = _put_page_type(pg, flags | PTF_preemptible, ++ mfn_to_page(_mfn(pfn))); + if ( likely(!rc) ) + put_page(pg); + } +@@ -1525,6 +1532,14 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) + + pl2e = map_domain_page(_mfn(pfn)); + ++ /* ++ * NB that alloc_l2_table will never set partial_pte on an l2; but ++ * free_l2_table might if a linear_pagetable entry is interrupted ++ * partway through de-validation. In that circumstance, ++ * get_page_from_l2e() will always return -EINVAL; and we must ++ * retain the type ref by doing the normal partial_flags tracking. ++ */ ++ + for ( i = page->nr_validated_ptes; i < L2_PAGETABLE_ENTRIES; + i++, partial_flags = 0 ) + { +@@ -1579,6 +1594,7 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) + page->partial_flags = partial_flags; + current->arch.old_guest_ptpg = NULL; + current->arch.old_guest_table = page; ++ current->arch.old_guest_table_partial = true; + } + } + if ( rc < 0 ) +@@ -1681,12 +1697,16 @@ static int alloc_l3_table(struct page_info *page) + * builds. + */ + if ( current->arch.old_guest_table == l3e_get_page(l3e) ) ++ { ++ ASSERT(current->arch.old_guest_table_partial); + page->partial_flags = PTF_partial_set; ++ } + else + ASSERT_UNREACHABLE(); + } + current->arch.old_guest_ptpg = NULL; + current->arch.old_guest_table = page; ++ current->arch.old_guest_table_partial = true; + } + while ( i-- > 0 ) + pl3e[i] = unadjust_guest_l3e(pl3e[i], d); +@@ -1860,12 +1880,16 @@ static int alloc_l4_table(struct page_info *page) + * builds. + */ + if ( current->arch.old_guest_table == l4e_get_page(pl4e[i]) ) ++ { ++ ASSERT(current->arch.old_guest_table_partial); + page->partial_flags = PTF_partial_set; ++ } + else + ASSERT_UNREACHABLE(); + } + current->arch.old_guest_ptpg = NULL; + current->arch.old_guest_table = page; ++ current->arch.old_guest_table_partial = true; + } + } + } +@@ -2782,6 +2806,28 @@ static int _put_page_type(struct page_info *page, unsigned int flags, + x = y; + nx = x - 1; + ++ /* ++ * Is this expected to do a full reference drop, or only ++ * cleanup partial validation / devalidation? ++ * ++ * If the former, the caller must hold a "full" type ref; ++ * which means the page must be validated. If the page is ++ * *not* fully validated, continuing would almost certainly ++ * open up a security hole. An exception to this is during ++ * domain destruction, where PGT_validated can be dropped ++ * without dropping a type ref. ++ * ++ * If the latter, do nothing unless type PGT_partial is set. ++ * If it is set, the type count must be 1. ++ */ ++ if ( !(flags & PTF_partial_set) ) ++ BUG_ON((x & PGT_partial) || ++ !((x & PGT_validated) || page_get_owner(page)->is_dying)); ++ else if ( !(x & PGT_partial) ) ++ return 0; ++ else ++ BUG_ON((x & PGT_count_mask) != 1); ++ + ASSERT((x & PGT_count_mask) != 0); + + switch ( nx & (PGT_locked | PGT_count_mask) ) +@@ -3041,17 +3087,34 @@ int put_old_guest_table(struct vcpu *v) + if ( !v->arch.old_guest_table ) + return 0; + +- switch ( rc = _put_page_type(v->arch.old_guest_table, PTF_preemptible, +- v->arch.old_guest_ptpg) ) ++ rc = _put_page_type(v->arch.old_guest_table, ++ PTF_preemptible | ++ ( v->arch.old_guest_table_partial ? ++ PTF_partial_set : 0 ), ++ v->arch.old_guest_ptpg); ++ ++ if ( rc == -ERESTART || rc == -EINTR ) + { +- case -EINTR: +- case -ERESTART: ++ v->arch.old_guest_table_partial = (rc == -ERESTART); + return -ERESTART; +- case 0: +- put_page(v->arch.old_guest_table); + } + ++ /* ++ * It shouldn't be possible for _put_page_type() to return ++ * anything else at the moment; but if it does happen in ++ * production, leaking the type ref is probably the best thing to ++ * do. Either way, drop the general ref held by old_guest_table. ++ */ ++ ASSERT(rc == 0); ++ ++ put_page(v->arch.old_guest_table); + v->arch.old_guest_table = NULL; ++ v->arch.old_guest_ptpg = NULL; ++ /* ++ * Safest default if someone sets old_guest_table without ++ * explicitly setting old_guest_table_partial. ++ */ ++ v->arch.old_guest_table_partial = true; + + return rc; + } +@@ -3201,11 +3264,11 @@ int new_guest_cr3(mfn_t mfn) + switch ( rc = put_page_and_type_preemptible(page) ) + { + case -EINTR: +- rc = -ERESTART; +- /* fallthrough */ + case -ERESTART: + curr->arch.old_guest_ptpg = NULL; + curr->arch.old_guest_table = page; ++ curr->arch.old_guest_table_partial = (rc == -ERESTART); ++ rc = -ERESTART; + break; + default: + BUG_ON(rc); +@@ -3479,6 +3542,7 @@ long do_mmuext_op( + { + curr->arch.old_guest_ptpg = NULL; + curr->arch.old_guest_table = page; ++ curr->arch.old_guest_table_partial = false; + } + } + } +@@ -3513,6 +3577,11 @@ long do_mmuext_op( + case -ERESTART: + curr->arch.old_guest_ptpg = NULL; + curr->arch.old_guest_table = page; ++ /* ++ * EINTR means we still hold the type ref; ERESTART ++ * means PGT_partial holds the type ref ++ */ ++ curr->arch.old_guest_table_partial = (rc == -ERESTART); + rc = 0; + break; + default: +@@ -3581,11 +3650,15 @@ long do_mmuext_op( + switch ( rc = put_page_and_type_preemptible(page) ) + { + case -EINTR: +- rc = -ERESTART; +- /* fallthrough */ + case -ERESTART: + curr->arch.old_guest_ptpg = NULL; + curr->arch.old_guest_table = page; ++ /* ++ * EINTR means we still hold the type ref; ++ * ERESTART means PGT_partial holds the ref ++ */ ++ curr->arch.old_guest_table_partial = (rc == -ERESTART); ++ rc = -ERESTART; + break; + default: + BUG_ON(rc); +diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h +index 1ac5a96c08..360c38bd83 100644 +--- a/xen/include/asm-x86/domain.h ++++ b/xen/include/asm-x86/domain.h +@@ -309,7 +309,7 @@ struct arch_domain + + struct paging_domain paging; + struct p2m_domain *p2m; +- /* To enforce lock ordering in the pod code wrt the ++ /* To enforce lock ordering in the pod code wrt the + * page_alloc lock */ + int page_alloc_unlock_level; + +@@ -542,6 +542,8 @@ struct arch_vcpu + struct page_info *old_guest_table; /* partially destructed pagetable */ + struct page_info *old_guest_ptpg; /* containing page table of the */ + /* former, if any */ ++ bool old_guest_table_partial; /* Are we dropping a type ref, or just ++ * finishing up a partial de-validation? */ + /* guest_table holds a ref to the page, and also a type-count unless + * shadow refcounts are in use */ + pagetable_t shadow_table[4]; /* (MFN) shadow(s) of guest */ +-- +2.23.0 + diff --git a/xsa301-4.11-1.patch b/xsa301-4.11-1.patch new file mode 100644 index 0000000..4d528fe --- /dev/null +++ b/xsa301-4.11-1.patch @@ -0,0 +1,80 @@ +From 21dfe8f707febd62869d4ebbaa155736870bebec Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Wed, 2 Oct 2019 12:06:50 +0100 +Subject: [PATCH 1/3] xen/arm: p2m: Avoid aliasing guest physical frame + +The P2M helpers implementation is quite lax and will end up to ignore +the unused top bits of a guest physical frame. + +This effectively means that p2m_set_entry() will create a mapping for a +different frame (it is always equal to gfn & (mask unused bits)). Yet +p2m->max_mapped_gfn will be updated using the original frame. + +At the moment, p2m_get_entry() and p2m_resolve_translation_fault() +assume that p2m_get_root_pointer() will always return a non-NULL pointer +when the GFN is smaller than p2m->max_mapped_gfn. + +Unfortunately, because of the aliasing described above, it would be +possible to set p2m->max_mapped_gfn high enough so it covers frame that +would lead p2m_get_root_pointer() to return NULL. + +As we don't sanity check the guest physical frame provided by a guest, a +malicious guest could craft a series of hypercalls that will hit the +BUG_ON() and therefore DoS Xen. + +To prevent aliasing, the function p2m_get_root_pointer() is now reworked +to return NULL If any of the unused top bits are not zero. The caller +can then decide what's the appropriate action to do. Since the two paths +(i.e. P2M_ROOT_PAGES == 1 and P2M_ROOT_PAGES != 1) are now very +similarly, take the opportunity to consolidate them making the code a +bit simpler. + +With this change, p2m_get_entry() will not try to insert a mapping as +the root pointer is invalid. + +Note that root_table is now switch to unsigned long as unsigned int is +not enough to hold part of a GFN. + +This is part of XSA-301. + +Reported-by: Julien Grall +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +--- + xen/arch/arm/p2m.c | 17 +++++------------ + 1 file changed, 5 insertions(+), 12 deletions(-) + +diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c +index d43c3aa896..3967ee7306 100644 +--- a/xen/arch/arm/p2m.c ++++ b/xen/arch/arm/p2m.c +@@ -177,21 +177,14 @@ void p2m_tlb_flush_sync(struct p2m_domain *p2m) + static lpae_t *p2m_get_root_pointer(struct p2m_domain *p2m, + gfn_t gfn) + { +- unsigned int root_table; +- +- if ( P2M_ROOT_PAGES == 1 ) +- return __map_domain_page(p2m->root); ++ unsigned long root_table; + + /* +- * Concatenated root-level tables. The table number will be the +- * offset at the previous level. It is not possible to +- * concatenate a level-0 root. ++ * While the root table index is the offset from the previous level, ++ * we can't use (P2M_ROOT_LEVEL - 1) because the root level might be ++ * 0. Yet we still want to check if all the unused bits are zeroed. + */ +- ASSERT(P2M_ROOT_LEVEL > 0); +- +- root_table = gfn_x(gfn) >> (level_orders[P2M_ROOT_LEVEL - 1]); +- root_table &= LPAE_ENTRY_MASK; +- ++ root_table = gfn_x(gfn) >> (level_orders[P2M_ROOT_LEVEL] + LPAE_SHIFT); + if ( root_table >= P2M_ROOT_PAGES ) + return NULL; + +-- +2.11.0 + diff --git a/xsa301-4.11-2.patch b/xsa301-4.11-2.patch new file mode 100644 index 0000000..33b6150 --- /dev/null +++ b/xsa301-4.11-2.patch @@ -0,0 +1,92 @@ +From 4426d993b7ee0966fb39531dc5a269ce8493ca97 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Wed, 2 Oct 2019 12:35:59 +0100 +Subject: [PATCH 2/3] xen/arm: p2m: Avoid off-by-one check on + p2m->max_mapped_gfn + +The code base is using inconsistently the field p2m->max_mapped_gfn. +Some of the useres expect that p2m->max_guest_gfn contain the highest +mapped GFN while others expect highest + 1. + +p2m->max_guest_gfn is set as highest + 1, because of that the sanity +check on the GFN in p2m_resolved_translation_fault() and +p2m_get_entry() can be bypassed when GFN == p2m->max_guest_gfn. + +p2m_get_root_pointer(p2m->max_guest_gfn) may return NULL if it is +outside of address range supported and therefore the BUG_ON() could be +hit. + +The current value hold in p2m->max_mapped_gfn is inconsistent with the +expectation of the common code (see domain_get_maximum_gpfn()) and also +the documentation of the field. + +Rather than changing the check in p2m_translation_fault() and +p2m_get_entry(), p2m->max_mapped_gfn is now containing the highest +mapped GFN and the callers assuming "highest + 1" are now adjusted. + +Take the opportunity to use 1UL rather than 1 as page_order could +theoritically big enough to overflow a 32-bit integer. + +Lastly, the documentation of the field max_guest_gfn to reflect how it +is computed. + +This is part of XSA-301. + +Reported-by: Julien Grall +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +--- + xen/arch/arm/p2m.c | 6 +++--- + xen/include/asm-arm/p2m.h | 5 +---- + 2 files changed, 4 insertions(+), 7 deletions(-) + +diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c +index 3967ee7306..c7e049901d 100644 +--- a/xen/arch/arm/p2m.c ++++ b/xen/arch/arm/p2m.c +@@ -931,7 +931,7 @@ static int __p2m_set_entry(struct p2m_domain *p2m, + p2m_write_pte(entry, pte, p2m->clean_pte); + + p2m->max_mapped_gfn = gfn_max(p2m->max_mapped_gfn, +- gfn_add(sgfn, 1 << page_order)); ++ gfn_add(sgfn, (1UL << page_order) - 1)); + p2m->lowest_mapped_gfn = gfn_min(p2m->lowest_mapped_gfn, sgfn); + } + +@@ -1291,7 +1291,7 @@ int relinquish_p2m_mapping(struct domain *d) + p2m_write_lock(p2m); + + start = p2m->lowest_mapped_gfn; +- end = p2m->max_mapped_gfn; ++ end = gfn_add(p2m->max_mapped_gfn, 1); + + for ( ; gfn_x(start) < gfn_x(end); + start = gfn_next_boundary(start, order) ) +@@ -1356,7 +1356,7 @@ int p2m_cache_flush(struct domain *d, gfn_t start, unsigned long nr) + p2m_read_lock(p2m); + + start = gfn_max(start, p2m->lowest_mapped_gfn); +- end = gfn_min(end, p2m->max_mapped_gfn); ++ end = gfn_min(end, gfn_add(p2m->max_mapped_gfn, 1)); + + for ( ; gfn_x(start) < gfn_x(end); start = next_gfn ) + { +diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h +index 8823707c17..7f1f7e9109 100644 +--- a/xen/include/asm-arm/p2m.h ++++ b/xen/include/asm-arm/p2m.h +@@ -38,10 +38,7 @@ struct p2m_domain { + /* Current Translation Table Base Register for the p2m */ + uint64_t vttbr; + +- /* +- * Highest guest frame that's ever been mapped in the p2m +- * Only takes into account ram and foreign mapping +- */ ++ /* Highest guest frame that's ever been mapped in the p2m */ + gfn_t max_mapped_gfn; + + /* +-- +2.11.0 + diff --git a/xsa301-4.11-3.patch b/xsa301-4.11-3.patch new file mode 100644 index 0000000..55a701a --- /dev/null +++ b/xsa301-4.11-3.patch @@ -0,0 +1,49 @@ +From 61c73af08b4ede1fc8cfd2cf72661e6c7cfdbeaa Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Wed, 2 Oct 2019 10:55:07 +0100 +Subject: [PATCH 3/3] xen/arm: p2m: Don't check the return of + p2m_get_root_pointer() with BUG_ON() + +It turns out that the BUG_ON() was actually reachable with well-crafted +hypercalls. The BUG_ON() is here to prevent catch logical error, so +crashing Xen is a bit over the top. + +While all the holes should now be fixed, it would be better to downgrade +the BUG_ON() to something less fatal to prevent any more DoS. + +The BUG_ON() in p2m_get_entry() is now replaced by ASSERT_UNREACHABLE() +to catch mistake in debug build and return INVALID_MFN for production +build. The interface also requires to set page_order to give an idea of +the size of "hole". So 'level' is now set so we report a hole of size of +the an entry of the root page-table. This stays inline with what happen +when the GFN is higher than p2m->max_mapped_gfn. + +This is part of XSA-301. + +Reported-by: Julien Grall +Signed-off-by: Julien Grall +--- + xen/arch/arm/p2m.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c +index c7e049901d..af3515df42 100644 +--- a/xen/arch/arm/p2m.c ++++ b/xen/arch/arm/p2m.c +@@ -318,7 +318,12 @@ mfn_t p2m_get_entry(struct p2m_domain *p2m, gfn_t gfn, + * the table should always be non-NULL because the gfn is below + * p2m->max_mapped_gfn and the root table pages are always present. + */ +- BUG_ON(table == NULL); ++ if ( !table ) ++ { ++ ASSERT_UNREACHABLE(); ++ level = P2M_ROOT_LEVEL; ++ goto out; ++ } + + for ( level = P2M_ROOT_LEVEL; level < 3; level++ ) + { +-- +2.11.0 + diff --git a/xsa302-4.11-0001-IOMMU-add-missing-HVM-check.patch b/xsa302-4.11-0001-IOMMU-add-missing-HVM-check.patch new file mode 100644 index 0000000..c3d4435 --- /dev/null +++ b/xsa302-4.11-0001-IOMMU-add-missing-HVM-check.patch @@ -0,0 +1,37 @@ +From bbca29f88d9ad9c7e91125a3b5d5f13a23e5801f Mon Sep 17 00:00:00 2001 +From: Jan Beulich +Date: Wed, 2 Oct 2019 13:36:59 +0200 +Subject: [PATCH 1/2] IOMMU: add missing HVM check +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Fix an unguarded d->arch.hvm access in assign_device(). + +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné +Acked-by: Andrew Cooper + +(cherry picked from commit 41fd1009cd7416b73d745a77c24b4e8d1a296fe6) +Signed-off-by: Ian Jackson +--- + xen/drivers/passthrough/pci.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c +index f51cae7f4e..037aba7c94 100644 +--- a/xen/drivers/passthrough/pci.c ++++ b/xen/drivers/passthrough/pci.c +@@ -1416,7 +1416,8 @@ static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) + /* Prevent device assign if mem paging or mem sharing have been + * enabled for this domain */ + if ( unlikely(!need_iommu(d) && +- (d->arch.hvm_domain.mem_sharing_enabled || ++ ((is_hvm_domain(d) && ++ d->arch.hvm_domain.mem_sharing_enabled) || + vm_event_check_ring(d->vm_event_paging) || + p2m_get_hostp2m(d)->global_logdirty)) ) + return -EXDEV; +-- +2.11.0 + diff --git a/xsa302-4.11-0002-passthrough-quarantine-PCI-devices.patch b/xsa302-4.11-0002-passthrough-quarantine-PCI-devices.patch new file mode 100644 index 0000000..5204c9f --- /dev/null +++ b/xsa302-4.11-0002-passthrough-quarantine-PCI-devices.patch @@ -0,0 +1,498 @@ +From ec99857f59f7f06236f11ca8b0b2303e5e745cc4 Mon Sep 17 00:00:00 2001 +From: Paul Durrant +Date: Mon, 14 Oct 2019 17:52:59 +0100 +Subject: [PATCH 2/2] passthrough: quarantine PCI devices + +When a PCI device is assigned to an untrusted domain, it is possible for +that domain to program the device to DMA to an arbitrary address. The +IOMMU is used to protect the host from malicious DMA by making sure that +the device addresses can only target memory assigned to the guest. However, +when the guest domain is torn down the device is assigned back to dom0, +thus allowing any in-flight DMA to potentially target critical host data. + +This patch introduces a 'quarantine' for PCI devices using dom_io. When +the toolstack makes a device assignable (by binding it to pciback), it +will now also assign it to DOMID_IO and the device will only be assigned +back to dom0 when the device is made unassignable again. Whilst device is +assignable it will only ever transfer between dom_io and guest domains. +dom_io is actually only used as a sentinel domain for quarantining purposes; +it is not configured with any IOMMU mappings. Assignment to dom_io simply +means that the device's initiator (requestor) identifier is not present in +the IOMMU's device table and thus any DMA transactions issued will be +terminated with a fault condition. + +In addition, a fix to assignment handling is made for VT-d. Failure +during the assignment step should not lead to a device still being +associated with its prior owner. Hand the device to DomIO temporarily, +until the assignment step has completed successfully. Remove the PI +hooks from the source domain then earlier as well. + +Failure of the recovery reassign_device_ownership() may not go silent: +There e.g. may still be left over RMRR mappings in the domain assignment +to which has failed, and hence we can't allow that domain to continue +executing. + +NOTE: This patch also includes one printk() cleanup; the + "XEN_DOMCTL_assign_device: " tag is dropped in iommu_do_pci_domctl(), + since similar printk()-s elsewhere also don't log such a tag. + +This is XSA-302. + +Signed-off-by: Paul Durrant +Signed-off-by: Jan Beulich +Signed-off-by: Ian Jackson +--- + tools/libxl/libxl_pci.c | 25 +++++++++++- + xen/arch/x86/mm.c | 2 + + xen/common/domctl.c | 14 ++++++- + xen/drivers/passthrough/amd/pci_amd_iommu.c | 10 ++++- + xen/drivers/passthrough/iommu.c | 9 +++++ + xen/drivers/passthrough/pci.c | 59 ++++++++++++++++++++++------- + xen/drivers/passthrough/vtd/iommu.c | 40 ++++++++++++++++--- + xen/include/xen/pci.h | 3 ++ + 8 files changed, 138 insertions(+), 24 deletions(-) + +diff --git a/tools/libxl/libxl_pci.c b/tools/libxl/libxl_pci.c +index 4755a0c93c..81890a91ac 100644 +--- a/tools/libxl/libxl_pci.c ++++ b/tools/libxl/libxl_pci.c +@@ -754,6 +754,7 @@ static int libxl__device_pci_assignable_add(libxl__gc *gc, + libxl_device_pci *pcidev, + int rebind) + { ++ libxl_ctx *ctx = libxl__gc_owner(gc); + unsigned dom, bus, dev, func; + char *spath, *driver_path = NULL; + int rc; +@@ -779,7 +780,7 @@ static int libxl__device_pci_assignable_add(libxl__gc *gc, + } + if ( rc ) { + LOG(WARN, PCI_BDF" already assigned to pciback", dom, bus, dev, func); +- return 0; ++ goto quarantine; + } + + /* Check to see if there's already a driver that we need to unbind from */ +@@ -810,6 +811,19 @@ static int libxl__device_pci_assignable_add(libxl__gc *gc, + return ERROR_FAIL; + } + ++quarantine: ++ /* ++ * DOMID_IO is just a sentinel domain, without any actual mappings, ++ * so always pass XEN_DOMCTL_DEV_RDM_RELAXED to avoid assignment being ++ * unnecessarily denied. ++ */ ++ rc = xc_assign_device(ctx->xch, DOMID_IO, pcidev_encode_bdf(pcidev), ++ XEN_DOMCTL_DEV_RDM_RELAXED); ++ if ( rc < 0 ) { ++ LOG(ERROR, "failed to quarantine "PCI_BDF, dom, bus, dev, func); ++ return ERROR_FAIL; ++ } ++ + return 0; + } + +@@ -817,9 +831,18 @@ static int libxl__device_pci_assignable_remove(libxl__gc *gc, + libxl_device_pci *pcidev, + int rebind) + { ++ libxl_ctx *ctx = libxl__gc_owner(gc); + int rc; + char *driver_path; + ++ /* De-quarantine */ ++ rc = xc_deassign_device(ctx->xch, DOMID_IO, pcidev_encode_bdf(pcidev)); ++ if ( rc < 0 ) { ++ LOG(ERROR, "failed to de-quarantine "PCI_BDF, pcidev->domain, pcidev->bus, ++ pcidev->dev, pcidev->func); ++ return ERROR_FAIL; ++ } ++ + /* Unbind from pciback */ + if ( (rc=pciback_dev_is_assigned(gc, pcidev)) < 0 ) { + return ERROR_FAIL; +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index e6a4cb28f8..c1ab57f9a5 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -295,9 +295,11 @@ void __init arch_init_memory(void) + * Initialise our DOMID_IO domain. + * This domain owns I/O pages that are within the range of the page_info + * array. Mappings occur at the priv of the caller. ++ * Quarantined PCI devices will be associated with this domain. + */ + dom_io = domain_create(DOMID_IO, NULL); + BUG_ON(IS_ERR(dom_io)); ++ INIT_LIST_HEAD(&dom_io->arch.pdev_list); + + /* + * Initialise our COW domain. +diff --git a/xen/common/domctl.c b/xen/common/domctl.c +index 9b7bc083ee..741d774cd1 100644 +--- a/xen/common/domctl.c ++++ b/xen/common/domctl.c +@@ -392,6 +392,16 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) + + switch ( op->cmd ) + { ++ case XEN_DOMCTL_assign_device: ++ case XEN_DOMCTL_deassign_device: ++ if ( op->domain == DOMID_IO ) ++ { ++ d = dom_io; ++ break; ++ } ++ else if ( op->domain == DOMID_INVALID ) ++ return -ESRCH; ++ /* fall through */ + case XEN_DOMCTL_test_assign_device: + if ( op->domain == DOMID_INVALID ) + { +@@ -413,7 +423,7 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) + + if ( !domctl_lock_acquire() ) + { +- if ( d ) ++ if ( d && d != dom_io ) + rcu_unlock_domain(d); + return hypercall_create_continuation( + __HYPERVISOR_domctl, "h", u_domctl); +@@ -1148,7 +1158,7 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) + domctl_lock_release(); + + domctl_out_unlock_domonly: +- if ( d ) ++ if ( d && d != dom_io ) + rcu_unlock_domain(d); + + if ( copyback && __copy_to_guest(u_domctl, op, 1) ) +diff --git a/xen/drivers/passthrough/amd/pci_amd_iommu.c b/xen/drivers/passthrough/amd/pci_amd_iommu.c +index 12d2695b89..ec8baae717 100644 +--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c ++++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c +@@ -118,6 +118,10 @@ static void amd_iommu_setup_domain_device( + u8 bus = pdev->bus; + const struct domain_iommu *hd = dom_iommu(domain); + ++ /* dom_io is used as a sentinel for quarantined devices */ ++ if ( domain == dom_io ) ++ return; ++ + BUG_ON( !hd->arch.root_table || !hd->arch.paging_mode || + !iommu->dev_table.buffer ); + +@@ -305,6 +309,10 @@ void amd_iommu_disable_domain_device(struct domain *domain, + int req_id; + u8 bus = pdev->bus; + ++ /* dom_io is used as a sentinel for quarantined devices */ ++ if ( domain == dom_io ) ++ return; ++ + BUG_ON ( iommu->dev_table.buffer == NULL ); + req_id = get_dma_requestor_id(iommu->seg, PCI_BDF2(bus, devfn)); + dte = iommu->dev_table.buffer + (req_id * IOMMU_DEV_TABLE_ENTRY_SIZE); +@@ -391,7 +399,7 @@ static int amd_iommu_assign_device(struct domain *d, u8 devfn, + ivrs_mappings[req_id].read_permission); + } + +- return reassign_device(hardware_domain, d, devfn, pdev); ++ return reassign_device(pdev->domain, d, devfn, pdev); + } + + static void deallocate_next_page_table(struct page_info *pg, int level) +diff --git a/xen/drivers/passthrough/iommu.c b/xen/drivers/passthrough/iommu.c +index 04b0be37d3..8027d96f1c 100644 +--- a/xen/drivers/passthrough/iommu.c ++++ b/xen/drivers/passthrough/iommu.c +@@ -219,6 +219,9 @@ void iommu_teardown(struct domain *d) + { + const struct domain_iommu *hd = dom_iommu(d); + ++ if ( d == dom_io ) ++ return; ++ + d->need_iommu = 0; + hd->platform_ops->teardown(d); + tasklet_schedule(&iommu_pt_cleanup_tasklet); +@@ -229,6 +232,9 @@ int iommu_construct(struct domain *d) + if ( need_iommu(d) > 0 ) + return 0; + ++ if ( d == dom_io ) ++ return 0; ++ + if ( !iommu_use_hap_pt(d) ) + { + int rc; +@@ -404,6 +410,9 @@ int __init iommu_setup(void) + printk("I/O virtualisation %sabled\n", iommu_enabled ? "en" : "dis"); + if ( iommu_enabled ) + { ++ if ( iommu_domain_init(dom_io) ) ++ panic("Could not set up quarantine\n"); ++ + printk(" - Dom0 mode: %s\n", + iommu_passthrough ? "Passthrough" : + iommu_dom0_strict ? "Strict" : "Relaxed"); +diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c +index 037aba7c94..fb010a547b 100644 +--- a/xen/drivers/passthrough/pci.c ++++ b/xen/drivers/passthrough/pci.c +@@ -1389,19 +1389,29 @@ static int iommu_remove_device(struct pci_dev *pdev) + return hd->platform_ops->remove_device(pdev->devfn, pci_to_dev(pdev)); + } + +-/* +- * If the device isn't owned by the hardware domain, it means it already +- * has been assigned to other domain, or it doesn't exist. +- */ + static int device_assigned(u16 seg, u8 bus, u8 devfn) + { + struct pci_dev *pdev; ++ int rc = 0; + + pcidevs_lock(); +- pdev = pci_get_pdev_by_domain(hardware_domain, seg, bus, devfn); ++ ++ pdev = pci_get_pdev(seg, bus, devfn); ++ ++ if ( !pdev ) ++ rc = -ENODEV; ++ /* ++ * If the device exists and it is not owned by either the hardware ++ * domain or dom_io then it must be assigned to a guest, or be ++ * hidden (owned by dom_xen). ++ */ ++ else if ( pdev->domain != hardware_domain && ++ pdev->domain != dom_io ) ++ rc = -EBUSY; ++ + pcidevs_unlock(); + +- return pdev ? 0 : -EBUSY; ++ return rc; + } + + static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) +@@ -1415,7 +1425,8 @@ static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) + + /* Prevent device assign if mem paging or mem sharing have been + * enabled for this domain */ +- if ( unlikely(!need_iommu(d) && ++ if ( d != dom_io && ++ unlikely(!need_iommu(d) && + ((is_hvm_domain(d) && + d->arch.hvm_domain.mem_sharing_enabled) || + vm_event_check_ring(d->vm_event_paging) || +@@ -1432,12 +1443,20 @@ static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) + return rc; + } + +- pdev = pci_get_pdev_by_domain(hardware_domain, seg, bus, devfn); ++ pdev = pci_get_pdev(seg, bus, devfn); ++ ++ rc = -ENODEV; + if ( !pdev ) +- { +- rc = pci_get_pdev(seg, bus, devfn) ? -EBUSY : -ENODEV; + goto done; +- } ++ ++ rc = 0; ++ if ( d == pdev->domain ) ++ goto done; ++ ++ rc = -EBUSY; ++ if ( pdev->domain != hardware_domain && ++ pdev->domain != dom_io ) ++ goto done; + + if ( pdev->msix ) + msixtbl_init(d); +@@ -1460,6 +1479,10 @@ static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) + } + + done: ++ /* The device is assigned to dom_io so mark it as quarantined */ ++ if ( !rc && d == dom_io ) ++ pdev->quarantine = true; ++ + if ( !has_arch_pdevs(d) && need_iommu(d) ) + iommu_teardown(d); + pcidevs_unlock(); +@@ -1472,6 +1495,7 @@ int deassign_device(struct domain *d, u16 seg, u8 bus, u8 devfn) + { + const struct domain_iommu *hd = dom_iommu(d); + struct pci_dev *pdev = NULL; ++ struct domain *target; + int ret = 0; + + if ( !iommu_enabled || !hd->platform_ops ) +@@ -1482,12 +1506,16 @@ int deassign_device(struct domain *d, u16 seg, u8 bus, u8 devfn) + if ( !pdev ) + return -ENODEV; + ++ /* De-assignment from dom_io should de-quarantine the device */ ++ target = (pdev->quarantine && pdev->domain != dom_io) ? ++ dom_io : hardware_domain; ++ + while ( pdev->phantom_stride ) + { + devfn += pdev->phantom_stride; + if ( PCI_SLOT(devfn) != PCI_SLOT(pdev->devfn) ) + break; +- ret = hd->platform_ops->reassign_device(d, hardware_domain, devfn, ++ ret = hd->platform_ops->reassign_device(d, target, devfn, + pci_to_dev(pdev)); + if ( !ret ) + continue; +@@ -1498,7 +1526,7 @@ int deassign_device(struct domain *d, u16 seg, u8 bus, u8 devfn) + } + + devfn = pdev->devfn; +- ret = hd->platform_ops->reassign_device(d, hardware_domain, devfn, ++ ret = hd->platform_ops->reassign_device(d, target, devfn, + pci_to_dev(pdev)); + if ( ret ) + { +@@ -1508,6 +1536,9 @@ int deassign_device(struct domain *d, u16 seg, u8 bus, u8 devfn) + return ret; + } + ++ if ( pdev->domain == hardware_domain ) ++ pdev->quarantine = false; ++ + pdev->fault.count = 0; + + if ( !has_arch_pdevs(d) && need_iommu(d) ) +@@ -1686,7 +1717,7 @@ int iommu_do_pci_domctl( + ret = hypercall_create_continuation(__HYPERVISOR_domctl, + "h", u_domctl); + else if ( ret ) +- printk(XENLOG_G_ERR "XEN_DOMCTL_assign_device: " ++ printk(XENLOG_G_ERR + "assign %04x:%02x:%02x.%u to dom%d failed (%d)\n", + seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), + d->domain_id, ret); +diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c +index 4c719d4ee7..19f7d13013 100644 +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -1338,6 +1338,10 @@ int domain_context_mapping_one( + int agaw, rc, ret; + bool_t flush_dev_iotlb; + ++ /* dom_io is used as a sentinel for quarantined devices */ ++ if ( domain == dom_io ) ++ return 0; ++ + ASSERT(pcidevs_locked()); + spin_lock(&iommu->lock); + maddr = bus_to_context_maddr(iommu, bus); +@@ -1573,6 +1577,10 @@ int domain_context_unmap_one( + int iommu_domid, rc, ret; + bool_t flush_dev_iotlb; + ++ /* dom_io is used as a sentinel for quarantined devices */ ++ if ( domain == dom_io ) ++ return 0; ++ + ASSERT(pcidevs_locked()); + spin_lock(&iommu->lock); + +@@ -1705,6 +1713,10 @@ static int domain_context_unmap(struct domain *domain, u8 devfn, + goto out; + } + ++ /* dom_io is used as a sentinel for quarantined devices */ ++ if ( domain == dom_io ) ++ 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 +@@ -2389,6 +2401,15 @@ static int reassign_device_ownership( + if ( ret ) + return ret; + ++ if ( devfn == pdev->devfn ) ++ { ++ list_move(&pdev->domain_list, &dom_io->arch.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); + +@@ -2407,15 +2428,13 @@ static int reassign_device_ownership( + pdev->domain = target; + } + +- if ( !has_arch_pdevs(source) ) +- vmx_pi_hooks_deassign(source); +- + return ret; + } + + static int intel_iommu_assign_device( + struct domain *d, u8 devfn, struct pci_dev *pdev, u32 flag) + { ++ struct domain *s = pdev->domain; + struct acpi_rmrr_unit *rmrr; + int ret = 0, i; + u16 bdf, seg; +@@ -2458,8 +2477,8 @@ static int intel_iommu_assign_device( + } + } + +- ret = reassign_device_ownership(hardware_domain, d, devfn, pdev); +- if ( ret ) ++ ret = reassign_device_ownership(s, d, devfn, pdev); ++ if ( ret || d == dom_io ) + return ret; + + /* Setup rmrr identity mapping */ +@@ -2472,11 +2491,20 @@ static int intel_iommu_assign_device( + ret = rmrr_identity_mapping(d, 1, rmrr, flag); + if ( ret ) + { +- reassign_device_ownership(d, hardware_domain, devfn, pdev); ++ 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 %04x:%02x:%02x.%u from %pd (%d)\n", ++ seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), d, rc); ++ domain_crash(d); ++ } + break; + } + } +diff --git a/xen/include/xen/pci.h b/xen/include/xen/pci.h +index 4cfa774615..066364bdef 100644 +--- a/xen/include/xen/pci.h ++++ b/xen/include/xen/pci.h +@@ -88,6 +88,9 @@ struct pci_dev { + + nodeid_t node; /* NUMA node */ + ++ /* Device to be quarantined, don't automatically re-assign to dom0 */ ++ bool quarantine; ++ + enum pdev_type { + DEV_TYPE_PCI_UNKNOWN, + DEV_TYPE_PCIe_ENDPOINT, +-- +2.11.0 + diff --git a/xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch b/xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch new file mode 100644 index 0000000..afb1096 --- /dev/null +++ b/xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch @@ -0,0 +1,74 @@ +From c8cb33fa64c9ccbfa2a494a9dad2e0a763c09176 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Tue, 1 Oct 2019 13:07:53 +0100 +Subject: [PATCH 1/4] xen/arm32: entry: Split __DEFINE_ENTRY_TRAP in two + +The preprocessing macro __DEFINE_ENTRY_TRAP is used to generate trap +entry function. While the macro is fairly small today, follow-up patches +will increase the size signicantly. + +In general, assembly macros are more readable as they allow you to name +parameters and avoid '\'. So the actual implementation of the trap is +now switched to an assembly macro. + +This is part of XSA-303. + +Reported-by: Julien Grall +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +Reviewed-by: Andre Przywara +--- + xen/arch/arm/arm32/entry.S | 34 +++++++++++++++++++--------------- + 1 file changed, 19 insertions(+), 15 deletions(-) + +diff --git a/xen/arch/arm/arm32/entry.S b/xen/arch/arm/arm32/entry.S +index 0b4cd19abd..4a762e04f1 100644 +--- a/xen/arch/arm/arm32/entry.S ++++ b/xen/arch/arm/arm32/entry.S +@@ -126,24 +126,28 @@ abort_guest_exit_end: + skip_check: + mov pc, lr + +-/* +- * Macro to define trap entry. The iflags corresponds to the list of +- * interrupts (Asynchronous Abort, IRQ, FIQ) to unmask. +- */ ++ /* ++ * Macro to define trap entry. The iflags corresponds to the list of ++ * interrupts (Asynchronous Abort, IRQ, FIQ) to unmask. ++ */ ++ .macro vector trap, iflags ++ SAVE_ALL ++ cpsie \iflags ++ adr lr, return_from_trap ++ mov r0, sp ++ /* ++ * Save the stack pointer in r11. It will be restored after the ++ * trap has been handled (see return_from_trap). ++ */ ++ mov r11, sp ++ bic sp, #7 /* Align the stack pointer (noop on guest trap) */ ++ b do_trap_\trap ++ .endm ++ + #define __DEFINE_TRAP_ENTRY(trap, iflags) \ + ALIGN; \ + trap_##trap: \ +- SAVE_ALL; \ +- cpsie iflags; \ +- adr lr, return_from_trap; \ +- mov r0, sp; \ +- /* \ +- * Save the stack pointer in r11. It will be restored after the \ +- * trap has been handled (see return_from_trap). \ +- */ \ +- mov r11, sp; \ +- bic sp, #7; /* Align the stack pointer (noop on guest trap) */ \ +- b do_trap_##trap ++ vector trap, iflags + + /* Trap handler which unmask IRQ/Abort, keep FIQ masked */ + #define DEFINE_TRAP_ENTRY(trap) __DEFINE_TRAP_ENTRY(trap, ai) +-- +2.11.0 + diff --git a/xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch b/xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch new file mode 100644 index 0000000..35f9c04 --- /dev/null +++ b/xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch @@ -0,0 +1,97 @@ +From be7379207c83fa74f8a6c22a8ea213f02714776f Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Tue, 1 Oct 2019 13:15:48 +0100 +Subject: [PATCH 2/4] xen/arm32: entry: Fold the macro SAVE_ALL in the macro + vector + +Follow-up rework will require the macro vector to distinguish between +a trap from a guest vs while in the hypervisor. + +The macro SAVE_ALL already has code to distinguish between the two and +it is only called by the vector macro. So fold the former into the +latter. This will help to avoid duplicating the check. + +This is part of XSA-303. + +Reported-by: Julien Grall +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +Reviewed-by: Andre Przywara +--- + xen/arch/arm/arm32/entry.S | 46 +++++++++++++++++++++++----------------------- + 1 file changed, 23 insertions(+), 23 deletions(-) + +diff --git a/xen/arch/arm/arm32/entry.S b/xen/arch/arm/arm32/entry.S +index 4a762e04f1..150cbc0b4b 100644 +--- a/xen/arch/arm/arm32/entry.S ++++ b/xen/arch/arm/arm32/entry.S +@@ -13,27 +13,6 @@ + #define RESTORE_BANKED(mode) \ + RESTORE_ONE_BANKED(SP_##mode) ; RESTORE_ONE_BANKED(LR_##mode) ; RESTORE_ONE_BANKED(SPSR_##mode) + +-#define SAVE_ALL \ +- sub sp, #(UREGS_SP_usr - UREGS_sp); /* SP, LR, SPSR, PC */ \ +- push {r0-r12}; /* Save R0-R12 */ \ +- \ +- mrs r11, ELR_hyp; /* ELR_hyp is return address. */\ +- str r11, [sp, #UREGS_pc]; \ +- \ +- str lr, [sp, #UREGS_lr]; \ +- \ +- add r11, sp, #UREGS_kernel_sizeof+4; \ +- str r11, [sp, #UREGS_sp]; \ +- \ +- mrc CP32(r11, HSR); /* Save exception syndrome */ \ +- str r11, [sp, #UREGS_hsr]; \ +- \ +- mrs r11, SPSR_hyp; \ +- str r11, [sp, #UREGS_cpsr]; \ +- and r11, #PSR_MODE_MASK; \ +- cmp r11, #PSR_MODE_HYP; \ +- blne save_guest_regs +- + save_guest_regs: + #ifdef CONFIG_ARM32_HARDEN_BRANCH_PREDICTOR + /* +@@ -52,7 +31,7 @@ save_guest_regs: + ldr r11, =0xffffffff /* Clobber SP which is only valid for hypervisor frames. */ + str r11, [sp, #UREGS_sp] + SAVE_ONE_BANKED(SP_usr) +- /* LR_usr is the same physical register as lr and is saved in SAVE_ALL */ ++ /* LR_usr is the same physical register as lr and is saved by the caller */ + SAVE_BANKED(svc) + SAVE_BANKED(abt) + SAVE_BANKED(und) +@@ -131,7 +110,28 @@ skip_check: + * interrupts (Asynchronous Abort, IRQ, FIQ) to unmask. + */ + .macro vector trap, iflags +- SAVE_ALL ++ /* Save registers in the stack */ ++ sub sp, #(UREGS_SP_usr - UREGS_sp) /* SP, LR, SPSR, PC */ ++ push {r0-r12} /* Save R0-R12 */ ++ mrs r11, ELR_hyp /* ELR_hyp is return address */ ++ str r11, [sp, #UREGS_pc] ++ ++ str lr, [sp, #UREGS_lr] ++ ++ add r11, sp, #(UREGS_kernel_sizeof + 4) ++ ++ str r11, [sp, #UREGS_sp] ++ ++ mrc CP32(r11, HSR) /* Save exception syndrome */ ++ str r11, [sp, #UREGS_hsr] ++ ++ mrs r11, SPSR_hyp ++ str r11, [sp, #UREGS_cpsr] ++ and r11, #PSR_MODE_MASK ++ cmp r11, #PSR_MODE_HYP ++ blne save_guest_regs ++ ++ /* We are ready to handle the trap, setup the registers and jump. */ + cpsie \iflags + adr lr, return_from_trap + mov r0, sp +-- +2.11.0 + diff --git a/xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch b/xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch new file mode 100644 index 0000000..5168452 --- /dev/null +++ b/xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch @@ -0,0 +1,226 @@ +From 098fe877967870ffda2dfd9629a5fd272f6aacdc Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Fri, 11 Oct 2019 17:49:28 +0100 +Subject: [PATCH 3/4] xen/arm32: Don't blindly unmask interrupts on trap + without a change of level + +Exception vectors will unmask interrupts regardless the state of them in +the interrupted context. + +One of the consequences is IRQ will be unmasked when receiving an +undefined instruction exception (used by WARN*) from the hypervisor. +This could result to unexpected behavior such as deadlock (if a lock was +shared with interrupts). + +In a nutshell, interrupts should only be unmasked when it is safe to do. +Xen only unmask IRQ and Abort interrupts, so the logic can stay simple. + +As vectors exceptions may be shared between guest and hypervisor, we now +need to have a different policy for the interrupts. + +On exception from hypervisor, each vector will select the list of +interrupts to inherit from the interrupted context. Any interrupts not +listed will be kept masked. + +On exception from the guest, the Abort and IRQ will be unmasked +depending on the exact vector. + +The interrupts will be kept unmasked when the vector cannot used by +either guest or hypervisor. + +Note that each vector is not anymore preceded by ALIGN. This is fine +because the alignment is already bigger than what we need. + +This is part of XSA-303. + +Reported-by: Julien Grall +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +Reviewed-by: Andre Przywara +--- + xen/arch/arm/arm32/entry.S | 138 +++++++++++++++++++++++++++++++++++---------- + 1 file changed, 109 insertions(+), 29 deletions(-) + +diff --git a/xen/arch/arm/arm32/entry.S b/xen/arch/arm/arm32/entry.S +index 150cbc0b4b..ec90cca093 100644 +--- a/xen/arch/arm/arm32/entry.S ++++ b/xen/arch/arm/arm32/entry.S +@@ -4,6 +4,17 @@ + #include + #include + ++/* ++ * Short-hands to defined the interrupts (A, I, F) ++ * ++ * _ means the interrupt state will not change ++ * X means the state of interrupt X will change ++ * ++ * To be used with msr cpsr_* only ++ */ ++#define IFLAGS_AIF PSR_ABT_MASK | PSR_IRQ_MASK | PSR_FIQ_MASK ++#define IFLAGS_A_F PSR_ABT_MASK | PSR_FIQ_MASK ++ + #define SAVE_ONE_BANKED(reg) mrs r11, reg; str r11, [sp, #UREGS_##reg] + #define RESTORE_ONE_BANKED(reg) ldr r11, [sp, #UREGS_##reg]; msr reg, r11 + +@@ -106,10 +117,18 @@ skip_check: + mov pc, lr + + /* +- * Macro to define trap entry. The iflags corresponds to the list of +- * interrupts (Asynchronous Abort, IRQ, FIQ) to unmask. ++ * Macro to define a trap entry. ++ * ++ * @guest_iflags: Optional list of interrupts to unmask when ++ * entering from guest context. As this is used with cpsie, ++ * the letter (a, i, f) should be used. ++ * ++ * @hyp_iflags: Optional list of interrupts to inherit when ++ * entering from hypervisor context. Any interrupts not ++ * listed will be kept unchanged. As this is used with cpsr_*, ++ * IFLAGS_* short-hands should be used. + */ +- .macro vector trap, iflags ++ .macro vector trap, guest_iflags=n, hyp_iflags=0 + /* Save registers in the stack */ + sub sp, #(UREGS_SP_usr - UREGS_sp) /* SP, LR, SPSR, PC */ + push {r0-r12} /* Save R0-R12 */ +@@ -127,12 +146,39 @@ skip_check: + + mrs r11, SPSR_hyp + str r11, [sp, #UREGS_cpsr] +- and r11, #PSR_MODE_MASK +- cmp r11, #PSR_MODE_HYP +- blne save_guest_regs + ++ /* ++ * We need to distinguish whether we came from guest or ++ * hypervisor context. ++ */ ++ and r0, r11, #PSR_MODE_MASK ++ cmp r0, #PSR_MODE_HYP ++ ++ bne 1f ++ /* ++ * Trap from the hypervisor ++ * ++ * Inherit the state of the interrupts from the hypervisor ++ * context. For that we need to use SPSR (stored in r11) and ++ * modify CPSR accordingly. ++ * ++ * CPSR = (CPSR & ~hyp_iflags) | (SPSR & hyp_iflags) ++ */ ++ mrs r10, cpsr ++ bic r10, r10, #\hyp_iflags ++ and r11, r11, #\hyp_iflags ++ orr r10, r10, r11 ++ msr cpsr_cx, r10 ++ b 2f ++ ++1: ++ /* Trap from the guest */ ++ bl save_guest_regs ++ .if \guest_iflags != n ++ cpsie \guest_iflags ++ .endif ++2: + /* We are ready to handle the trap, setup the registers and jump. */ +- cpsie \iflags + adr lr, return_from_trap + mov r0, sp + /* +@@ -144,20 +190,6 @@ skip_check: + b do_trap_\trap + .endm + +-#define __DEFINE_TRAP_ENTRY(trap, iflags) \ +- ALIGN; \ +-trap_##trap: \ +- vector trap, iflags +- +-/* Trap handler which unmask IRQ/Abort, keep FIQ masked */ +-#define DEFINE_TRAP_ENTRY(trap) __DEFINE_TRAP_ENTRY(trap, ai) +- +-/* Trap handler which unmask Abort, keep IRQ/FIQ masked */ +-#define DEFINE_TRAP_ENTRY_NOIRQ(trap) __DEFINE_TRAP_ENTRY(trap, a) +- +-/* Trap handler which unmask IRQ, keep Abort/FIQ masked */ +-#define DEFINE_TRAP_ENTRY_NOABORT(trap) __DEFINE_TRAP_ENTRY(trap, i) +- + .align 5 + GLOBAL(hyp_traps_vector) + b trap_reset /* 0x00 - Reset */ +@@ -228,14 +260,62 @@ decode_vectors: + + #endif /* CONFIG_HARDEN_BRANCH_PREDICTOR */ + +-DEFINE_TRAP_ENTRY(reset) +-DEFINE_TRAP_ENTRY(undefined_instruction) +-DEFINE_TRAP_ENTRY(hypervisor_call) +-DEFINE_TRAP_ENTRY(prefetch_abort) +-DEFINE_TRAP_ENTRY(guest_sync) +-DEFINE_TRAP_ENTRY_NOIRQ(irq) +-DEFINE_TRAP_ENTRY_NOIRQ(fiq) +-DEFINE_TRAP_ENTRY_NOABORT(data_abort) ++/* Vector not used by the Hypervisor. */ ++trap_reset: ++ vector reset ++ ++/* ++ * Vector only used by the Hypervisor. ++ * ++ * While the exception can be executed with all the interrupts (e.g. ++ * IRQ) unmasked, the interrupted context may have purposefully masked ++ * some of them. So we want to inherit the state from the interrupted ++ * context. ++ */ ++trap_undefined_instruction: ++ vector undefined_instruction, hyp_iflags=IFLAGS_AIF ++ ++/* We should never reach this trap */ ++trap_hypervisor_call: ++ vector hypervisor_call ++ ++/* ++ * Vector only used by the hypervisor. ++ * ++ * While the exception can be executed with all the interrupts (e.g. ++ * IRQ) unmasked, the interrupted context may have purposefully masked ++ * some of them. So we want to inherit the state from the interrupted ++ * context. ++ */ ++trap_prefetch_abort: ++ vector prefetch_abort, hyp_iflags=IFLAGS_AIF ++ ++/* ++ * Vector only used by the hypervisor. ++ * ++ * Data Abort should be rare and most likely fatal. It is best to not ++ * unmask any interrupts to limit the amount of code that can run before ++ * the Data Abort is treated. ++ */ ++trap_data_abort: ++ vector data_abort ++ ++/* Vector only used by the guest. We can unmask Abort/IRQ. */ ++trap_guest_sync: ++ vector guest_sync, guest_iflags=ai ++ ++ ++/* Vector used by the hypervisor and the guest. */ ++trap_irq: ++ vector irq, guest_iflags=a, hyp_iflags=IFLAGS_A_F ++ ++/* ++ * Vector used by the hypervisor and the guest. ++ * ++ * FIQ are not meant to happen, so we don't unmask any interrupts. ++ */ ++trap_fiq: ++ vector fiq + + return_from_trap: + /* +-- +2.11.0 + diff --git a/xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch b/xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch new file mode 100644 index 0000000..106cbf9 --- /dev/null +++ b/xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch @@ -0,0 +1,114 @@ +From c6d290ce157a044dec417fdda8db71e41a37d744 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Mon, 7 Oct 2019 18:10:56 +0100 +Subject: [PATCH 4/4] xen/arm64: Don't blindly unmask interrupts on trap + without a change of level + +Some of the traps without a change of the level (i.e. hypervisor -> +hypervisor) will unmask interrupts regardless the state of them in the +interrupted context. + +One of the consequences is IRQ will be unmasked when receiving a +synchronous exception (used by WARN*()). This could result to unexpected +behavior such as deadlock (if a lock was shared with interrupts). + +In a nutshell, interrupts should only be unmasked when it is safe to +do. Xen only unmask IRQ and Abort interrupts, so the logic can stay +simple: + - hyp_error: All the interrupts are now kept masked. SError should + be pretty rare and if ever happen then we most likely want to + avoid any other interrupts to be generated. The potential main + "caller" is during virtual SError synchronization on the exit + path from the guest (see check_pending_vserror). + + - hyp_sync: The interrupts state is inherited from the interrupted + context. + + - hyp_irq: All the interrupts but IRQ state are inherited from the + interrupted context. IRQ is kept masked. + +This is part of XSA-303. + +Reported-by: Julien Grall +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini +Reviewed-by: Andre Przywara +--- + xen/arch/arm/arm64/entry.S | 47 ++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 43 insertions(+), 4 deletions(-) + +diff --git a/xen/arch/arm/arm64/entry.S b/xen/arch/arm/arm64/entry.S +index 2d9a2713a1..3e41ba65b6 100644 +--- a/xen/arch/arm/arm64/entry.S ++++ b/xen/arch/arm/arm64/entry.S +@@ -188,24 +188,63 @@ hyp_error_invalid: + entry hyp=1 + invalid BAD_ERROR + ++/* ++ * SError received while running in the hypervisor mode. ++ * ++ * Technically, we could unmask the IRQ if it were unmasked in the ++ * interrupted context. However, this require to check the PSTATE. For ++ * simplicity, as SError should be rare and potentially fatal, ++ * all interrupts are kept masked. ++ */ + hyp_error: + entry hyp=1 +- msr daifclr, #2 + mov x0, sp + bl do_trap_hyp_serror + exit hyp=1 + +-/* Traps taken in Current EL with SP_ELx */ ++/* ++ * Synchronous exception received while running in the hypervisor mode. ++ * ++ * While the exception could be executed with all the interrupts (e.g. ++ * IRQ) unmasked, the interrupted context may have purposefully masked ++ * some of them. So we want to inherit the state from the interrupted ++ * context. ++ */ + hyp_sync: + entry hyp=1 +- msr daifclr, #6 ++ ++ /* Inherit interrupts */ ++ mrs x0, SPSR_el2 ++ and x0, x0, #(PSR_DBG_MASK | PSR_ABT_MASK | PSR_IRQ_MASK | PSR_FIQ_MASK) ++ msr daif, x0 ++ + mov x0, sp + bl do_trap_hyp_sync + exit hyp=1 + ++/* ++ * IRQ received while running in the hypervisor mode. ++ * ++ * While the exception could be executed with all the interrupts but IRQ ++ * unmasked, the interrupted context may have purposefully masked some ++ * of them. So we want to inherit the state from the interrupt context ++ * and keep IRQ masked. ++ * ++ * XXX: We may want to consider an ordering between interrupts (e.g. if ++ * SError are masked, then IRQ should be masked too). However, this ++ * would require some rework in some paths (e.g. panic, livepatch) to ++ * ensure the ordering is enforced everywhere. ++ */ + hyp_irq: + entry hyp=1 +- msr daifclr, #4 ++ ++ /* Inherit D, A, F interrupts and keep I masked */ ++ mrs x0, SPSR_el2 ++ mov x1, #(PSR_DBG_MASK | PSR_ABT_MASK | PSR_FIQ_MASK) ++ and x0, x0, x1 ++ orr x0, x0, #PSR_IRQ_MASK ++ msr daif, x0 ++ + mov x0, sp + bl do_trap_irq + exit hyp=1 +-- +2.11.0 + From 181c01aee591b2b9d73e40d34607f1a0f416e0c3 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 12 Nov 2019 20:14:31 +0000 Subject: [PATCH 021/194] add CVE and bugzilla references --- xen.spec | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/xen.spec b/xen.spec index e687f4a..9bc9a6c 100644 --- a/xen.spec +++ b/xen.spec @@ -902,16 +902,17 @@ fi %changelog * Thu Oct 31 2019 Michael Young - 4.12.1-4 -- VCPUOP_initialise DoS [XSA-296, CVE-2019-18420] +- VCPUOP_initialise DoS [XSA-296, CVE-2019-18420] (#1771368) - missing descriptor table limit checking in x86 PV emulation [XSA-298, - CVE-2019-18425] + CVE-2019-18425] (#1771341) - Issues with restartable PV type change operations [XSA-299, CVE-2019-18421] (#1767726) - add-to-physmap can be abused to DoS Arm hosts [XSA-301, CVE-2019-18423] + (#1771345) - passed through PCI devices may corrupt host memory after deassignment [XSA-302, CVE-2019-18424] (#1767731) - ARM: Interrupts are unconditionally unmasked in exception handlers - [XSA-303, CVE-2019-18422] + [XSA-303, CVE-2019-18422] (#1771443) * Thu Oct 03 2019 Miro Hrončok - 4.12.1-3 - Rebuilt for Python 3.8.0rc1 (#1748018) @@ -928,7 +929,8 @@ fi - Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild * Wed Jun 19 2019 Michael Young - 4.12.0-4 -- Unlimited Arm Atomics Operations [XSA-295] (#1720760) +- Unlimited Arm Atomics Operations [XSA-295, CVE-2019-17349, + CVE-2019-17350] (#1720760) - some debug files are now properly packaged in debuginfo rpms * Tue Jun 18 2019 Zbigniew Jędrzejewski-Szmek @@ -961,15 +963,19 @@ fi * Tue Mar 05 2019 Michael Young - 4.11.1-4 - xen: various flaws (#1685577) - grant table transfer issues on large hosts [XSA-284] - race with pass-through device hotplug [XSA-285] - x86: steal_page violates page_struct access discipline [XSA-287] - x86: Inconsistent PV IOMMU discipline [XSA-288] - missing preemption in x86 PV page table unvalidation [XSA-290] - x86/PV: page type reference counting issue with failed IOMMU update [XSA-291] - x86: insufficient TLB flushing when using PCID [XSA-292] - x86: PV kernel context switch corruption [XSA-293] - x86 shadow: Insufficient TLB flushing when using PCID [XSA-294] + grant table transfer issues on large hosts [XSA-284, CVE-2019-17340] + race with pass-through device hotplug [XSA-285, CVE-2019-17341] + x86: steal_page violates page_struct access discipline + [XSA-287, CVE-2019-17342] + x86: Inconsistent PV IOMMU discipline [XSA-288, CVE-2019-17343] + missing preemption in x86 PV page table unvalidation + [XSA-290, CVE-2019-17344] + x86/PV: page type reference counting issue with failed IOMMU update + [XSA-291, CVE-2019-17345] + x86: insufficient TLB flushing when using PCID [XSA-292, CVE-2019-17346] + x86: PV kernel context switch corruption [XSA-293, CVE-2019-17347] + x86 shadow: Insufficient TLB flushing when using PCID [XSA-294, + CVE-2019-17348] * Thu Feb 14 2019 Michael Young - 4.11.1-3 - add gcc9 build fixes (#1676229) From e1f73cd6998f45750946b3e485212b4c7fffa870 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 12 Nov 2019 20:25:31 +0000 Subject: [PATCH 022/194] 2 security fixes x86: Machine Check Error on Page Size Change DoS [XSA-304, CVE-2018-12207] TSX Asynchronous Abort speculative side channel [XSA-305, CVE-2019-11135] --- xen.spec | 16 ++- xsa304-4.12-1.patch | 71 +++++++++++ xsa304-4.12-2.patch | 272 +++++++++++++++++++++++++++++++++++++++++ xsa304-4.12-3.patch | 108 +++++++++++++++++ xsa305-4.12-1.patch | 288 ++++++++++++++++++++++++++++++++++++++++++++ xsa305-4.12-2.patch | 192 +++++++++++++++++++++++++++++ 6 files changed, 946 insertions(+), 1 deletion(-) create mode 100644 xsa304-4.12-1.patch create mode 100644 xsa304-4.12-2.patch create mode 100644 xsa304-4.12-3.patch create mode 100644 xsa305-4.12-1.patch create mode 100644 xsa305-4.12-2.patch diff --git a/xen.spec b/xen.spec index 9bc9a6c..de5ba3e 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.12.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 @@ -126,6 +126,11 @@ Patch63: xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch Patch64: xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch Patch65: xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch Patch66: xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch +Patch67: xsa304-4.12-1.patch +Patch68: xsa304-4.12-2.patch +Patch69: xsa304-4.12-3.patch +Patch70: xsa305-4.12-1.patch +Patch71: xsa305-4.12-2.patch %if %build_qemutrad @@ -338,6 +343,11 @@ manage Xen virtual machines. %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 @@ -901,6 +911,10 @@ fi %endif %changelog +* Tue Nov 12 2019 Michael Young - 4.12.1-5 +- x86: Machine Check Error on Page Size Change DoS [XSA-304, CVE-2018-12207] +- TSX Asynchronous Abort speculative side channel [XSA-305, CVE-2019-11135] + * Thu Oct 31 2019 Michael Young - 4.12.1-4 - VCPUOP_initialise DoS [XSA-296, CVE-2019-18420] (#1771368) - missing descriptor table limit checking in x86 PV emulation [XSA-298, diff --git a/xsa304-4.12-1.patch b/xsa304-4.12-1.patch new file mode 100644 index 0000000..c2ed2c2 --- /dev/null +++ b/xsa304-4.12-1.patch @@ -0,0 +1,71 @@ +From: Andrew Cooper +Subject: x86/vtd: Hide superpage support for SandyBridge IOMMUs + +Something causes SandyBridge IOMMUs to choke when sharing EPT pagetables, and +an EPT superpage gets shattered. The root cause is still under investigation, +but the end result is unusable in combination with CVE-2018-12207 protections. + +This is part of XSA-304 / CVE-2018-12207 + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/drivers/passthrough/vtd/extern.h b/xen/drivers/passthrough/vtd/extern.h +index 16eada9fa2..a71c8b0f84 100644 +--- a/xen/drivers/passthrough/vtd/extern.h ++++ b/xen/drivers/passthrough/vtd/extern.h +@@ -97,6 +97,8 @@ void vtd_ops_postamble_quirk(struct iommu* iommu); + int __must_check me_wifi_quirk(struct domain *domain, + u8 bus, u8 devfn, int map); + void pci_vtd_quirk(const struct pci_dev *); ++void quirk_iommu_caps(struct iommu *iommu); ++ + bool_t platform_supports_intremap(void); + bool_t platform_supports_x2apic(void); + +diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c +index b3664ecbe0..5d34f75306 100644 +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -1215,6 +1215,8 @@ int __init iommu_alloc(struct acpi_drhd_unit *drhd) + if ( !(iommu->cap + 1) || !(iommu->ecap + 1) ) + return -ENODEV; + ++ quirk_iommu_caps(iommu); ++ + if ( cap_fault_reg_offset(iommu->cap) + + cap_num_fault_regs(iommu->cap) * PRIMARY_FAULT_REG_LEN >= PAGE_SIZE || + ecap_iotlb_offset(iommu->ecap) >= PAGE_SIZE ) +diff --git a/xen/drivers/passthrough/vtd/quirks.c b/xen/drivers/passthrough/vtd/quirks.c +index d6db862678..b02688e316 100644 +--- a/xen/drivers/passthrough/vtd/quirks.c ++++ b/xen/drivers/passthrough/vtd/quirks.c +@@ -540,3 +540,28 @@ void pci_vtd_quirk(const struct pci_dev *pdev) + break; + } + } ++ ++void __init quirk_iommu_caps(struct iommu *iommu) ++{ ++ /* ++ * IOMMU Quirks: ++ * ++ * SandyBridge IOMMUs claim support for 2M and 1G superpages, but don't ++ * implement superpages internally. ++ * ++ * There are issues changing the walk length under in-flight DMA, which ++ * has manifested as incompatibility between EPT/IOMMU sharing and the ++ * workaround for CVE-2018-12207 / XSA-304. Hide the superpages ++ * capabilities in the IOMMU, which will prevent Xen from sharing the EPT ++ * and IOMMU pagetables. ++ * ++ * Detection of SandyBridge unfortunately has to be done by processor ++ * model because the client parts don't expose their IOMMUs as PCI devices ++ * we could match with a Device ID. ++ */ ++ if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && ++ boot_cpu_data.x86 == 6 && ++ (boot_cpu_data.x86_model == 0x2a || ++ boot_cpu_data.x86_model == 0x2d) ) ++ iommu->cap &= ~(0xful << 34); ++} diff --git a/xsa304-4.12-2.patch b/xsa304-4.12-2.patch new file mode 100644 index 0000000..66d4301 --- /dev/null +++ b/xsa304-4.12-2.patch @@ -0,0 +1,272 @@ +From: Andrew Cooper +Subject: x86/vtx: Disable executable EPT superpages to work around + CVE-2018-12207 + +CVE-2018-12207 covers a set of errata on various Intel processors, whereby a +machine check exception can be generated in a corner case when an executable +mapping changes size or cacheability without TLB invalidation. HVM guest +kernels can trigger this to DoS the host. + +To mitigate, in affected hardware, all EPT superpages are marked NX. When an +instruction fetch violation is observed against the superpage, the superpage +is shattered to 4k and has execute permissions restored. This prevents the +guest kernel from being able to create the necessary preconditions in the iTLB +to exploit the vulnerability. + +This does come with a workload-dependent performance overhead, caused by +increased TLB pressure. Performance can be restored, if guest kernels are +trusted not to mount an attack, by specifying ept=exec-sp on the command line. + +This is part of XSA-304 / CVE-2018-12207 + +Signed-off-by: Andrew Cooper +Acked-by: George Dunlap +Reviewed-by: Jan Beulich + +diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc +index 85081fdc94..e283017015 100644 +--- a/docs/misc/xen-command-line.pandoc ++++ b/docs/misc/xen-command-line.pandoc +@@ -895,7 +895,7 @@ Controls for interacting with the system Extended Firmware Interface. + uncacheable. + + ### ept +-> `= List of [ ad=, pml= ]` ++> `= List of [ ad=, pml=, exec-sp= ]` + + > Applicability: Intel + +@@ -926,6 +926,16 @@ introduced with the Nehalem architecture. + disable PML. `pml=0` can be used to prevent the use of PML on otherwise + capable hardware. + ++* The `exec-sp` boolean controls whether EPT superpages with execute ++ permissions are permitted. In general this is good for performance. ++ ++ However, on processors vulnerable CVE-2018-12207, HVM guest kernels can ++ use executable superpages to crash the host. By default, executable ++ superpages are disabled on affected hardware. ++ ++ If HVM guest kernels are trusted not to mount a DoS against the system, ++ this option can enabled to regain performance. ++ + ### extra_guest_irqs + > `= [][,]` + +diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c +index 2089a77270..84191d4e4b 100644 +--- a/xen/arch/x86/hvm/hvm.c ++++ b/xen/arch/x86/hvm/hvm.c +@@ -1814,6 +1814,24 @@ int hvm_hap_nested_page_fault(paddr_t gpa, unsigned long gla, + break; + } + ++ /* ++ * Workaround for XSA-304 / CVE-2018-12207. If we take an execution ++ * fault against a non-executable superpage, shatter it to regain ++ * execute permissions. ++ */ ++ if ( page_order > 0 && npfec.insn_fetch && npfec.present && !violation ) ++ { ++ int res = p2m_set_entry(p2m, _gfn(gfn), mfn, PAGE_ORDER_4K, ++ p2mt, p2ma); ++ ++ if ( res ) ++ printk(XENLOG_ERR "Failed to shatter gfn %"PRI_gfn": %d\n", ++ gfn, res); ++ ++ rc = !res; ++ goto out_put_gfn; ++ } ++ + if ( violation ) + { + /* Should #VE be emulated for this fault? */ +diff --git a/xen/arch/x86/hvm/vmx/vmcs.c b/xen/arch/x86/hvm/vmx/vmcs.c +index 56519fee84..ec5ab860ad 100644 +--- a/xen/arch/x86/hvm/vmx/vmcs.c ++++ b/xen/arch/x86/hvm/vmx/vmcs.c +@@ -67,6 +67,7 @@ integer_param("ple_window", ple_window); + + static bool __read_mostly opt_ept_pml = true; + static s8 __read_mostly opt_ept_ad = -1; ++int8_t __read_mostly opt_ept_exec_sp = -1; + + static int __init parse_ept_param(const char *s) + { +@@ -82,6 +83,8 @@ static int __init parse_ept_param(const char *s) + opt_ept_ad = val; + else if ( (val = parse_boolean("pml", s, ss)) >= 0 ) + opt_ept_pml = val; ++ else if ( (val = parse_boolean("exec-sp", s, ss)) >= 0 ) ++ opt_ept_exec_sp = val; + else + rc = -EINVAL; + +diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c +index 26b7ddb5fe..28cba8ec28 100644 +--- a/xen/arch/x86/hvm/vmx/vmx.c ++++ b/xen/arch/x86/hvm/vmx/vmx.c +@@ -2445,6 +2445,102 @@ static void pi_notification_interrupt(struct cpu_user_regs *regs) + static void __init lbr_tsx_fixup_check(void); + static void __init bdw_erratum_bdf14_fixup_check(void); + ++/* ++ * Calculate whether the CPU is vulnerable to Instruction Fetch page ++ * size-change MCEs. ++ */ ++static bool __init has_if_pschange_mc(void) ++{ ++ uint64_t caps = 0; ++ ++ /* ++ * If we are virtualised, there is nothing we can do. Our EPT tables are ++ * shadowed by our hypervisor, and not walked by hardware. ++ */ ++ if ( cpu_has_hypervisor ) ++ return false; ++ ++ if ( boot_cpu_has(X86_FEATURE_ARCH_CAPS) ) ++ rdmsrl(MSR_ARCH_CAPABILITIES, caps); ++ ++ if ( caps & ARCH_CAPS_IF_PSCHANGE_MC_NO ) ++ return false; ++ ++ /* ++ * IF_PSCHANGE_MC 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 false; ++ ++ switch ( boot_cpu_data.x86_model ) ++ { ++ /* ++ * Core processors since at least Nehalem are vulnerable. ++ */ ++ case 0x1f: /* Auburndale / Havendale */ ++ case 0x1e: /* Nehalem */ ++ case 0x1a: /* Nehalem EP */ ++ case 0x2e: /* Nehalem EX */ ++ case 0x25: /* Westmere */ ++ case 0x2c: /* Westmere EP */ ++ case 0x2f: /* Westmere EX */ ++ case 0x2a: /* SandyBridge */ ++ case 0x2d: /* SandyBridge EP/EX */ ++ case 0x3a: /* IvyBridge */ ++ case 0x3e: /* IvyBridge EP/EX */ ++ case 0x3c: /* Haswell */ ++ case 0x3f: /* Haswell EX/EP */ ++ case 0x45: /* Haswell D */ ++ case 0x46: /* Haswell H */ ++ case 0x3d: /* Broadwell */ ++ case 0x47: /* Broadwell H */ ++ case 0x4f: /* Broadwell EP/EX */ ++ case 0x56: /* Broadwell D */ ++ case 0x4e: /* Skylake M */ ++ case 0x5e: /* Skylake D */ ++ case 0x55: /* Skylake-X / Cascade Lake */ ++ case 0x8e: /* Kaby / Coffee / Whiskey Lake M */ ++ case 0x9e: /* Kaby / Coffee / Whiskey Lake D */ ++ return true; ++ ++ /* ++ * Atom processors are not vulnerable. ++ */ ++ case 0x1c: /* Pineview */ ++ case 0x26: /* Lincroft */ ++ case 0x27: /* Penwell */ ++ case 0x35: /* Cloverview */ ++ case 0x36: /* Cedarview */ ++ case 0x37: /* Baytrail / Valleyview (Silvermont) */ ++ case 0x4d: /* Avaton / Rangely (Silvermont) */ ++ case 0x4c: /* Cherrytrail / Brasswell */ ++ case 0x4a: /* Merrifield */ ++ case 0x5a: /* Moorefield */ ++ case 0x5c: /* Goldmont */ ++ case 0x5d: /* SoFIA 3G Granite/ES2.1 */ ++ case 0x65: /* SoFIA LTE AOSP */ ++ case 0x5f: /* Denverton */ ++ case 0x6e: /* Cougar Mountain */ ++ case 0x75: /* Lightning Mountain */ ++ case 0x7a: /* Gemini Lake */ ++ case 0x86: /* Jacobsville */ ++ ++ /* ++ * Knights processors are not vulnerable. ++ */ ++ case 0x57: /* Knights Landing */ ++ case 0x85: /* Knights Mill */ ++ return false; ++ ++ default: ++ printk("Unrecognised CPU model %#x - assuming vulnerable to IF_PSCHANGE_MC\n", ++ boot_cpu_data.x86_model); ++ return true; ++ } ++} ++ + const struct hvm_function_table * __init start_vmx(void) + { + set_in_cr4(X86_CR4_VMXE); +@@ -2465,6 +2561,17 @@ const struct hvm_function_table * __init start_vmx(void) + */ + if ( cpu_has_vmx_ept && (cpu_has_vmx_pat || opt_force_ept) ) + { ++ bool cpu_has_bug_pschange_mc = has_if_pschange_mc(); ++ ++ if ( opt_ept_exec_sp == -1 ) ++ { ++ /* Default to non-executable superpages on vulnerable hardware. */ ++ opt_ept_exec_sp = !cpu_has_bug_pschange_mc; ++ ++ if ( cpu_has_bug_pschange_mc ) ++ printk("VMX: Disabling executable EPT superpages due to CVE-2018-12207\n"); ++ } ++ + vmx_function_table.hap_supported = 1; + vmx_function_table.altp2m_supported = 1; + +diff --git a/xen/arch/x86/mm/p2m-ept.c b/xen/arch/x86/mm/p2m-ept.c +index 952ebad82f..834d4798c8 100644 +--- a/xen/arch/x86/mm/p2m-ept.c ++++ b/xen/arch/x86/mm/p2m-ept.c +@@ -174,6 +174,12 @@ static void ept_p2m_type_to_flags(struct p2m_domain *p2m, ept_entry_t *entry, + break; + } + ++ /* ++ * Don't create executable superpages if we need to shatter them to ++ * protect against CVE-2018-12207. ++ */ ++ if ( !opt_ept_exec_sp && is_epte_superpage(entry) ) ++ entry->x = 0; + } + + #define GUEST_TABLE_MAP_FAILED 0 +diff --git a/xen/include/asm-x86/hvm/vmx/vmx.h b/xen/include/asm-x86/hvm/vmx/vmx.h +index ebaa74449b..371b912887 100644 +--- a/xen/include/asm-x86/hvm/vmx/vmx.h ++++ b/xen/include/asm-x86/hvm/vmx/vmx.h +@@ -28,6 +28,8 @@ + #include + #include + ++extern int8_t opt_ept_exec_sp; ++ + typedef union { + struct { + u64 r : 1, /* bit 0 - Read permission */ +diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h +index 637259bd1f..32746aa8ae 100644 +--- a/xen/include/asm-x86/msr-index.h ++++ b/xen/include/asm-x86/msr-index.h +@@ -52,6 +52,7 @@ + #define ARCH_CAPS_SKIP_L1DFL (_AC(1, ULL) << 3) + #define ARCH_CAPS_SSB_NO (_AC(1, ULL) << 4) + #define ARCH_CAPS_MDS_NO (_AC(1, ULL) << 5) ++#define ARCH_CAPS_IF_PSCHANGE_MC_NO (_AC(1, ULL) << 6) + + #define MSR_FLUSH_CMD 0x0000010b + #define FLUSH_CMD_L1D (_AC(1, ULL) << 0) diff --git a/xsa304-4.12-3.patch b/xsa304-4.12-3.patch new file mode 100644 index 0000000..04b4c45 --- /dev/null +++ b/xsa304-4.12-3.patch @@ -0,0 +1,108 @@ +From: Andrew Cooper +Subject: x86/vtx: Allow runtime modification of the exec-sp setting + +See patch for details. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +Reviewed-by: George Dunlap + +diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc +index e283017015..84221fe60a 100644 +--- a/docs/misc/xen-command-line.pandoc ++++ b/docs/misc/xen-command-line.pandoc +@@ -936,6 +936,21 @@ introduced with the Nehalem architecture. + If HVM guest kernels are trusted not to mount a DoS against the system, + this option can enabled to regain performance. + ++ This boolean may be modified at runtime using `xl set-parameters ++ ept=[no-]exec-sp` to switch between fast and secure. ++ ++ * When switching from secure to fast, preexisting HVM domains will run ++ at their current performance until they are rebooted; new domains will ++ run without any overhead. ++ ++ * When switching from fast to secure, all HVM domains will immediately ++ suffer a performance penalty. ++ ++ **Warning: No guarantee is made that this runtime option will be retained ++ indefinitely, or that it will retain this exact behaviour. It is ++ intended as an emergency option for people who first chose fast, then ++ change their minds to secure, and wish not to reboot.** ++ + ### extra_guest_irqs + > `= [][,]` + +diff --git a/xen/arch/x86/hvm/vmx/vmcs.c b/xen/arch/x86/hvm/vmx/vmcs.c +index ec5ab860ad..c4d8a5ba78 100644 +--- a/xen/arch/x86/hvm/vmx/vmcs.c ++++ b/xen/arch/x86/hvm/vmx/vmcs.c +@@ -95,6 +95,41 @@ static int __init parse_ept_param(const char *s) + } + custom_param("ept", parse_ept_param); + ++static int parse_ept_param_runtime(const char *s) ++{ ++ int val; ++ ++ if ( !cpu_has_vmx_ept || !hvm_funcs.hap_supported || ++ !(hvm_funcs.hap_capabilities & ++ (HVM_HAP_SUPERPAGE_2MB | HVM_HAP_SUPERPAGE_1GB)) ) ++ { ++ printk("VMX: EPT not available, or not in use - ignoring\n"); ++ return 0; ++ } ++ ++ if ( (val = parse_boolean("exec-sp", s, NULL)) < 0 ) ++ return -EINVAL; ++ ++ if ( val != opt_ept_exec_sp ) ++ { ++ struct domain *d; ++ ++ opt_ept_exec_sp = val; ++ ++ rcu_read_lock(&domlist_read_lock); ++ for_each_domain ( d ) ++ if ( paging_mode_hap(d) ) ++ p2m_change_entry_type_global(d, p2m_ram_rw, p2m_ram_rw); ++ rcu_read_unlock(&domlist_read_lock); ++ } ++ ++ printk("VMX: EPT executable superpages %sabled\n", ++ val ? "en" : "dis"); ++ ++ return 0; ++} ++custom_runtime_only_param("ept", parse_ept_param_runtime); ++ + /* Dynamic (run-time adjusted) execution control flags. */ + u32 vmx_pin_based_exec_control __read_mostly; + u32 vmx_cpu_based_exec_control __read_mostly; +diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c +index f518f86493..16608098b1 100644 +--- a/xen/arch/x86/mm/p2m.c ++++ b/xen/arch/x86/mm/p2m.c +@@ -289,15 +289,20 @@ static void change_entry_type_global(struct p2m_domain *p2m, + p2m_type_t ot, p2m_type_t nt) + { + p2m->change_entry_type_global(p2m, ot, nt); +- p2m->global_logdirty = (nt == p2m_ram_logdirty); ++ /* Don't allow 'recalculate' operations to change the logdirty state. */ ++ if ( ot != nt ) ++ p2m->global_logdirty = (nt == p2m_ram_logdirty); + } + ++/* ++ * May be called with ot = nt = p2m_ram_rw for its side effect of ++ * recalculating all PTEs in the p2m. ++ */ + void p2m_change_entry_type_global(struct domain *d, + p2m_type_t ot, p2m_type_t nt) + { + struct p2m_domain *hostp2m = p2m_get_hostp2m(d); + +- ASSERT(ot != nt); + ASSERT(p2m_is_changeable(ot) && p2m_is_changeable(nt)); + + p2m_lock(hostp2m); diff --git a/xsa305-4.12-1.patch b/xsa305-4.12-1.patch new file mode 100644 index 0000000..e1a91a5 --- /dev/null +++ b/xsa305-4.12-1.patch @@ -0,0 +1,288 @@ +From: Andrew Cooper +Subject: x86/tsx: Introduce tsx= to use MSR_TSX_CTRL when available + +To protect against the TSX Async Abort speculative vulnerability, Intel have +released new microcode for affected parts which introduce the MSR_TSX_CTRL +control, which allows TSX to be turned off. This will be architectural on +future parts. + +Introduce tsx= to provide a global on/off for TSX, including its enumeration +via CPUID. Provide stub virtualisation of this MSR, as it is not exposed to +guests at the moment. + +VMs may have booted before microcode is loaded, or before hosts have rebooted, +and they still want to migrate freely. A VM which booted seeing TSX can +migrate safely to hosts with TSX disabled - TSX will start unconditionally +aborting, but still behave in a manner compatible with the ABI. + +The guest-visible behaviour is equivalent to late loading the microcode and +setting the RTM_DISABLE bit in the course of live patching. + +This is part of XSA-305 / CVE-2019-11135 + +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 e283017015..b7e1bf8e8b 100644 +--- a/docs/misc/xen-command-line.pandoc ++++ b/docs/misc/xen-command-line.pandoc +@@ -2033,6 +2033,20 @@ Xen version. + ### tsc (x86) + > `= unstable | skewed | stable:socket` + ++### tsx ++ = ++ ++ Applicability: x86 ++ Default: true ++ ++Controls for the use of Transactional Synchronization eXtensions. ++ ++On Intel parts released in Q3 2019 (with updated microcode), and future parts, ++a control has been introduced which allows TSX to be turned off. ++ ++On systems with the ability to turn TSX off, this boolean offers system wide ++control of whether TSX is enabled or disabled. ++ + ### ucode (x86) + > `= [ | scan]` + +diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile +index 8a8d8f060f..9b9a4435fb 100644 +--- a/xen/arch/x86/Makefile ++++ b/xen/arch/x86/Makefile +@@ -66,6 +66,7 @@ obj-y += sysctl.o + obj-y += time.o + obj-y += trace.o + obj-y += traps.o ++obj-y += tsx.o + obj-y += usercopy.o + obj-y += x86_emulate.o + obj-$(CONFIG_TBOOT) += tboot.o +diff --git a/xen/arch/x86/cpuid.c b/xen/arch/x86/cpuid.c +index 57e80694f2..1727497459 100644 +--- a/xen/arch/x86/cpuid.c ++++ b/xen/arch/x86/cpuid.c +@@ -524,6 +524,20 @@ void recalculate_cpuid_policy(struct domain *d) + if ( cpu_has_itsc && (d->disable_migrate || d->arch.vtsc) ) + __set_bit(X86_FEATURE_ITSC, max_fs); + ++ /* ++ * On hardware with MSR_TSX_CTRL, the admin may have elected to disable ++ * TSX and hide the feature bits. Migrating-in VMs may have been booted ++ * pre-mitigation when the TSX features were visbile. ++ * ++ * This situation is compatible (albeit with a perf hit to any TSX code in ++ * the guest), so allow the feature bits to remain set. ++ */ ++ if ( cpu_has_tsx_ctrl ) ++ { ++ __set_bit(X86_FEATURE_HLE, max_fs); ++ __set_bit(X86_FEATURE_RTM, max_fs); ++ } ++ + /* Clamp the toolstacks choices to reality. */ + for ( i = 0; i < ARRAY_SIZE(fs); i++ ) + fs[i] &= max_fs[i]; +diff --git a/xen/arch/x86/msr.c b/xen/arch/x86/msr.c +index 56de0fe9e1..c2722d7c73 100644 +--- a/xen/arch/x86/msr.c ++++ b/xen/arch/x86/msr.c +@@ -132,6 +132,7 @@ int guest_rdmsr(const struct vcpu *v, uint32_t msr, uint64_t *val) + case MSR_FLUSH_CMD: + /* Write-only */ + case MSR_TSX_FORCE_ABORT: ++ case MSR_TSX_CTRL: + /* Not offered to guests. */ + goto gp_fault; + +@@ -260,6 +261,7 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) + case MSR_ARCH_CAPABILITIES: + /* Read-only */ + case MSR_TSX_FORCE_ABORT: ++ case MSR_TSX_CTRL: + /* Not offered to guests. */ + goto gp_fault; + +diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c +index cf790f36ef..c1c7c44000 100644 +--- a/xen/arch/x86/setup.c ++++ b/xen/arch/x86/setup.c +@@ -1594,6 +1594,8 @@ void __init noreturn __start_xen(unsigned long mbi_p) + + early_microcode_init(); + ++ tsx_init(); /* Needs microcode. May change HLE/RTM feature bits. */ ++ + identify_cpu(&boot_cpu_data); + + set_in_cr4(X86_CR4_OSFXSR | X86_CR4_OSXMMEXCPT); +diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c +index 737a44f055..e21cf0a310 100644 +--- a/xen/arch/x86/smpboot.c ++++ b/xen/arch/x86/smpboot.c +@@ -376,6 +376,8 @@ void start_secondary(void *unused) + if ( boot_cpu_has(X86_FEATURE_IBRSB) ) + wrmsrl(MSR_SPEC_CTRL, default_xen_spec_ctrl); + ++ tsx_init(); /* Needs microcode. May change HLE/RTM feature bits. */ ++ + if ( xen_guest ) + hypervisor_ap_setup(); + +diff --git a/xen/arch/x86/tsx.c b/xen/arch/x86/tsx.c +new file mode 100644 +index 0000000000..a8ec2ccc69 +--- /dev/null ++++ b/xen/arch/x86/tsx.c +@@ -0,0 +1,74 @@ ++#include ++#include ++ ++/* ++ * Valid values: ++ * 1 => Explicit tsx=1 ++ * 0 => Explicit tsx=0 ++ * -1 => Default, implicit tsx=1 ++ * ++ * This is arranged such that the bottom bit encodes whether TSX is actually ++ * disabled, while identifying various explicit (>=0) and implicit (<0) ++ * conditions. ++ */ ++int8_t __read_mostly opt_tsx = -1; ++int8_t __read_mostly cpu_has_tsx_ctrl = -1; ++ ++static int __init parse_tsx(const char *s) ++{ ++ int rc = 0, val = parse_bool(s, NULL); ++ ++ if ( val >= 0 ) ++ opt_tsx = val; ++ else ++ rc = -EINVAL; ++ ++ return rc; ++} ++custom_param("tsx", parse_tsx); ++ ++void tsx_init(void) ++{ ++ /* ++ * This function is first called between microcode being loaded, and CPUID ++ * being scanned generally. Calculate from raw data whether MSR_TSX_CTRL ++ * is available. ++ */ ++ if ( unlikely(cpu_has_tsx_ctrl < 0) ) ++ { ++ uint64_t caps = 0; ++ ++ if ( boot_cpu_data.cpuid_level >= 7 && ++ (cpuid_count_edx(7, 0) & cpufeat_mask(X86_FEATURE_ARCH_CAPS)) ) ++ rdmsrl(MSR_ARCH_CAPABILITIES, caps); ++ ++ cpu_has_tsx_ctrl = !!(caps & ARCH_CAPS_TSX_CTRL); ++ } ++ ++ if ( cpu_has_tsx_ctrl ) ++ { ++ uint64_t val; ++ ++ rdmsrl(MSR_TSX_CTRL, val); ++ ++ val &= ~(TSX_CTRL_RTM_DISABLE | TSX_CTRL_CPUID_CLEAR); ++ /* Check bottom bit only. Higher bits are various sentinals. */ ++ if ( !(opt_tsx & 1) ) ++ val |= TSX_CTRL_RTM_DISABLE | TSX_CTRL_CPUID_CLEAR; ++ ++ wrmsrl(MSR_TSX_CTRL, val); ++ } ++ else if ( opt_tsx >= 0 ) ++ printk_once(XENLOG_WARNING ++ "MSR_TSX_CTRL not available - Ignoring tsx= setting\n"); ++} ++ ++/* ++ * Local variables: ++ * mode: C ++ * c-file-style: "BSD" ++ * c-basic-offset: 4 ++ * tab-width: 4 ++ * indent-tabs-mode: nil ++ * End: ++ */ +diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h +index 32746aa8ae..d5f3899f73 100644 +--- a/xen/include/asm-x86/msr-index.h ++++ b/xen/include/asm-x86/msr-index.h +@@ -53,6 +53,7 @@ + #define ARCH_CAPS_SSB_NO (_AC(1, ULL) << 4) + #define ARCH_CAPS_MDS_NO (_AC(1, ULL) << 5) + #define ARCH_CAPS_IF_PSCHANGE_MC_NO (_AC(1, ULL) << 6) ++#define ARCH_CAPS_TSX_CTRL (_AC(1, ULL) << 7) + + #define MSR_FLUSH_CMD 0x0000010b + #define FLUSH_CMD_L1D (_AC(1, ULL) << 0) +@@ -60,6 +61,10 @@ + #define MSR_TSX_FORCE_ABORT 0x0000010f + #define TSX_FORCE_ABORT_RTM (_AC(1, ULL) << 0) + ++#define MSR_TSX_CTRL 0x00000122 ++#define TSX_CTRL_RTM_DISABLE (_AC(1, ULL) << 0) ++#define TSX_CTRL_CPUID_CLEAR (_AC(1, ULL) << 1) ++ + /* Intel MSRs. Some also available on other CPUs */ + #define MSR_IA32_PERFCTR0 0x000000c1 + #define MSR_IA32_A_PERFCTR0 0x000004c1 +diff --git a/xen/include/asm-x86/processor.h b/xen/include/asm-x86/processor.h +index d33ac34d29..1b52712180 100644 +--- a/xen/include/asm-x86/processor.h ++++ b/xen/include/asm-x86/processor.h +@@ -263,6 +263,16 @@ static always_inline unsigned int cpuid_count_ebx( + return ebx; + } + ++static always_inline unsigned int cpuid_count_edx( ++ unsigned int leaf, unsigned int subleaf) ++{ ++ unsigned int edx, tmp; ++ ++ cpuid_count(leaf, subleaf, &tmp, &tmp, &tmp, &edx); ++ ++ return edx; ++} ++ + static inline unsigned long read_cr0(void) + { + unsigned long cr0; +@@ -609,6 +619,9 @@ static inline uint8_t get_cpu_family(uint32_t raw, uint8_t *model, + return fam; + } + ++extern int8_t opt_tsx, cpu_has_tsx_ctrl; ++void tsx_init(void); ++ + #endif /* !__ASSEMBLY__ */ + + #endif /* __ASM_X86_PROCESSOR_H */ +diff --git a/xen/include/xen/lib.h b/xen/include/xen/lib.h +index 89939f43c8..6529f12dae 100644 +--- a/xen/include/xen/lib.h ++++ b/xen/include/xen/lib.h +@@ -114,6 +114,16 @@ extern int printk_ratelimit(void); + #define gprintk(lvl, fmt, args...) \ + printk(XENLOG_GUEST lvl "%pv " fmt, current, ## args) + ++#define printk_once(fmt, args...) \ ++({ \ ++ static bool __read_mostly once_; \ ++ if ( unlikely(!once_) ) \ ++ { \ ++ once_ = true; \ ++ printk(fmt, ## args); \ ++ } \ ++}) ++ + #ifdef NDEBUG + + static inline void diff --git a/xsa305-4.12-2.patch b/xsa305-4.12-2.patch new file mode 100644 index 0000000..07fba86 --- /dev/null +++ b/xsa305-4.12-2.patch @@ -0,0 +1,192 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Mitigate the TSX Asynchronous Abort sidechannel + +See patch documentation and comments. + +This is part of XSA-305 / CVE-2019-11135 + +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 b7e1bf8e8b..74e1e35b88 100644 +--- a/docs/misc/xen-command-line.pandoc ++++ b/docs/misc/xen-command-line.pandoc +@@ -1920,7 +1920,7 @@ extreme care.** + An overall boolean value, `spec-ctrl=no`, can be specified to turn off all + mitigations, including pieces of infrastructure used to virtualise certain + mitigation features for guests. This also includes settings which `xpti`, +-`smt`, `pv-l1tf` control, unless the respective option(s) have been ++`smt`, `pv-l1tf`, `tsx` control, unless the respective option(s) have been + specified earlier on the command line. + + Alternatively, a slightly more restricted `spec-ctrl=no-xen` can be used to +@@ -2037,7 +2037,7 @@ Xen version. + = + + Applicability: x86 +- Default: true ++ Default: false on parts vulnerable to TAA, true otherwise + + Controls for the use of Transactional Synchronization eXtensions. + +@@ -2047,6 +2047,19 @@ a control has been introduced which allows TSX to be turned off. + On systems with the ability to turn TSX off, this boolean offers system wide + control of whether TSX is enabled or disabled. + ++On parts vulnerable to CVE-2019-11135 / TSX Asynchronous Abort, the following ++logic applies: ++ ++ * An explicit `tsx=` choice is honoured, even if it is `true` and would ++ result in a vulnerable system. ++ ++ * When no explicit `tsx=` choice is given, parts vulnerable to TAA will be ++ mitigated by disabling TSX, as this is the lowest overhead option. ++ ++ * If the use of TSX is important, the more expensive TAA mitigations can be ++ opted in to with `smt=0 spec-ctrl=md-clear`, at which point TSX will remain ++ active by default. ++ + ### ucode (x86) + > `= [ | scan]` + +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index b37d40e643..800139d79c 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -96,6 +96,9 @@ static int __init parse_spec_ctrl(const char *s) + if ( opt_pv_l1tf_domu < 0 ) + opt_pv_l1tf_domu = 0; + ++ if ( opt_tsx == -1 ) ++ opt_tsx = -3; ++ + disable_common: + opt_rsb_pv = false; + opt_rsb_hvm = false; +@@ -306,7 +309,7 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + printk("Speculative mitigation facilities:\n"); + + /* Hardware features which pertain to speculative mitigations. */ +- printk(" Hardware features:%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", + (_7d0 & cpufeat_mask(X86_FEATURE_IBRSB)) ? " IBRS/IBPB" : "", + (_7d0 & cpufeat_mask(X86_FEATURE_STIBP)) ? " STIBP" : "", + (_7d0 & cpufeat_mask(X86_FEATURE_L1D_FLUSH)) ? " L1D_FLUSH" : "", +@@ -318,7 +321,9 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + (caps & ARCH_CAPS_RSBA) ? " RSBA" : "", + (caps & ARCH_CAPS_SKIP_L1DFL) ? " SKIP_L1DFL": "", + (caps & ARCH_CAPS_SSB_NO) ? " SSB_NO" : "", +- (caps & ARCH_CAPS_MDS_NO) ? " MDS_NO" : ""); ++ (caps & ARCH_CAPS_MDS_NO) ? " MDS_NO" : "", ++ (caps & ARCH_CAPS_TSX_CTRL) ? " TSX_CTRL" : "", ++ (caps & ARCH_CAPS_TAA_NO) ? " TAA_NO" : ""); + + /* Compiled-in support which pertains to mitigations. */ + if ( IS_ENABLED(CONFIG_INDIRECT_THUNK) || IS_ENABLED(CONFIG_SHADOW_PAGING) ) +@@ -332,7 +337,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, Other:%s%s%s\n", ++ printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s, Other:%s%s%s\n", + thunk == THUNK_NONE ? "N/A" : + thunk == THUNK_RETPOLINE ? "RETPOLINE" : + thunk == THUNK_LFENCE ? "LFENCE" : +@@ -341,6 +346,8 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + (default_xen_spec_ctrl & SPEC_CTRL_IBRS) ? "IBRS+" : "IBRS-", + !boot_cpu_has(X86_FEATURE_SSBD) ? "" : + (default_xen_spec_ctrl & SPEC_CTRL_SSBD) ? " SSBD+" : " SSBD-", ++ !(caps & ARCH_CAPS_TSX_CTRL) ? "" : ++ (opt_tsx & 1) ? " TSX+" : " TSX-", + opt_ibpb ? " IBPB" : "", + opt_l1d_flush ? " L1D_FLUSH" : "", + opt_md_clear_pv || opt_md_clear_hvm ? " VERW" : ""); +@@ -862,6 +869,7 @@ void __init init_speculation_mitigations(void) + { + enum ind_thunk thunk = THUNK_DEFAULT; + bool use_spec_ctrl = false, ibrs = false, hw_smt_enabled; ++ bool cpu_has_bug_taa; + uint64_t caps = 0; + + if ( boot_cpu_has(X86_FEATURE_ARCH_CAPS) ) +@@ -1086,6 +1094,53 @@ void __init init_speculation_mitigations(void) + "enabled. Mitigations will not be fully effective. Please\n" + "choose an explicit smt= setting. See XSA-297.\n"); + ++ /* ++ * Vulnerability to TAA is a little complicated to quantify. ++ * ++ * In the pipeline, it is just another way to get speculative access to ++ * stale load port, store buffer or fill buffer data, and therefore can be ++ * considered a superset of MDS (on TSX-capable parts). On parts which ++ * predate MDS_NO, the existing VERW flushing will mitigate this ++ * sidechannel as well. ++ * ++ * On parts which contain MDS_NO, the lack of VERW flushing means that an ++ * attacker can still use TSX to target microarchitectural buffers to leak ++ * secrets. Therefore, we consider TAA to be the set of TSX-capable parts ++ * which have MDS_NO but lack TAA_NO. ++ * ++ * Note: cpu_has_rtm (== hle) could already be hidden by `tsx=0` on the ++ * cmdline. MSR_TSX_CTRL will only appear on TSX-capable parts, so ++ * we check both to spot TSX in a microcode/cmdline independent way. ++ */ ++ cpu_has_bug_taa = ++ (cpu_has_rtm || (caps & ARCH_CAPS_TSX_CTRL)) && ++ (caps & (ARCH_CAPS_MDS_NO | ARCH_CAPS_TAA_NO)) == ARCH_CAPS_MDS_NO; ++ ++ /* ++ * On TAA-affected hardware, disabling TSX is the preferred mitigation, vs ++ * the MDS mitigation of disabling HT and using VERW flushing. ++ * ++ * On CPUs which advertise MDS_NO, VERW has no flushing side effect until ++ * the TSX_CTRL microcode is loaded, despite the MD_CLEAR CPUID bit being ++ * advertised, and there isn't a MD_CLEAR_2 flag to use... ++ * ++ * If we're on affected hardware, able to do something about it (which ++ * implies that VERW now works), no explicit TSX choice and traditional ++ * MDS mitigations (no-SMT, VERW) not obviosuly in use (someone might ++ * plausibly value TSX higher than Hyperthreading...), disable TSX to ++ * mitigate TAA. ++ */ ++ if ( opt_tsx == -1 && cpu_has_bug_taa && (caps & ARCH_CAPS_TSX_CTRL) && ++ ((hw_smt_enabled && opt_smt) || ++ !boot_cpu_has(X86_FEATURE_SC_VERW_IDLE)) ) ++ { ++ setup_clear_cpu_cap(X86_FEATURE_HLE); ++ setup_clear_cpu_cap(X86_FEATURE_RTM); ++ ++ opt_tsx = 0; ++ tsx_init(); ++ } ++ + print_details(thunk, caps); + + /* +diff --git a/xen/arch/x86/tsx.c b/xen/arch/x86/tsx.c +index a8ec2ccc69..2d202a0d4e 100644 +--- a/xen/arch/x86/tsx.c ++++ b/xen/arch/x86/tsx.c +@@ -5,7 +5,8 @@ + * Valid values: + * 1 => Explicit tsx=1 + * 0 => Explicit tsx=0 +- * -1 => Default, implicit tsx=1 ++ * -1 => Default, implicit tsx=1, may change to 0 to mitigate TAA ++ * -3 => Implicit tsx=1 (feed-through from spec-ctrl=0) + * + * This is arranged such that the bottom bit encodes whether TSX is actually + * disabled, while identifying various explicit (>=0) and implicit (<0) +diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h +index d5f3899f73..3971b992d3 100644 +--- a/xen/include/asm-x86/msr-index.h ++++ b/xen/include/asm-x86/msr-index.h +@@ -54,6 +54,7 @@ + #define ARCH_CAPS_MDS_NO (_AC(1, ULL) << 5) + #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 MSR_FLUSH_CMD 0x0000010b + #define FLUSH_CMD_L1D (_AC(1, ULL) << 0) From 3b402adc22be6ae8feaf75279cece717bbe0fe7b Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 12 Nov 2019 22:27:28 +0000 Subject: [PATCH 023/194] add missing XSA-299 patches --- xen.spec | 57 +- ...set-PGT_pinned-on-a-partially-de-val.patch | 99 +++ ...out-partial_pte-tristate-into-indivi.patch | 618 ++++++++++++++++++ ...-for-_put_page_type-rather-than-a-bo.patch | 140 ++++ ...t_page_and_type_from_mfn-conditional.patch | 79 +++ ...c_l-23-_table-clear-partial_flags-wh.patch | 111 ++++ ...ways-retain-a-general-ref-on-partial.patch | 378 +++++++++++ ...PTF_partial_set-and-PTF_partial_gene.patch | 227 +++++++ ...handle-linear-pagetable-promotion-fa.patch | 106 +++ ...mm-Fix-nested-de-validation-on-error.patch | 166 +++++ ...p-a-type-ref-unless-you-held-a-ref-t.patch | 413 ++++++++++++ 11 files changed, 2377 insertions(+), 17 deletions(-) create mode 100644 xsa299-4.12-0002-x86-mm-Don-t-re-set-PGT_pinned-on-a-partially-de-val.patch create mode 100644 xsa299-4.12-0003-x86-mm-Separate-out-partial_pte-tristate-into-indivi.patch create mode 100644 xsa299-4.12-0004-x86-mm-Use-flags-for-_put_page_type-rather-than-a-bo.patch create mode 100644 xsa299-4.12-0005-x86-mm-Rework-get_page_and_type_from_mfn-conditional.patch create mode 100644 xsa299-4.12-0006-x86-mm-Have-alloc_l-23-_table-clear-partial_flags-wh.patch create mode 100644 xsa299-4.12-0007-x86-mm-Always-retain-a-general-ref-on-partial.patch create mode 100644 xsa299-4.12-0008-x86-mm-Collapse-PTF_partial_set-and-PTF_partial_gene.patch create mode 100644 xsa299-4.12-0009-x86-mm-Properly-handle-linear-pagetable-promotion-fa.patch create mode 100644 xsa299-4.12-0010-x86-mm-Fix-nested-de-validation-on-error.patch create mode 100644 xsa299-4.12-0011-x86-mm-Don-t-drop-a-type-ref-unless-you-held-a-ref-t.patch diff --git a/xen.spec b/xen.spec index de5ba3e..5e7cb22 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.12.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 @@ -117,20 +117,30 @@ Patch54: xen.python38.patch Patch55: xsa296.patch Patch56: xsa298.patch Patch57: xsa299-4.12-0001-x86-mm-L1TF-checks-don-t-leave-a-partial-entry.patch -Patch58: xsa301-master-1.patch -Patch59: xsa301-master-2.patch -Patch60: xsa301-master-3.patch -Patch61: xsa302-4.12-0001-IOMMU-add-missing-HVM-check.patch -Patch62: xsa302-4.12-0002-passthrough-quarantine-PCI-devices.patch -Patch63: xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch -Patch64: xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch -Patch65: xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch -Patch66: xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch -Patch67: xsa304-4.12-1.patch -Patch68: xsa304-4.12-2.patch -Patch69: xsa304-4.12-3.patch -Patch70: xsa305-4.12-1.patch -Patch71: xsa305-4.12-2.patch +Patch58: xsa299-4.12-0002-x86-mm-Don-t-re-set-PGT_pinned-on-a-partially-de-val.patch +Patch59: xsa299-4.12-0003-x86-mm-Separate-out-partial_pte-tristate-into-indivi.patch +Patch60: xsa299-4.12-0004-x86-mm-Use-flags-for-_put_page_type-rather-than-a-bo.patch +Patch61: xsa299-4.12-0005-x86-mm-Rework-get_page_and_type_from_mfn-conditional.patch +Patch62: xsa299-4.12-0006-x86-mm-Have-alloc_l-23-_table-clear-partial_flags-wh.patch +Patch63: xsa299-4.12-0007-x86-mm-Always-retain-a-general-ref-on-partial.patch +Patch64: xsa299-4.12-0008-x86-mm-Collapse-PTF_partial_set-and-PTF_partial_gene.patch +Patch65: xsa299-4.12-0009-x86-mm-Properly-handle-linear-pagetable-promotion-fa.patch +Patch66: xsa299-4.12-0010-x86-mm-Fix-nested-de-validation-on-error.patch +Patch67: xsa299-4.12-0011-x86-mm-Don-t-drop-a-type-ref-unless-you-held-a-ref-t.patch +Patch68: xsa301-master-1.patch +Patch69: xsa301-master-2.patch +Patch70: xsa301-master-3.patch +Patch71: xsa302-4.12-0001-IOMMU-add-missing-HVM-check.patch +Patch72: xsa302-4.12-0002-passthrough-quarantine-PCI-devices.patch +Patch73: xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch +Patch74: xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch +Patch75: xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch +Patch76: xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch +Patch77: xsa304-4.12-1.patch +Patch78: xsa304-4.12-2.patch +Patch79: xsa304-4.12-3.patch +Patch80: xsa305-4.12-1.patch +Patch81: xsa305-4.12-2.patch %if %build_qemutrad @@ -335,10 +345,8 @@ manage Xen virtual machines. %patch58 -p1 %patch59 -p1 %patch60 -p1 -%ifarch %{ix86} x86_64 %patch61 -p1 %patch62 -p1 -%endif %patch63 -p1 %patch64 -p1 %patch65 -p1 @@ -347,7 +355,19 @@ manage Xen virtual machines. %patch68 -p1 %patch69 -p1 %patch70 -p1 +%ifarch %{ix86} x86_64 %patch71 -p1 +%patch72 -p1 +%endif +%patch73 -p1 +%patch74 -p1 +%patch75 -p1 +%patch76 -p1 +%patch77 -p1 +%patch78 -p1 +%patch79 -p1 +%patch80 -p1 +%patch81 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -911,6 +931,9 @@ fi %endif %changelog +* Tue Nov 12 2019 Michael Young - 4.12.1-6 +- add missing XSA-299 patches + * Tue Nov 12 2019 Michael Young - 4.12.1-5 - x86: Machine Check Error on Page Size Change DoS [XSA-304, CVE-2018-12207] - TSX Asynchronous Abort speculative side channel [XSA-305, CVE-2019-11135] diff --git a/xsa299-4.12-0002-x86-mm-Don-t-re-set-PGT_pinned-on-a-partially-de-val.patch b/xsa299-4.12-0002-x86-mm-Don-t-re-set-PGT_pinned-on-a-partially-de-val.patch new file mode 100644 index 0000000..a74598e --- /dev/null +++ b/xsa299-4.12-0002-x86-mm-Don-t-re-set-PGT_pinned-on-a-partially-de-val.patch @@ -0,0 +1,99 @@ +From b490792c18f74b76ec8161721c1e07f810e36309 Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Thu, 10 Oct 2019 17:57:49 +0100 +Subject: [PATCH 02/11] x86/mm: Don't re-set PGT_pinned on a partially + de-validated page + +When unpinning pagetables, if an operation is interrupted, +relinquish_memory() re-sets PGT_pinned so that the un-pin will +pickedup again when the hypercall restarts. + +This is appropriate when put_page_and_type_preemptible() returns +-EINTR, which indicates that the page is back in its initial state +(i.e., completely validated). However, for -ERESTART, this leads to a +state where a page has both PGT_pinned and PGT_partial set. + +This happens to work at the moment, although it's not really a +"canonical" state; but in subsequent patches, where we need to make a +distinction in handling between PGT_validated and PGT_partial pages, +this causes issues. + +Move to a "canonical" state by: +- Only re-setting PGT_pinned on -EINTR +- Re-dropping the refcount held by PGT_pinned on -ERESTART + +In the latter case, the PGT_partial bit will be cleared further down +with the rest of the other PGT_partial pages. + +While here, clean up some trainling whitespace. + +This is part of XSA-299. + +Reported-by: George Dunlap +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +--- + xen/arch/x86/domain.c | 31 ++++++++++++++++++++++++++++--- + 1 file changed, 28 insertions(+), 3 deletions(-) + +diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c +index 2585327834..59df8a6d8d 100644 +--- a/xen/arch/x86/domain.c ++++ b/xen/arch/x86/domain.c +@@ -114,7 +114,7 @@ static void play_dead(void) + * this case, heap corruption or #PF can occur (when heap debugging is + * enabled). For example, even printk() can involve tasklet scheduling, + * which touches per-cpu vars. +- * ++ * + * Consider very carefully when adding code to *dead_idle. Most hypervisor + * subsystems are unsafe to call. + */ +@@ -1909,9 +1909,34 @@ static int relinquish_memory( + break; + case -ERESTART: + case -EINTR: ++ /* ++ * -EINTR means PGT_validated has been re-set; re-set ++ * PGT_pinned again so that it gets picked up next time ++ * around. ++ * ++ * -ERESTART, OTOH, means PGT_partial is set instead. Put ++ * it back on the list, but don't set PGT_pinned; the ++ * section below will finish off de-validation. But we do ++ * need to drop the general ref associated with ++ * PGT_pinned, since put_page_and_type_preemptible() ++ * didn't do it. ++ * ++ * NB we can do an ASSERT for PGT_validated, since we ++ * "own" the type ref; but theoretically, the PGT_partial ++ * could be cleared by someone else. ++ */ ++ if ( ret == -EINTR ) ++ { ++ ASSERT(page->u.inuse.type_info & PGT_validated); ++ set_bit(_PGT_pinned, &page->u.inuse.type_info); ++ } ++ else ++ put_page(page); ++ + ret = -ERESTART; ++ ++ /* Put the page back on the list and drop the ref we grabbed above */ + page_list_add(page, list); +- set_bit(_PGT_pinned, &page->u.inuse.type_info); + put_page(page); + goto out; + default: +@@ -2161,7 +2186,7 @@ void vcpu_kick(struct vcpu *v) + * pending flag. These values may fluctuate (after all, we hold no + * locks) but the key insight is that each change will cause + * evtchn_upcall_pending to be polled. +- * ++ * + * NB2. We save the running flag across the unblock to avoid a needless + * IPI for domains that we IPI'd to unblock. + */ +-- +2.23.0 + diff --git a/xsa299-4.12-0003-x86-mm-Separate-out-partial_pte-tristate-into-indivi.patch b/xsa299-4.12-0003-x86-mm-Separate-out-partial_pte-tristate-into-indivi.patch new file mode 100644 index 0000000..226e548 --- /dev/null +++ b/xsa299-4.12-0003-x86-mm-Separate-out-partial_pte-tristate-into-indivi.patch @@ -0,0 +1,618 @@ +From 0f9f61e5737fdd346550ec6e30161fa99e4653fa Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Thu, 10 Oct 2019 17:57:49 +0100 +Subject: [PATCH 03/11] x86/mm: Separate out partial_pte tristate into + individual flags + +At the moment, partial_pte is a tri-state that contains two distinct bits +of information: + +1. If zero, the pte at index [nr_validated_ptes] is un-validated. If + non-zero, the pte was last seen with PGT_partial set. + +2. If positive, the pte at index [nr_validated_ptes] does not hold a + general reference count. If negative, it does. + +To make future patches more clear, separate out this functionality +into two distinct, named bits: PTF_partial_set (for #1) and +PTF_partial_general_ref (for #2). + +Additionally, a number of functions which need this information also +take other flags to control behavior (such as `preemptible` and +`defer`). These are hard to read in the caller (since you only see +'true' or 'false'), and ugly when many are added together. In +preparation for adding yet another flag in a future patch, collapse +all of these into a single `flag` variable. + +NB that this does mean checking for what was previously the '-1' +condition a bit more ugly in the put_page_from_lNe functions (since +you have to check for both partial_set and general ref); but this +clause will go away in a future patch. + +Also note that the original comment had an off-by-one error: +partial_flags (like partial_pte before it) concerns +plNe[nr_validated_ptes], not plNe[nr_validated_ptes+1]. + +No functional change intended. + +This is part of XSA-299. + +Reported-by: George Dunlap +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +--- + xen/arch/x86/mm.c | 165 ++++++++++++++++++++++++--------------- + xen/include/asm-x86/mm.h | 41 +++++++--- + 2 files changed, 128 insertions(+), 78 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index a1b55c10ff..3f6f8cc9b8 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -1094,20 +1094,35 @@ get_page_from_l1e( + } + + #ifdef CONFIG_PV ++ ++/* ++ * The following flags are used to specify behavior of various get and ++ * put commands. The first two are also stored in page->partial_flags ++ * to indicate the state of the page pointed to by ++ * page->pte[page->nr_validated_entries]. See the comment in mm.h for ++ * more information. ++ */ ++#define PTF_partial_set (1 << 0) ++#define PTF_partial_general_ref (1 << 1) ++#define PTF_preemptible (1 << 2) ++#define PTF_defer (1 << 3) ++ + static int get_page_and_type_from_mfn( + mfn_t mfn, unsigned long type, struct domain *d, +- int partial, int preemptible) ++ unsigned int flags) + { + struct page_info *page = mfn_to_page(mfn); + int rc; ++ bool preemptible = flags & PTF_preemptible, ++ partial_ref = flags & PTF_partial_general_ref; + +- if ( likely(partial >= 0) && ++ if ( likely(!partial_ref) && + unlikely(!get_page_from_mfn(mfn, d)) ) + return -EINVAL; + + rc = _get_page_type(page, type, preemptible); + +- if ( unlikely(rc) && partial >= 0 && ++ if ( unlikely(rc) && !partial_ref && + (!preemptible || page != current->arch.old_guest_table) ) + put_page(page); + +@@ -1117,7 +1132,7 @@ static int get_page_and_type_from_mfn( + define_get_linear_pagetable(l2); + static int + get_page_from_l2e( +- l2_pgentry_t l2e, unsigned long pfn, struct domain *d, int partial) ++ l2_pgentry_t l2e, unsigned long pfn, struct domain *d, unsigned int flags) + { + unsigned long mfn = l2e_get_pfn(l2e); + int rc; +@@ -1129,8 +1144,9 @@ get_page_from_l2e( + return -EINVAL; + } + +- rc = get_page_and_type_from_mfn(_mfn(mfn), PGT_l1_page_table, d, +- partial, false); ++ ASSERT(!(flags & PTF_preemptible)); ++ ++ rc = get_page_and_type_from_mfn(_mfn(mfn), PGT_l1_page_table, d, flags); + if ( unlikely(rc == -EINVAL) && get_l2_linear_pagetable(l2e, pfn, d) ) + rc = 0; + +@@ -1140,7 +1156,7 @@ get_page_from_l2e( + define_get_linear_pagetable(l3); + static int + get_page_from_l3e( +- l3_pgentry_t l3e, unsigned long pfn, struct domain *d, int partial) ++ l3_pgentry_t l3e, unsigned long pfn, struct domain *d, unsigned int flags) + { + int rc; + +@@ -1152,7 +1168,7 @@ get_page_from_l3e( + } + + rc = get_page_and_type_from_mfn( +- l3e_get_mfn(l3e), PGT_l2_page_table, d, partial, 1); ++ l3e_get_mfn(l3e), PGT_l2_page_table, d, flags | PTF_preemptible); + if ( unlikely(rc == -EINVAL) && + !is_pv_32bit_domain(d) && + get_l3_linear_pagetable(l3e, pfn, d) ) +@@ -1164,7 +1180,7 @@ get_page_from_l3e( + define_get_linear_pagetable(l4); + static int + get_page_from_l4e( +- l4_pgentry_t l4e, unsigned long pfn, struct domain *d, int partial) ++ l4_pgentry_t l4e, unsigned long pfn, struct domain *d, unsigned int flags) + { + int rc; + +@@ -1176,7 +1192,7 @@ get_page_from_l4e( + } + + rc = get_page_and_type_from_mfn( +- l4e_get_mfn(l4e), PGT_l3_page_table, d, partial, 1); ++ l4e_get_mfn(l4e), PGT_l3_page_table, d, flags | PTF_preemptible); + if ( unlikely(rc == -EINVAL) && get_l4_linear_pagetable(l4e, pfn, d) ) + rc = 0; + +@@ -1277,7 +1293,7 @@ static void put_data_page(struct page_info *page, bool writeable) + * Note also that this automatically deals correctly with linear p.t.'s. + */ + static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, +- int partial, bool defer) ++ unsigned int flags) + { + int rc = 0; + +@@ -1300,12 +1316,13 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, + struct page_info *pg = l2e_get_page(l2e); + struct page_info *ptpg = mfn_to_page(_mfn(pfn)); + +- if ( unlikely(partial > 0) ) ++ if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == ++ PTF_partial_set ) + { +- ASSERT(!defer); ++ ASSERT(!(flags & PTF_defer)); + rc = _put_page_type(pg, true, ptpg); + } +- else if ( defer ) ++ else if ( flags & PTF_defer ) + { + current->arch.old_guest_ptpg = ptpg; + current->arch.old_guest_table = pg; +@@ -1322,7 +1339,7 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, + } + + static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, +- int partial, bool defer) ++ unsigned int flags) + { + struct page_info *pg; + int rc; +@@ -1345,13 +1362,14 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, + + pg = l3e_get_page(l3e); + +- if ( unlikely(partial > 0) ) ++ if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == ++ PTF_partial_set ) + { +- ASSERT(!defer); ++ ASSERT(!(flags & PTF_defer)); + return _put_page_type(pg, true, mfn_to_page(_mfn(pfn))); + } + +- if ( defer ) ++ if ( flags & PTF_defer ) + { + current->arch.old_guest_ptpg = mfn_to_page(_mfn(pfn)); + current->arch.old_guest_table = pg; +@@ -1366,7 +1384,7 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, + } + + static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, +- int partial, bool defer) ++ unsigned int flags) + { + int rc = 1; + +@@ -1375,13 +1393,14 @@ static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, + { + struct page_info *pg = l4e_get_page(l4e); + +- if ( unlikely(partial > 0) ) ++ if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == ++ PTF_partial_set ) + { +- ASSERT(!defer); ++ ASSERT(!(flags & PTF_defer)); + return _put_page_type(pg, true, mfn_to_page(_mfn(pfn))); + } + +- if ( defer ) ++ if ( flags & PTF_defer ) + { + current->arch.old_guest_ptpg = mfn_to_page(_mfn(pfn)); + current->arch.old_guest_table = pg; +@@ -1492,12 +1511,13 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) + unsigned long pfn = mfn_x(page_to_mfn(page)); + l2_pgentry_t *pl2e; + unsigned int i; +- int rc = 0, partial = page->partial_pte; ++ int rc = 0; ++ unsigned int partial_flags = page->partial_flags; + + pl2e = map_domain_page(_mfn(pfn)); + + for ( i = page->nr_validated_ptes; i < L2_PAGETABLE_ENTRIES; +- i++, partial = 0 ) ++ i++, partial_flags = 0 ) + { + l2_pgentry_t l2e; + +@@ -1520,17 +1540,18 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) + rc = -EINTR; + } + else +- rc = get_page_from_l2e(l2e, pfn, d, partial); ++ rc = get_page_from_l2e(l2e, pfn, d, partial_flags); + + if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; +- page->partial_pte = partial ?: 1; ++ /* Set 'set', retain 'general ref' */ ++ page->partial_flags = partial_flags | PTF_partial_set; + } + else if ( rc == -EINTR && i ) + { + page->nr_validated_ptes = i; +- page->partial_pte = 0; ++ page->partial_flags = 0; + rc = -ERESTART; + } + else if ( rc < 0 && rc != -EINTR ) +@@ -1539,7 +1560,7 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) + if ( i ) + { + page->nr_validated_ptes = i; +- page->partial_pte = 0; ++ page->partial_flags = 0; + current->arch.old_guest_ptpg = NULL; + current->arch.old_guest_table = page; + } +@@ -1563,7 +1584,8 @@ static int alloc_l3_table(struct page_info *page) + unsigned long pfn = mfn_x(page_to_mfn(page)); + l3_pgentry_t *pl3e; + unsigned int i; +- int rc = 0, partial = page->partial_pte; ++ int rc = 0; ++ unsigned int partial_flags = page->partial_flags; + + pl3e = map_domain_page(_mfn(pfn)); + +@@ -1578,7 +1600,7 @@ static int alloc_l3_table(struct page_info *page) + memset(pl3e + 4, 0, (L3_PAGETABLE_ENTRIES - 4) * sizeof(*pl3e)); + + for ( i = page->nr_validated_ptes; i < L3_PAGETABLE_ENTRIES; +- i++, partial = 0 ) ++ i++, partial_flags = 0 ) + { + l3_pgentry_t l3e = pl3e[i]; + +@@ -1597,7 +1619,8 @@ static int alloc_l3_table(struct page_info *page) + else + rc = get_page_and_type_from_mfn( + l3e_get_mfn(l3e), +- PGT_l2_page_table | PGT_pae_xen_l2, d, partial, 1); ++ PGT_l2_page_table | PGT_pae_xen_l2, d, ++ partial_flags | PTF_preemptible); + } + else if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) ) + { +@@ -1606,17 +1629,18 @@ static int alloc_l3_table(struct page_info *page) + rc = -EINTR; + } + else +- rc = get_page_from_l3e(l3e, pfn, d, partial); ++ rc = get_page_from_l3e(l3e, pfn, d, partial_flags); + + if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; +- page->partial_pte = partial ?: 1; ++ /* Set 'set', leave 'general ref' set if this entry was set */ ++ page->partial_flags = partial_flags | PTF_partial_set; + } + else if ( rc == -EINTR && i ) + { + page->nr_validated_ptes = i; +- page->partial_pte = 0; ++ page->partial_flags = 0; + rc = -ERESTART; + } + if ( rc < 0 ) +@@ -1633,7 +1657,7 @@ static int alloc_l3_table(struct page_info *page) + if ( i ) + { + page->nr_validated_ptes = i; +- page->partial_pte = 0; ++ page->partial_flags = 0; + current->arch.old_guest_ptpg = NULL; + current->arch.old_guest_table = page; + } +@@ -1767,10 +1791,11 @@ static int alloc_l4_table(struct page_info *page) + unsigned long pfn = mfn_x(page_to_mfn(page)); + l4_pgentry_t *pl4e = map_domain_page(_mfn(pfn)); + unsigned int i; +- int rc = 0, partial = page->partial_pte; ++ int rc = 0; ++ unsigned int partial_flags = page->partial_flags; + + for ( i = page->nr_validated_ptes; i < L4_PAGETABLE_ENTRIES; +- i++, partial = 0 ) ++ i++, partial_flags = 0 ) + { + l4_pgentry_t l4e; + +@@ -1786,12 +1811,13 @@ static int alloc_l4_table(struct page_info *page) + rc = -EINTR; + } + else +- rc = get_page_from_l4e(l4e, pfn, d, partial); ++ rc = get_page_from_l4e(l4e, pfn, d, partial_flags); + + if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; +- page->partial_pte = partial ?: 1; ++ /* Set 'set', leave 'general ref' set if this entry was set */ ++ page->partial_flags = partial_flags | PTF_partial_set; + } + else if ( rc < 0 ) + { +@@ -1801,7 +1827,7 @@ static int alloc_l4_table(struct page_info *page) + if ( i ) + { + page->nr_validated_ptes = i; +- page->partial_pte = 0; ++ page->partial_flags = 0; + if ( rc == -EINTR ) + rc = -ERESTART; + else +@@ -1853,19 +1879,20 @@ static int free_l2_table(struct page_info *page) + struct domain *d = page_get_owner(page); + unsigned long pfn = mfn_x(page_to_mfn(page)); + l2_pgentry_t *pl2e; +- int rc = 0, partial = page->partial_pte; +- unsigned int i = page->nr_validated_ptes - !partial; ++ int rc = 0; ++ unsigned int partial_flags = page->partial_flags, ++ i = page->nr_validated_ptes - !(partial_flags & PTF_partial_set); + + pl2e = map_domain_page(_mfn(pfn)); + + for ( ; ; ) + { + if ( is_guest_l2_slot(d, page->u.inuse.type_info, i) ) +- rc = put_page_from_l2e(pl2e[i], pfn, partial, false); ++ rc = put_page_from_l2e(pl2e[i], pfn, partial_flags); + if ( rc < 0 ) + break; + +- partial = 0; ++ partial_flags = 0; + + if ( !i-- ) + break; +@@ -1887,12 +1914,14 @@ static int free_l2_table(struct page_info *page) + else if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; +- page->partial_pte = partial ?: -1; ++ page->partial_flags = (partial_flags & PTF_partial_set) ? ++ partial_flags : ++ (PTF_partial_set | PTF_partial_general_ref); + } + else if ( rc == -EINTR && i < L2_PAGETABLE_ENTRIES - 1 ) + { + page->nr_validated_ptes = i + 1; +- page->partial_pte = 0; ++ page->partial_flags = 0; + rc = -ERESTART; + } + +@@ -1904,18 +1933,19 @@ static int free_l3_table(struct page_info *page) + struct domain *d = page_get_owner(page); + unsigned long pfn = mfn_x(page_to_mfn(page)); + l3_pgentry_t *pl3e; +- int rc = 0, partial = page->partial_pte; +- unsigned int i = page->nr_validated_ptes - !partial; ++ int rc = 0; ++ unsigned int partial_flags = page->partial_flags, ++ i = page->nr_validated_ptes - !(partial_flags & PTF_partial_set); + + pl3e = map_domain_page(_mfn(pfn)); + + for ( ; ; ) + { +- rc = put_page_from_l3e(pl3e[i], pfn, partial, 0); ++ rc = put_page_from_l3e(pl3e[i], pfn, partial_flags); + if ( rc < 0 ) + break; + +- partial = 0; ++ partial_flags = 0; + if ( rc == 0 ) + pl3e[i] = unadjust_guest_l3e(pl3e[i], d); + +@@ -1934,12 +1964,14 @@ static int free_l3_table(struct page_info *page) + if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; +- page->partial_pte = partial ?: -1; ++ page->partial_flags = (partial_flags & PTF_partial_set) ? ++ partial_flags : ++ (PTF_partial_set | PTF_partial_general_ref); + } + else if ( rc == -EINTR && i < L3_PAGETABLE_ENTRIES - 1 ) + { + page->nr_validated_ptes = i + 1; +- page->partial_pte = 0; ++ page->partial_flags = 0; + rc = -ERESTART; + } + return rc > 0 ? 0 : rc; +@@ -1950,26 +1982,29 @@ static int free_l4_table(struct page_info *page) + struct domain *d = page_get_owner(page); + unsigned long pfn = mfn_x(page_to_mfn(page)); + l4_pgentry_t *pl4e = map_domain_page(_mfn(pfn)); +- int rc = 0, partial = page->partial_pte; +- unsigned int i = page->nr_validated_ptes - !partial; ++ int rc = 0; ++ unsigned partial_flags = page->partial_flags, ++ i = page->nr_validated_ptes - !(partial_flags & PTF_partial_set); + + do { + if ( is_guest_l4_slot(d, i) ) +- rc = put_page_from_l4e(pl4e[i], pfn, partial, 0); ++ rc = put_page_from_l4e(pl4e[i], pfn, partial_flags); + if ( rc < 0 ) + break; +- partial = 0; ++ partial_flags = 0; + } while ( i-- ); + + if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; +- page->partial_pte = partial ?: -1; ++ page->partial_flags = (partial_flags & PTF_partial_set) ? ++ partial_flags : ++ (PTF_partial_set | PTF_partial_general_ref); + } + else if ( rc == -EINTR && i < L4_PAGETABLE_ENTRIES - 1 ) + { + page->nr_validated_ptes = i + 1; +- page->partial_pte = 0; ++ page->partial_flags = 0; + rc = -ERESTART; + } + +@@ -2247,7 +2282,7 @@ static int mod_l2_entry(l2_pgentry_t *pl2e, + return -EBUSY; + } + +- put_page_from_l2e(ol2e, pfn, 0, true); ++ put_page_from_l2e(ol2e, pfn, PTF_defer); + + return rc; + } +@@ -2315,7 +2350,7 @@ static int mod_l3_entry(l3_pgentry_t *pl3e, + if ( !create_pae_xen_mappings(d, pl3e) ) + BUG(); + +- put_page_from_l3e(ol3e, pfn, 0, 1); ++ put_page_from_l3e(ol3e, pfn, PTF_defer); + return rc; + } + +@@ -2378,7 +2413,7 @@ static int mod_l4_entry(l4_pgentry_t *pl4e, + return -EFAULT; + } + +- put_page_from_l4e(ol4e, pfn, 0, 1); ++ put_page_from_l4e(ol4e, pfn, PTF_defer); + return rc; + } + #endif /* CONFIG_PV */ +@@ -2649,7 +2684,7 @@ int free_page_type(struct page_info *page, unsigned long type, + if ( !(type & PGT_partial) ) + { + page->nr_validated_ptes = 1U << PAGETABLE_ORDER; +- page->partial_pte = 0; ++ page->partial_flags = 0; + } + + switch ( type & PGT_type_mask ) +@@ -2946,7 +2981,7 @@ static int _get_page_type(struct page_info *page, unsigned long type, + if ( !(x & PGT_partial) ) + { + page->nr_validated_ptes = 0; +- page->partial_pte = 0; ++ page->partial_flags = 0; + } + page->linear_pt_count = 0; + rc = alloc_page_type(page, type, preemptible); +@@ -3122,7 +3157,7 @@ int new_guest_cr3(mfn_t mfn) + return 0; + } + +- rc = get_page_and_type_from_mfn(mfn, PGT_root_page_table, d, 0, 1); ++ rc = get_page_and_type_from_mfn(mfn, PGT_root_page_table, d, PTF_preemptible); + switch ( rc ) + { + case 0: +@@ -3473,7 +3508,7 @@ long do_mmuext_op( + if ( op.arg1.mfn != 0 ) + { + rc = get_page_and_type_from_mfn( +- _mfn(op.arg1.mfn), PGT_root_page_table, currd, 0, 1); ++ _mfn(op.arg1.mfn), PGT_root_page_table, currd, PTF_preemptible); + + if ( unlikely(rc) ) + { +diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h +index 6faa563167..8406ac3c37 100644 +--- a/xen/include/asm-x86/mm.h ++++ b/xen/include/asm-x86/mm.h +@@ -228,19 +228,34 @@ struct page_info + * setting the flag must not drop that reference, whereas the instance + * clearing it will have to. + * +- * If @partial_pte is positive then PTE at @nr_validated_ptes+1 has +- * been partially validated. This implies that the general reference +- * to the page (acquired from get_page_from_lNe()) would be dropped +- * (again due to the apparent failure) and hence must be re-acquired +- * when resuming the validation, but must not be dropped when picking +- * up the page for invalidation. ++ * If partial_flags & PTF_partial_set is set, then the page at ++ * at @nr_validated_ptes had PGT_partial set as a result of an ++ * operation on the current page. (That page may or may not ++ * still have PGT_partial set.) + * +- * If @partial_pte is negative then PTE at @nr_validated_ptes+1 has +- * been partially invalidated. This is basically the opposite case of +- * above, i.e. the general reference to the page was not dropped in +- * put_page_from_lNe() (due to the apparent failure), and hence it +- * must be dropped when the put operation is resumed (and completes), +- * but it must not be acquired if picking up the page for validation. ++ * If PTF_partial_general_ref is set, then the PTE at ++ * @nr_validated_ptef holds a general reference count for the ++ * page. ++ * ++ * This happens: ++ * - During de-validation, if de-validation of the page was ++ * interrupted ++ * - During validation, if an invalid entry is encountered and ++ * validation is preemptible ++ * - During validation, if PTF_partial_general_ref was set on ++ * this entry to begin with (perhaps because we're picking ++ * up from a partial de-validation). ++ * ++ * When resuming validation, if PTF_partial_general_ref is clear, ++ * then a general reference must be re-acquired; if it is set, no ++ * reference should be acquired. ++ * ++ * When resuming de-validation, if PTF_partial_general_ref is ++ * clear, no reference should be dropped; if it is set, a ++ * reference should be dropped. ++ * ++ * NB that PTF_partial_set and PTF_partial_general_ref are ++ * defined in mm.c, the only place where they are used. + * + * The 3rd field, @linear_pt_count, indicates + * - by a positive value, how many same-level page table entries a page +@@ -251,7 +266,7 @@ struct page_info + struct { + u16 nr_validated_ptes:PAGETABLE_ORDER + 1; + u16 :16 - PAGETABLE_ORDER - 1 - 2; +- s16 partial_pte:2; ++ u16 partial_flags:2; + s16 linear_pt_count; + }; + +-- +2.23.0 + diff --git a/xsa299-4.12-0004-x86-mm-Use-flags-for-_put_page_type-rather-than-a-bo.patch b/xsa299-4.12-0004-x86-mm-Use-flags-for-_put_page_type-rather-than-a-bo.patch new file mode 100644 index 0000000..d07c233 --- /dev/null +++ b/xsa299-4.12-0004-x86-mm-Use-flags-for-_put_page_type-rather-than-a-bo.patch @@ -0,0 +1,140 @@ +From db1d801aa8dcb918a27486a6e8d9cf5d7307dec3 Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Thu, 10 Oct 2019 17:57:49 +0100 +Subject: [PATCH 04/11] x86/mm: Use flags for _put_page_type rather than a + boolean + +This is in mainly in preparation for _put_page_type taking the +partial_flags value in the future. It also makes it easier to read in +the caller (since you see a flag name rather than `true` or `false`). + +No functional change intended. + +This is part of XSA-299. + +Reported-by: George Dunlap +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +--- + xen/arch/x86/mm.c | 25 +++++++++++++------------ + 1 file changed, 13 insertions(+), 12 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 3f6f8cc9b8..0740b61af8 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -1200,7 +1200,7 @@ get_page_from_l4e( + } + #endif /* CONFIG_PV */ + +-static int _put_page_type(struct page_info *page, bool preemptible, ++static int _put_page_type(struct page_info *page, unsigned int flags, + struct page_info *ptpg); + + void put_page_from_l1e(l1_pgentry_t l1e, struct domain *l1e_owner) +@@ -1320,7 +1320,7 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, + PTF_partial_set ) + { + ASSERT(!(flags & PTF_defer)); +- rc = _put_page_type(pg, true, ptpg); ++ rc = _put_page_type(pg, PTF_preemptible, ptpg); + } + else if ( flags & PTF_defer ) + { +@@ -1329,7 +1329,7 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, + } + else + { +- rc = _put_page_type(pg, true, ptpg); ++ rc = _put_page_type(pg, PTF_preemptible, ptpg); + if ( likely(!rc) ) + put_page(pg); + } +@@ -1366,7 +1366,7 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, + PTF_partial_set ) + { + ASSERT(!(flags & PTF_defer)); +- return _put_page_type(pg, true, mfn_to_page(_mfn(pfn))); ++ return _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); + } + + if ( flags & PTF_defer ) +@@ -1376,7 +1376,7 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, + return 0; + } + +- rc = _put_page_type(pg, true, mfn_to_page(_mfn(pfn))); ++ rc = _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); + if ( likely(!rc) ) + put_page(pg); + +@@ -1397,7 +1397,7 @@ static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, + PTF_partial_set ) + { + ASSERT(!(flags & PTF_defer)); +- return _put_page_type(pg, true, mfn_to_page(_mfn(pfn))); ++ return _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); + } + + if ( flags & PTF_defer ) +@@ -1407,7 +1407,7 @@ static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, + return 0; + } + +- rc = _put_page_type(pg, true, mfn_to_page(_mfn(pfn))); ++ rc = _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); + if ( likely(!rc) ) + put_page(pg); + } +@@ -2757,10 +2757,11 @@ static int _put_final_page_type(struct page_info *page, unsigned long type, + } + + +-static int _put_page_type(struct page_info *page, bool preemptible, ++static int _put_page_type(struct page_info *page, unsigned int flags, + struct page_info *ptpg) + { + unsigned long nx, x, y = page->u.inuse.type_info; ++ bool preemptible = flags & PTF_preemptible; + + ASSERT(current_locked_page_ne_check(page)); + +@@ -2969,7 +2970,7 @@ static int _get_page_type(struct page_info *page, unsigned long type, + + if ( unlikely(iommu_ret) ) + { +- _put_page_type(page, false, NULL); ++ _put_page_type(page, 0, NULL); + rc = iommu_ret; + goto out; + } +@@ -2996,7 +2997,7 @@ static int _get_page_type(struct page_info *page, unsigned long type, + + void put_page_type(struct page_info *page) + { +- int rc = _put_page_type(page, false, NULL); ++ int rc = _put_page_type(page, 0, NULL); + ASSERT(rc == 0); + (void)rc; + } +@@ -3013,7 +3014,7 @@ int get_page_type(struct page_info *page, unsigned long type) + + int put_page_type_preemptible(struct page_info *page) + { +- return _put_page_type(page, true, NULL); ++ return _put_page_type(page, PTF_preemptible, NULL); + } + + int get_page_type_preemptible(struct page_info *page, unsigned long type) +@@ -3030,7 +3031,7 @@ int put_old_guest_table(struct vcpu *v) + if ( !v->arch.old_guest_table ) + return 0; + +- switch ( rc = _put_page_type(v->arch.old_guest_table, true, ++ switch ( rc = _put_page_type(v->arch.old_guest_table, PTF_preemptible, + v->arch.old_guest_ptpg) ) + { + case -EINTR: +-- +2.23.0 + diff --git a/xsa299-4.12-0005-x86-mm-Rework-get_page_and_type_from_mfn-conditional.patch b/xsa299-4.12-0005-x86-mm-Rework-get_page_and_type_from_mfn-conditional.patch new file mode 100644 index 0000000..9cfbb73 --- /dev/null +++ b/xsa299-4.12-0005-x86-mm-Rework-get_page_and_type_from_mfn-conditional.patch @@ -0,0 +1,79 @@ +From 6f257854c8778774210281c5c21028c4b7739b44 Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Thu, 10 Oct 2019 17:57:49 +0100 +Subject: [PATCH 05/11] x86/mm: Rework get_page_and_type_from_mfn conditional + +Make it easier to read by declaring the conditions in which we will +retain the ref, rather than the conditions under which we release it. + +The only way (page == current->arch.old_guest_table) can be true is if +preemptible is true; so remove this from the query itself, and add an +ASSERT() to that effect on the opposite path. + +No functional change intended. + +NB that alloc_lN_table() mishandle the "linear pt failure" situation +described in the comment; this will be addressed in a future patch. + +This is part of XSA-299. + +Reported-by: George Dunlap +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +--- + xen/arch/x86/mm.c | 39 +++++++++++++++++++++++++++++++++++++-- + 1 file changed, 37 insertions(+), 2 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 0740b61af8..0a4d39a2c3 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -1122,8 +1122,43 @@ static int get_page_and_type_from_mfn( + + rc = _get_page_type(page, type, preemptible); + +- if ( unlikely(rc) && !partial_ref && +- (!preemptible || page != current->arch.old_guest_table) ) ++ /* ++ * Retain the refcount if: ++ * - page is fully validated (rc == 0) ++ * - page is not validated (rc < 0) but: ++ * - We came in with a reference (partial_ref) ++ * - page is partially validated but there's been an error ++ * (page == current->arch.old_guest_table) ++ * ++ * The partial_ref-on-error clause is worth an explanation. There ++ * are two scenarios where partial_ref might be true coming in: ++ * - mfn has been partially demoted as type `type`; i.e. has ++ * PGT_partial set ++ * - mfn has been partially demoted as L(type+1) (i.e., a linear ++ * page; e.g. we're being called from get_page_from_l2e with ++ * type == PGT_l1_table, but the mfn is PGT_l2_table) ++ * ++ * If there's an error, in the first case, _get_page_type will ++ * either return -ERESTART, in which case we want to retain the ++ * ref (as the caller will consider it retained), or -EINVAL, in ++ * which case old_guest_table will be set; in both cases, we need ++ * to retain the ref. ++ * ++ * In the second case, if there's an error, _get_page_type() can ++ * *only* return -EINVAL, and *never* set old_guest_table. In ++ * that case we also want to retain the reference, to allow the ++ * page to continue to be torn down (i.e., PGT_partial cleared) ++ * safely. ++ * ++ * Also note that we shouldn't be able to leave with the reference ++ * count retained unless we succeeded, or the operation was ++ * preemptible. ++ */ ++ if ( likely(!rc) || partial_ref ) ++ /* nothing */; ++ else if ( page == current->arch.old_guest_table ) ++ ASSERT(preemptible); ++ else + put_page(page); + + return rc; +-- +2.23.0 + diff --git a/xsa299-4.12-0006-x86-mm-Have-alloc_l-23-_table-clear-partial_flags-wh.patch b/xsa299-4.12-0006-x86-mm-Have-alloc_l-23-_table-clear-partial_flags-wh.patch new file mode 100644 index 0000000..72ee3ea --- /dev/null +++ b/xsa299-4.12-0006-x86-mm-Have-alloc_l-23-_table-clear-partial_flags-wh.patch @@ -0,0 +1,111 @@ +From 4ad70553611a7a4e4494d5a3b51b5cc295a488e0 Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Thu, 10 Oct 2019 17:57:49 +0100 +Subject: [PATCH 06/11] x86/mm: Have alloc_l[23]_table clear partial_flags when + preempting + +In order to allow recursive pagetable promotions and demotions to be +interrupted, Xen must keep track of the state of the sub-pages +promoted or demoted. This is stored in two elements in the page +struct: nr_entries_validated and partial_flags. + +The rule is that entries [0, nr_entries_validated) should always be +validated and hold a general reference count. If partial_flags is +zero, then [nr_entries_validated] is not validated and no reference +count is held. If PTF_partial_set is set, then [nr_entries_validated] +is partially validated. + +At the moment, a distinction is made between promotion and demotion +with regard to whether the entry itself "holds" a general reference +count: when entry promotion is interrupted (i.e., returns -ERESTART), +the entry is not considered to hold a reference; when entry demotion +is interrupted, the entry is still considered to hold a general +reference. + +PTF_partial_general_ref is used to distinguish between these cases. +If clear, it's a partial promotion => no general reference count held +by the entry; if set, it's partial demotion, so a general reference +count held. Because promotions and demotions can be interleaved, this +value is passed to get_page_and_type_from_mfn and put_page_from_l*e, +to be able to properly handle reference counts. + +Unfortunately, when alloc_l[23]_table check hypercall_preempt_check() +and return -ERESTART, they set nr_entries_validated, but don't clear +partial_flags. + +If we were picking up from a previously-interrupted promotion, that +means that PTF_partial_set would be set even though +[nr_entries_validated] was not partially validated. This means that +if the page in this state were de-validated, put_page_type() would +erroneously be called on that entry. + +Perhaps worse, if we were racing with a de-validation, then we might +leave both PTF_partial_set and PTF_partial_general_ref; and when +de-validation picked up again, both the type and the general ref would +be erroneously dropped from [nr_entries_validated]. + +In a sense, the real issue here is code duplication. Rather than +duplicate the interruption code, set rc to -EINTR and fall through to +the code which already handles that case correctly. + +Given the logic at this point, it should be impossible for +partial_flags to be non-zero; add an ASSERT() to catch any changes. + +This is part of XSA-299. + +Reported-by: George Dunlap +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +--- + xen/arch/x86/mm.c | 25 ++++++------------------- + 1 file changed, 6 insertions(+), 19 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 0a4d39a2c3..bbd29a68f4 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -1554,21 +1554,13 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) + for ( i = page->nr_validated_ptes; i < L2_PAGETABLE_ENTRIES; + i++, partial_flags = 0 ) + { +- l2_pgentry_t l2e; ++ l2_pgentry_t l2e = pl2e[i]; + + if ( i > page->nr_validated_ptes && hypercall_preempt_check() ) +- { +- page->nr_validated_ptes = i; +- rc = -ERESTART; +- break; +- } +- +- if ( !is_guest_l2_slot(d, type, i) ) ++ rc = -EINTR; ++ else if ( !is_guest_l2_slot(d, type, i) ) + continue; +- +- l2e = pl2e[i]; +- +- if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) ) ++ else if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) ) + { + if ( !pv_l1tf_check_l2e(d, l2e) ) + continue; +@@ -1640,13 +1632,8 @@ static int alloc_l3_table(struct page_info *page) + l3_pgentry_t l3e = pl3e[i]; + + if ( i > page->nr_validated_ptes && hypercall_preempt_check() ) +- { +- page->nr_validated_ptes = i; +- rc = -ERESTART; +- break; +- } +- +- if ( is_pv_32bit_domain(d) && (i == 3) ) ++ rc = -EINTR; ++ else if ( is_pv_32bit_domain(d) && (i == 3) ) + { + if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || + (l3e_get_flags(l3e) & l3_disallow_mask(d)) ) +-- +2.23.0 + diff --git a/xsa299-4.12-0007-x86-mm-Always-retain-a-general-ref-on-partial.patch b/xsa299-4.12-0007-x86-mm-Always-retain-a-general-ref-on-partial.patch new file mode 100644 index 0000000..ef390e2 --- /dev/null +++ b/xsa299-4.12-0007-x86-mm-Always-retain-a-general-ref-on-partial.patch @@ -0,0 +1,378 @@ +From 51fe4e67d954649fcf103116be6206a769f0db1e Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Thu, 10 Oct 2019 17:57:49 +0100 +Subject: [PATCH 07/11] x86/mm: Always retain a general ref on partial + +In order to allow recursive pagetable promotions and demotions to be +interrupted, Xen must keep track of the state of the sub-pages +promoted or demoted. This is stored in two elements in the page struct: +nr_entries_validated and partial_flags. + +The rule is that entries [0, nr_entries_validated) should always be +validated and hold a general reference count. If partial_flags is +zero, then [nr_entries_validated] is not validated and no reference +count is held. If PTF_partial_set is set, then [nr_entries_validated] +is partially validated. + +At the moment, a distinction is made between promotion and demotion +with regard to whether the entry itself "holds" a general reference +count: when entry promotion is interrupted (i.e., returns -ERESTART), +the entry is not considered to hold a reference; when entry demotion +is interrupted, the entry is still considered to hold a general +reference. + +PTF_partial_general_ref is used to distinguish between these cases. +If clear, it's a partial promotion => no general reference count held +by the entry; if set, it's partial demotion, so a general reference +count held. Because promotions and demotions can be interleaved, this +value is passed to get_page_and_type_from_mfn and put_page_from_l*e, +to be able to properly handle reference counts. + +Unfortunately, because a refcount is not held, it is possible to +engineer a situation where PFT_partial_set is set but the page in +question has been assigned to another domain. A sketch is provided in +the appendix. + +Fix this by having the parent page table entry hold a general +reference count whenever PFT_partial_set is set. (For clarity of +change, keep two separate flags. These will be collapsed in a +subsequent changeset.) + +This has two basic implications. On the put_page_from_lNe() side, +this mean that the (partial_set && !partial_ref) case can never happen, +and no longer needs to be special-cased. + +Secondly, because both flags are set together, there's no need to carry over +existing bits from partial_pte. + +(NB there is still another issue with calling _put_page_type() on a +page which had PGT_partial set; that will be handled in a subsequent +patch.) + +On the get_page_and_type_from_mfn() side, we need to distinguish +between callers which hold a reference on partial (i.e., +alloc_lN_table()), and those which do not (new_cr3, PIN_LN_TABLE, and +so on): pass a flag if the type should be retained on interruption. + +NB that since l1 promotion can't be preempted, that get_page_from_l2e +can't return -ERESTART. + +This is part of XSA-299. + +Reported-by: George Dunlap +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +----- +* Appendix: Engineering PTF_partial_set while a page belongs to a + foreign domain + +Suppose A is a page which can be promoted to an l3, and B is a page +which can be promoted to an l2, and A[x] points to B. B has +PGC_allocated set but no other general references. + +V1: PIN_L3 A. + A is validated, B is validated. + A.type_count = 1 | PGT_validated | PGT_pinned + B.type_count = 1 | PGT_validated + B.count = 2 | PGC_allocated (A[x] holds a general ref) + +V1: UNPIN A. + A begins de-validation. + Arrange to be interrupted when i < x + V1->old_guest_table = A + V1->old_guest_table_ref_held = false + A.type_count = 1 | PGT_partial + A.nr_validated_entries = i < x + B.type_count = 0 + B.count = 1 | PGC_allocated + +V2: MOD_L4_ENTRY to point some l4e to A. + Picks up re-validation of A. + Arrange to be interrupted halfway through B's validation + B.type_count = 1 | PGT_partial + B.count = 2 | PGC_allocated (PGT_partial holds a general ref) + A.type_count = 1 | PGT_partial + A.nr_validated_entries = x + A.partial_pte = PTF_partial_set + +V3: MOD_L3_ENTRY to point some other l3e (not in A) to B. + Validates B. + B.type_count = 1 | PGT_validated + B.count = 2 | PGC_allocated ("other l3e" holds a general ref) + +V3: MOD_L3_ENTRY to clear l3e pointing to B. + Devalidates B. + B.type_count = 0 + B.count = 1 | PGC_allocated + +V3: decrease_reservation(B) + Clears PGC_allocated + B.count = 0 => B is freed + +B gets assigned to a different domain + +V1: Restarts UNPIN of A + put_old_guest_table(A) + ... + free_l3_table(A) + +Now since A.partial_flags has PTF_partial_set, free_l3_table() will +call put_page_from_l3e() on A[x], which points to B, while B is owned +by another domain. + +If A[x] held a general refcount for B on partial validation, as it does +for partial de-validation, then B would still have a reference count of +1 after PGC_allocated was freed; so B wouldn't be freed until after +put_page_from_l3e() had happend on A[x]. +--- + xen/arch/x86/mm.c | 84 +++++++++++++++++++++++----------------- + xen/include/asm-x86/mm.h | 15 ++++--- + 2 files changed, 58 insertions(+), 41 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index bbd29a68f4..4d3ebf341d 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -1102,10 +1102,11 @@ get_page_from_l1e( + * page->pte[page->nr_validated_entries]. See the comment in mm.h for + * more information. + */ +-#define PTF_partial_set (1 << 0) +-#define PTF_partial_general_ref (1 << 1) +-#define PTF_preemptible (1 << 2) +-#define PTF_defer (1 << 3) ++#define PTF_partial_set (1 << 0) ++#define PTF_partial_general_ref (1 << 1) ++#define PTF_preemptible (1 << 2) ++#define PTF_defer (1 << 3) ++#define PTF_retain_ref_on_restart (1 << 4) + + static int get_page_and_type_from_mfn( + mfn_t mfn, unsigned long type, struct domain *d, +@@ -1114,7 +1115,11 @@ static int get_page_and_type_from_mfn( + struct page_info *page = mfn_to_page(mfn); + int rc; + bool preemptible = flags & PTF_preemptible, +- partial_ref = flags & PTF_partial_general_ref; ++ partial_ref = flags & PTF_partial_general_ref, ++ partial_set = flags & PTF_partial_set, ++ retain_ref = flags & PTF_retain_ref_on_restart; ++ ++ ASSERT(partial_ref == partial_set); + + if ( likely(!partial_ref) && + unlikely(!get_page_from_mfn(mfn, d)) ) +@@ -1127,13 +1132,15 @@ static int get_page_and_type_from_mfn( + * - page is fully validated (rc == 0) + * - page is not validated (rc < 0) but: + * - We came in with a reference (partial_ref) ++ * - page is partially validated (rc == -ERESTART), and the ++ * caller has asked the ref to be retained in that case + * - page is partially validated but there's been an error + * (page == current->arch.old_guest_table) + * + * The partial_ref-on-error clause is worth an explanation. There + * are two scenarios where partial_ref might be true coming in: +- * - mfn has been partially demoted as type `type`; i.e. has +- * PGT_partial set ++ * - mfn has been partially promoted / demoted as type `type`; ++ * i.e. has PGT_partial set + * - mfn has been partially demoted as L(type+1) (i.e., a linear + * page; e.g. we're being called from get_page_from_l2e with + * type == PGT_l1_table, but the mfn is PGT_l2_table) +@@ -1156,7 +1163,8 @@ static int get_page_and_type_from_mfn( + */ + if ( likely(!rc) || partial_ref ) + /* nothing */; +- else if ( page == current->arch.old_guest_table ) ++ else if ( page == current->arch.old_guest_table || ++ (retain_ref && rc == -ERESTART) ) + ASSERT(preemptible); + else + put_page(page); +@@ -1354,8 +1362,8 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, + if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == + PTF_partial_set ) + { +- ASSERT(!(flags & PTF_defer)); +- rc = _put_page_type(pg, PTF_preemptible, ptpg); ++ /* partial_set should always imply partial_ref */ ++ BUG(); + } + else if ( flags & PTF_defer ) + { +@@ -1400,8 +1408,8 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, + if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == + PTF_partial_set ) + { +- ASSERT(!(flags & PTF_defer)); +- return _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); ++ /* partial_set should always imply partial_ref */ ++ BUG(); + } + + if ( flags & PTF_defer ) +@@ -1431,8 +1439,8 @@ static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, + if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == + PTF_partial_set ) + { +- ASSERT(!(flags & PTF_defer)); +- return _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); ++ /* partial_set should always imply partial_ref */ ++ BUG(); + } + + if ( flags & PTF_defer ) +@@ -1569,13 +1577,22 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) + else + rc = get_page_from_l2e(l2e, pfn, d, partial_flags); + +- if ( rc == -ERESTART ) +- { +- page->nr_validated_ptes = i; +- /* Set 'set', retain 'general ref' */ +- page->partial_flags = partial_flags | PTF_partial_set; +- } +- else if ( rc == -EINTR && i ) ++ /* ++ * It shouldn't be possible for get_page_from_l2e to return ++ * -ERESTART, since we never call this with PTF_preemptible. ++ * (alloc_l1_table may return -EINTR on an L1TF-vulnerable ++ * entry.) ++ * ++ * NB that while on a "clean" promotion, we can never get ++ * PGT_partial. It is possible to arrange for an l2e to ++ * contain a partially-devalidated l2; but in that case, both ++ * of the following functions will fail anyway (the first ++ * because the page in question is not an l1; the second ++ * because the page is not fully validated). ++ */ ++ ASSERT(rc != -ERESTART); ++ ++ if ( rc == -EINTR && i ) + { + page->nr_validated_ptes = i; + page->partial_flags = 0; +@@ -1584,6 +1601,7 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) + else if ( rc < 0 && rc != -EINTR ) + { + gdprintk(XENLOG_WARNING, "Failure in alloc_l2_table: slot %#x\n", i); ++ ASSERT(current->arch.old_guest_table == NULL); + if ( i ) + { + page->nr_validated_ptes = i; +@@ -1642,7 +1660,7 @@ static int alloc_l3_table(struct page_info *page) + rc = get_page_and_type_from_mfn( + l3e_get_mfn(l3e), + PGT_l2_page_table | PGT_pae_xen_l2, d, +- partial_flags | PTF_preemptible); ++ partial_flags | PTF_preemptible | PTF_retain_ref_on_restart); + } + else if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) ) + { +@@ -1651,13 +1669,14 @@ static int alloc_l3_table(struct page_info *page) + rc = -EINTR; + } + else +- rc = get_page_from_l3e(l3e, pfn, d, partial_flags); ++ rc = get_page_from_l3e(l3e, pfn, d, ++ partial_flags | PTF_retain_ref_on_restart); + + if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; + /* Set 'set', leave 'general ref' set if this entry was set */ +- page->partial_flags = partial_flags | PTF_partial_set; ++ page->partial_flags = PTF_partial_set | PTF_partial_general_ref; + } + else if ( rc == -EINTR && i ) + { +@@ -1833,13 +1852,14 @@ static int alloc_l4_table(struct page_info *page) + rc = -EINTR; + } + else +- rc = get_page_from_l4e(l4e, pfn, d, partial_flags); ++ rc = get_page_from_l4e(l4e, pfn, d, ++ partial_flags | PTF_retain_ref_on_restart); + + if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; + /* Set 'set', leave 'general ref' set if this entry was set */ +- page->partial_flags = partial_flags | PTF_partial_set; ++ page->partial_flags = PTF_partial_set | PTF_partial_general_ref; + } + else if ( rc < 0 ) + { +@@ -1936,9 +1956,7 @@ static int free_l2_table(struct page_info *page) + else if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; +- page->partial_flags = (partial_flags & PTF_partial_set) ? +- partial_flags : +- (PTF_partial_set | PTF_partial_general_ref); ++ page->partial_flags = PTF_partial_set | PTF_partial_general_ref; + } + else if ( rc == -EINTR && i < L2_PAGETABLE_ENTRIES - 1 ) + { +@@ -1986,9 +2004,7 @@ static int free_l3_table(struct page_info *page) + if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; +- page->partial_flags = (partial_flags & PTF_partial_set) ? +- partial_flags : +- (PTF_partial_set | PTF_partial_general_ref); ++ page->partial_flags = PTF_partial_set | PTF_partial_general_ref; + } + else if ( rc == -EINTR && i < L3_PAGETABLE_ENTRIES - 1 ) + { +@@ -2019,9 +2035,7 @@ static int free_l4_table(struct page_info *page) + if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; +- page->partial_flags = (partial_flags & PTF_partial_set) ? +- partial_flags : +- (PTF_partial_set | PTF_partial_general_ref); ++ page->partial_flags = PTF_partial_set | PTF_partial_general_ref; + } + else if ( rc == -EINTR && i < L4_PAGETABLE_ENTRIES - 1 ) + { +diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h +index 8406ac3c37..02079e1324 100644 +--- a/xen/include/asm-x86/mm.h ++++ b/xen/include/asm-x86/mm.h +@@ -238,22 +238,25 @@ struct page_info + * page. + * + * This happens: +- * - During de-validation, if de-validation of the page was ++ * - During validation or de-validation, if the operation was + * interrupted + * - During validation, if an invalid entry is encountered and + * validation is preemptible + * - During validation, if PTF_partial_general_ref was set on +- * this entry to begin with (perhaps because we're picking +- * up from a partial de-validation). ++ * this entry to begin with (perhaps because it picked up a ++ * previous operation) + * +- * When resuming validation, if PTF_partial_general_ref is clear, +- * then a general reference must be re-acquired; if it is set, no +- * reference should be acquired. ++ * When resuming validation, if PTF_partial_general_ref is ++ * clear, then a general reference must be re-acquired; if it ++ * is set, no reference should be acquired. + * + * When resuming de-validation, if PTF_partial_general_ref is + * clear, no reference should be dropped; if it is set, a + * reference should be dropped. + * ++ * NB at the moment, PTF_partial_set should be set if and only if ++ * PTF_partial_general_ref is set. ++ * + * NB that PTF_partial_set and PTF_partial_general_ref are + * defined in mm.c, the only place where they are used. + * +-- +2.23.0 + diff --git a/xsa299-4.12-0008-x86-mm-Collapse-PTF_partial_set-and-PTF_partial_gene.patch b/xsa299-4.12-0008-x86-mm-Collapse-PTF_partial_set-and-PTF_partial_gene.patch new file mode 100644 index 0000000..6cf41d1 --- /dev/null +++ b/xsa299-4.12-0008-x86-mm-Collapse-PTF_partial_set-and-PTF_partial_gene.patch @@ -0,0 +1,227 @@ +From 8a8d836f7f7418e659d37817a66cd7a6b115042b Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Thu, 10 Oct 2019 17:57:49 +0100 +Subject: [PATCH 08/11] x86/mm: Collapse PTF_partial_set and + PTF_partial_general_ref into one + +...now that they are equivalent. No functional change intended. + +Reported-by: George Dunlap +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +--- + xen/arch/x86/mm.c | 50 +++++++++++----------------------------- + xen/include/asm-x86/mm.h | 29 +++++++++++------------ + 2 files changed, 26 insertions(+), 53 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 4d3ebf341d..886e93b8aa 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -1097,13 +1097,12 @@ get_page_from_l1e( + + /* + * The following flags are used to specify behavior of various get and +- * put commands. The first two are also stored in page->partial_flags +- * to indicate the state of the page pointed to by ++ * put commands. The first is also stored in page->partial_flags to ++ * indicate the state of the page pointed to by + * page->pte[page->nr_validated_entries]. See the comment in mm.h for + * more information. + */ + #define PTF_partial_set (1 << 0) +-#define PTF_partial_general_ref (1 << 1) + #define PTF_preemptible (1 << 2) + #define PTF_defer (1 << 3) + #define PTF_retain_ref_on_restart (1 << 4) +@@ -1115,13 +1114,10 @@ static int get_page_and_type_from_mfn( + struct page_info *page = mfn_to_page(mfn); + int rc; + bool preemptible = flags & PTF_preemptible, +- partial_ref = flags & PTF_partial_general_ref, + partial_set = flags & PTF_partial_set, + retain_ref = flags & PTF_retain_ref_on_restart; + +- ASSERT(partial_ref == partial_set); +- +- if ( likely(!partial_ref) && ++ if ( likely(!partial_set) && + unlikely(!get_page_from_mfn(mfn, d)) ) + return -EINVAL; + +@@ -1131,14 +1127,14 @@ static int get_page_and_type_from_mfn( + * Retain the refcount if: + * - page is fully validated (rc == 0) + * - page is not validated (rc < 0) but: +- * - We came in with a reference (partial_ref) ++ * - We came in with a reference (partial_set) + * - page is partially validated (rc == -ERESTART), and the + * caller has asked the ref to be retained in that case + * - page is partially validated but there's been an error + * (page == current->arch.old_guest_table) + * +- * The partial_ref-on-error clause is worth an explanation. There +- * are two scenarios where partial_ref might be true coming in: ++ * The partial_set-on-error clause is worth an explanation. There ++ * are two scenarios where partial_set might be true coming in: + * - mfn has been partially promoted / demoted as type `type`; + * i.e. has PGT_partial set + * - mfn has been partially demoted as L(type+1) (i.e., a linear +@@ -1161,7 +1157,7 @@ static int get_page_and_type_from_mfn( + * count retained unless we succeeded, or the operation was + * preemptible. + */ +- if ( likely(!rc) || partial_ref ) ++ if ( likely(!rc) || partial_set ) + /* nothing */; + else if ( page == current->arch.old_guest_table || + (retain_ref && rc == -ERESTART) ) +@@ -1359,13 +1355,7 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, + struct page_info *pg = l2e_get_page(l2e); + struct page_info *ptpg = mfn_to_page(_mfn(pfn)); + +- if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == +- PTF_partial_set ) +- { +- /* partial_set should always imply partial_ref */ +- BUG(); +- } +- else if ( flags & PTF_defer ) ++ if ( flags & PTF_defer ) + { + current->arch.old_guest_ptpg = ptpg; + current->arch.old_guest_table = pg; +@@ -1405,13 +1395,6 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, + + pg = l3e_get_page(l3e); + +- if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == +- PTF_partial_set ) +- { +- /* partial_set should always imply partial_ref */ +- BUG(); +- } +- + if ( flags & PTF_defer ) + { + current->arch.old_guest_ptpg = mfn_to_page(_mfn(pfn)); +@@ -1436,13 +1419,6 @@ static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, + { + struct page_info *pg = l4e_get_page(l4e); + +- if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == +- PTF_partial_set ) +- { +- /* partial_set should always imply partial_ref */ +- BUG(); +- } +- + if ( flags & PTF_defer ) + { + current->arch.old_guest_ptpg = mfn_to_page(_mfn(pfn)); +@@ -1676,7 +1652,7 @@ static int alloc_l3_table(struct page_info *page) + { + page->nr_validated_ptes = i; + /* Set 'set', leave 'general ref' set if this entry was set */ +- page->partial_flags = PTF_partial_set | PTF_partial_general_ref; ++ page->partial_flags = PTF_partial_set; + } + else if ( rc == -EINTR && i ) + { +@@ -1859,7 +1835,7 @@ static int alloc_l4_table(struct page_info *page) + { + page->nr_validated_ptes = i; + /* Set 'set', leave 'general ref' set if this entry was set */ +- page->partial_flags = PTF_partial_set | PTF_partial_general_ref; ++ page->partial_flags = PTF_partial_set; + } + else if ( rc < 0 ) + { +@@ -1956,7 +1932,7 @@ static int free_l2_table(struct page_info *page) + else if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; +- page->partial_flags = PTF_partial_set | PTF_partial_general_ref; ++ page->partial_flags = PTF_partial_set; + } + else if ( rc == -EINTR && i < L2_PAGETABLE_ENTRIES - 1 ) + { +@@ -2004,7 +1980,7 @@ static int free_l3_table(struct page_info *page) + if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; +- page->partial_flags = PTF_partial_set | PTF_partial_general_ref; ++ page->partial_flags = PTF_partial_set; + } + else if ( rc == -EINTR && i < L3_PAGETABLE_ENTRIES - 1 ) + { +@@ -2035,7 +2011,7 @@ static int free_l4_table(struct page_info *page) + if ( rc == -ERESTART ) + { + page->nr_validated_ptes = i; +- page->partial_flags = PTF_partial_set | PTF_partial_general_ref; ++ page->partial_flags = PTF_partial_set; + } + else if ( rc == -EINTR && i < L4_PAGETABLE_ENTRIES - 1 ) + { +diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h +index 02079e1324..f0fd35bf6b 100644 +--- a/xen/include/asm-x86/mm.h ++++ b/xen/include/asm-x86/mm.h +@@ -233,7 +233,7 @@ struct page_info + * operation on the current page. (That page may or may not + * still have PGT_partial set.) + * +- * If PTF_partial_general_ref is set, then the PTE at ++ * Additionally, if PTF_partial_set is set, then the PTE at + * @nr_validated_ptef holds a general reference count for the + * page. + * +@@ -242,23 +242,20 @@ struct page_info + * interrupted + * - During validation, if an invalid entry is encountered and + * validation is preemptible +- * - During validation, if PTF_partial_general_ref was set on +- * this entry to begin with (perhaps because it picked up a ++ * - During validation, if PTF_partial_set was set on this ++ * entry to begin with (perhaps because it picked up a + * previous operation) + * +- * When resuming validation, if PTF_partial_general_ref is +- * clear, then a general reference must be re-acquired; if it +- * is set, no reference should be acquired. ++ * When resuming validation, if PTF_partial_set is clear, then ++ * a general reference must be re-acquired; if it is set, no ++ * reference should be acquired. + * +- * When resuming de-validation, if PTF_partial_general_ref is +- * clear, no reference should be dropped; if it is set, a +- * reference should be dropped. ++ * When resuming de-validation, if PTF_partial_set is clear, ++ * no reference should be dropped; if it is set, a reference ++ * should be dropped. + * +- * NB at the moment, PTF_partial_set should be set if and only if +- * PTF_partial_general_ref is set. +- * +- * NB that PTF_partial_set and PTF_partial_general_ref are +- * defined in mm.c, the only place where they are used. ++ * NB that PTF_partial_set is defined in mm.c, the only place ++ * where it is used. + * + * The 3rd field, @linear_pt_count, indicates + * - by a positive value, how many same-level page table entries a page +@@ -268,8 +265,8 @@ struct page_info + */ + struct { + u16 nr_validated_ptes:PAGETABLE_ORDER + 1; +- u16 :16 - PAGETABLE_ORDER - 1 - 2; +- u16 partial_flags:2; ++ u16 :16 - PAGETABLE_ORDER - 1 - 1; ++ u16 partial_flags:1; + s16 linear_pt_count; + }; + +-- +2.23.0 + diff --git a/xsa299-4.12-0009-x86-mm-Properly-handle-linear-pagetable-promotion-fa.patch b/xsa299-4.12-0009-x86-mm-Properly-handle-linear-pagetable-promotion-fa.patch new file mode 100644 index 0000000..bbaba79 --- /dev/null +++ b/xsa299-4.12-0009-x86-mm-Properly-handle-linear-pagetable-promotion-fa.patch @@ -0,0 +1,106 @@ +From da3d1d258e54fe600f7f75287183b74d957ec63b Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Thu, 10 Oct 2019 17:57:49 +0100 +Subject: [PATCH 09/11] x86/mm: Properly handle linear pagetable promotion + failures + +In order to allow recursive pagetable promotions and demotions to be +interrupted, Xen must keep track of the state of the sub-pages +promoted or demoted. This is stored in two elements in the page +struct: nr_entries_validated and partial_flags. + +The rule is that entries [0, nr_entries_validated) should always be +validated and hold a general reference count. If partial_flags is +zero, then [nr_entries_validated] is not validated and no reference +count is held. If PTF_partial_set is set, then [nr_entries_validated] +is partially validated, and a general reference count is held. + +Unfortunately, in cases where an entry began with PTF_partial_set set, +and get_page_from_lNe() returns -EINVAL, the PTF_partial_set bit is +erroneously dropped. (This scenario can be engineered mainly by the +use of interleaving of promoting and demoting a page which has "linear +pagetable" entries; see the appendix for a sketch.) This means that +we will "leak" a general reference count on the page in question, +preventing the page from being freed. + +Fix this by setting page->partial_flags to the partial_flags local +variable. + +This is part of XSA-299. + +Reported-by: George Dunlap +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +----- +Appendix + +Suppose A and B can both be promoted to L2 pages, and A[x] points to B. + +V1: PIN_L2 B. + B.type_count = 1 | PGT_validated + B.count = 2 | PGC_allocated + +V1: MOD_L3_ENTRY pointing something to A. + In the process of validating A[x], grab an extra type / ref on B: + B.type_count = 2 | PGT_validated + B.count = 3 | PGC_allocated + A.type_count = 1 | PGT_validated + A.count = 2 | PGC_allocated + +V1: UNPIN B. + B.type_count = 1 | PGT_validate + B.count = 2 | PGC_allocated + +V1: MOD_L3_ENTRY removing the reference to A. + De-validate A, down to A[x], which points to B. + Drop the final type on B. Arrange to be interrupted. + B.type_count = 1 | PGT_partial + B.count = 2 | PGC_allocated + A.type_count = 1 | PGT_partial + A.nr_validated_entries = x + A.partial_pte = -1 + +V2: MOD_L3_ENTRY adds a reference to A. + +At this point, get_page_from_l2e(A[x]) tries +get_page_and_type_from_mfn(), which fails because it's the wrong type; +and get_l2_linear_pagetable() also fails, because B isn't validated as +an l2 anymore. +--- + xen/arch/x86/mm.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 886e93b8aa..0a094291da 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -1581,7 +1581,7 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) + if ( i ) + { + page->nr_validated_ptes = i; +- page->partial_flags = 0; ++ page->partial_flags = partial_flags; + current->arch.old_guest_ptpg = NULL; + current->arch.old_guest_table = page; + } +@@ -1674,7 +1674,7 @@ static int alloc_l3_table(struct page_info *page) + if ( i ) + { + page->nr_validated_ptes = i; +- page->partial_flags = 0; ++ page->partial_flags = partial_flags; + current->arch.old_guest_ptpg = NULL; + current->arch.old_guest_table = page; + } +@@ -1845,7 +1845,7 @@ static int alloc_l4_table(struct page_info *page) + if ( i ) + { + page->nr_validated_ptes = i; +- page->partial_flags = 0; ++ page->partial_flags = partial_flags; + if ( rc == -EINTR ) + rc = -ERESTART; + else +-- +2.23.0 + diff --git a/xsa299-4.12-0010-x86-mm-Fix-nested-de-validation-on-error.patch b/xsa299-4.12-0010-x86-mm-Fix-nested-de-validation-on-error.patch new file mode 100644 index 0000000..7d5f022 --- /dev/null +++ b/xsa299-4.12-0010-x86-mm-Fix-nested-de-validation-on-error.patch @@ -0,0 +1,166 @@ +From b3e169dc8daeae85b0b51c25fdb142e2e552ec7f Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Thu, 10 Oct 2019 17:57:49 +0100 +Subject: [PATCH 10/11] x86/mm: Fix nested de-validation on error + +If an invalid entry is discovered when validating a page-table tree, +the entire tree which has so far been validated must be de-validated. +Since this may take a long time, alloc_l[2-4]_table() set current +vcpu's old_guest_table immediately; put_old_guest_table() will make +sure that put_page_type() will be called to finish off the +de-validation before any other MMU operations can happen on the vcpu. + +The invariant for partial pages should be: + +* Entries [0, nr_validated_ptes) should be completely validated; + put_page_type() will de-validate these. + +* If [nr_validated_ptes] is partially validated, partial_flags should + set PTF_partiaL_set. put_page_type() will be called on this page to + finish off devalidation, and the appropriate refcount adjustments + will be done. + +alloc_l[2-3]_table() indicates partial validation to its callers by +setting current->old_guest_table. + +Unfortunately, this is mishandled. + +Take the case where validating lNe[x] returns an error. + +First, alloc_l3_table() doesn't check old_guest_table at all; as a +result, partial_flags is not set when it should be. nr_validated_ptes +is set to x; and since PFT_partial_set clear, de-validation resumes at +nr_validated_ptes-1. This means that the l2 page at pl3e[x] will not +have put_page_type() called on it when de-validating the rest of the +l3: it will be stuck in the PGT_partial state until the domain is +destroyed, or until it is re-used as an l2. (Any other page type will +fail.) + +Worse, alloc_l4_table(), rather than setting PTF_partial_set as it +should, sets nr_validated_ptes to x+1. When de-validating, since +partial is 0, this will correctly resume calling put_page_type at [x]; +but, if the put_page_type() is never called, but instead +get_page_type() is called, validation will pick up at [x+1], +neglecting to validate [x]. If the rest of the validation succeeds, +the l4 will be validated even though [x] is invalid. + +Fix this in both cases by setting PTF_partial_set if old_guest_table +is set. + +While here, add some safety catches: +- old_guest_table must point to the page contained in + [nr_validated_ptes]. +- alloc_l1_page shouldn't set old_guest_table + +If we experience one of these situations in production builds, it's +safer to avoid calling put_page_type for the pages in question. If +they have PGT_partial set, they will be cleaned up on domain +destruction; if not, we have no idea whether a type count is safe to +drop. Retaining an extra type ref that should have been dropped may +trigger a BUG() on the free_domain_page() path, but dropping a type +count that shouldn't be dropped may cause a privilege escalation. + +This is part of XSA-299. + +Reported-by: George Dunlap +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +--- + xen/arch/x86/mm.c | 53 +++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 51 insertions(+), 2 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 0a094291da..a432e69c74 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -1580,6 +1580,20 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) + ASSERT(current->arch.old_guest_table == NULL); + if ( i ) + { ++ /* ++ * alloc_l1_table() doesn't set old_guest_table; it does ++ * its own tear-down immediately on failure. If it ++ * did we'd need to check it and set partial_flags as we ++ * do in alloc_l[34]_table(). ++ * ++ * Note on the use of ASSERT: if it's non-null and ++ * hasn't been cleaned up yet, it should have ++ * PGT_partial set; and so the type will be cleaned up ++ * on domain destruction. Unfortunately, we would ++ * leak the general ref held by old_guest_table; but ++ * leaking a page is less bad than a host crash. ++ */ ++ ASSERT(current->arch.old_guest_table == NULL); + page->nr_validated_ptes = i; + page->partial_flags = partial_flags; + current->arch.old_guest_ptpg = NULL; +@@ -1607,6 +1621,7 @@ static int alloc_l3_table(struct page_info *page) + unsigned int i; + int rc = 0; + unsigned int partial_flags = page->partial_flags; ++ l3_pgentry_t l3e = l3e_empty(); + + pl3e = map_domain_page(_mfn(pfn)); + +@@ -1623,7 +1638,7 @@ static int alloc_l3_table(struct page_info *page) + for ( i = page->nr_validated_ptes; i < L3_PAGETABLE_ENTRIES; + i++, partial_flags = 0 ) + { +- l3_pgentry_t l3e = pl3e[i]; ++ l3e = pl3e[i]; + + if ( i > page->nr_validated_ptes && hypercall_preempt_check() ) + rc = -EINTR; +@@ -1675,6 +1690,24 @@ static int alloc_l3_table(struct page_info *page) + { + page->nr_validated_ptes = i; + page->partial_flags = partial_flags; ++ if ( current->arch.old_guest_table ) ++ { ++ /* ++ * We've experienced a validation failure. If ++ * old_guest_table is set, "transfer" the general ++ * reference count to pl3e[nr_validated_ptes] by ++ * setting PTF_partial_set. ++ * ++ * As a precaution, check that old_guest_table is the ++ * page pointed to by pl3e[nr_validated_ptes]. If ++ * not, it's safer to leak a type ref on production ++ * builds. ++ */ ++ if ( current->arch.old_guest_table == l3e_get_page(l3e) ) ++ page->partial_flags = PTF_partial_set; ++ else ++ ASSERT_UNREACHABLE(); ++ } + current->arch.old_guest_ptpg = NULL; + current->arch.old_guest_table = page; + } +@@ -1851,7 +1884,23 @@ static int alloc_l4_table(struct page_info *page) + else + { + if ( current->arch.old_guest_table ) +- page->nr_validated_ptes++; ++ { ++ /* ++ * We've experienced a validation failure. If ++ * old_guest_table is set, "transfer" the general ++ * reference count to pl3e[nr_validated_ptes] by ++ * setting PTF_partial_set. ++ * ++ * As a precaution, check that old_guest_table is the ++ * page pointed to by pl4e[nr_validated_ptes]. If ++ * not, it's safer to leak a type ref on production ++ * builds. ++ */ ++ if ( current->arch.old_guest_table == l4e_get_page(l4e) ) ++ page->partial_flags = PTF_partial_set; ++ else ++ ASSERT_UNREACHABLE(); ++ } + current->arch.old_guest_ptpg = NULL; + current->arch.old_guest_table = page; + } +-- +2.23.0 + diff --git a/xsa299-4.12-0011-x86-mm-Don-t-drop-a-type-ref-unless-you-held-a-ref-t.patch b/xsa299-4.12-0011-x86-mm-Don-t-drop-a-type-ref-unless-you-held-a-ref-t.patch new file mode 100644 index 0000000..ad7e6fe --- /dev/null +++ b/xsa299-4.12-0011-x86-mm-Don-t-drop-a-type-ref-unless-you-held-a-ref-t.patch @@ -0,0 +1,413 @@ +From ea3dc624c5e6325a9c2f079e52a85965d4ab6ce8 Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Thu, 10 Oct 2019 17:57:50 +0100 +Subject: [PATCH 11/11] x86/mm: Don't drop a type ref unless you held a ref to + begin with + +Validation and de-validation of pagetable trees may take arbitrarily +large amounts of time, and so must be preemptible. This is indicated +by setting the PGT_partial bit in the type_info, and setting +nr_validated_entries and partial_flags appropriately. Specifically, +if the entry at [nr_validated_entries] is partially validated, +partial_flags should have the PGT_partial_set bit set, and the entry +should hold a general reference count. During de-validation, +put_page_type() is called on partially validated entries. + +Unfortunately, there are a number of issues with the current algorithm. + +First, doing a "normal" put_page_type() is not safe when no type ref +is held: there is nothing to stop another vcpu from coming along and +picking up validation again: at which point the put_page_type may drop +the only page ref on an in-use page. Some examples are listed in the +appendix. + +The core issue is that put_page_type() is being called both to clean +up PGT_partial, and to drop a type count; and has no way of knowing +which is which; and so if in between, PGT_partial is cleared, +put_page_type() will drop the type ref erroneously. + +What is needed is to distinguish between two states: +- Dropping a type ref which is held +- Cleaning up a page which has been partially de/validated + +Fix this by telling put_page_type() which of the two activities you +intend. + +When cleaning up a partial de/validation, take no action unless you +find a page partially validated. + +If put_page_type() is called without PTF_partial_set, and finds the +page in a PGT_partial state anyway, then there's certainly been a +misaccounting somewhere, and carrying on would almost certainly cause +a security issue, so crash the host instead. + +In put_page_from_lNe, pass partial_flags on to _put_page_type(). + +old_guest_table may be set either with a fully validated page (when +using the "deferred put" pattern), or with a partially validated page +(when a normal "de-validation" is interrupted, or when a validation +fails part-way through due to invalid entries). Add a flag, +old_guest_table_partial, to indicate which of these it is, and use +that to pass the appropriate flag to _put_page_type(). + +While here, delete stray trailing whitespace. + +This is part of XSA-299. + +Reported-by: George Dunlap +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +----- +Appendix: + +Suppose page A, when interpreted as an l3 pagetable, contains all +valid entries; and suppose A[x] points to page B, which when +interpreted as an l2 pagetable, contains all valid entries. + +P1: PIN_L3_TABLE + A -> PGT_l3_table | 1 | valid + B -> PGT_l2_table | 1 | valid + +P1: UNPIN_TABLE + > Arrange to interrupt after B has been de-validated + B: + type_info -> PGT_l2_table | 0 + A: + type_info -> PGT_l3_table | 1 | partial + nr_validated_enties -> (less than x) + +P2: mod_l4_entry to point to A + > Arrange for this to be interrupted while B is being validated + B: + type_info -> PGT_l2_table | 1 | partial + (nr_validated_entires &c set as appropriate) + A: + type_info -> PGT_l3_table | 1 | partial + nr_validated_entries -> x + partial_pte = 1 + +P3: mod_l3_entry some other unrelated l3 to point to B: + B: + type_info -> PGT_l2_table | 1 + +P1: Restart UNPIN_TABLE + +At this point, since A.nr_validate_entries == x and A.partial_pte != +0, free_l3_table() will call put_page_from_l3e() on pl3e[x], dropping +its type count to 0 while it's still being pointed to by some other l3 + +A similar issue arises with old_guest_table. Consider the following +scenario: + +Suppose A is a page which, when interpreted as an l2, has valid entries +until entry x, which is invalid. + +V1: PIN_L2_TABLE(A) + + A -> PGT_l2_table | 1 | PGT_partial + V1 -> old_guest_table = A + + +V2: PIN_L2_TABLE(A) + + A -> PGT_l2_table | 1 | PGT_partial + V2 -> old_guest_table = A + + put_old_guest_table() + _put_page_type(A) + A -> PGT_l2_table | 0 + +V1: + put_old_guest_table() + _put_page_type(A) # UNDERFLOW + +Indeed, it is possible to engineer for old_guest_table for every vcpu +a guest has to point to the same page. +--- + xen/arch/x86/domain.c | 6 +++ + xen/arch/x86/mm.c | 99 +++++++++++++++++++++++++++++++----- + xen/include/asm-x86/domain.h | 4 +- + 3 files changed, 95 insertions(+), 14 deletions(-) + +diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c +index 59df8a6d8d..f1ae5f89f5 100644 +--- a/xen/arch/x86/domain.c ++++ b/xen/arch/x86/domain.c +@@ -1104,9 +1104,15 @@ int arch_set_info_guest( + rc = -ERESTART; + /* Fallthrough */ + case -ERESTART: ++ /* ++ * NB that we're putting the kernel-mode table ++ * here, which we've already successfully ++ * validated above; hence partial = false; ++ */ + v->arch.old_guest_ptpg = NULL; + v->arch.old_guest_table = + pagetable_get_page(v->arch.guest_table); ++ v->arch.old_guest_table_partial = false; + v->arch.guest_table = pagetable_null(); + break; + default: +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index a432e69c74..81774368a0 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -1359,10 +1359,11 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, + { + current->arch.old_guest_ptpg = ptpg; + current->arch.old_guest_table = pg; ++ current->arch.old_guest_table_partial = false; + } + else + { +- rc = _put_page_type(pg, PTF_preemptible, ptpg); ++ rc = _put_page_type(pg, flags | PTF_preemptible, ptpg); + if ( likely(!rc) ) + put_page(pg); + } +@@ -1385,6 +1386,7 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, + unsigned long mfn = l3e_get_pfn(l3e); + bool writeable = l3e_get_flags(l3e) & _PAGE_RW; + ++ ASSERT(!(flags & PTF_partial_set)); + ASSERT(!(mfn & ((1UL << (L3_PAGETABLE_SHIFT - PAGE_SHIFT)) - 1))); + do { + put_data_page(mfn_to_page(_mfn(mfn)), writeable); +@@ -1397,12 +1399,14 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, + + if ( flags & PTF_defer ) + { ++ ASSERT(!(flags & PTF_partial_set)); + current->arch.old_guest_ptpg = mfn_to_page(_mfn(pfn)); + current->arch.old_guest_table = pg; ++ current->arch.old_guest_table_partial = false; + return 0; + } + +- rc = _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); ++ rc = _put_page_type(pg, flags | PTF_preemptible, mfn_to_page(_mfn(pfn))); + if ( likely(!rc) ) + put_page(pg); + +@@ -1421,12 +1425,15 @@ static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, + + if ( flags & PTF_defer ) + { ++ ASSERT(!(flags & PTF_partial_set)); + current->arch.old_guest_ptpg = mfn_to_page(_mfn(pfn)); + current->arch.old_guest_table = pg; ++ current->arch.old_guest_table_partial = false; + return 0; + } + +- rc = _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); ++ rc = _put_page_type(pg, flags | PTF_preemptible, ++ mfn_to_page(_mfn(pfn))); + if ( likely(!rc) ) + put_page(pg); + } +@@ -1535,6 +1542,14 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) + + pl2e = map_domain_page(_mfn(pfn)); + ++ /* ++ * NB that alloc_l2_table will never set partial_pte on an l2; but ++ * free_l2_table might if a linear_pagetable entry is interrupted ++ * partway through de-validation. In that circumstance, ++ * get_page_from_l2e() will always return -EINVAL; and we must ++ * retain the type ref by doing the normal partial_flags tracking. ++ */ ++ + for ( i = page->nr_validated_ptes; i < L2_PAGETABLE_ENTRIES; + i++, partial_flags = 0 ) + { +@@ -1598,6 +1613,7 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) + page->partial_flags = partial_flags; + current->arch.old_guest_ptpg = NULL; + current->arch.old_guest_table = page; ++ current->arch.old_guest_table_partial = true; + } + } + if ( rc < 0 ) +@@ -1704,12 +1720,16 @@ static int alloc_l3_table(struct page_info *page) + * builds. + */ + if ( current->arch.old_guest_table == l3e_get_page(l3e) ) ++ { ++ ASSERT(current->arch.old_guest_table_partial); + page->partial_flags = PTF_partial_set; ++ } + else + ASSERT_UNREACHABLE(); + } + current->arch.old_guest_ptpg = NULL; + current->arch.old_guest_table = page; ++ current->arch.old_guest_table_partial = true; + } + while ( i-- > 0 ) + pl3e[i] = unadjust_guest_l3e(pl3e[i], d); +@@ -1897,12 +1917,16 @@ static int alloc_l4_table(struct page_info *page) + * builds. + */ + if ( current->arch.old_guest_table == l4e_get_page(l4e) ) ++ { ++ ASSERT(current->arch.old_guest_table_partial); + page->partial_flags = PTF_partial_set; ++ } + else + ASSERT_UNREACHABLE(); + } + current->arch.old_guest_ptpg = NULL; + current->arch.old_guest_table = page; ++ current->arch.old_guest_table_partial = true; + } + } + } +@@ -2831,6 +2855,28 @@ static int _put_page_type(struct page_info *page, unsigned int flags, + x = y; + nx = x - 1; + ++ /* ++ * Is this expected to do a full reference drop, or only ++ * cleanup partial validation / devalidation? ++ * ++ * If the former, the caller must hold a "full" type ref; ++ * which means the page must be validated. If the page is ++ * *not* fully validated, continuing would almost certainly ++ * open up a security hole. An exception to this is during ++ * domain destruction, where PGT_validated can be dropped ++ * without dropping a type ref. ++ * ++ * If the latter, do nothing unless type PGT_partial is set. ++ * If it is set, the type count must be 1. ++ */ ++ if ( !(flags & PTF_partial_set) ) ++ BUG_ON((x & PGT_partial) || ++ !((x & PGT_validated) || page_get_owner(page)->is_dying)); ++ else if ( !(x & PGT_partial) ) ++ return 0; ++ else ++ BUG_ON((x & PGT_count_mask) != 1); ++ + ASSERT((x & PGT_count_mask) != 0); + + switch ( nx & (PGT_locked | PGT_count_mask) ) +@@ -3092,17 +3138,34 @@ int put_old_guest_table(struct vcpu *v) + if ( !v->arch.old_guest_table ) + return 0; + +- switch ( rc = _put_page_type(v->arch.old_guest_table, PTF_preemptible, +- v->arch.old_guest_ptpg) ) ++ rc = _put_page_type(v->arch.old_guest_table, ++ PTF_preemptible | ++ ( v->arch.old_guest_table_partial ? ++ PTF_partial_set : 0 ), ++ v->arch.old_guest_ptpg); ++ ++ if ( rc == -ERESTART || rc == -EINTR ) + { +- case -EINTR: +- case -ERESTART: ++ v->arch.old_guest_table_partial = (rc == -ERESTART); + return -ERESTART; +- case 0: +- put_page(v->arch.old_guest_table); + } + ++ /* ++ * It shouldn't be possible for _put_page_type() to return ++ * anything else at the moment; but if it does happen in ++ * production, leaking the type ref is probably the best thing to ++ * do. Either way, drop the general ref held by old_guest_table. ++ */ ++ ASSERT(rc == 0); ++ ++ put_page(v->arch.old_guest_table); + v->arch.old_guest_table = NULL; ++ v->arch.old_guest_ptpg = NULL; ++ /* ++ * Safest default if someone sets old_guest_table without ++ * explicitly setting old_guest_table_partial. ++ */ ++ v->arch.old_guest_table_partial = true; + + return rc; + } +@@ -3253,11 +3316,11 @@ int new_guest_cr3(mfn_t mfn) + switch ( rc = put_page_and_type_preemptible(page) ) + { + case -EINTR: +- rc = -ERESTART; +- /* fallthrough */ + case -ERESTART: + curr->arch.old_guest_ptpg = NULL; + curr->arch.old_guest_table = page; ++ curr->arch.old_guest_table_partial = (rc == -ERESTART); ++ rc = -ERESTART; + break; + default: + BUG_ON(rc); +@@ -3494,6 +3557,7 @@ long do_mmuext_op( + { + curr->arch.old_guest_ptpg = NULL; + curr->arch.old_guest_table = page; ++ curr->arch.old_guest_table_partial = false; + } + } + } +@@ -3528,6 +3592,11 @@ long do_mmuext_op( + case -ERESTART: + curr->arch.old_guest_ptpg = NULL; + curr->arch.old_guest_table = page; ++ /* ++ * EINTR means we still hold the type ref; ERESTART ++ * means PGT_partial holds the type ref ++ */ ++ curr->arch.old_guest_table_partial = (rc == -ERESTART); + rc = 0; + break; + default: +@@ -3596,11 +3665,15 @@ long do_mmuext_op( + switch ( rc = put_page_and_type_preemptible(page) ) + { + case -EINTR: +- rc = -ERESTART; +- /* fallthrough */ + case -ERESTART: + curr->arch.old_guest_ptpg = NULL; + curr->arch.old_guest_table = page; ++ /* ++ * EINTR means we still hold the type ref; ++ * ERESTART means PGT_partial holds the ref ++ */ ++ curr->arch.old_guest_table_partial = (rc == -ERESTART); ++ rc = -ERESTART; + break; + default: + BUG_ON(rc); +diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h +index 214e44ce1c..2cfce7b36b 100644 +--- a/xen/include/asm-x86/domain.h ++++ b/xen/include/asm-x86/domain.h +@@ -307,7 +307,7 @@ struct arch_domain + + struct paging_domain paging; + struct p2m_domain *p2m; +- /* To enforce lock ordering in the pod code wrt the ++ /* To enforce lock ordering in the pod code wrt the + * page_alloc lock */ + int page_alloc_unlock_level; + +@@ -581,6 +581,8 @@ struct arch_vcpu + struct page_info *old_guest_table; /* partially destructed pagetable */ + struct page_info *old_guest_ptpg; /* containing page table of the */ + /* former, if any */ ++ bool old_guest_table_partial; /* Are we dropping a type ref, or just ++ * finishing up a partial de-validation? */ + /* guest_table holds a ref to the page, and also a type-count unless + * shadow refcounts are in use */ + pagetable_t shadow_table[4]; /* (MFN) shadow(s) of guest */ +-- +2.23.0 + From b6c6fc0ef1b06d51f99d184f9c00ad575420ce85 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 26 Nov 2019 18:34:05 +0000 Subject: [PATCH 024/194] Device quarantine for alternate pci assignment methods [XSA-306] --- xen.spec | 9 ++++- xsa306-4.12.patch | 91 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 xsa306-4.12.patch diff --git a/xen.spec b/xen.spec index 5e7cb22..7009a35 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.12.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 @@ -141,6 +141,7 @@ Patch78: xsa304-4.12-2.patch Patch79: xsa304-4.12-3.patch Patch80: xsa305-4.12-1.patch Patch81: xsa305-4.12-2.patch +Patch82: xsa306-4.12.patch %if %build_qemutrad @@ -368,6 +369,9 @@ manage Xen virtual machines. %patch79 -p1 %patch80 -p1 %patch81 -p1 +%ifarch %{ix86} x86_64 +%patch82 -p1 +%endif # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -931,6 +935,9 @@ fi %endif %changelog +* Tue Nov 26 2019 Michael Young - 4.12.1-7 +- Device quarantine for alternate pci assignment methods [XSA-306] + * Tue Nov 12 2019 Michael Young - 4.12.1-6 - add missing XSA-299 patches diff --git a/xsa306-4.12.patch b/xsa306-4.12.patch new file mode 100644 index 0000000..13147b5 --- /dev/null +++ b/xsa306-4.12.patch @@ -0,0 +1,91 @@ +From: Jan Beulich +Subject: IOMMU: default to always quarantining PCI devices + +XSA-302 relies on the use of libxl's "assignable-add" feature to prepare +devices to be assigned to untrusted guests. + +Unfortunately, this is not considered a strictly required step for +device assignment. The PCI passthrough documentation on the wiki +describes alternate ways of preparing devices for assignment, and +libvirt uses its own ways as well. Hosts where these alternate methods +are used will still leave the system in a vulnerable state after the +device comes back from a guest. + +Default to always quarantining PCI devices, but provide a command line +option to revert back to prior behavior (such that people who both +sufficiently trust their guests and want to be able to use devices in +Dom0 again after they had been in use by a guest wouldn't need to +"manually" move such devices back from DomIO to Dom0). + +This is XSA-306. + +Reported-by: Marek Marczykowski-Górecki +Signed-off-by: Jan Beulich +Reviewed-by: Wei Liu + +--- a/docs/misc/xen-command-line.pandoc ++++ b/docs/misc/xen-command-line.pandoc +@@ -1171,7 +1171,7 @@ detection of systems known to misbehave + > Default: `new` unless directed-EOI is supported + + ### iommu +- = List of [ , verbose, debug, force, required, ++ = List of [ , verbose, debug, force, required, quarantine, + sharept, intremap, intpost, crash-disable, + snoop, qinval, igfx, amd-iommu-perdev-intremap, + dom0-{passthrough,strict} ] +@@ -1209,6 +1209,12 @@ boolean (e.g. `iommu=no`) can override t + will prevent Xen from booting if IOMMUs aren't discovered and enabled + successfully. + ++* The `quarantine` boolean can be used to control Xen's behavior when ++ de-assigning devices from guests. If enabled (the default), Xen always ++ quarantines such devices; they must be explicitly assigned back to Dom0 ++ before they can be used there again. If disabled, Xen will only ++ quarantine devices the toolstack hass arranged for getting quarantined. ++ + * The `sharept` boolean controls whether the IOMMU pagetables are shared + with the CPU-side HAP pagetables, or allocated separately. Sharing + reduces the memory overhead, but doesn't work in combination with CPU-side +--- a/xen/drivers/passthrough/iommu.c ++++ b/xen/drivers/passthrough/iommu.c +@@ -30,6 +30,7 @@ bool_t __initdata iommu_enable = 1; + bool_t __read_mostly iommu_enabled; + bool_t __read_mostly force_iommu; + bool_t __read_mostly iommu_verbose; ++bool __read_mostly iommu_quarantine = true; + bool_t __read_mostly iommu_igfx = 1; + bool_t __read_mostly iommu_snoop = 1; + bool_t __read_mostly iommu_qinval = 1; +@@ -74,6 +75,8 @@ static int __init parse_iommu_param(cons + else if ( (val = parse_boolean("force", s, ss)) >= 0 || + (val = parse_boolean("required", s, ss)) >= 0 ) + force_iommu = val; ++ else if ( (val = parse_boolean("quarantine", s, ss)) >= 0 ) ++ iommu_quarantine = val; + else if ( (val = parse_boolean("igfx", s, ss)) >= 0 ) + iommu_igfx = val; + else if ( (val = parse_boolean("verbose", s, ss)) >= 0 ) +--- a/xen/drivers/passthrough/pci.c ++++ b/xen/drivers/passthrough/pci.c +@@ -1548,7 +1548,8 @@ int deassign_device(struct domain *d, u1 + return -ENODEV; + + /* De-assignment from dom_io should de-quarantine the device */ +- target = (pdev->quarantine && pdev->domain != dom_io) ? ++ target = ((pdev->quarantine || iommu_quarantine) && ++ pdev->domain != dom_io) ? + dom_io : hardware_domain; + + while ( pdev->phantom_stride ) +--- a/xen/include/xen/iommu.h ++++ b/xen/include/xen/iommu.h +@@ -53,7 +53,7 @@ static inline bool_t dfn_eq(dfn_t x, dfn + } + + extern bool_t iommu_enable, iommu_enabled; +-extern bool_t force_iommu, iommu_verbose, iommu_igfx; ++extern bool force_iommu, iommu_quarantine, iommu_verbose, iommu_igfx; + extern bool_t iommu_snoop, iommu_qinval, iommu_intremap, iommu_intpost; + extern bool_t iommu_hap_pt_share; + extern bool_t iommu_debug; From e4c797f0d83290f8ed2be780031dfe8543bddf0c Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 11 Dec 2019 22:41:13 +0000 Subject: [PATCH 025/194] 5 security fixes denial of service in find_next_bit() [XSA-307, CVE-2019-19581, CVE-2019-19582] (#1780559) denial of service in HVM/PVH guest userspace code [XSA-308, CVE-2019-19583] (#1782206) privilege escalation due to malicious PV guest [XSA-309, CVE-2019-19578] (#1782210) Further issues with restartable PV type change operations [XSA-310, CVE-2019-19580] (#1782207) vulnerability in dynamic height handling for AMD IOMMU pagetables [XSA-311, CVE-2019-19577] (#1782208) --- ...32f0d815bee3102ede328676e1a4ca1926c7.patch | 341 ++++++++++++++++++ ...ecb9991bf2d1ddb933872c3dfbd26300ca68.patch | 110 ++++++ xen.spec | 40 +- xsa307.patch | 99 +++++ xsa308.patch | 74 ++++ xsa309.patch | 58 +++ ...uest_table-when-destroying-vcpu-page.patch | 167 +++++++++ ...e_lN_table-Retain-partial_flags-on-E.patch | 104 ++++++ ...h_memory-Grab-an-extra-type-ref-when.patch | 75 ++++ xsa311-4.12.patch | 194 ++++++++++ 10 files changed, 1256 insertions(+), 6 deletions(-) create mode 100644 xen.git-1e8932f0d815bee3102ede328676e1a4ca1926c7.patch create mode 100644 xen.git-36d2ecb9991bf2d1ddb933872c3dfbd26300ca68.patch create mode 100644 xsa307.patch create mode 100644 xsa308.patch create mode 100644 xsa309.patch create mode 100644 xsa310-0001-x86-mm-Set-old_guest_table-when-destroying-vcpu-page.patch create mode 100644 xsa310-0002-x86-mm-alloc-free_lN_table-Retain-partial_flags-on-E.patch create mode 100644 xsa310-0003-x86-mm-relinquish_memory-Grab-an-extra-type-ref-when.patch create mode 100644 xsa311-4.12.patch diff --git a/xen.git-1e8932f0d815bee3102ede328676e1a4ca1926c7.patch b/xen.git-1e8932f0d815bee3102ede328676e1a4ca1926c7.patch new file mode 100644 index 0000000..edbd01a --- /dev/null +++ b/xen.git-1e8932f0d815bee3102ede328676e1a4ca1926c7.patch @@ -0,0 +1,341 @@ +From 1e8932f0d815bee3102ede328676e1a4ca1926c7 Mon Sep 17 00:00:00 2001 +From: Paul Durrant +Date: Fri, 6 Dec 2019 12:46:24 +0100 +Subject: [PATCH] x86 / iommu: set up a scratch page in the quarantine domain + +This patch introduces a new iommu_op to facilitate a per-implementation +quarantine set up, and then further code for x86 implementations +(amd and vtd) to set up a read-only scratch page to serve as the source +for DMA reads whilst a device is assigned to dom_io. DMA writes will +continue to fault as before. + +The reason for doing this is that some hardware may continue to re-try +DMA (despite FLR) in the event of an error, or even BME being cleared, and +will fail to deal with DMA read faults gracefully. Having a scratch page +mapped will allow pending DMA reads to complete and thus such buggy +hardware will eventually be quiesced. + +NOTE: These modifications are restricted to x86 implementations only as + the buggy h/w I am aware of is only used with Xen in an x86 + environment. ARM may require similar code but, since I am not + aware of the need, this patch does not modify any ARM implementation. + +Signed-off-by: Paul Durrant +Reviewed-by: Jan Beulich +master commit: ea38867831da67eed0e9c61672c8941016b63dd9 +master date: 2019-11-29 18:27:54 +0000 +--- + xen/drivers/passthrough/amd/iommu_map.c | 62 ++++++++++++++++++++++ + xen/drivers/passthrough/amd/pci_amd_iommu.c | 14 ++--- + xen/drivers/passthrough/iommu.c | 17 +++++- + xen/drivers/passthrough/vtd/iommu.c | 74 ++++++++++++++++++++++----- + xen/include/asm-x86/hvm/svm/amd-iommu-proto.h | 3 ++ + xen/include/xen/iommu.h | 1 + + 6 files changed, 147 insertions(+), 24 deletions(-) + +diff --git a/xen/drivers/passthrough/amd/iommu_map.c b/xen/drivers/passthrough/amd/iommu_map.c +index 628aa60230..f876ce832b 100644 +--- a/xen/drivers/passthrough/amd/iommu_map.c ++++ b/xen/drivers/passthrough/amd/iommu_map.c +@@ -758,6 +758,68 @@ void amd_iommu_share_p2m(struct domain *d) + } + } + ++int __init amd_iommu_quarantine_init(struct domain *d) ++{ ++ struct domain_iommu *hd = dom_iommu(d); ++ unsigned long max_gfn = ++ PFN_DOWN((1ul << DEFAULT_DOMAIN_ADDRESS_WIDTH) - 1); ++ unsigned int level = amd_iommu_get_paging_mode(max_gfn); ++ uint64_t *table; ++ ++ if ( hd->arch.root_table ) ++ { ++ ASSERT_UNREACHABLE(); ++ return 0; ++ } ++ ++ spin_lock(&hd->arch.mapping_lock); ++ ++ hd->arch.root_table = alloc_amd_iommu_pgtable(); ++ if ( !hd->arch.root_table ) ++ goto out; ++ ++ table = __map_domain_page(hd->arch.root_table); ++ while ( level ) ++ { ++ struct page_info *pg; ++ unsigned int i; ++ ++ /* ++ * The pgtable allocator is fine for the leaf page, as well as ++ * page table pages, and the resulting allocations are always ++ * zeroed. ++ */ ++ pg = alloc_amd_iommu_pgtable(); ++ if ( !pg ) ++ break; ++ ++ for ( i = 0; i < PTE_PER_TABLE_SIZE; i++ ) ++ { ++ uint32_t *pde = (uint32_t *)&table[i]; ++ ++ /* ++ * 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); ++ } ++ ++ unmap_domain_page(table); ++ table = __map_domain_page(pg); ++ level--; ++ } ++ unmap_domain_page(table); ++ ++ out: ++ spin_unlock(&hd->arch.mapping_lock); ++ ++ amd_iommu_flush_all_pages(d); ++ ++ /* Pages leaked in failure case */ ++ return level ? -ENOMEM : 0; ++} ++ + /* + * Local variables: + * mode: C +diff --git a/xen/drivers/passthrough/amd/pci_amd_iommu.c b/xen/drivers/passthrough/amd/pci_amd_iommu.c +index 15c13e1163..93ecae4a6c 100644 +--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c ++++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c +@@ -120,10 +120,6 @@ static void amd_iommu_setup_domain_device( + u8 bus = pdev->bus; + const struct domain_iommu *hd = dom_iommu(domain); + +- /* dom_io is used as a sentinel for quarantined devices */ +- if ( domain == dom_io ) +- return; +- + BUG_ON( !hd->arch.root_table || !hd->arch.paging_mode || + !iommu->dev_table.buffer ); + +@@ -226,7 +222,7 @@ static int __must_check allocate_domain_resources(struct domain_iommu *hd) + return rc; + } + +-static int get_paging_mode(unsigned long entries) ++int amd_iommu_get_paging_mode(unsigned long entries) + { + int level = 1; + +@@ -248,7 +244,8 @@ static int amd_iommu_domain_init(struct domain *d) + + /* For pv and dom0, stick with get_paging_mode(max_page) + * For HVM dom0, use 2 level page table at first */ +- hd->arch.paging_mode = is_hvm_domain(d) ? 2 : get_paging_mode(max_page); ++ hd->arch.paging_mode = is_hvm_domain(d) ? ++ 2 : amd_iommu_get_paging_mode(max_page); + return 0; + } + +@@ -281,10 +278,6 @@ void amd_iommu_disable_domain_device(struct domain *domain, + int req_id; + u8 bus = pdev->bus; + +- /* dom_io is used as a sentinel for quarantined devices */ +- if ( domain == dom_io ) +- return; +- + BUG_ON ( iommu->dev_table.buffer == NULL ); + req_id = get_dma_requestor_id(iommu->seg, PCI_BDF2(bus, devfn)); + dte = iommu->dev_table.buffer + (req_id * IOMMU_DEV_TABLE_ENTRY_SIZE); +@@ -581,6 +574,7 @@ static void amd_dump_p2m_table(struct domain *d) + static const struct iommu_ops __initconstrel amd_iommu_ops = { + .init = amd_iommu_domain_init, + .hwdom_init = amd_iommu_hwdom_init, ++ .quarantine_init = amd_iommu_quarantine_init, + .add_device = amd_iommu_add_device, + .remove_device = amd_iommu_remove_device, + .assign_device = amd_iommu_assign_device, +diff --git a/xen/drivers/passthrough/iommu.c b/xen/drivers/passthrough/iommu.c +index 20db08e1df..a747bfb946 100644 +--- a/xen/drivers/passthrough/iommu.c ++++ b/xen/drivers/passthrough/iommu.c +@@ -497,6 +497,21 @@ int iommu_iotlb_flush_all(struct domain *d, unsigned int flush_flags) + return rc; + } + ++static int __init iommu_quarantine_init(void) ++{ ++ const struct domain_iommu *hd = dom_iommu(dom_io); ++ int rc; ++ ++ rc = iommu_domain_init(dom_io); ++ if ( rc ) ++ return rc; ++ ++ if ( !hd->platform_ops->quarantine_init ) ++ return 0; ++ ++ return hd->platform_ops->quarantine_init(dom_io); ++} ++ + int __init iommu_setup(void) + { + int rc = -ENODEV; +@@ -530,7 +545,7 @@ int __init iommu_setup(void) + printk("I/O virtualisation %sabled\n", iommu_enabled ? "en" : "dis"); + if ( iommu_enabled ) + { +- if ( iommu_domain_init(dom_io) ) ++ if ( iommu_quarantine_init() ) + panic("Could not set up quarantine\n"); + + printk(" - Dom0 mode: %s\n", +diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c +index 5663e9740d..576e72eba1 100644 +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -192,7 +192,7 @@ u64 alloc_pgtable_maddr(struct acpi_drhd_unit *drhd, unsigned long npages) + nodeid_t node = NUMA_NO_NODE; + unsigned int i; + +- rhsa = drhd_to_rhsa(drhd); ++ rhsa = drhd ? drhd_to_rhsa(drhd) : NULL; + if ( rhsa ) + node = pxm_to_node(rhsa->proximity_domain); + +@@ -1340,10 +1340,6 @@ int domain_context_mapping_one( + int agaw, rc, ret; + bool_t flush_dev_iotlb; + +- /* dom_io is used as a sentinel for quarantined devices */ +- if ( domain == dom_io ) +- return 0; +- + ASSERT(pcidevs_locked()); + spin_lock(&iommu->lock); + maddr = bus_to_context_maddr(iommu, bus); +@@ -1579,10 +1575,6 @@ int domain_context_unmap_one( + int iommu_domid, rc, ret; + bool_t flush_dev_iotlb; + +- /* dom_io is used as a sentinel for quarantined devices */ +- if ( domain == dom_io ) +- return 0; +- + ASSERT(pcidevs_locked()); + spin_lock(&iommu->lock); + +@@ -1715,10 +1707,6 @@ static int domain_context_unmap(struct domain *domain, u8 devfn, + goto out; + } + +- /* dom_io is used as a sentinel for quarantined devices */ +- if ( domain == dom_io ) +- 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 +@@ -2736,9 +2724,69 @@ static void vtd_dump_p2m_table(struct domain *d) + vtd_dump_p2m_table_level(hd->arch.pgd_maddr, agaw_to_level(hd->arch.agaw), 0, 0); + } + ++static int __init intel_iommu_quarantine_init(struct domain *d) ++{ ++ struct domain_iommu *hd = dom_iommu(d); ++ struct dma_pte *parent; ++ unsigned int agaw = width_to_agaw(DEFAULT_DOMAIN_ADDRESS_WIDTH); ++ unsigned int level = agaw_to_level(agaw); ++ int rc; ++ ++ if ( hd->arch.pgd_maddr ) ++ { ++ ASSERT_UNREACHABLE(); ++ return 0; ++ } ++ ++ spin_lock(&hd->arch.mapping_lock); ++ ++ hd->arch.pgd_maddr = alloc_pgtable_maddr(NULL, 1); ++ if ( !hd->arch.pgd_maddr ) ++ goto out; ++ ++ parent = map_vtd_domain_page(hd->arch.pgd_maddr); ++ while ( level ) ++ { ++ 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. ++ */ ++ maddr = alloc_pgtable_maddr(NULL, 1); ++ if ( !maddr ) ++ break; ++ ++ for ( offset = 0; offset < PTE_NUM; offset++ ) ++ { ++ struct dma_pte *pte = &parent[offset]; ++ ++ dma_set_pte_addr(*pte, maddr); ++ dma_set_pte_readable(*pte); ++ } ++ iommu_flush_cache_page(parent, 1); ++ ++ unmap_vtd_domain_page(parent); ++ parent = map_vtd_domain_page(maddr); ++ level--; ++ } ++ unmap_vtd_domain_page(parent); ++ ++ out: ++ spin_unlock(&hd->arch.mapping_lock); ++ ++ rc = iommu_flush_iotlb_all(d); ++ ++ /* Pages leaked in failure case */ ++ return level ? -ENOMEM : rc; ++} ++ + const struct iommu_ops __initconstrel intel_iommu_ops = { + .init = intel_iommu_domain_init, + .hwdom_init = intel_iommu_hwdom_init, ++ .quarantine_init = intel_iommu_quarantine_init, + .add_device = intel_iommu_add_device, + .enable_device = intel_iommu_enable_device, + .remove_device = intel_iommu_remove_device, +diff --git a/xen/include/asm-x86/hvm/svm/amd-iommu-proto.h b/xen/include/asm-x86/hvm/svm/amd-iommu-proto.h +index c5697565d6..5a160b89ca 100644 +--- a/xen/include/asm-x86/hvm/svm/amd-iommu-proto.h ++++ b/xen/include/asm-x86/hvm/svm/amd-iommu-proto.h +@@ -51,6 +51,9 @@ void get_iommu_features(struct amd_iommu *iommu); + int amd_iommu_init(void); + int amd_iommu_update_ivrs_mapping_acpi(void); + ++int amd_iommu_get_paging_mode(unsigned long entries); ++int amd_iommu_quarantine_init(struct domain *d); ++ + /* mapping functions */ + int __must_check amd_iommu_map_page(struct domain *d, dfn_t dfn, + mfn_t mfn, unsigned int flags, +diff --git a/xen/include/xen/iommu.h b/xen/include/xen/iommu.h +index 70ee53d083..617c993f50 100644 +--- a/xen/include/xen/iommu.h ++++ b/xen/include/xen/iommu.h +@@ -189,6 +189,7 @@ typedef int iommu_grdm_t(xen_pfn_t start, xen_ulong_t nr, u32 id, void *ctxt); + struct iommu_ops { + int (*init)(struct domain *d); + void (*hwdom_init)(struct domain *d); ++ int (*quarantine_init)(struct domain *d); + int (*add_device)(u8 devfn, device_t *dev); + int (*enable_device)(device_t *dev); + int (*remove_device)(u8 devfn, device_t *dev); +-- +2.11.0 + diff --git a/xen.git-36d2ecb9991bf2d1ddb933872c3dfbd26300ca68.patch b/xen.git-36d2ecb9991bf2d1ddb933872c3dfbd26300ca68.patch new file mode 100644 index 0000000..448871a --- /dev/null +++ b/xen.git-36d2ecb9991bf2d1ddb933872c3dfbd26300ca68.patch @@ -0,0 +1,110 @@ +From 36d2ecb9991bf2d1ddb933872c3dfbd26300ca68 Mon Sep 17 00:00:00 2001 +From: Jan Beulich +Date: Mon, 25 Nov 2019 16:01:00 +0100 +Subject: [PATCH] AMD/IOMMU: don't needlessly trigger errors/crashes when + unmapping a page + +Unmapping a page which has never been mapped should be a no-op (note how +it already is in case there was no root page table allocated). There's +in particular no need to grow the number of page table levels in use, +and there's also no need to allocate intermediate page tables except +when needing to split a large page. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant +Acked-by: Andrew Cooper +master commit: ad591454f069647c36a7daaa9ec23384c0263f0b +master date: 2019-11-12 11:08:34 +0100 +--- + xen/drivers/passthrough/amd/iommu_map.c | 40 +++++++++++++++------------------ + 1 file changed, 18 insertions(+), 22 deletions(-) + +diff --git a/xen/drivers/passthrough/amd/iommu_map.c b/xen/drivers/passthrough/amd/iommu_map.c +index 67329b0c95..628aa60230 100644 +--- a/xen/drivers/passthrough/amd/iommu_map.c ++++ b/xen/drivers/passthrough/amd/iommu_map.c +@@ -343,7 +343,7 @@ uint64_t amd_iommu_get_address_from_pte(void *pte) + * page tables. + */ + static int iommu_pde_from_dfn(struct domain *d, unsigned long dfn, +- unsigned long pt_mfn[]) ++ unsigned long pt_mfn[], bool map) + { + uint64_t *pde, *next_table_vaddr; + unsigned long next_table_mfn; +@@ -356,6 +356,13 @@ static int iommu_pde_from_dfn(struct domain *d, unsigned long dfn, + + BUG_ON( table == NULL || level < 1 || level > 6 ); + ++ /* ++ * A frame number past what the current page tables can represent can't ++ * possibly have a mapping. ++ */ ++ if ( dfn >> (PTE_PER_TABLE_SHIFT * level) ) ++ return 0; ++ + next_table_mfn = mfn_x(page_to_mfn(table)); + + if ( level == 1 ) +@@ -415,6 +422,9 @@ static int iommu_pde_from_dfn(struct domain *d, unsigned long dfn, + /* Install lower level page table for non-present entries */ + else if ( !iommu_is_pte_present((uint32_t *)pde) ) + { ++ if ( !map ) ++ return 0; ++ + if ( next_table_mfn == 0 ) + { + table = alloc_amd_iommu_pgtable(); +@@ -577,7 +587,7 @@ int amd_iommu_map_page(struct domain *d, dfn_t dfn, mfn_t mfn, + } + } + +- if ( iommu_pde_from_dfn(d, dfn_x(dfn), pt_mfn) || (pt_mfn[1] == 0) ) ++ if ( iommu_pde_from_dfn(d, dfn_x(dfn), pt_mfn, true) || (pt_mfn[1] == 0) ) + { + spin_unlock(&hd->arch.mapping_lock); + AMD_IOMMU_DEBUG("Invalid IO pagetable entry dfn = %"PRI_dfn"\n", +@@ -615,24 +625,7 @@ int amd_iommu_unmap_page(struct domain *d, dfn_t dfn, + return 0; + } + +- /* Since HVM domain is initialized with 2 level IO page table, +- * we might need a deeper page table for lager dfn now */ +- if ( is_hvm_domain(d) ) +- { +- int rc = update_paging_mode(d, dfn_x(dfn)); +- +- if ( rc ) +- { +- spin_unlock(&hd->arch.mapping_lock); +- AMD_IOMMU_DEBUG("Update page mode failed dfn = %"PRI_dfn"\n", +- dfn_x(dfn)); +- if ( rc != -EADDRNOTAVAIL ) +- domain_crash(d); +- return rc; +- } +- } +- +- if ( iommu_pde_from_dfn(d, dfn_x(dfn), pt_mfn) || (pt_mfn[1] == 0) ) ++ if ( iommu_pde_from_dfn(d, dfn_x(dfn), pt_mfn, false) ) + { + spin_unlock(&hd->arch.mapping_lock); + AMD_IOMMU_DEBUG("Invalid IO pagetable entry dfn = %"PRI_dfn"\n", +@@ -641,8 +634,11 @@ int amd_iommu_unmap_page(struct domain *d, dfn_t dfn, + return -EFAULT; + } + +- /* mark PTE as 'page not present' */ +- *flush_flags |= clear_iommu_pte_present(pt_mfn[1], dfn_x(dfn)); ++ if ( pt_mfn[1] ) ++ { ++ /* Mark PTE as 'page not present'. */ ++ *flush_flags |= clear_iommu_pte_present(pt_mfn[1], dfn_x(dfn)); ++ } + + spin_unlock(&hd->arch.mapping_lock); + +-- +2.11.0 + diff --git a/xen.spec b/xen.spec index 7009a35..a72a5eb 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.12.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 @@ -142,6 +142,15 @@ Patch79: xsa304-4.12-3.patch Patch80: xsa305-4.12-1.patch Patch81: xsa305-4.12-2.patch Patch82: xsa306-4.12.patch +Patch83: xsa307.patch +Patch84: xsa308.patch +Patch85: xsa309.patch +Patch86: xsa310-0001-x86-mm-Set-old_guest_table-when-destroying-vcpu-page.patch +Patch87: xsa310-0002-x86-mm-alloc-free_lN_table-Retain-partial_flags-on-E.patch +Patch88: xsa310-0003-x86-mm-relinquish_memory-Grab-an-extra-type-ref-when.patch +Patch89: xen.git-36d2ecb9991bf2d1ddb933872c3dfbd26300ca68.patch +Patch90: xen.git-1e8932f0d815bee3102ede328676e1a4ca1926c7.patch +Patch91: xsa311-4.12.patch %if %build_qemutrad @@ -356,10 +365,8 @@ manage Xen virtual machines. %patch68 -p1 %patch69 -p1 %patch70 -p1 -%ifarch %{ix86} x86_64 %patch71 -p1 %patch72 -p1 -%endif %patch73 -p1 %patch74 -p1 %patch75 -p1 @@ -369,9 +376,16 @@ manage Xen virtual machines. %patch79 -p1 %patch80 -p1 %patch81 -p1 -%ifarch %{ix86} x86_64 %patch82 -p1 -%endif +%patch83 -p1 +%patch84 -p1 +%patch85 -p1 +%patch86 -p1 +%patch87 -p1 +%patch88 -p1 +%patch89 -p1 +%patch90 -p1 +%patch91 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -935,8 +949,22 @@ fi %endif %changelog +* Wed Dec 11 2019 Michael Young - 4.12.1-8 +- denial of service in find_next_bit() [XSA-307, CVE-2019-19581, + CVE-2019-19582] (#1780559) +- denial of service in HVM/PVH guest userspace code [XSA-308, + CVE-2019-19583] (#1782206) +- privilege escalation due to malicious PV guest [XSA-309, CVE-2019-19578] + (#1782210) +- Further issues with restartable PV type change operations [XSA-310, + CVE-2019-19580] (#1782207) +- vulnerability in dynamic height handling for AMD IOMMU pagetables + [XSA-311, CVE-2019-19577] (#1782208) +- add patches needed to apply XSA-311 + * Tue Nov 26 2019 Michael Young - 4.12.1-7 -- Device quarantine for alternate pci assignment methods [XSA-306] +- Device quarantine for alternate pci assignment methods [XSA-306, + CVE-2019-19579] (#1780559) * Tue Nov 12 2019 Michael Young - 4.12.1-6 - add missing XSA-299 patches diff --git a/xsa307.patch b/xsa307.patch new file mode 100644 index 0000000..82b4ada --- /dev/null +++ b/xsa307.patch @@ -0,0 +1,99 @@ +From: Jan Beulich +Subject: x86+Arm32: make find_next_{,zero_}bit() have well defined behavior + +These functions getting used with the 2nd and 3rd arguments being equal +wasn't well defined: Arm64 reliably returns the value of the 2nd +argument in this case, while on x86 for bitmaps up to 64 bits wide the +return value was undefined (due to the undefined behavior of a shift of +a value by the number of bits it's wide) when the incoming value was 64. +On Arm32 an actual out of bounds access would happen when the +size/offset value is a multiple of 32; if this access doesn't fault, the +return value would have been sufficiently correct afaict. + +Make the functions consistently tolerate the last two arguments being +equal (and in fact the 3rd argument being greater or equal to the 2nd), +in favor of finding and fixing all the use sites that violate the +original more strict assumption. + +This is XSA-307. + +Signed-off-by: Jan Beulich +Acked-by: Julien Grall +--- +The most obvious (albeit still indirect) exposure to guests is +evtchn_check_pollers(), which imo makes this a security issue at least +for Arm32. + +This was originally already discussed between (at least) Andrew and me, +and I don't really recall who brought up the issue first. + +Note that Arm's Linux origin of the code may call for syncing +publication with them. Then again I don't want to tell them just to see +them go public ahead of us. + +--- a/xen/arch/arm/arm32/lib/findbit.S ++++ b/xen/arch/arm/arm32/lib/findbit.S +@@ -42,8 +42,8 @@ ENDPROC(_find_first_zero_bit_le) + * Prototype: int find_next_zero_bit(void *addr, unsigned int maxbit, int offset) + */ + ENTRY(_find_next_zero_bit_le) +- teq r1, #0 +- beq 3b ++ cmp r1, r2 ++ bls 3b + ands ip, r2, #7 + beq 1b @ If new byte, goto old routine + ARM( ldrb r3, [r0, r2, lsr #3] ) +@@ -83,8 +83,8 @@ ENDPROC(_find_first_bit_le) + * Prototype: int find_next_zero_bit(void *addr, unsigned int maxbit, int offset) + */ + ENTRY(_find_next_bit_le) +- teq r1, #0 +- beq 3b ++ cmp r1, r2 ++ bls 3b + ands ip, r2, #7 + beq 1b @ If new byte, goto old routine + ARM( ldrb r3, [r0, r2, lsr #3] ) +@@ -117,8 +117,8 @@ ENTRY(_find_first_zero_bit_be) + ENDPROC(_find_first_zero_bit_be) + + ENTRY(_find_next_zero_bit_be) +- teq r1, #0 +- beq 3b ++ cmp r1, r2 ++ bls 3b + ands ip, r2, #7 + beq 1b @ If new byte, goto old routine + eor r3, r2, #0x18 @ big endian byte ordering +@@ -151,8 +151,8 @@ ENTRY(_find_first_bit_be) + ENDPROC(_find_first_bit_be) + + ENTRY(_find_next_bit_be) +- teq r1, #0 +- beq 3b ++ cmp r1, r2 ++ bls 3b + ands ip, r2, #7 + beq 1b @ If new byte, goto old routine + eor r3, r2, #0x18 @ big endian byte ordering +--- a/xen/include/asm-x86/bitops.h ++++ b/xen/include/asm-x86/bitops.h +@@ -358,7 +358,7 @@ static always_inline unsigned int __scan + const unsigned long *a__ = (addr); \ + unsigned int s__ = (size); \ + unsigned int o__ = (off); \ +- if ( __builtin_constant_p(size) && !s__ ) \ ++ if ( o__ >= s__ ) \ + r__ = s__; \ + else if ( __builtin_constant_p(size) && s__ <= BITS_PER_LONG ) \ + r__ = o__ + __scanbit(*(const unsigned long *)(a__) >> o__, s__); \ +@@ -390,7 +390,7 @@ static always_inline unsigned int __scan + const unsigned long *a__ = (addr); \ + unsigned int s__ = (size); \ + unsigned int o__ = (off); \ +- if ( __builtin_constant_p(size) && !s__ ) \ ++ if ( o__ >= s__ ) \ + r__ = s__; \ + else if ( __builtin_constant_p(size) && s__ <= BITS_PER_LONG ) \ + r__ = o__ + __scanbit(~*(const unsigned long *)(a__) >> o__, s__); \ diff --git a/xsa308.patch b/xsa308.patch new file mode 100644 index 0000000..7abe3ef --- /dev/null +++ b/xsa308.patch @@ -0,0 +1,74 @@ +From: Andrew Cooper +Subject: x86/vtx: Work around SingleStep + STI/MovSS VMEntry failures + +See patch comment for technical details. + +Concerning the timeline, this was first discovered in the aftermath of +XSA-156 which caused #DB to be intercepted unconditionally, but only in +its SingleStep + STI form which is restricted to privileged software. + +After working with Intel and identifying the problematic vmentry check, +this workaround was suggested, and the patch was posted in an RFC +series. Outstanding work for that series (not breaking Introspection) +is still pending, and this fix from it (which wouldn't have been good +enough in its original form) wasn't committed. + +A vmentry failure was reported to xen-devel, and debugging identified +this bug in its SingleStep + MovSS form by way of INT1, which does not +involve the use of any privileged instructions, and proving this to be a +security issue. + +This is XSA-308 + +Reported-by: Håkon Alstadheim +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +Acked-by: Kevin Tian + +diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c +index 6a5eeb5c13..59b836f43f 100644 +--- a/xen/arch/x86/hvm/vmx/vmx.c ++++ b/xen/arch/x86/hvm/vmx/vmx.c +@@ -3816,6 +3816,42 @@ void vmx_vmexit_handler(struct cpu_user_regs *regs) + HVMTRACE_1D(TRAP_DEBUG, exit_qualification); + __restore_debug_registers(v); + write_debugreg(6, exit_qualification | DR_STATUS_RESERVED_ONE); ++ ++ /* ++ * Work around SingleStep + STI/MovSS VMEntry failures. ++ * ++ * We intercept #DB unconditionally to work around CVE-2015-8104 / ++ * XSA-156 (guest-kernel induced host DoS). ++ * ++ * STI/MovSS shadows block/defer interrupts/exceptions (exact ++ * details are complicated and poorly documented). Debug ++ * exceptions delayed for any reason are stored in the ++ * PENDING_DBG_EXCEPTIONS field. ++ * ++ * The falling edge of PENDING_DBG causes #DB to be delivered, ++ * resulting in a VMExit, as #DB is intercepted. The VMCS still ++ * reports blocked-by-STI/MovSS. ++ * ++ * The VMEntry checks when EFLAGS.TF is set don't like a VMCS in ++ * this state. Despite a #DB queued in VMENTRY_INTR_INFO, the ++ * state is rejected as DR6.BS isn't pending. Fix this up. ++ */ ++ if ( unlikely(regs->eflags & X86_EFLAGS_TF) ) ++ { ++ unsigned long int_info; ++ ++ __vmread(GUEST_INTERRUPTIBILITY_INFO, &int_info); ++ ++ if ( int_info & (VMX_INTR_SHADOW_STI | VMX_INTR_SHADOW_MOV_SS) ) ++ { ++ unsigned long pending_dbg; ++ ++ __vmread(GUEST_PENDING_DBG_EXCEPTIONS, &pending_dbg); ++ __vmwrite(GUEST_PENDING_DBG_EXCEPTIONS, ++ pending_dbg | DR_STEP); ++ } ++ } ++ + if ( !v->domain->debugger_attached ) + { + unsigned long insn_len = 0; diff --git a/xsa309.patch b/xsa309.patch new file mode 100644 index 0000000..8bd9237 --- /dev/null +++ b/xsa309.patch @@ -0,0 +1,58 @@ +From 523e3974ed2213719a19218f5b246e382ceef18a Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Wed, 30 Oct 2019 17:05:28 +0000 +Subject: [PATCH] x86/mm: Don't reset linear_pt_count on partial validation + +"Linear pagetables" is a technique which involves either pointing a +pagetable at itself, or to another pagetable the same or higher level. +Xen has limited support for linear pagetables: A page may either point +to itself, or point to another page of the same level (i.e., L2 to L2, +L3 to L3, and so on). + +XSA-240 introduced an additional restriction that limited the "depth" +of such chains by allowing pages to either *point to* other pages of +the same level, or *be pointed to* by other pages of the same level, +but not both. To implement this, we keep track of the number of +outstanding times a page points to or is pointed to another page +table, to prevent both from happening at the same time. + +Unfortunately, the original commit introducing this reset this count +when resuming validation of a partially-validated pagetable, dropping +some "linear_pt_entry" counts. + +On debug builds on systems where guests used this feature, this might +lead to crashes that look like this: + + Assertion 'oc > 0' failed at mm.c:874 + +Worse, if an attacker could engineer such a situation to occur, they +might be able to make loops or other abitrary chains of linear +pagetables, leading to the denial-of-service situation outlined in +XSA-240. + +This is XSA-309. + +Reported-by: Manuel Bouyer +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +--- + xen/arch/x86/mm.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 7d4dd80a85..01393fb0da 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -3059,8 +3059,8 @@ static int _get_page_type(struct page_info *page, unsigned long type, + { + page->nr_validated_ptes = 0; + page->partial_flags = 0; ++ page->linear_pt_count = 0; + } +- page->linear_pt_count = 0; + rc = alloc_page_type(page, type, preemptible); + } + +-- +2.24.0 + diff --git a/xsa310-0001-x86-mm-Set-old_guest_table-when-destroying-vcpu-page.patch b/xsa310-0001-x86-mm-Set-old_guest_table-when-destroying-vcpu-page.patch new file mode 100644 index 0000000..3eb3533 --- /dev/null +++ b/xsa310-0001-x86-mm-Set-old_guest_table-when-destroying-vcpu-page.patch @@ -0,0 +1,167 @@ +From 7c537dc8d28a03064a14171ed5c6fc329531816a Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Tue, 19 Nov 2019 11:40:34 +0000 +Subject: [PATCH 1/3] x86/mm: Set old_guest_table when destroying vcpu + pagetables + +Changeset 6c4efc1eba ("x86/mm: Don't drop a type ref unless you held a +ref to begin with"), part of XSA-299, changed the calling discipline +of put_page_type() such that if put_page_type() returned -ERESTART +(indicating a partially de-validated page), subsequent calls to +put_page_type() must be called with PTF_partial_set. If called on a +partially de-validated page but without PTF_partial_set, Xen will +BUG(), because to do otherwise would risk opening up the kind of +privilege escalation bug described in XSA-299. + +One place this was missed was in vcpu_destroy_pagetables(). +put_page_and_type_preemptible() is called, but on -ERESTART, the +entire operation is simply restarted, causing put_page_type() to be +called on a partially de-validated page without PTF_partial_set. The +result was that if such an operation were interrupted, Xen would hit a +BUG(). + +Fix this by having vcpu_destroy_pagetables() consistently pass off +interrupted de-validations to put_old_page_type(): +- Unconditionally clear references to the page, even if + put_page_and_type failed +- Set old_guest_table and old_guest_table_partial appropriately + +While here, do some refactoring: + + - Move clearing of arch.cr3 to the top of the function + + - Now that clearing is unconditional, move the unmap to the same + conditional as the l4tab mapping. This also allows us to reduce + the scope of the l4tab variable. + + - Avoid code duplication by looping to drop references on + guest_table_user + +This is part of XSA-310. + +Reported-by: Sarah Newman +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +--- +Added in v2. + +Changes in v3: +- Minor comment / whitespace fixes +--- + xen/arch/x86/mm.c | 75 +++++++++++++++++++++++++++++------------------ + 1 file changed, 47 insertions(+), 28 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 01393fb0da..a759afc9e3 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -3142,40 +3142,36 @@ int put_old_guest_table(struct vcpu *v) + int vcpu_destroy_pagetables(struct vcpu *v) + { + unsigned long mfn = pagetable_get_pfn(v->arch.guest_table); +- struct page_info *page; +- l4_pgentry_t *l4tab = NULL; ++ struct page_info *page = NULL; + int rc = put_old_guest_table(v); ++ bool put_guest_table_user = false; + + if ( rc ) + return rc; + ++ v->arch.cr3 = 0; ++ ++ /* ++ * Get the top-level guest page; either the guest_table itself, for ++ * 64-bit, or the top-level l4 entry for 32-bit. Either way, remove ++ * the reference to that page. ++ */ + if ( is_pv_32bit_vcpu(v) ) + { +- l4tab = map_domain_page(_mfn(mfn)); +- mfn = l4e_get_pfn(*l4tab); +- } ++ l4_pgentry_t *l4tab = map_domain_page(_mfn(mfn)); + +- if ( mfn ) +- { +- page = mfn_to_page(_mfn(mfn)); +- if ( paging_mode_refcounts(v->domain) ) +- put_page(page); +- else +- rc = put_page_and_type_preemptible(page); +- } +- +- if ( l4tab ) +- { +- if ( !rc ) +- l4e_write(l4tab, l4e_empty()); ++ mfn = l4e_get_pfn(*l4tab); ++ l4e_write(l4tab, l4e_empty()); + unmap_domain_page(l4tab); + } +- else if ( !rc ) ++ else + { + v->arch.guest_table = pagetable_null(); ++ put_guest_table_user = true; ++ } + +- /* Drop ref to guest_table_user (from MMUEXT_NEW_USER_BASEPTR) */ +- mfn = pagetable_get_pfn(v->arch.guest_table_user); ++ /* Free that page if non-zero */ ++ do { + if ( mfn ) + { + page = mfn_to_page(_mfn(mfn)); +@@ -3183,18 +3179,41 @@ int vcpu_destroy_pagetables(struct vcpu *v) + put_page(page); + else + rc = put_page_and_type_preemptible(page); ++ mfn = 0; + } +- if ( !rc ) +- v->arch.guest_table_user = pagetable_null(); +- } + +- v->arch.cr3 = 0; ++ if ( !rc && put_guest_table_user ) ++ { ++ /* Drop ref to guest_table_user (from MMUEXT_NEW_USER_BASEPTR) */ ++ mfn = pagetable_get_pfn(v->arch.guest_table_user); ++ v->arch.guest_table_user = pagetable_null(); ++ put_guest_table_user = false; ++ } ++ } while ( mfn ); + + /* +- * put_page_and_type_preemptible() is liable to return -EINTR. The +- * callers of us expect -ERESTART so convert it over. ++ * If a "put" operation was interrupted, finish things off in ++ * put_old_guest_table() when the operation is restarted. + */ +- return rc != -EINTR ? rc : -ERESTART; ++ switch ( rc ) ++ { ++ case -EINTR: ++ case -ERESTART: ++ v->arch.old_guest_ptpg = NULL; ++ v->arch.old_guest_table = page; ++ v->arch.old_guest_table_partial = (rc == -ERESTART); ++ rc = -ERESTART; ++ break; ++ default: ++ /* ++ * Failure to 'put' a page may cause it to leak, but that's ++ * less bad than a crash. ++ */ ++ ASSERT(rc == 0); ++ break; ++ } ++ ++ return rc; + } + + int new_guest_cr3(mfn_t mfn) +-- +2.24.0 + diff --git a/xsa310-0002-x86-mm-alloc-free_lN_table-Retain-partial_flags-on-E.patch b/xsa310-0002-x86-mm-alloc-free_lN_table-Retain-partial_flags-on-E.patch new file mode 100644 index 0000000..12c04e4 --- /dev/null +++ b/xsa310-0002-x86-mm-alloc-free_lN_table-Retain-partial_flags-on-E.patch @@ -0,0 +1,104 @@ +From 128cb126aee9b4a2855ab898fdfbfe7009fbf1f5 Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Thu, 31 Oct 2019 11:17:38 +0000 +Subject: [PATCH 2/3] x86/mm: alloc/free_lN_table: Retain partial_flags on + -EINTR + +When validating or de-validating pages (in alloc_lN_table and +free_lN_table respectively), the `partial_flags` local variable is +used to keep track of whether the "current" PTE started the entire +operation in a "may be partial" state. + +One of the patches in XSA-299 addressed the fact that it is possible +for a previously-partially-validated entry to subsequently be found to +have invalid entries (indicated by returning -EINVAL); in which case +page->partial_flags needs to be set to indicate that the current PTE +may have the partial bit set (and thus _put_page_type() should be +called with PTF_partial_set). + +Unfortunately, the patches in XSA-299 assumed that once +put_page_from_lNe() returned -ERESTART on a page, it was not possible +for it to return -EINTR. This turns out to be true for +alloc_lN_table() and free_lN_table, but not for _get_page_type() and +_put_page_type(): both can return -EINTR when called on pages with +PGT_partial set. In these cases, the pages PGT_partial will still be +set; failing to set partial_flags appropriately may allow an attacker +to do a privilege escalation similar to those described in XSA-299. + +Fix this by always copying the local partial_flags variable into +page->partial_flags when exiting early. + +NB that on the "get" side, no adjustment to nr_validated_entries is +needed: whether pte[i] is partially validated or entirely +un-validated, we want nr_validated_entries = i. On the "put" side, +however, we need to adjust nr_validated_entries appropriately: if +pte[i] is entirely validated, we want nr_validated_entries = i + 1; if +pte[i] is partially validated, we want nr_validated_entries = i. + +This is part of XSA-310. + +Reported-by: Sarah Newman +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +--- + xen/arch/x86/mm.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index a759afc9e3..97c8d73b7b 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -1557,7 +1557,7 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) + if ( rc == -EINTR && i ) + { + page->nr_validated_ptes = i; +- page->partial_flags = 0; ++ page->partial_flags = partial_flags;; + rc = -ERESTART; + } + else if ( rc < 0 && rc != -EINTR ) +@@ -1660,7 +1660,7 @@ static int alloc_l3_table(struct page_info *page) + else if ( rc == -EINTR && i ) + { + page->nr_validated_ptes = i; +- page->partial_flags = 0; ++ page->partial_flags = partial_flags; + rc = -ERESTART; + } + if ( rc < 0 ) +@@ -1982,8 +1982,8 @@ static int free_l2_table(struct page_info *page) + } + else if ( rc == -EINTR && i < L2_PAGETABLE_ENTRIES - 1 ) + { +- page->nr_validated_ptes = i + 1; +- page->partial_flags = 0; ++ page->nr_validated_ptes = i + !(partial_flags & PTF_partial_set); ++ page->partial_flags = partial_flags; + rc = -ERESTART; + } + +@@ -2030,8 +2030,8 @@ static int free_l3_table(struct page_info *page) + } + else if ( rc == -EINTR && i < L3_PAGETABLE_ENTRIES - 1 ) + { +- page->nr_validated_ptes = i + 1; +- page->partial_flags = 0; ++ page->nr_validated_ptes = i + !(partial_flags & PTF_partial_set); ++ page->partial_flags = partial_flags; + rc = -ERESTART; + } + return rc > 0 ? 0 : rc; +@@ -2061,8 +2061,8 @@ static int free_l4_table(struct page_info *page) + } + else if ( rc == -EINTR && i < L4_PAGETABLE_ENTRIES - 1 ) + { +- page->nr_validated_ptes = i + 1; +- page->partial_flags = 0; ++ page->nr_validated_ptes = i + !(partial_flags & PTF_partial_set); ++ page->partial_flags = partial_flags; + rc = -ERESTART; + } + +-- +2.24.0 + diff --git a/xsa310-0003-x86-mm-relinquish_memory-Grab-an-extra-type-ref-when.patch b/xsa310-0003-x86-mm-relinquish_memory-Grab-an-extra-type-ref-when.patch new file mode 100644 index 0000000..9ee4238 --- /dev/null +++ b/xsa310-0003-x86-mm-relinquish_memory-Grab-an-extra-type-ref-when.patch @@ -0,0 +1,75 @@ +From e9f835982a726ae16997c566b5eafab74f8b4cb7 Mon Sep 17 00:00:00 2001 +From: George Dunlap +Date: Mon, 28 Oct 2019 14:33:51 +0000 +Subject: [PATCH 3/3] x86/mm: relinquish_memory: Grab an extra type ref when + setting PGT_partial + +The PGT_partial bit in page->type_info holds both a type count and a +general ref count. During domain tear-down, when free_page_type() +returns -ERESTART, relinquish_memory() correctly handles the general +ref count, but fails to grab an extra type count when setting +PGT_partial. When this bit is eventually cleared, type_count underflows +and triggers the following BUG in page_alloc.c:free_domheap_pages(): + + BUG_ON((pg[i].u.inuse.type_info & PGT_count_mask) != 0); + +As far as we can tell, this page underflow cannot be exploited any any +other way: The page can't be used as a pagetable by the dying domain +because it's dying; it can't be used as a pagetable by any other +domain since it belongs to the dying domain; and ownership can't +transfer to any other domain without hitting the BUG_ON() in +free_domheap_pages(). + +(steal_page() won't work on a page in this state, since it requires +PGC_allocated to be set, and PGC_allocated will already have been +cleared.) + +Fix this by grabbing an extra type ref if setting PGT_partial in +relinquish_memory. + +This is part of XSA-310. + +Reported-by: Sarah Newman +Signed-off-by: George Dunlap +Acked-by: Jan Beulich +--- +v2: +- Move discussion of potential exploits into the commit message +- Keep PGT_partial and put_page() ordering +--- + xen/arch/x86/domain.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c +index f1dd86e12e..51880fc50d 100644 +--- a/xen/arch/x86/domain.c ++++ b/xen/arch/x86/domain.c +@@ -2049,6 +2049,25 @@ static int relinquish_memory( + goto out; + case -ERESTART: + page_list_add(page, list); ++ /* ++ * PGT_partial holds a type ref and a general ref. ++ * If we came in with PGT_partial set, then we 1) ++ * don't need to grab an extra type count, and 2) ++ * do need to drop the extra page ref we grabbed ++ * at the top of the loop. If we didn't come in ++ * with PGT_partial set, we 1) do need to drab an ++ * extra type count, but 2) can transfer the page ++ * ref we grabbed above to it. ++ * ++ * Note that we must increment type_info before ++ * setting PGT_partial. Theoretically it should ++ * be safe to drop the page ref before setting ++ * PGT_partial, but do it afterwards just to be ++ * extra safe. ++ */ ++ if ( !(x & PGT_partial) ) ++ page->u.inuse.type_info++; ++ smp_wmb(); + page->u.inuse.type_info |= PGT_partial; + if ( x & PGT_partial ) + put_page(page); +-- +2.24.0 + diff --git a/xsa311-4.12.patch b/xsa311-4.12.patch new file mode 100644 index 0000000..6c18188 --- /dev/null +++ b/xsa311-4.12.patch @@ -0,0 +1,194 @@ +From: Andrew Cooper +Subject: AMD/IOMMU: Cease using a dynamic height for the IOMMU pagetables + +update_paging_mode() has multiple bugs: + + 1) Booting with iommu=debug will cause it to inform you that that it called + without the pdev_list lock held. + 2) When growing by more than a single level, it leaks the newly allocated + table(s) in the case of a further error. + +Furthermore, the choice of default level for a domain has issues: + + 1) All HVM guests grow from 2 to 3 levels during construction because of the + position of the VRAM just below the 4G boundary, so defaulting to 2 is a + waste of effort. + 2) The limit for PV guests doesn't take memory hotplug into account, and + isn't dynamic at runtime like HVM guests. This means that a PV guest may + get RAM which it can't map in the IOMMU. + +The dynamic height is a property unique to AMD, and adds a substantial +quantity of complexity for what is a marginal performance improvement. Remove +the complexity by removing the dynamic height. + +PV guests now get 3 or 4 levels based on any hotplug regions in the host. +This only makes a difference for hardware which previously had all RAM below +the 512G boundary, and a hotplug region above. + +HVM guests now get 4 levels (which will be sufficient until 256TB guests +become a thing), because we don't currently have the information to know when +3 would be safe to use. + +The overhead of this extra level is not expected to be noticeable. It costs +one page (4k) per domain, and one extra IO-TLB paging structure cache entry +which is very hot and less likely to be evicted. + +This is XSA-311. + +Reported-by: XXX PERSON 3 +Signed-off-by: Andrew Cooper +Signed-off-by: Andrew Cooper +Acked-by: Jan Beulich + +diff --git a/xen/drivers/passthrough/amd/iommu_map.c b/xen/drivers/passthrough/amd/iommu_map.c +index 628aa60230..9a222c95e1 100644 +--- a/xen/drivers/passthrough/amd/iommu_map.c ++++ b/xen/drivers/passthrough/amd/iommu_map.c +@@ -455,100 +455,6 @@ static int iommu_pde_from_dfn(struct domain *d, unsigned long dfn, + return 0; + } + +-static int update_paging_mode(struct domain *d, unsigned long dfn) +-{ +- uint16_t bdf; +- void *device_entry; +- unsigned int req_id, level, offset; +- unsigned long flags; +- struct pci_dev *pdev; +- struct amd_iommu *iommu = NULL; +- struct page_info *new_root = NULL; +- struct page_info *old_root = NULL; +- void *new_root_vaddr; +- unsigned long old_root_mfn; +- struct domain_iommu *hd = dom_iommu(d); +- +- if ( dfn == dfn_x(INVALID_DFN) ) +- return -EADDRNOTAVAIL; +- ASSERT(!(dfn >> DEFAULT_DOMAIN_ADDRESS_WIDTH)); +- +- level = hd->arch.paging_mode; +- old_root = hd->arch.root_table; +- offset = dfn >> (PTE_PER_TABLE_SHIFT * (level - 1)); +- +- ASSERT(spin_is_locked(&hd->arch.mapping_lock) && is_hvm_domain(d)); +- +- while ( offset >= PTE_PER_TABLE_SIZE ) +- { +- /* Allocate and install a new root table. +- * Only upper I/O page table grows, no need to fix next level bits */ +- new_root = alloc_amd_iommu_pgtable(); +- if ( new_root == NULL ) +- { +- AMD_IOMMU_DEBUG("%s Cannot allocate I/O page table\n", +- __func__); +- return -ENOMEM; +- } +- +- new_root_vaddr = __map_domain_page(new_root); +- old_root_mfn = mfn_x(page_to_mfn(old_root)); +- set_iommu_pde_present(new_root_vaddr, old_root_mfn, level, +- !!IOMMUF_writable, !!IOMMUF_readable); +- level++; +- old_root = new_root; +- offset >>= PTE_PER_TABLE_SHIFT; +- unmap_domain_page(new_root_vaddr); +- } +- +- if ( new_root != NULL ) +- { +- hd->arch.paging_mode = level; +- hd->arch.root_table = new_root; +- +- if ( !pcidevs_locked() ) +- AMD_IOMMU_DEBUG("%s Try to access pdev_list " +- "without aquiring pcidevs_lock.\n", __func__); +- +- /* Update device table entries using new root table and paging mode */ +- for_each_pdev( d, pdev ) +- { +- if ( pdev->type == DEV_TYPE_PCI_HOST_BRIDGE ) +- continue; +- +- bdf = PCI_BDF2(pdev->bus, pdev->devfn); +- iommu = find_iommu_for_device(pdev->seg, bdf); +- if ( !iommu ) +- { +- AMD_IOMMU_DEBUG("%s Fail to find iommu.\n", __func__); +- return -ENODEV; +- } +- +- spin_lock_irqsave(&iommu->lock, flags); +- do { +- req_id = get_dma_requestor_id(pdev->seg, bdf); +- device_entry = iommu->dev_table.buffer + +- (req_id * IOMMU_DEV_TABLE_ENTRY_SIZE); +- +- /* valid = 0 only works for dom0 passthrough mode */ +- amd_iommu_set_root_page_table((uint32_t *)device_entry, +- page_to_maddr(hd->arch.root_table), +- d->domain_id, +- hd->arch.paging_mode, 1); +- +- amd_iommu_flush_device(iommu, req_id); +- bdf += pdev->phantom_stride; +- } while ( PCI_DEVFN2(bdf) != pdev->devfn && +- PCI_SLOT(bdf) == PCI_SLOT(pdev->devfn) ); +- spin_unlock_irqrestore(&iommu->lock, flags); +- } +- +- /* For safety, invalidate all entries */ +- amd_iommu_flush_all_pages(d); +- } +- return 0; +-} +- + int amd_iommu_map_page(struct domain *d, dfn_t dfn, mfn_t mfn, + unsigned int flags, unsigned int *flush_flags) + { +@@ -573,20 +479,6 @@ int amd_iommu_map_page(struct domain *d, dfn_t dfn, mfn_t mfn, + return rc; + } + +- /* Since HVM domain is initialized with 2 level IO page table, +- * we might need a deeper page table for wider dfn now */ +- if ( is_hvm_domain(d) ) +- { +- if ( update_paging_mode(d, dfn_x(dfn)) ) +- { +- spin_unlock(&hd->arch.mapping_lock); +- AMD_IOMMU_DEBUG("Update page mode failed dfn = %"PRI_dfn"\n", +- dfn_x(dfn)); +- domain_crash(d); +- return -EFAULT; +- } +- } +- + if ( iommu_pde_from_dfn(d, dfn_x(dfn), pt_mfn, true) || (pt_mfn[1] == 0) ) + { + spin_unlock(&hd->arch.mapping_lock); +diff --git a/xen/drivers/passthrough/amd/pci_amd_iommu.c b/xen/drivers/passthrough/amd/pci_amd_iommu.c +index 15c13e1163..57dc2c5f20 100644 +--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c ++++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c +@@ -242,10 +242,17 @@ static int amd_iommu_domain_init(struct domain *d) + { + struct domain_iommu *hd = dom_iommu(d); + +- /* For pv and dom0, stick with get_paging_mode(max_page) +- * For HVM dom0, use 2 level page table at first */ +- hd->arch.paging_mode = is_hvm_domain(d) ? +- 2 : amd_iommu_get_paging_mode(max_page); ++ /* ++ * Choose the number of levels for the IOMMU page tables. ++ * - PV needs 3 or 4, depending on whether there is RAM (including hotplug ++ * RAM) above the 512G boundary. ++ * - 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. ++ */ ++ hd->arch.paging_mode = is_hvm_domain(d) ++ ? 4 : amd_iommu_get_paging_mode(get_upper_mfn_bound()); ++ + return 0; + } + From 5f01f1feca6ddf57172d650f2a77444fe1b4a4a7 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 12 Dec 2019 00:22:46 +0000 Subject: [PATCH 026/194] fix a bug reference --- xen.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index a72a5eb..f942e98 100644 --- a/xen.spec +++ b/xen.spec @@ -951,7 +951,7 @@ fi %changelog * Wed Dec 11 2019 Michael Young - 4.12.1-8 - denial of service in find_next_bit() [XSA-307, CVE-2019-19581, - CVE-2019-19582] (#1780559) + CVE-2019-19582] (#1778191) - denial of service in HVM/PVH guest userspace code [XSA-308, CVE-2019-19583] (#1782206) - privilege escalation due to malicious PV guest [XSA-309, CVE-2019-19578] From 8a43935ac47f037934e05c065467c024f2e60ec6 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 12 Dec 2019 00:25:50 +0000 Subject: [PATCH 027/194] correct the correction to the bug reference --- xen.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index f942e98..ad033cd 100644 --- a/xen.spec +++ b/xen.spec @@ -951,7 +951,7 @@ fi %changelog * Wed Dec 11 2019 Michael Young - 4.12.1-8 - denial of service in find_next_bit() [XSA-307, CVE-2019-19581, - CVE-2019-19582] (#1778191) + CVE-2019-19582] (#1782211) - denial of service in HVM/PVH guest userspace code [XSA-308, CVE-2019-19583] (#1782206) - privilege escalation due to malicious PV guest [XSA-309, CVE-2019-19578] From 7fc59869cb0ebe42f1ea513a9047e69974d248fc Mon Sep 17 00:00:00 2001 From: Michael Young Date: Sun, 15 Dec 2019 19:03:55 +0000 Subject: [PATCH 028/194] fix build with OCaml 4.09.0 --- xen.ocaml.patch | 112 ++++++++++++++++++++++++++++++++++++++++++++++++ xen.spec | 7 ++- 2 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 xen.ocaml.patch diff --git a/xen.ocaml.patch b/xen.ocaml.patch new file mode 100644 index 0000000..bef10c2 --- /dev/null +++ b/xen.ocaml.patch @@ -0,0 +1,112 @@ +--- xen-4.12.1/tools/ocaml/libs/xentoollog/xentoollog_stubs.c.orig 2019-08-09 11:44:35.000000000 +0100 ++++ xen-4.12.1/tools/ocaml/libs/xentoollog/xentoollog_stubs.c 2019-12-15 15:54:48.701817927 +0000 +@@ -90,7 +90,7 @@ + CAMLparam0(); + CAMLlocalN(args, 4); + struct caml_xtl *xtl = (struct caml_xtl*)logger; +- value *func = caml_named_value(xtl->vmessage_cb) ; ++ const value *func = caml_named_value(xtl->vmessage_cb) ; + char *msg; + + if (func == NULL) +@@ -120,7 +120,7 @@ + CAMLparam0(); + CAMLlocalN(args, 5); + struct caml_xtl *xtl = (struct caml_xtl*)logger; +- value *func = caml_named_value(xtl->progress_cb) ; ++ const value *func = caml_named_value(xtl->progress_cb) ; + + if (func == NULL) + caml_raise_sys_error(caml_copy_string("Unable to find callback")); +--- xen-4.12.1/tools/ocaml/libs/xl/xenlight_stubs.c.orig 2019-08-09 11:44:35.000000000 +0100 ++++ xen-4.12.1/tools/ocaml/libs/xl/xenlight_stubs.c 2019-12-15 16:40:38.825318818 +0000 +@@ -75,7 +75,7 @@ + { + CAMLparam0(); + CAMLlocal1(arg); +- static value *exc = NULL; ++ const value *exc = NULL; + + /* First time around, lookup by name */ + if (!exc) +@@ -424,7 +424,7 @@ + caml_leave_blocking_section(); + CAMLparam0(); + CAMLlocal2(error, tmp); +- static value *func = NULL; ++ const value *func = NULL; + value *p = (value *) for_callback; + + if (func == NULL) { +@@ -1118,7 +1118,7 @@ + + static void raise_eof(void) + { +- static value *exc = NULL; ++ const value *exc = NULL; + + /* First time around, lookup by name */ + if (!exc) +@@ -1259,7 +1259,7 @@ + CAMLparam0(); + CAMLlocalN(args, 4); + int ret = 0; +- static value *func = NULL; ++ const value *func = NULL; + value *p = (value *) user; + value *for_app; + +@@ -1302,7 +1302,7 @@ + CAMLparam0(); + CAMLlocalN(args, 4); + int ret = 0; +- static value *func = NULL; ++ const value *func = NULL; + value *p = (value *) user; + value *for_app = *for_app_registration_update; + +@@ -1341,7 +1341,7 @@ + caml_leave_blocking_section(); + CAMLparam0(); + CAMLlocalN(args, 3); +- static value *func = NULL; ++ const value *func = NULL; + value *p = (value *) user; + value *for_app = for_app_registration; + +@@ -1383,7 +1383,7 @@ + CAMLlocal2(sec, usec); + CAMLlocalN(args, 4); + int ret = 0; +- static value *func = NULL; ++ const value *func = NULL; + value *p = (value *) user; + struct timeout_handles *handles; + +@@ -1435,7 +1435,7 @@ + CAMLlocal1(for_app_update); + CAMLlocalN(args, 2); + int ret = 0; +- static value *func = NULL; ++ const value *func = NULL; + value *p = (value *) user; + struct timeout_handles *handles = *for_app_registration_update; + +@@ -1551,7 +1551,7 @@ + CAMLparam0(); + CAMLlocalN(args, 2); + struct user_with_ctx *c_user = (struct user_with_ctx *) user; +- static value *func = NULL; ++ const value *func = NULL; + + if (func == NULL) { + /* First time around, lookup by name */ +@@ -1574,7 +1574,7 @@ + CAMLparam0(); + CAMLlocalN(args, 4); + struct user_with_ctx *c_user = (struct user_with_ctx *) user; +- static value *func = NULL; ++ const value *func = NULL; + + if (func == NULL) { + /* First time around, lookup by name */ diff --git a/xen.spec b/xen.spec index ad033cd..3f2f4e9 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.12.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 @@ -151,6 +151,7 @@ Patch88: xsa310-0003-x86-mm-relinquish_memory-Grab-an-extra-type-ref-when.patch Patch89: xen.git-36d2ecb9991bf2d1ddb933872c3dfbd26300ca68.patch Patch90: xen.git-1e8932f0d815bee3102ede328676e1a4ca1926c7.patch Patch91: xsa311-4.12.patch +Patch92: xen.ocaml.patch %if %build_qemutrad @@ -386,6 +387,7 @@ manage Xen virtual machines. %patch89 -p1 %patch90 -p1 %patch91 -p1 +%patch92 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -949,6 +951,9 @@ fi %endif %changelog +* Sun Dec 15 2019 Michael Young - 4.12.1-9 +- fix build with OCaml 4.09.0 + * Wed Dec 11 2019 Michael Young - 4.12.1-8 - denial of service in find_next_bit() [XSA-307, CVE-2019-19581, CVE-2019-19582] (#1782211) From 75bef3ece11cb380ababa32da687bd42db88d733 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 18 Dec 2019 20:12:13 +0000 Subject: [PATCH 029/194] update to xen-4.13.0 --- .gitignore | 2 +- sources | 2 +- ...32f0d815bee3102ede328676e1a4ca1926c7.patch | 341 ---- ...ecb9991bf2d1ddb933872c3dfbd26300ca68.patch | 110 -- xen.hypervisor.config | 12 +- xen.ocaml.patch | 112 -- xen.python3.patch | 1749 ----------------- xen.python38.patch | 94 - xen.spec | 100 +- xsa296.patch | 195 -- xsa298.patch | 89 - ...F-checks-don-t-leave-a-partial-entry.patch | 94 - ...set-PGT_pinned-on-a-partially-de-val.patch | 99 - ...out-partial_pte-tristate-into-indivi.patch | 618 ------ ...-for-_put_page_type-rather-than-a-bo.patch | 140 -- ...t_page_and_type_from_mfn-conditional.patch | 79 - ...c_l-23-_table-clear-partial_flags-wh.patch | 111 -- ...ways-retain-a-general-ref-on-partial.patch | 378 ---- ...PTF_partial_set-and-PTF_partial_gene.patch | 227 --- ...handle-linear-pagetable-promotion-fa.patch | 106 - ...mm-Fix-nested-de-validation-on-error.patch | 166 -- ...p-a-type-ref-unless-you-held-a-ref-t.patch | 413 ---- xsa301-master-1.patch | 80 - xsa301-master-2.patch | 92 - xsa301-master-3.patch | 67 - ....12-0001-IOMMU-add-missing-HVM-check.patch | 37 - ...2-passthrough-quarantine-PCI-devices.patch | 499 ----- ...try-Split-__DEFINE_ENTRY_TRAP-in-two.patch | 74 - ...Fold-the-macro-SAVE_ALL-in-the-macro.patch | 97 - ...blindly-unmask-interrupts-on-trap-wi.patch | 226 --- ...blindly-unmask-interrupts-on-trap-wi.patch | 114 -- xsa304-4.12-1.patch | 71 - xsa304-4.12-2.patch | 272 --- xsa304-4.12-3.patch | 108 - xsa305-4.12-1.patch | 288 --- xsa305-4.12-2.patch | 192 -- xsa306-4.12.patch | 91 - xsa307.patch | 99 - xsa308.patch | 74 - xsa309.patch | 58 - ...uest_table-when-destroying-vcpu-page.patch | 167 -- ...e_lN_table-Retain-partial_flags-on-E.patch | 104 - ...h_memory-Grab-an-extra-type-ref-when.patch | 75 - xsa311-4.12.patch | 194 -- 44 files changed, 28 insertions(+), 8288 deletions(-) delete mode 100644 xen.git-1e8932f0d815bee3102ede328676e1a4ca1926c7.patch delete mode 100644 xen.git-36d2ecb9991bf2d1ddb933872c3dfbd26300ca68.patch delete mode 100644 xen.ocaml.patch delete mode 100644 xen.python3.patch delete mode 100644 xen.python38.patch delete mode 100644 xsa296.patch delete mode 100644 xsa298.patch delete mode 100644 xsa299-4.12-0001-x86-mm-L1TF-checks-don-t-leave-a-partial-entry.patch delete mode 100644 xsa299-4.12-0002-x86-mm-Don-t-re-set-PGT_pinned-on-a-partially-de-val.patch delete mode 100644 xsa299-4.12-0003-x86-mm-Separate-out-partial_pte-tristate-into-indivi.patch delete mode 100644 xsa299-4.12-0004-x86-mm-Use-flags-for-_put_page_type-rather-than-a-bo.patch delete mode 100644 xsa299-4.12-0005-x86-mm-Rework-get_page_and_type_from_mfn-conditional.patch delete mode 100644 xsa299-4.12-0006-x86-mm-Have-alloc_l-23-_table-clear-partial_flags-wh.patch delete mode 100644 xsa299-4.12-0007-x86-mm-Always-retain-a-general-ref-on-partial.patch delete mode 100644 xsa299-4.12-0008-x86-mm-Collapse-PTF_partial_set-and-PTF_partial_gene.patch delete mode 100644 xsa299-4.12-0009-x86-mm-Properly-handle-linear-pagetable-promotion-fa.patch delete mode 100644 xsa299-4.12-0010-x86-mm-Fix-nested-de-validation-on-error.patch delete mode 100644 xsa299-4.12-0011-x86-mm-Don-t-drop-a-type-ref-unless-you-held-a-ref-t.patch delete mode 100644 xsa301-master-1.patch delete mode 100644 xsa301-master-2.patch delete mode 100644 xsa301-master-3.patch delete mode 100644 xsa302-4.12-0001-IOMMU-add-missing-HVM-check.patch delete mode 100644 xsa302-4.12-0002-passthrough-quarantine-PCI-devices.patch delete mode 100644 xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch delete mode 100644 xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch delete mode 100644 xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch delete mode 100644 xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch delete mode 100644 xsa304-4.12-1.patch delete mode 100644 xsa304-4.12-2.patch delete mode 100644 xsa304-4.12-3.patch delete mode 100644 xsa305-4.12-1.patch delete mode 100644 xsa305-4.12-2.patch delete mode 100644 xsa306-4.12.patch delete mode 100644 xsa307.patch delete mode 100644 xsa308.patch delete mode 100644 xsa309.patch delete mode 100644 xsa310-0001-x86-mm-Set-old_guest_table-when-destroying-vcpu-page.patch delete mode 100644 xsa310-0002-x86-mm-alloc-free_lN_table-Retain-partial_flags-on-E.patch delete mode 100644 xsa310-0003-x86-mm-relinquish_memory-Grab-an-extra-type-ref-when.patch delete mode 100644 xsa311-4.12.patch diff --git a/.gitignore b/.gitignore index 0c4dcc0..65b3a89 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.12.1.tar.gz +/xen-4.13.0.tar.gz diff --git a/sources b/sources index 7f28202..5f3458b 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.12.1.tar.gz) = be15d3af6b109771aaf59b3621e4e1560b7d9a963d9b7f6a1f6f4a1c907fd97434b060c15f75849b44fbda33f26eb51b030d14d068c6ad5103ad240fe7a98f40 +SHA512 (xen-4.13.0.tar.gz) = 5b2ded9a2fe3f7ddf40eed1fa9858baead06233a01eb6099cc45b3c78b6c3823acfe7b731910733e87125dfa49d08c53f74c215fb1b320a92b44b87a0a105225 diff --git a/xen.git-1e8932f0d815bee3102ede328676e1a4ca1926c7.patch b/xen.git-1e8932f0d815bee3102ede328676e1a4ca1926c7.patch deleted file mode 100644 index edbd01a..0000000 --- a/xen.git-1e8932f0d815bee3102ede328676e1a4ca1926c7.patch +++ /dev/null @@ -1,341 +0,0 @@ -From 1e8932f0d815bee3102ede328676e1a4ca1926c7 Mon Sep 17 00:00:00 2001 -From: Paul Durrant -Date: Fri, 6 Dec 2019 12:46:24 +0100 -Subject: [PATCH] x86 / iommu: set up a scratch page in the quarantine domain - -This patch introduces a new iommu_op to facilitate a per-implementation -quarantine set up, and then further code for x86 implementations -(amd and vtd) to set up a read-only scratch page to serve as the source -for DMA reads whilst a device is assigned to dom_io. DMA writes will -continue to fault as before. - -The reason for doing this is that some hardware may continue to re-try -DMA (despite FLR) in the event of an error, or even BME being cleared, and -will fail to deal with DMA read faults gracefully. Having a scratch page -mapped will allow pending DMA reads to complete and thus such buggy -hardware will eventually be quiesced. - -NOTE: These modifications are restricted to x86 implementations only as - the buggy h/w I am aware of is only used with Xen in an x86 - environment. ARM may require similar code but, since I am not - aware of the need, this patch does not modify any ARM implementation. - -Signed-off-by: Paul Durrant -Reviewed-by: Jan Beulich -master commit: ea38867831da67eed0e9c61672c8941016b63dd9 -master date: 2019-11-29 18:27:54 +0000 ---- - xen/drivers/passthrough/amd/iommu_map.c | 62 ++++++++++++++++++++++ - xen/drivers/passthrough/amd/pci_amd_iommu.c | 14 ++--- - xen/drivers/passthrough/iommu.c | 17 +++++- - xen/drivers/passthrough/vtd/iommu.c | 74 ++++++++++++++++++++++----- - xen/include/asm-x86/hvm/svm/amd-iommu-proto.h | 3 ++ - xen/include/xen/iommu.h | 1 + - 6 files changed, 147 insertions(+), 24 deletions(-) - -diff --git a/xen/drivers/passthrough/amd/iommu_map.c b/xen/drivers/passthrough/amd/iommu_map.c -index 628aa60230..f876ce832b 100644 ---- a/xen/drivers/passthrough/amd/iommu_map.c -+++ b/xen/drivers/passthrough/amd/iommu_map.c -@@ -758,6 +758,68 @@ void amd_iommu_share_p2m(struct domain *d) - } - } - -+int __init amd_iommu_quarantine_init(struct domain *d) -+{ -+ struct domain_iommu *hd = dom_iommu(d); -+ unsigned long max_gfn = -+ PFN_DOWN((1ul << DEFAULT_DOMAIN_ADDRESS_WIDTH) - 1); -+ unsigned int level = amd_iommu_get_paging_mode(max_gfn); -+ uint64_t *table; -+ -+ if ( hd->arch.root_table ) -+ { -+ ASSERT_UNREACHABLE(); -+ return 0; -+ } -+ -+ spin_lock(&hd->arch.mapping_lock); -+ -+ hd->arch.root_table = alloc_amd_iommu_pgtable(); -+ if ( !hd->arch.root_table ) -+ goto out; -+ -+ table = __map_domain_page(hd->arch.root_table); -+ while ( level ) -+ { -+ struct page_info *pg; -+ unsigned int i; -+ -+ /* -+ * The pgtable allocator is fine for the leaf page, as well as -+ * page table pages, and the resulting allocations are always -+ * zeroed. -+ */ -+ pg = alloc_amd_iommu_pgtable(); -+ if ( !pg ) -+ break; -+ -+ for ( i = 0; i < PTE_PER_TABLE_SIZE; i++ ) -+ { -+ uint32_t *pde = (uint32_t *)&table[i]; -+ -+ /* -+ * 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); -+ } -+ -+ unmap_domain_page(table); -+ table = __map_domain_page(pg); -+ level--; -+ } -+ unmap_domain_page(table); -+ -+ out: -+ spin_unlock(&hd->arch.mapping_lock); -+ -+ amd_iommu_flush_all_pages(d); -+ -+ /* Pages leaked in failure case */ -+ return level ? -ENOMEM : 0; -+} -+ - /* - * Local variables: - * mode: C -diff --git a/xen/drivers/passthrough/amd/pci_amd_iommu.c b/xen/drivers/passthrough/amd/pci_amd_iommu.c -index 15c13e1163..93ecae4a6c 100644 ---- a/xen/drivers/passthrough/amd/pci_amd_iommu.c -+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c -@@ -120,10 +120,6 @@ static void amd_iommu_setup_domain_device( - u8 bus = pdev->bus; - const struct domain_iommu *hd = dom_iommu(domain); - -- /* dom_io is used as a sentinel for quarantined devices */ -- if ( domain == dom_io ) -- return; -- - BUG_ON( !hd->arch.root_table || !hd->arch.paging_mode || - !iommu->dev_table.buffer ); - -@@ -226,7 +222,7 @@ static int __must_check allocate_domain_resources(struct domain_iommu *hd) - return rc; - } - --static int get_paging_mode(unsigned long entries) -+int amd_iommu_get_paging_mode(unsigned long entries) - { - int level = 1; - -@@ -248,7 +244,8 @@ static int amd_iommu_domain_init(struct domain *d) - - /* For pv and dom0, stick with get_paging_mode(max_page) - * For HVM dom0, use 2 level page table at first */ -- hd->arch.paging_mode = is_hvm_domain(d) ? 2 : get_paging_mode(max_page); -+ hd->arch.paging_mode = is_hvm_domain(d) ? -+ 2 : amd_iommu_get_paging_mode(max_page); - return 0; - } - -@@ -281,10 +278,6 @@ void amd_iommu_disable_domain_device(struct domain *domain, - int req_id; - u8 bus = pdev->bus; - -- /* dom_io is used as a sentinel for quarantined devices */ -- if ( domain == dom_io ) -- return; -- - BUG_ON ( iommu->dev_table.buffer == NULL ); - req_id = get_dma_requestor_id(iommu->seg, PCI_BDF2(bus, devfn)); - dte = iommu->dev_table.buffer + (req_id * IOMMU_DEV_TABLE_ENTRY_SIZE); -@@ -581,6 +574,7 @@ static void amd_dump_p2m_table(struct domain *d) - static const struct iommu_ops __initconstrel amd_iommu_ops = { - .init = amd_iommu_domain_init, - .hwdom_init = amd_iommu_hwdom_init, -+ .quarantine_init = amd_iommu_quarantine_init, - .add_device = amd_iommu_add_device, - .remove_device = amd_iommu_remove_device, - .assign_device = amd_iommu_assign_device, -diff --git a/xen/drivers/passthrough/iommu.c b/xen/drivers/passthrough/iommu.c -index 20db08e1df..a747bfb946 100644 ---- a/xen/drivers/passthrough/iommu.c -+++ b/xen/drivers/passthrough/iommu.c -@@ -497,6 +497,21 @@ int iommu_iotlb_flush_all(struct domain *d, unsigned int flush_flags) - return rc; - } - -+static int __init iommu_quarantine_init(void) -+{ -+ const struct domain_iommu *hd = dom_iommu(dom_io); -+ int rc; -+ -+ rc = iommu_domain_init(dom_io); -+ if ( rc ) -+ return rc; -+ -+ if ( !hd->platform_ops->quarantine_init ) -+ return 0; -+ -+ return hd->platform_ops->quarantine_init(dom_io); -+} -+ - int __init iommu_setup(void) - { - int rc = -ENODEV; -@@ -530,7 +545,7 @@ int __init iommu_setup(void) - printk("I/O virtualisation %sabled\n", iommu_enabled ? "en" : "dis"); - if ( iommu_enabled ) - { -- if ( iommu_domain_init(dom_io) ) -+ if ( iommu_quarantine_init() ) - panic("Could not set up quarantine\n"); - - printk(" - Dom0 mode: %s\n", -diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c -index 5663e9740d..576e72eba1 100644 ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -192,7 +192,7 @@ u64 alloc_pgtable_maddr(struct acpi_drhd_unit *drhd, unsigned long npages) - nodeid_t node = NUMA_NO_NODE; - unsigned int i; - -- rhsa = drhd_to_rhsa(drhd); -+ rhsa = drhd ? drhd_to_rhsa(drhd) : NULL; - if ( rhsa ) - node = pxm_to_node(rhsa->proximity_domain); - -@@ -1340,10 +1340,6 @@ int domain_context_mapping_one( - int agaw, rc, ret; - bool_t flush_dev_iotlb; - -- /* dom_io is used as a sentinel for quarantined devices */ -- if ( domain == dom_io ) -- return 0; -- - ASSERT(pcidevs_locked()); - spin_lock(&iommu->lock); - maddr = bus_to_context_maddr(iommu, bus); -@@ -1579,10 +1575,6 @@ int domain_context_unmap_one( - int iommu_domid, rc, ret; - bool_t flush_dev_iotlb; - -- /* dom_io is used as a sentinel for quarantined devices */ -- if ( domain == dom_io ) -- return 0; -- - ASSERT(pcidevs_locked()); - spin_lock(&iommu->lock); - -@@ -1715,10 +1707,6 @@ static int domain_context_unmap(struct domain *domain, u8 devfn, - goto out; - } - -- /* dom_io is used as a sentinel for quarantined devices */ -- if ( domain == dom_io ) -- 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 -@@ -2736,9 +2724,69 @@ static void vtd_dump_p2m_table(struct domain *d) - vtd_dump_p2m_table_level(hd->arch.pgd_maddr, agaw_to_level(hd->arch.agaw), 0, 0); - } - -+static int __init intel_iommu_quarantine_init(struct domain *d) -+{ -+ struct domain_iommu *hd = dom_iommu(d); -+ struct dma_pte *parent; -+ unsigned int agaw = width_to_agaw(DEFAULT_DOMAIN_ADDRESS_WIDTH); -+ unsigned int level = agaw_to_level(agaw); -+ int rc; -+ -+ if ( hd->arch.pgd_maddr ) -+ { -+ ASSERT_UNREACHABLE(); -+ return 0; -+ } -+ -+ spin_lock(&hd->arch.mapping_lock); -+ -+ hd->arch.pgd_maddr = alloc_pgtable_maddr(NULL, 1); -+ if ( !hd->arch.pgd_maddr ) -+ goto out; -+ -+ parent = map_vtd_domain_page(hd->arch.pgd_maddr); -+ while ( level ) -+ { -+ 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. -+ */ -+ maddr = alloc_pgtable_maddr(NULL, 1); -+ if ( !maddr ) -+ break; -+ -+ for ( offset = 0; offset < PTE_NUM; offset++ ) -+ { -+ struct dma_pte *pte = &parent[offset]; -+ -+ dma_set_pte_addr(*pte, maddr); -+ dma_set_pte_readable(*pte); -+ } -+ iommu_flush_cache_page(parent, 1); -+ -+ unmap_vtd_domain_page(parent); -+ parent = map_vtd_domain_page(maddr); -+ level--; -+ } -+ unmap_vtd_domain_page(parent); -+ -+ out: -+ spin_unlock(&hd->arch.mapping_lock); -+ -+ rc = iommu_flush_iotlb_all(d); -+ -+ /* Pages leaked in failure case */ -+ return level ? -ENOMEM : rc; -+} -+ - const struct iommu_ops __initconstrel intel_iommu_ops = { - .init = intel_iommu_domain_init, - .hwdom_init = intel_iommu_hwdom_init, -+ .quarantine_init = intel_iommu_quarantine_init, - .add_device = intel_iommu_add_device, - .enable_device = intel_iommu_enable_device, - .remove_device = intel_iommu_remove_device, -diff --git a/xen/include/asm-x86/hvm/svm/amd-iommu-proto.h b/xen/include/asm-x86/hvm/svm/amd-iommu-proto.h -index c5697565d6..5a160b89ca 100644 ---- a/xen/include/asm-x86/hvm/svm/amd-iommu-proto.h -+++ b/xen/include/asm-x86/hvm/svm/amd-iommu-proto.h -@@ -51,6 +51,9 @@ void get_iommu_features(struct amd_iommu *iommu); - int amd_iommu_init(void); - int amd_iommu_update_ivrs_mapping_acpi(void); - -+int amd_iommu_get_paging_mode(unsigned long entries); -+int amd_iommu_quarantine_init(struct domain *d); -+ - /* mapping functions */ - int __must_check amd_iommu_map_page(struct domain *d, dfn_t dfn, - mfn_t mfn, unsigned int flags, -diff --git a/xen/include/xen/iommu.h b/xen/include/xen/iommu.h -index 70ee53d083..617c993f50 100644 ---- a/xen/include/xen/iommu.h -+++ b/xen/include/xen/iommu.h -@@ -189,6 +189,7 @@ typedef int iommu_grdm_t(xen_pfn_t start, xen_ulong_t nr, u32 id, void *ctxt); - struct iommu_ops { - int (*init)(struct domain *d); - void (*hwdom_init)(struct domain *d); -+ int (*quarantine_init)(struct domain *d); - int (*add_device)(u8 devfn, device_t *dev); - int (*enable_device)(device_t *dev); - int (*remove_device)(u8 devfn, device_t *dev); --- -2.11.0 - diff --git a/xen.git-36d2ecb9991bf2d1ddb933872c3dfbd26300ca68.patch b/xen.git-36d2ecb9991bf2d1ddb933872c3dfbd26300ca68.patch deleted file mode 100644 index 448871a..0000000 --- a/xen.git-36d2ecb9991bf2d1ddb933872c3dfbd26300ca68.patch +++ /dev/null @@ -1,110 +0,0 @@ -From 36d2ecb9991bf2d1ddb933872c3dfbd26300ca68 Mon Sep 17 00:00:00 2001 -From: Jan Beulich -Date: Mon, 25 Nov 2019 16:01:00 +0100 -Subject: [PATCH] AMD/IOMMU: don't needlessly trigger errors/crashes when - unmapping a page - -Unmapping a page which has never been mapped should be a no-op (note how -it already is in case there was no root page table allocated). There's -in particular no need to grow the number of page table levels in use, -and there's also no need to allocate intermediate page tables except -when needing to split a large page. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant -Acked-by: Andrew Cooper -master commit: ad591454f069647c36a7daaa9ec23384c0263f0b -master date: 2019-11-12 11:08:34 +0100 ---- - xen/drivers/passthrough/amd/iommu_map.c | 40 +++++++++++++++------------------ - 1 file changed, 18 insertions(+), 22 deletions(-) - -diff --git a/xen/drivers/passthrough/amd/iommu_map.c b/xen/drivers/passthrough/amd/iommu_map.c -index 67329b0c95..628aa60230 100644 ---- a/xen/drivers/passthrough/amd/iommu_map.c -+++ b/xen/drivers/passthrough/amd/iommu_map.c -@@ -343,7 +343,7 @@ uint64_t amd_iommu_get_address_from_pte(void *pte) - * page tables. - */ - static int iommu_pde_from_dfn(struct domain *d, unsigned long dfn, -- unsigned long pt_mfn[]) -+ unsigned long pt_mfn[], bool map) - { - uint64_t *pde, *next_table_vaddr; - unsigned long next_table_mfn; -@@ -356,6 +356,13 @@ static int iommu_pde_from_dfn(struct domain *d, unsigned long dfn, - - BUG_ON( table == NULL || level < 1 || level > 6 ); - -+ /* -+ * A frame number past what the current page tables can represent can't -+ * possibly have a mapping. -+ */ -+ if ( dfn >> (PTE_PER_TABLE_SHIFT * level) ) -+ return 0; -+ - next_table_mfn = mfn_x(page_to_mfn(table)); - - if ( level == 1 ) -@@ -415,6 +422,9 @@ static int iommu_pde_from_dfn(struct domain *d, unsigned long dfn, - /* Install lower level page table for non-present entries */ - else if ( !iommu_is_pte_present((uint32_t *)pde) ) - { -+ if ( !map ) -+ return 0; -+ - if ( next_table_mfn == 0 ) - { - table = alloc_amd_iommu_pgtable(); -@@ -577,7 +587,7 @@ int amd_iommu_map_page(struct domain *d, dfn_t dfn, mfn_t mfn, - } - } - -- if ( iommu_pde_from_dfn(d, dfn_x(dfn), pt_mfn) || (pt_mfn[1] == 0) ) -+ if ( iommu_pde_from_dfn(d, dfn_x(dfn), pt_mfn, true) || (pt_mfn[1] == 0) ) - { - spin_unlock(&hd->arch.mapping_lock); - AMD_IOMMU_DEBUG("Invalid IO pagetable entry dfn = %"PRI_dfn"\n", -@@ -615,24 +625,7 @@ int amd_iommu_unmap_page(struct domain *d, dfn_t dfn, - return 0; - } - -- /* Since HVM domain is initialized with 2 level IO page table, -- * we might need a deeper page table for lager dfn now */ -- if ( is_hvm_domain(d) ) -- { -- int rc = update_paging_mode(d, dfn_x(dfn)); -- -- if ( rc ) -- { -- spin_unlock(&hd->arch.mapping_lock); -- AMD_IOMMU_DEBUG("Update page mode failed dfn = %"PRI_dfn"\n", -- dfn_x(dfn)); -- if ( rc != -EADDRNOTAVAIL ) -- domain_crash(d); -- return rc; -- } -- } -- -- if ( iommu_pde_from_dfn(d, dfn_x(dfn), pt_mfn) || (pt_mfn[1] == 0) ) -+ if ( iommu_pde_from_dfn(d, dfn_x(dfn), pt_mfn, false) ) - { - spin_unlock(&hd->arch.mapping_lock); - AMD_IOMMU_DEBUG("Invalid IO pagetable entry dfn = %"PRI_dfn"\n", -@@ -641,8 +634,11 @@ int amd_iommu_unmap_page(struct domain *d, dfn_t dfn, - return -EFAULT; - } - -- /* mark PTE as 'page not present' */ -- *flush_flags |= clear_iommu_pte_present(pt_mfn[1], dfn_x(dfn)); -+ if ( pt_mfn[1] ) -+ { -+ /* Mark PTE as 'page not present'. */ -+ *flush_flags |= clear_iommu_pte_present(pt_mfn[1], dfn_x(dfn)); -+ } - - spin_unlock(&hd->arch.mapping_lock); - --- -2.11.0 - diff --git a/xen.hypervisor.config b/xen.hypervisor.config index e3d9369..cf6aa02 100644 --- a/xen.hypervisor.config +++ b/xen.hypervisor.config @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Xen/x86 4.12.0 Configuration +# Xen/x86 4.13.0 Configuration # CONFIG_X86_64=y CONFIG_X86=y @@ -17,6 +17,8 @@ CONFIG_SHADOW_PAGING=y # CONFIG_BIGMEM is not set # CONFIG_HVM_FEP is not set CONFIG_TBOOT=y +CONFIG_XEN_ALIGN_DEFAULT=y +# CONFIG_XEN_ALIGN_2M is not set # CONFIG_XEN_GUEST is not set # @@ -36,6 +38,12 @@ CONFIG_HAS_KEXEC=y CONFIG_HAS_GDBSX=y CONFIG_HAS_IOPORTS=y CONFIG_NEEDS_LIBELF=y + +# +# Speculative hardening +# +CONFIG_SPECULATIVE_HARDEN_ARRAY=y +CONFIG_SPECULATIVE_HARDEN_BRANCH=y CONFIG_KEXEC=y CONFIG_XENOPROF=y # CONFIG_XSM is not set @@ -48,6 +56,7 @@ CONFIG_SCHED_DEFAULT="credit2" CONFIG_CRYPTO=y CONFIG_LIVEPATCH=y CONFIG_FAST_SYMBOL_LOOKUP=y +CONFIG_ENFORCE_UNIQUE_SYMBOLS=y CONFIG_CMDLINE="" CONFIG_DOM0_MEM="" @@ -107,6 +116,7 @@ CONFIG_MPSOC_PLATFORM=y CONFIG_HAS_DEVICE_TREE=y CONFIG_HAS_CADENCE_UART=y CONFIG_HAS_MVEBU=y +CONFIG_HAS_MESON=y CONFIG_HAS_PL011=y CONFIG_HAS_SCIF=y CONFIG_ARM_SMMU=y diff --git a/xen.ocaml.patch b/xen.ocaml.patch deleted file mode 100644 index bef10c2..0000000 --- a/xen.ocaml.patch +++ /dev/null @@ -1,112 +0,0 @@ ---- xen-4.12.1/tools/ocaml/libs/xentoollog/xentoollog_stubs.c.orig 2019-08-09 11:44:35.000000000 +0100 -+++ xen-4.12.1/tools/ocaml/libs/xentoollog/xentoollog_stubs.c 2019-12-15 15:54:48.701817927 +0000 -@@ -90,7 +90,7 @@ - CAMLparam0(); - CAMLlocalN(args, 4); - struct caml_xtl *xtl = (struct caml_xtl*)logger; -- value *func = caml_named_value(xtl->vmessage_cb) ; -+ const value *func = caml_named_value(xtl->vmessage_cb) ; - char *msg; - - if (func == NULL) -@@ -120,7 +120,7 @@ - CAMLparam0(); - CAMLlocalN(args, 5); - struct caml_xtl *xtl = (struct caml_xtl*)logger; -- value *func = caml_named_value(xtl->progress_cb) ; -+ const value *func = caml_named_value(xtl->progress_cb) ; - - if (func == NULL) - caml_raise_sys_error(caml_copy_string("Unable to find callback")); ---- xen-4.12.1/tools/ocaml/libs/xl/xenlight_stubs.c.orig 2019-08-09 11:44:35.000000000 +0100 -+++ xen-4.12.1/tools/ocaml/libs/xl/xenlight_stubs.c 2019-12-15 16:40:38.825318818 +0000 -@@ -75,7 +75,7 @@ - { - CAMLparam0(); - CAMLlocal1(arg); -- static value *exc = NULL; -+ const value *exc = NULL; - - /* First time around, lookup by name */ - if (!exc) -@@ -424,7 +424,7 @@ - caml_leave_blocking_section(); - CAMLparam0(); - CAMLlocal2(error, tmp); -- static value *func = NULL; -+ const value *func = NULL; - value *p = (value *) for_callback; - - if (func == NULL) { -@@ -1118,7 +1118,7 @@ - - static void raise_eof(void) - { -- static value *exc = NULL; -+ const value *exc = NULL; - - /* First time around, lookup by name */ - if (!exc) -@@ -1259,7 +1259,7 @@ - CAMLparam0(); - CAMLlocalN(args, 4); - int ret = 0; -- static value *func = NULL; -+ const value *func = NULL; - value *p = (value *) user; - value *for_app; - -@@ -1302,7 +1302,7 @@ - CAMLparam0(); - CAMLlocalN(args, 4); - int ret = 0; -- static value *func = NULL; -+ const value *func = NULL; - value *p = (value *) user; - value *for_app = *for_app_registration_update; - -@@ -1341,7 +1341,7 @@ - caml_leave_blocking_section(); - CAMLparam0(); - CAMLlocalN(args, 3); -- static value *func = NULL; -+ const value *func = NULL; - value *p = (value *) user; - value *for_app = for_app_registration; - -@@ -1383,7 +1383,7 @@ - CAMLlocal2(sec, usec); - CAMLlocalN(args, 4); - int ret = 0; -- static value *func = NULL; -+ const value *func = NULL; - value *p = (value *) user; - struct timeout_handles *handles; - -@@ -1435,7 +1435,7 @@ - CAMLlocal1(for_app_update); - CAMLlocalN(args, 2); - int ret = 0; -- static value *func = NULL; -+ const value *func = NULL; - value *p = (value *) user; - struct timeout_handles *handles = *for_app_registration_update; - -@@ -1551,7 +1551,7 @@ - CAMLparam0(); - CAMLlocalN(args, 2); - struct user_with_ctx *c_user = (struct user_with_ctx *) user; -- static value *func = NULL; -+ const value *func = NULL; - - if (func == NULL) { - /* First time around, lookup by name */ -@@ -1574,7 +1574,7 @@ - CAMLparam0(); - CAMLlocalN(args, 4); - struct user_with_ctx *c_user = (struct user_with_ctx *) user; -- static value *func = NULL; -+ const value *func = NULL; - - if (func == NULL) { - /* First time around, lookup by name */ diff --git a/xen.python3.patch b/xen.python3.patch deleted file mode 100644 index a89b4d9..0000000 --- a/xen.python3.patch +++ /dev/null @@ -1,1749 +0,0 @@ -From a9047a722ba5de38e7c1d762ffcfb74c36725fe2 Mon Sep 17 00:00:00 2001 -From: Andrew Cooper -Date: Mon, 11 Mar 2019 19:18:40 +0000 -Subject: [PATCH] tools/xen-foreign: Update python scripts to be Py3 compatible - -The issues are: - * dict.has_key() was completely removed in Py3 - * dict.keys() is an iterable rather than list in Py3, so .sort() doesn't work. - * list.sort(cmp=) was deprecated in Py2.4 and removed in Py3. - -The has_key() issue is trivially fixed by switching to using the in keyword. -The sorting issue could be trivially fixed, but take the opportunity to -improve the code. - -The reason for the sorting is to ensure that "unsigned long" gets replaced -before "long", and the only reason sorting is necessary is because -inttypes[arch] is needlessly a dictionary. Update inttypes[arch] to be a list -of tuples rather than a dictionary, and process them in list order. - -Reported-by: George Dunlap -Signed-off-by: Andrew Cooper -Acked-by: Wei Liu ---- - tools/include/xen-foreign/mkchecker.py | 2 +- - tools/include/xen-foreign/mkheader.py | 58 +++++++++++++------------- - 2 files changed, 29 insertions(+), 31 deletions(-) - -diff --git a/tools/include/xen-foreign/mkchecker.py b/tools/include/xen-foreign/mkchecker.py -index fdad869a91..199b0eebbc 100644 ---- a/tools/include/xen-foreign/mkchecker.py -+++ b/tools/include/xen-foreign/mkchecker.py -@@ -37,7 +37,7 @@ for struct in structs: - f.write('\tprintf("%%-25s |", "%s");\n' % struct); - for a in archs: - s = struct + "_" + a; -- if compat_arches.has_key(a): -+ if a in compat_arches: - compat = compat_arches[a] - c = struct + "_" + compat; - else: -diff --git a/tools/include/xen-foreign/mkheader.py b/tools/include/xen-foreign/mkheader.py -index 97e0c7a984..fb268f0dce 100644 ---- a/tools/include/xen-foreign/mkheader.py -+++ b/tools/include/xen-foreign/mkheader.py -@@ -17,13 +17,13 @@ header = {}; - footer = {}; - - #arm --inttypes["arm32"] = { -- "unsigned long" : "__danger_unsigned_long_on_arm32", -- "long" : "__danger_long_on_arm32", -- "xen_pfn_t" : "uint64_t", -- "xen_ulong_t" : "uint64_t", -- "uint64_t" : "__align8__ uint64_t", --}; -+inttypes["arm32"] = [ -+ ("unsigned long", "__danger_unsigned_long_on_arm32"), -+ ("long", "__danger_long_on_arm32"), -+ ("xen_pfn_t", "uint64_t"), -+ ("xen_ulong_t", "uint64_t"), -+ ("uint64_t", "__align8__ uint64_t"), -+] - header["arm32"] = """ - #define __arm___ARM32 1 - #if defined(__GNUC__) && !defined(__STRICT_ANSI__) -@@ -38,13 +38,13 @@ footer["arm32"] = """ - #undef __DECL_REG - """ - --inttypes["arm64"] = { -- "unsigned long" : "__danger_unsigned_long_on_arm64", -- "long" : "__danger_long_on_arm64", -- "xen_pfn_t" : "uint64_t", -- "xen_ulong_t" : "uint64_t", -- "uint64_t" : "__align8__ uint64_t", --}; -+inttypes["arm64"] = [ -+ ("unsigned long", "__danger_unsigned_long_on_arm64"), -+ ("long", "__danger_long_on_arm64"), -+ ("xen_pfn_t", "uint64_t"), -+ ("xen_ulong_t", "uint64_t"), -+ ("uint64_t", "__align8__ uint64_t"), -+] - header["arm64"] = """ - #define __aarch64___ARM64 1 - #if defined(__GNUC__) && !defined(__STRICT_ANSI__) -@@ -60,12 +60,12 @@ footer["arm64"] = """ - """ - - # x86_32 --inttypes["x86_32"] = { -- "unsigned long" : "uint32_t", -- "long" : "uint32_t", -- "xen_pfn_t" : "uint32_t", -- "xen_ulong_t" : "uint32_t", --}; -+inttypes["x86_32"] = [ -+ ("unsigned long", "uint32_t"), -+ ("long", "uint32_t"), -+ ("xen_pfn_t", "uint32_t"), -+ ("xen_ulong_t", "uint32_t"), -+] - header["x86_32"] = """ - #define __DECL_REG_LO8(which) uint32_t e ## which ## x - #define __DECL_REG_LO16(name) uint32_t e ## name -@@ -79,12 +79,12 @@ footer["x86_32"] = """ - """; - - # x86_64 --inttypes["x86_64"] = { -- "unsigned long" : "__align8__ uint64_t", -- "long" : "__align8__ uint64_t", -- "xen_pfn_t" : "__align8__ uint64_t", -- "xen_ulong_t" : "__align8__ uint64_t", --}; -+inttypes["x86_64"] = [ -+ ("unsigned long", "__align8__ uint64_t"), -+ ("long", "__align8__ uint64_t"), -+ ("xen_pfn_t", "__align8__ uint64_t"), -+ ("xen_ulong_t", "__align8__ uint64_t"), -+] - header["x86_64"] = """ - #if defined(__GNUC__) && !defined(__STRICT_ANSI__) - # define __DECL_REG(name) union { uint64_t r ## name, e ## name; } -@@ -205,10 +205,8 @@ for struct in structs: - output = re.sub("\\b(%s)_t\\b" % struct, "\\1_%s_t" % arch, output); - - # replace: integer types --integers = inttypes[arch].keys(); --integers.sort(lambda a, b: cmp(len(b),len(a))); --for type in integers: -- output = re.sub("\\b%s\\b" % type, inttypes[arch][type], output); -+for old, new in inttypes[arch]: -+ output = re.sub("\\b%s\\b" % old, new, output) - - # print results - f = open(outfile, "w"); --- -2.17.2 - -From 72288c156662e764cc47605f51842b4d0fcc3582 Mon Sep 17 00:00:00 2001 -From: Wei Liu -Date: Mon, 11 Mar 2019 17:16:45 +0000 -Subject: [PATCH 1/8] Update Python requirement to 2.6 - -CentOS 5, which was the reason for the 2.4 restriction, is EOL. CentOS -6 ships 2.6. - -Bump the version to 2.6 in README. Update configure. - -Signed-off-by: Wei Liu -Acked-by: Andrew Cooper ---- - README | 4 ++-- - tools/configure | 8 ++++---- - tools/configure.ac | 2 +- - 3 files changed, 7 insertions(+), 7 deletions(-) - -diff --git a/README b/README -index 6346825bc0..d00715c2fd 100644 ---- a/README -+++ b/README -@@ -46,7 +46,7 @@ provided by your OS distributor: - - GCC 4.8 or later - - GNU Binutils 2.24 or later - * Development install of zlib (e.g., zlib-dev) -- * Development install of Python 2, v2.4 or later (e.g., python-dev) -+ * Development install of Python 2, v2.6 or later (e.g., python-dev) - * Development install of curses (e.g., libncurses-dev) - * Development install of openssl (e.g., openssl-dev) - * Development install of x11 (e.g. xorg-x11-dev) -@@ -177,7 +177,7 @@ Python Runtime Libraries - - Various tools, such as pygrub, have the following runtime dependencies: - -- * Python 2, v2.4 or later. -+ * Python 2, v2.6 or later. - URL: http://www.python.org/ - Debian: python - -diff --git a/tools/configure b/tools/configure -index acc857510e..d0065b811d 100755 ---- a/tools/configure -+++ b/tools/configure -@@ -7002,15 +7002,15 @@ if test x"${PYTHONPATH}" = x"no" - then - as_fn_error $? "Unable to find $PYTHON, please install $PYTHON" "$LINENO" 5 - fi --{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for python version >= 2.3 " >&5 --$as_echo_n "checking for python version >= 2.3 ... " >&6; } --`$PYTHON -c 'import sys; sys.exit(eval("sys.version_info < (2, 3)"))'` -+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for python version >= 2.6 " >&5 -+$as_echo_n "checking for python version >= 2.6 ... " >&6; } -+`$PYTHON -c 'import sys; sys.exit(eval("sys.version_info < (2, 6)"))'` - if test "$?" != "0" - then - python_version=`$PYTHON -V 2>&1` - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 - $as_echo "no" >&6; } -- as_fn_error $? "$python_version is too old, minimum required version is 2.3" "$LINENO" 5 -+ as_fn_error $? "$python_version is too old, minimum required version is 2.6" "$LINENO" 5 - else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 - $as_echo "yes" >&6; } -diff --git a/tools/configure.ac b/tools/configure.ac -index 1499344ce6..c9fd69ddfa 100644 ---- a/tools/configure.ac -+++ b/tools/configure.ac -@@ -358,7 +358,7 @@ AS_IF([echo "$PYTHON" | grep -q "^/"], [ - ],[test -z "$PYTHON"], [PYTHON="python"], - [AC_MSG_ERROR([PYTHON specified, but is not an absolute path])]) - AX_PATH_PROG_OR_FAIL([PYTHONPATH], [$PYTHON]) --AX_CHECK_PYTHON_VERSION([2], [3]) -+AX_CHECK_PYTHON_VERSION([2], [6]) - - AS_IF([test "$cross_compiling" != yes], [ - AX_CHECK_PYTHON_DEVEL() --- -2.17.2 - - -From e81209fd4ea705f4de4b61d05e6ab0aea9bb0b88 Mon Sep 17 00:00:00 2001 -From: Wei Liu -Date: Mon, 11 Mar 2019 12:58:05 +0000 -Subject: [PATCH 2/8] pygrub/fsimage: drop unused struct - -Signed-off-by: Wei Liu -Reviewed-by: Andrew Cooper ---- - tools/pygrub/src/fsimage/fsimage.c | 7 ------- - 1 file changed, 7 deletions(-) - -diff --git a/tools/pygrub/src/fsimage/fsimage.c b/tools/pygrub/src/fsimage/fsimage.c -index 743a3fb7b8..780207791c 100644 ---- a/tools/pygrub/src/fsimage/fsimage.c -+++ b/tools/pygrub/src/fsimage/fsimage.c -@@ -43,13 +43,6 @@ typedef struct fsimage_file { - fsi_file_t *file; - } fsimage_file_t; - --struct foo { -- int ref; -- int size; -- long hash; -- int state; --}; -- - static PyObject * - fsimage_file_read(fsimage_file_t *file, PyObject *args, PyObject *kwargs) - { --- -2.17.2 - - -From b9e1368af14ded6aee3bdf64e8329628b16291ff Mon Sep 17 00:00:00 2001 -From: Wei Liu -Date: Mon, 11 Mar 2019 12:55:29 +0000 -Subject: [PATCH 3/8] pygrub: change tabs into spaces - -Not sure why Python 2 never complained, but Python 3 does. - -Change tabs to spaces. - -Signed-off-by: Wei Liu -Reviewed-by: Andrew Cooper ---- - tools/pygrub/src/pygrub | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub -index 52a8965ad9..1189b1ca48 100755 ---- a/tools/pygrub/src/pygrub -+++ b/tools/pygrub/src/pygrub -@@ -858,7 +858,7 @@ if __name__ == "__main__": - output_directory = a - - if debug: -- logging.basicConfig(level=logging.DEBUG) -+ logging.basicConfig(level=logging.DEBUG) - - - try: -@@ -917,7 +917,7 @@ if __name__ == "__main__": - # IOErrors raised by fsimage.open - # RuntimeErrors raised by run_grub if no menu.lst present - if debug: -- traceback.print_exc() -+ traceback.print_exc() - fs = None - continue - --- -2.17.2 - - -From 16cc3362aed39e3093419b9df6ec73269071d063 Mon Sep 17 00:00:00 2001 -From: Wei Liu -Date: Tue, 5 Mar 2019 12:32:06 +0000 -Subject: [PATCH 4/8] build/m4: make python_devel.m4 work with both python 2 - and 3 - -Do the following: - -1. Change the form of "print". -2. Use AC_CHECK_FUNC to avoid the need to generate library name. -3. Remove unused stuff. - -Signed-off-by: Wei Liu -Reviewed-by: Anthony PERARD ---- - m4/python_devel.m4 | 23 ++++++----------- - tools/configure | 64 ++++++---------------------------------------- - 2 files changed, 16 insertions(+), 71 deletions(-) - -diff --git a/m4/python_devel.m4 b/m4/python_devel.m4 -index 05ea4ef7e2..f9cb23aee1 100644 ---- a/m4/python_devel.m4 -+++ b/m4/python_devel.m4 -@@ -1,38 +1,31 @@ - AC_DEFUN([AX_CHECK_PYTHON_DEVEL], [ - ac_previous_cppflags=$CPPFLAGS - ac_previous_ldflags=$LDFLAGS --ac_python_version=`$PYTHON -c 'import distutils.sysconfig; \ -- print distutils.sysconfig.get_config_var("VERSION")'` - AC_PATH_PROG([pyconfig], [$PYTHON-config], [no]) - AS_IF([test x"$pyconfig" = x"no"], [ - dnl For those that don't have python-config - CPPFLAGS="$CFLAGS `$PYTHON -c 'import distutils.sysconfig; \ - print "-I" + distutils.sysconfig.get_config_var("INCLUDEPY")'`" - CPPFLAGS="$CPPFLAGS `$PYTHON -c 'import distutils.sysconfig; \ -- print distutils.sysconfig.get_config_var("CFLAGS")'`" -- PYTHON_LIBS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ -- print distutils.sysconfig.get_config_var("LIBS")'`" -- PYTHON_LIBS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ -- print distutils.sysconfig.get_config_var("SYSLIBS")'`" -+ print(distutils.sysconfig.get_config_var("CFLAGS"))'`" - LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ -- print "-L" + distutils.sysconfig.get_python_lib(plat_specific=1,\ -- standard_lib=1) + "/config"'`" -+ print("-L" + distutils.sysconfig.get_python_lib(plat_specific=1,\ -+ standard_lib=1) + "/config")'`" - LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ -- print distutils.sysconfig.get_config_var("LINKFORSHARED")'`" -+ print(distutils.sysconfig.get_config_var("LINKFORSHARED"))'`" - LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ -- print distutils.sysconfig.get_config_var("LDFLAGS")'`" -+ print(distutils.sysconfig.get_config_var("LDFLAGS"))'`" - ], [ - dnl If python-config is found use it - CPPFLAGS="$CFLAGS `$PYTHON-config --cflags`" - LDFLAGS="$LDFLAGS `$PYTHON-config --ldflags`" -- PYTHON_LIBS="$LIBS `$PYTHON-config --libs`" - ]) - - AC_CHECK_HEADER([Python.h], [], - [AC_MSG_ERROR([Unable to find Python development headers])],) --AC_CHECK_LIB(python$ac_python_version, PyArg_ParseTuple, [], -- [AC_MSG_ERROR([Unable to find a suitable python development library])], -- [$PYTHON_LIBS]) -+AC_CHECK_FUNC([PyArg_ParseTuple], [], -+ [AC_MSG_ERROR([Unable to find a suitable python development library])]) -+ - CPPFLAGS=$ac_previous_cppflags - LDFLAGS=$ac_previous_ldflags - ]) -diff --git a/tools/configure b/tools/configure -index d0065b811d..e1fa5d6b0f 100755 ---- a/tools/configure -+++ b/tools/configure -@@ -7418,8 +7418,6 @@ if test "$cross_compiling" != yes; then : - - ac_previous_cppflags=$CPPFLAGS - ac_previous_ldflags=$LDFLAGS --ac_python_version=`$PYTHON -c 'import distutils.sysconfig; \ -- print distutils.sysconfig.get_config_var("VERSION")'` - # Extract the first word of "$PYTHON-config", so it can be a program name with args. - set dummy $PYTHON-config; ac_word=$2 - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -@@ -7466,24 +7464,19 @@ if test x"$pyconfig" = x"no"; then : - CPPFLAGS="$CFLAGS `$PYTHON -c 'import distutils.sysconfig; \ - print "-I" + distutils.sysconfig.get_config_var("INCLUDEPY")'`" - CPPFLAGS="$CPPFLAGS `$PYTHON -c 'import distutils.sysconfig; \ -- print distutils.sysconfig.get_config_var("CFLAGS")'`" -- PYTHON_LIBS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ -- print distutils.sysconfig.get_config_var("LIBS")'`" -- PYTHON_LIBS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ -- print distutils.sysconfig.get_config_var("SYSLIBS")'`" -+ print(distutils.sysconfig.get_config_var("CFLAGS"))'`" - LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ -- print "-L" + distutils.sysconfig.get_python_lib(plat_specific=1,\ -- standard_lib=1) + "/config"'`" -+ print("-L" + distutils.sysconfig.get_python_lib(plat_specific=1,\ -+ standard_lib=1) + "/config")'`" - LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ -- print distutils.sysconfig.get_config_var("LINKFORSHARED")'`" -+ print(distutils.sysconfig.get_config_var("LINKFORSHARED"))'`" - LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ -- print distutils.sysconfig.get_config_var("LDFLAGS")'`" -+ print(distutils.sysconfig.get_config_var("LDFLAGS"))'`" - - else - - CPPFLAGS="$CFLAGS `$PYTHON-config --cflags`" - LDFLAGS="$LDFLAGS `$PYTHON-config --ldflags`" -- PYTHON_LIBS="$LIBS `$PYTHON-config --libs`" - - fi - -@@ -7495,55 +7488,14 @@ else - fi - - --as_ac_Lib=`$as_echo "ac_cv_lib_python$ac_python_version''_PyArg_ParseTuple" | $as_tr_sh` --{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for PyArg_ParseTuple in -lpython$ac_python_version" >&5 --$as_echo_n "checking for PyArg_ParseTuple in -lpython$ac_python_version... " >&6; } --if eval \${$as_ac_Lib+:} false; then : -- $as_echo_n "(cached) " >&6 --else -- ac_check_lib_save_LIBS=$LIBS --LIBS="-lpython$ac_python_version $PYTHON_LIBS $LIBS" --cat confdefs.h - <<_ACEOF >conftest.$ac_ext --/* end confdefs.h. */ -- --/* Override any GCC internal prototype to avoid an error. -- Use char because int might match the return type of a GCC -- builtin and then its argument prototype would still apply. */ --#ifdef __cplusplus --extern "C" --#endif --char PyArg_ParseTuple (); --int --main () --{ --return PyArg_ParseTuple (); -- ; -- return 0; --} --_ACEOF --if ac_fn_c_try_link "$LINENO"; then : -- eval "$as_ac_Lib=yes" --else -- eval "$as_ac_Lib=no" --fi --rm -f core conftest.err conftest.$ac_objext \ -- conftest$ac_exeext conftest.$ac_ext --LIBS=$ac_check_lib_save_LIBS --fi --eval ac_res=\$$as_ac_Lib -- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 --$as_echo "$ac_res" >&6; } --if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : -- cat >>confdefs.h <<_ACEOF --#define `$as_echo "HAVE_LIBpython$ac_python_version" | $as_tr_cpp` 1 --_ACEOF -- -- LIBS="-lpython$ac_python_version $LIBS" -+ac_fn_c_check_func "$LINENO" "PyArg_ParseTuple" "ac_cv_func_PyArg_ParseTuple" -+if test "x$ac_cv_func_PyArg_ParseTuple" = xyes; then : - - else - as_fn_error $? "Unable to find a suitable python development library" "$LINENO" 5 - fi - -+ - CPPFLAGS=$ac_previous_cppflags - LDFLAGS=$ac_previous_ldflags - --- -2.17.2 - - -From 660d2dd863802ef464c90b32f187cb65861f8185 Mon Sep 17 00:00:00 2001 -From: Wei Liu -Date: Thu, 7 Mar 2019 12:33:38 +0000 -Subject: [PATCH 5/8] libxl: make python scripts work with python 2.6 and up - -Go through transformations suggested by 2to3 and pick the necessary -ones. - -Signed-off-by: Wei Liu -Reviewed-by: Andrew Cooper ---- - tools/libxl/gentest.py | 4 +++- - tools/libxl/gentypes.py | 12 +++++++----- - tools/libxl/idl.py | 15 ++++++++------- - 3 files changed, 18 insertions(+), 13 deletions(-) - -diff --git a/tools/libxl/gentest.py b/tools/libxl/gentest.py -index 989959fc68..1cc7eebc82 100644 ---- a/tools/libxl/gentest.py -+++ b/tools/libxl/gentest.py -@@ -1,5 +1,7 @@ - #!/usr/bin/python - -+from __future__ import print_function -+ - import os - import sys - import re -@@ -86,7 +88,7 @@ def gen_rand_init(ty, v, indent = " ", parent = None): - - if __name__ == '__main__': - if len(sys.argv) < 3: -- print >>sys.stderr, "Usage: gentest.py " -+ print("Usage: gentest.py ", file=sys.stderr) - sys.exit(1) - - random.seed(os.getenv('LIBXL_TESTIDL_SEED')) -diff --git a/tools/libxl/gentypes.py b/tools/libxl/gentypes.py -index 88e5c5f30e..6417c9dd8c 100644 ---- a/tools/libxl/gentypes.py -+++ b/tools/libxl/gentypes.py -@@ -1,5 +1,7 @@ - #!/usr/bin/python - -+from __future__ import print_function -+ - import sys - import re - -@@ -576,14 +578,14 @@ def libxl_C_enum_from_string(ty, str, e, indent = " "): - - if __name__ == '__main__': - if len(sys.argv) != 6: -- print >>sys.stderr, "Usage: gentypes.py
" -+ print("Usage: gentypes.py
", file=sys.stderr) - sys.exit(1) - - (_, idlname, header, header_private, header_json, impl) = sys.argv - - (builtins,types) = idl.parse(idlname) - -- print "outputting libxl type definitions to %s" % header -+ print("outputting libxl type definitions to %s" % header) - - f = open(header, "w") - -@@ -633,7 +635,7 @@ if __name__ == '__main__': - f.write("""#endif /* %s */\n""" % (header_define)) - f.close() - -- print "outputting libxl JSON definitions to %s" % header_json -+ print("outputting libxl JSON definitions to %s" % header_json) - - f = open(header_json, "w") - -@@ -657,7 +659,7 @@ if __name__ == '__main__': - f.write("""#endif /* %s */\n""" % header_json_define) - f.close() - -- print "outputting libxl type internal definitions to %s" % header_private -+ print("outputting libxl type internal definitions to %s" % header_private) - - f = open(header_private, "w") - -@@ -683,7 +685,7 @@ if __name__ == '__main__': - f.write("""#endif /* %s */\n""" % header_json_define) - f.close() - -- print "outputting libxl type implementations to %s" % impl -+ print("outputting libxl type implementations to %s" % impl) - - f = open(impl, "w") - f.write(""" -diff --git a/tools/libxl/idl.py b/tools/libxl/idl.py -index 2a7f3c44fe..d7367503b4 100644 ---- a/tools/libxl/idl.py -+++ b/tools/libxl/idl.py -@@ -1,3 +1,5 @@ -+from __future__ import print_function -+ - import sys - - PASS_BY_VALUE = 1 -@@ -11,7 +13,7 @@ DIR_BOTH = 3 - _default_namespace = "" - def namespace(s): - if type(s) != str: -- raise TypeError, "Require a string for the default namespace." -+ raise TypeError("Require a string for the default namespace.") - global _default_namespace - _default_namespace = s - -@@ -346,7 +348,7 @@ class OrderedDict(dict): - return [(x,self[x]) for x in self.__ordered] - - def parse(f): -- print >>sys.stderr, "Parsing %s" % f -+ print("Parsing %s" % f, file=sys.stderr) - - globs = {} - locs = OrderedDict() -@@ -362,11 +364,10 @@ def parse(f): - globs[n] = t - - try: -- execfile(f, globs, locs) -- except SyntaxError,e: -- raise SyntaxError, \ -- "Errors were found at line %d while processing %s:\n\t%s"\ -- %(e.lineno,f,e.text) -+ exec(compile(open(f).read(), f, 'exec'), globs, locs) -+ except SyntaxError as e: -+ raise SyntaxError("Errors were found at line %d while processing %s:\n\t%s" -+ % (e.lineno, f, e.text)) - - types = [t for t in locs.ordered_values() if isinstance(t,Type)] - --- -2.17.2 - - -From 0aabd89dcfee9ee2a6caaa2ec7a475daf5cada53 Mon Sep 17 00:00:00 2001 -From: Wei Liu -Date: Thu, 7 Mar 2019 12:45:47 +0000 -Subject: [PATCH 6/8] pygrub: make python scripts work with 2.6 and up - -Run 2to3 and pick the sensible suggestions. - -Import print_function and absolute_import so 2.6 can work. - -There has never been a curses.wrapper module according to 2.x and 3.x -doc, only a function, so "import curses.wrapper" is not correct. It -happened to work because 2.x implemented a (undocumented) module. - -We only need to import curses to make curses.wrapper available to -pygrub. - -Signed-off-by: Wei Liu -Reviewed-by: Andrew Cooper ---- - tools/pygrub/src/ExtLinuxConf.py | 19 +++++---- - tools/pygrub/src/GrubConf.py | 39 ++++++++++-------- - tools/pygrub/src/LiloConf.py | 19 +++++---- - tools/pygrub/src/pygrub | 71 ++++++++++++++++---------------- - 4 files changed, 78 insertions(+), 70 deletions(-) - -diff --git a/tools/pygrub/src/ExtLinuxConf.py b/tools/pygrub/src/ExtLinuxConf.py -index d1789bf020..9fd635b9cf 100644 ---- a/tools/pygrub/src/ExtLinuxConf.py -+++ b/tools/pygrub/src/ExtLinuxConf.py -@@ -10,9 +10,11 @@ - # along with this program; If not, see . - # - -+from __future__ import print_function, absolute_import -+ - import sys, re, os - import logging --import GrubConf -+from . import GrubConf - - class ExtLinuxImage(object): - def __init__(self, lines, path): -@@ -32,7 +34,8 @@ class ExtLinuxImage(object): - self.lines = [] - self.path = path - self.root = "" -- map(self.set_from_line, lines) -+ for line in lines: -+ self.set_from_line(line) - - def set_from_line(self, line, replace = None): - (com, arg) = GrubConf.grub_exact_split(line, 2) -@@ -67,7 +70,7 @@ class ExtLinuxImage(object): - setattr(self, "initrd", a.replace("initrd=", "")) - arg = arg.replace(a, "") - -- if com is not None and self.commands.has_key(com): -+ if com is not None and com in self.commands: - if self.commands[com] is not None: - setattr(self, self.commands[com], re.sub('^"(.+)"$', r"\1", arg.strip())) - else: -@@ -136,7 +139,7 @@ class ExtLinuxConfigFile(object): - def parse(self, buf = None): - if buf is None: - if self.filename is None: -- raise ValueError, "No config file defined to parse!" -+ raise ValueError("No config file defined to parse!") - - f = open(self.filename, 'r') - lines = f.readlines() -@@ -167,7 +170,7 @@ class ExtLinuxConfigFile(object): - - (com, arg) = GrubConf.grub_exact_split(l, 2) - com = com.lower() -- if self.commands.has_key(com): -+ if com in self.commands: - if self.commands[com] is not None: - setattr(self, self.commands[com], arg.strip()) - else: -@@ -207,8 +210,8 @@ class ExtLinuxConfigFile(object): - - if __name__ == "__main__": - if len(sys.argv) < 2: -- raise RuntimeError, "Need a configuration file to read" -+ raise RuntimeError("Need a configuration file to read") - g = ExtLinuxConfigFile(sys.argv[1]) - for i in g.images: -- print i -- print g.default -+ print(i) -+ print(g.default) -diff --git a/tools/pygrub/src/GrubConf.py b/tools/pygrub/src/GrubConf.py -index dc810d55cb..f8d3799dc0 100644 ---- a/tools/pygrub/src/GrubConf.py -+++ b/tools/pygrub/src/GrubConf.py -@@ -12,6 +12,8 @@ - # along with this program; If not, see . - # - -+from __future__ import print_function, absolute_import -+ - import os, sys - import logging - import re -@@ -44,7 +46,7 @@ def get_path(s): - return (None, s) - idx = s.find(')') - if idx == -1: -- raise ValueError, "Unable to find matching ')'" -+ raise ValueError("Unable to find matching ')'") - d = s[:idx] - return (GrubDiskPart(d), s[idx + 1:]) - -@@ -100,7 +102,8 @@ class _GrubImage(object): - " initrd: %s\n" %(self.title, self.root, self.kernel, - self.args, self.initrd)) - def _parse(self, lines): -- map(self.set_from_line, lines) -+ for line in lines: -+ self.set_from_line(line) - - def reset(self, lines): - self._root = self._initrd = self._kernel = self._args = None -@@ -141,7 +144,7 @@ class GrubImage(_GrubImage): - def set_from_line(self, line, replace = None): - (com, arg) = grub_exact_split(line, 2) - -- if self.commands.has_key(com): -+ if com in self.commands: - if self.commands[com] is not None: - setattr(self, self.commands[com], arg.strip()) - else: -@@ -177,7 +180,7 @@ class _GrubConfigFile(object): - self.parse() - - def parse(self, buf = None): -- raise RuntimeError, "unimplemented parse function" -+ raise RuntimeError("unimplemented parse function") - - def hasPasswordAccess(self): - return self.passwordAccess -@@ -201,7 +204,7 @@ class _GrubConfigFile(object): - import crypt - if crypt.crypt(password, pwd[1]) == pwd[1]: - return True -- except Exception, e: -+ except Exception as e: - self.passExc = "Can't verify password: %s" % str(e) - return False - -@@ -213,7 +216,7 @@ class _GrubConfigFile(object): - - def set(self, line): - (com, arg) = grub_exact_split(line, 2) -- if self.commands.has_key(com): -+ if com in self.commands: - if self.commands[com] is not None: - setattr(self, self.commands[com], arg.strip()) - else: -@@ -233,7 +236,7 @@ class _GrubConfigFile(object): - self._default = val - - if self._default < 0: -- raise ValueError, "default must be positive number" -+ raise ValueError("default must be positive number") - default = property(_get_default, _set_default) - - def set_splash(self, val): -@@ -265,7 +268,7 @@ class GrubConfigFile(_GrubConfigFile): - def parse(self, buf = None): - if buf is None: - if self.filename is None: -- raise ValueError, "No config file defined to parse!" -+ raise ValueError("No config file defined to parse!") - - f = open(self.filename, 'r') - lines = f.readlines() -@@ -296,7 +299,7 @@ class GrubConfigFile(_GrubConfigFile): - continue - - (com, arg) = grub_exact_split(l, 2) -- if self.commands.has_key(com): -+ if com in self.commands: - if self.commands[com] is not None: - setattr(self, self.commands[com], arg.strip()) - else: -@@ -328,7 +331,7 @@ class Grub2Image(_GrubImage): - if com == "set": - (com,arg) = grub2_handle_set(arg) - -- if self.commands.has_key(com): -+ if com in self.commands: - if self.commands[com] is not None: - setattr(self, self.commands[com], arg.strip()) - else: -@@ -364,7 +367,7 @@ class Grub2ConfigFile(_GrubConfigFile): - def parse(self, buf = None): - if buf is None: - if self.filename is None: -- raise ValueError, "No config file defined to parse!" -+ raise ValueError("No config file defined to parse!") - - f = open(self.filename, 'r') - lines = f.readlines() -@@ -398,7 +401,7 @@ class Grub2ConfigFile(_GrubConfigFile): - title_match = re.match('^menuentry ["\'](.*?)["\'] (.*){', l) - if title_match: - if img is not None: -- raise RuntimeError, "syntax error: cannot nest menuentry (%d %s)" % (len(img),img) -+ raise RuntimeError("syntax error: cannot nest menuentry (%d %s)" % (len(img),img)) - img = [] - title = title_match.group(1) - continue -@@ -413,7 +416,7 @@ class Grub2ConfigFile(_GrubConfigFile): - menu_level -= 1 - continue - else: -- raise RuntimeError, "syntax error: closing brace without menuentry" -+ raise RuntimeError("syntax error: closing brace without menuentry") - - self.add_image(Grub2Image(title, img)) - img = None -@@ -428,7 +431,7 @@ class Grub2ConfigFile(_GrubConfigFile): - if com == "set": - (com,arg) = grub2_handle_set(arg) - -- if self.commands.has_key(com): -+ if com in self.commands: - if self.commands[com] is not None: - arg_strip = arg.strip() - if arg_strip == "${saved_entry}" or arg_strip == "${next_entry}": -@@ -443,7 +446,7 @@ class Grub2ConfigFile(_GrubConfigFile): - logging.warning("Unknown directive %s" %(com,)) - - if img is not None: -- raise RuntimeError, "syntax error: end of file with open menuentry(%d %s)" % (len(img),img) -+ raise RuntimeError("syntax error: end of file with open menuentry(%d %s)" % (len(img),img)) - - if self.hasPassword(): - self.setPasswordAccess(False) -@@ -462,12 +465,12 @@ class Grub2ConfigFile(_GrubConfigFile): - - if __name__ == "__main__": - if len(sys.argv) < 3: -- raise RuntimeError, "Need a grub version (\"grub\" or \"grub2\") and a grub.conf or grub.cfg to read" -+ raise RuntimeError('Need a grub version ("grub" or "grub2") and a grub.conf or grub.cfg to read') - if sys.argv[1] == "grub": - g = GrubConfigFile(sys.argv[2]) - elif sys.argv[1] == "grub2": - g = Grub2ConfigFile(sys.argv[2]) - else: -- raise RuntimeError, "Unknown config type %s" % sys.argv[1] -+ raise RuntimeError("Unknown config type %s" % sys.argv[1]) - for i in g.images: -- print i #, i.title, i.root, i.kernel, i.args, i.initrd -+ print(i) #, i.title, i.root, i.kernel, i.args, i.initrd -diff --git a/tools/pygrub/src/LiloConf.py b/tools/pygrub/src/LiloConf.py -index 2cb649f115..e3bfcb5244 100644 ---- a/tools/pygrub/src/LiloConf.py -+++ b/tools/pygrub/src/LiloConf.py -@@ -2,9 +2,11 @@ - #LiloConf.py - # - -+from __future__ import print_function, absolute_import -+ - import sys, re, os - import logging --import GrubConf -+from . import GrubConf - - class LiloImage(object): - def __init__(self, lines, path): -@@ -24,12 +26,13 @@ class LiloImage(object): - self.lines = [] - self.path = path - self.root = "" -- map(self.set_from_line, lines) -+ for line in lines: -+ self.set_from_line(line) - - def set_from_line(self, line, replace = None): - (com, arg) = GrubConf.grub_exact_split(line, 2) - -- if self.commands.has_key(com): -+ if com in self.commands: - if self.commands[com] is not None: - setattr(self, self.commands[com], re.sub('^"(.+)"$', r"\1", arg.strip())) - else: -@@ -97,7 +100,7 @@ class LiloConfigFile(object): - def parse(self, buf = None): - if buf is None: - if self.filename is None: -- raise ValueError, "No config file defined to parse!" -+ raise ValueError("No config file defined to parse!") - - f = open(self.filename, 'r') - lines = f.readlines() -@@ -127,7 +130,7 @@ class LiloConfigFile(object): - continue - - (com, arg) = GrubConf.grub_exact_split(l, 2) -- if self.commands.has_key(com): -+ if com in self.commands: - if self.commands[com] is not None: - setattr(self, self.commands[com], arg.strip()) - else: -@@ -170,8 +173,8 @@ class LiloConfigFile(object): - - if __name__ == "__main__": - if len(sys.argv) < 2: -- raise RuntimeError, "Need a lilo.conf to read" -+ raise RuntimeError("Need a lilo.conf to read") - g = LiloConfigFile(sys.argv[1]) - for i in g.images: -- print i #, i.title, i.root, i.kernel, i.args, i.initrd -- print g.default -+ print(i) #, i.title, i.root, i.kernel, i.args, i.initrd -+ print(g.default) -diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub -index 1189b1ca48..dbdce315c6 100755 ---- a/tools/pygrub/src/pygrub -+++ b/tools/pygrub/src/pygrub -@@ -12,13 +12,15 @@ - # along with this program; If not, see . - # - -+from __future__ import print_function -+ - import os, sys, string, struct, tempfile, re, traceback, stat, errno - import copy - import logging - import platform - import xen.lowlevel.xc - --import curses, _curses, curses.wrapper, curses.textpad, curses.ascii -+import curses, _curses, curses.textpad, curses.ascii - import getopt - - import xenfsimage -@@ -77,7 +79,7 @@ def get_solaris_slice(file, offset): - buf = os.read(fd, 512) - os.close(fd) - if struct.unpack(">sys.stderr, "Using %s to parse %s" % (parser,f) -+ print("Using %s to parse %s" % (parser,f), file=sys.stderr) - self.cf = parser() - self.cf.filename = f - break - if self.__dict__.get('cf', None) is None: -- raise RuntimeError, "couldn't find bootloader config file in the image provided." -+ raise RuntimeError("couldn't find bootloader config file in the image provided.") - f = fs.open_file(self.cf.filename) - # limit read size to avoid pathological cases - buf = f.read(FS_READ_MAX) -@@ -628,11 +627,11 @@ def run_grub(file, entry, fs, cfg_args): - if list_entries: - for i in range(len(g.cf.images)): - img = g.cf.images[i] -- print "title: %s" % img.title -- print " root: %s" % img.root -- print " kernel: %s" % img.kernel[1] -- print " args: %s" % img.args -- print " initrd: %s" % img.initrd[1] -+ print("title: %s" % img.title) -+ print(" root: %s" % img.root) -+ print(" kernel: %s" % img.kernel[1]) -+ print(" args: %s" % img.args) -+ print(" initrd: %s" % img.initrd[1]) - - if interactive and not list_entries: - curses.wrapper(run_main) -@@ -646,7 +645,7 @@ def run_grub(file, entry, fs, cfg_args): - sel = idx - - if sel == -1: -- print "No kernel image selected!" -+ print("No kernel image selected!") - sys.exit(1) - - try: -@@ -731,7 +730,7 @@ def format_sxp(kernel, ramdisk, args): - def format_simple(kernel, ramdisk, args, sep): - for check in (kernel, ramdisk, args): - if check is not None and sep in check: -- raise RuntimeError, "simple format cannot represent delimiter-containing value" -+ raise RuntimeError("simple format cannot represent delimiter-containing value") - s = ("kernel %s" % kernel) + sep - if ramdisk: - s += ("ramdisk %s" % ramdisk) + sep -@@ -744,7 +743,7 @@ if __name__ == "__main__": - sel = None - - def usage(): -- print >> sys.stderr, "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],) -+ 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): -@@ -755,8 +754,8 @@ if __name__ == "__main__": - sys.exit("The requested %s file does not exist" % file_type) - try: - datafile = fs.open_file(file_to_read) -- except Exception, e: -- print >>sys.stderr, e -+ 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) -@@ -769,8 +768,8 @@ if __name__ == "__main__": - return ret - try: - os.write(tfd, data) -- except Exception, e: -- print >>sys.stderr, e -+ except Exception as e: -+ print(e, file=sys.stderr) - os.close(tfd) - os.unlink(ret) - del datafile -@@ -834,7 +833,7 @@ if __name__ == "__main__": - try: - part_offs = [ int(a) ] - except ValueError: -- print "offset value must be an integer" -+ print("offset value must be an integer") - usage() - sys.exit(1) - elif o in ("--entry",): -@@ -847,13 +846,13 @@ if __name__ == "__main__": - debug = True - elif o in ("--output-format",): - if a not in ["sxp", "simple", "simple0"]: -- print "unknown output format %s" % a -+ print("unknown output format %s" % a) - usage() - sys.exit(1) - output_format = a - elif o in ("--output-directory",): - if not os.path.isdir(a): -- print "%s is not an existing directory" % a -+ print("%s is not an existing directory" % a) - sys.exit(1) - output_directory = a - -@@ -862,8 +861,8 @@ if __name__ == "__main__": - - - try: -- os.makedirs(output_directory, 0700) -- except OSError,e: -+ os.makedirs(output_directory, 0o700) -+ except OSError as e: - if (e.errno == errno.EEXIST) and os.path.isdir(output_directory): - pass - else: -@@ -877,10 +876,10 @@ if __name__ == "__main__": - # debug - if isconfig: - chosencfg = run_grub(file, entry, fs, incfg["args"]) -- print " kernel: %s" % chosencfg["kernel"] -+ print(" kernel: %s" % chosencfg["kernel"]) - if chosencfg["ramdisk"]: -- print " initrd: %s" % chosencfg["ramdisk"] -- print " args: %s" % chosencfg["args"] -+ print(" initrd: %s" % chosencfg["ramdisk"]) -+ print(" args: %s" % chosencfg["args"]) - sys.exit(0) - - # if boot filesystem is set then pass to fsimage.open -@@ -926,7 +925,7 @@ if __name__ == "__main__": - - # Did looping through partitions find us a kernel? - if fs is None: -- raise RuntimeError, "Unable to find partition containing kernel" -+ raise RuntimeError("Unable to find partition containing kernel") - - bootcfg["kernel"] = copy_from_image(fs, chosencfg["kernel"], "kernel", - output_directory, not_really) --- -2.17.2 - - -From 83a204e6951c6358f995da3b60dd61224e9d41ac Mon Sep 17 00:00:00 2001 -From: Wei Liu -Date: Tue, 5 Mar 2019 14:13:17 +0000 -Subject: [PATCH 7/8] pygrub/fsimage: make it work with python 3 - -With the help of two porting guides and cpython source code: - -1. Use PyBytes to replace PyString counterparts. -2. Use PyVarObject_HEAD_INIT. -3. Remove usage of Py_FindMethod. -4. Use new module initialisation routine. - -For #3, Py_FindMethod was removed, yet an alternative wasn't -documented. The code is the result of reverse-engineering cpython -commit 6116d4a1d1 - -https://docs.python.org/3/howto/cporting.html -http://python3porting.com/cextensions.html - -Signed-off-by: Wei Liu -Reviewed-by: Andrew Cooper ---- - tools/pygrub/src/fsimage/fsimage.c | 123 ++++++++++++++++------------- - 1 file changed, 69 insertions(+), 54 deletions(-) - -diff --git a/tools/pygrub/src/fsimage/fsimage.c b/tools/pygrub/src/fsimage/fsimage.c -index 780207791c..2ebbbe35df 100644 ---- a/tools/pygrub/src/fsimage/fsimage.c -+++ b/tools/pygrub/src/fsimage/fsimage.c -@@ -26,12 +26,6 @@ - #include - #include - --#if (PYTHON_API_VERSION >= 1011) --#define PY_PAD 0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L --#else --#define PY_PAD 0L,0L,0L,0L --#endif -- - typedef struct fsimage_fs { - PyObject_HEAD - fsi_t *fs; -@@ -59,12 +53,24 @@ fsimage_file_read(fsimage_file_t *file, PyObject *args, PyObject *kwargs) - - bufsize = size ? size : 4096; - -- if ((buffer = PyString_FromStringAndSize(NULL, bufsize)) == NULL) -+ buffer = -+#if PY_MAJOR_VERSION < 3 -+ PyString_FromStringAndSize(NULL, bufsize); -+#else -+ PyBytes_FromStringAndSize(NULL, bufsize); -+#endif -+ -+ if (buffer == NULL) - return (NULL); - - while (1) { - int err; -- void *buf = PyString_AS_STRING(buffer) + bytesread; -+ void *buf = -+#if PY_MAJOR_VERSION < 3 -+ PyString_AS_STRING(buffer) + bytesread; -+#else -+ PyBytes_AS_STRING(buffer) + bytesread; -+#endif - - err = fsi_pread_file(file->file, buf, bufsize, - bytesread + offset); -@@ -84,12 +90,20 @@ fsimage_file_read(fsimage_file_t *file, PyObject *args, PyObject *kwargs) - if (bufsize == 0) - break; - } else { -+#if PY_MAJOR_VERSION < 3 - if (_PyString_Resize(&buffer, bytesread + bufsize) < 0) -+#else -+ if (_PyBytes_Resize(&buffer, bytesread + bufsize) < 0) -+#endif - return (NULL); - } - } - -+#if PY_MAJOR_VERSION < 3 - _PyString_Resize(&buffer, bytesread); -+#else -+ _PyBytes_Resize(&buffer, bytesread); -+#endif - return (buffer); - } - -@@ -106,11 +120,13 @@ static struct PyMethodDef fsimage_file_methods[] = { - { NULL, NULL, 0, NULL } - }; - -+#if PY_MAJOR_VERSION < 3 - static PyObject * - fsimage_file_getattr(fsimage_file_t *file, char *name) - { - return (Py_FindMethod(fsimage_file_methods, (PyObject *)file, name)); - } -+#endif - - static void - fsimage_file_dealloc(fsimage_file_t *file) -@@ -123,29 +139,18 @@ fsimage_file_dealloc(fsimage_file_t *file) - - static char fsimage_file_type__doc__[] = "Filesystem image file"; - PyTypeObject fsimage_file_type = { -- PyObject_HEAD_INIT(&PyType_Type) -- 0, /* ob_size */ -- "xenfsimage.file", /* tp_name */ -- sizeof(fsimage_file_t), /* tp_size */ -- 0, /* tp_itemsize */ -- (destructor) fsimage_file_dealloc, /* tp_dealloc */ -- 0, /* tp_print */ -- (getattrfunc) fsimage_file_getattr, /* tp_getattr */ -- 0, /* tp_setattr */ -- 0, /* tp_compare */ -- 0, /* tp_repr */ -- 0, /* tp_as_number */ -- 0, /* tp_as_sequence */ -- 0, /* tp_as_mapping */ -- 0, /* tp_hash */ -- 0, /* tp_call */ -- 0, /* tp_str */ -- 0, /* tp_getattro */ -- 0, /* tp_setattro */ -- 0, /* tp_as_buffer */ -- Py_TPFLAGS_DEFAULT, /* tp_flags */ -- fsimage_file_type__doc__, -- PY_PAD -+ PyVarObject_HEAD_INIT(&PyType_Type, 0) -+ .tp_name = "xenfsimage.file", -+ .tp_basicsize = sizeof(fsimage_file_t), -+ .tp_dealloc = (destructor) fsimage_file_dealloc, -+#if PY_MAJOR_VERSION < 3 -+ .tp_getattr = (getattrfunc) fsimage_file_getattr, -+#endif -+ .tp_flags = Py_TPFLAGS_DEFAULT, -+ .tp_doc = fsimage_file_type__doc__, -+#if PY_MAJOR_VERSION >= 3 -+ .tp_methods = fsimage_file_methods, -+#endif - }; - - static PyObject * -@@ -208,11 +213,13 @@ static struct PyMethodDef fsimage_fs_methods[] = { - { NULL, NULL, 0, NULL } - }; - -+#if PY_MAJOR_VERSION < 3 - static PyObject * - fsimage_fs_getattr(fsimage_fs_t *fs, char *name) - { - return (Py_FindMethod(fsimage_fs_methods, (PyObject *)fs, name)); - } -+#endif - - static void - fsimage_fs_dealloc (fsimage_fs_t *fs) -@@ -225,29 +232,18 @@ fsimage_fs_dealloc (fsimage_fs_t *fs) - PyDoc_STRVAR(fsimage_fs_type__doc__, "Filesystem image"); - - PyTypeObject fsimage_fs_type = { -- PyObject_HEAD_INIT(&PyType_Type) -- 0, /* ob_size */ -- "xenfsimage.fs", /* tp_name */ -- sizeof(fsimage_fs_t), /* tp_size */ -- 0, /* tp_itemsize */ -- (destructor) fsimage_fs_dealloc, /* tp_dealloc */ -- 0, /* tp_print */ -- (getattrfunc) fsimage_fs_getattr, /* tp_getattr */ -- 0, /* tp_setattr */ -- 0, /* tp_compare */ -- 0, /* tp_repr */ -- 0, /* tp_as_number */ -- 0, /* tp_as_sequence */ -- 0, /* tp_as_mapping */ -- 0, /* tp_hash */ -- 0, /* tp_call */ -- 0, /* tp_str */ -- 0, /* tp_getattro */ -- 0, /* tp_setattro */ -- 0, /* tp_as_buffer */ -- Py_TPFLAGS_DEFAULT, /* tp_flags */ -- fsimage_fs_type__doc__, -- PY_PAD -+ PyVarObject_HEAD_INIT(&PyType_Type, 0) -+ .tp_name = "xenfsimage.fs", -+ .tp_basicsize = sizeof(fsimage_fs_t), -+ .tp_dealloc = (destructor) fsimage_fs_dealloc, -+#if PY_MAJOR_VERSION < 3 -+ .tp_getattr = (getattrfunc) fsimage_fs_getattr, -+#endif -+ .tp_flags = Py_TPFLAGS_DEFAULT, -+ .tp_doc = fsimage_fs_type__doc__, -+#if PY_MAJOR_VERSION >= 3 -+ .tp_methods = fsimage_fs_methods, -+#endif - }; - - static PyObject * -@@ -309,8 +305,27 @@ static struct PyMethodDef fsimage_module_methods[] = { - { NULL, NULL, 0, NULL } - }; - -+#if PY_MAJOR_VERSION >= 3 -+static struct PyModuleDef fsimage_module_def = { -+ PyModuleDef_HEAD_INIT, -+ .m_name = "xenfsimage", -+ .m_size = -1, -+ .m_methods = fsimage_module_methods, -+}; -+#endif -+ - PyMODINIT_FUNC -+#if PY_MAJOR_VERSION >= 3 -+PyInit_xenfsimage(void) -+#else - initxenfsimage(void) -+#endif - { -+#if PY_MAJOR_VERSION < 3 - Py_InitModule("xenfsimage", fsimage_module_methods); -+#else -+ if (PyType_Ready(&fsimage_fs_type) < 0 || PyType_Ready(&fsimage_file_type) < 0) -+ return NULL; -+ return PyModule_Create(&fsimage_module_def); -+#endif - } --- -2.17.2 - - -From 9b0bc91b3b32856df014fab9de40f463c89a8b1e Mon Sep 17 00:00:00 2001 -From: Wei Liu -Date: Mon, 11 Mar 2019 17:19:19 +0000 -Subject: [PATCH 8/8] README: remove requirement on Python 2 - -Now that all python scripts are compatible with Python 2.6 and above, -remove the restriction. - -Signed-off-by: Wei Liu -Acked-by: Andrew Cooper ---- - README | 10 ++-------- - 1 file changed, 2 insertions(+), 8 deletions(-) - -diff --git a/README b/README -index d00715c2fd..23e4f7c3dc 100644 ---- a/README -+++ b/README -@@ -46,7 +46,7 @@ provided by your OS distributor: - - GCC 4.8 or later - - GNU Binutils 2.24 or later - * Development install of zlib (e.g., zlib-dev) -- * Development install of Python 2, v2.6 or later (e.g., python-dev) -+ * Development install of Python 2.6 or later (e.g., python-dev) - * Development install of curses (e.g., libncurses-dev) - * Development install of openssl (e.g., openssl-dev) - * Development install of x11 (e.g. xorg-x11-dev) -@@ -177,16 +177,10 @@ Python Runtime Libraries - - Various tools, such as pygrub, have the following runtime dependencies: - -- * Python 2, v2.6 or later. -+ * Python 2.6 or later. - URL: http://www.python.org/ - Debian: python - --Note that the build system expects `python` to be python2. If your system --has `python` pointing to python3 (as in the case of Arch Linux or Anaconda), --you'll need to specify a path to a python2 binary when running configure: -- -- PYTHON=/usr/bin/python2 ./configure -- - Intel(R) Trusted Execution Technology Support - ============================================= - --- -2.17.2 - -From 38a85fe8d98b54da7f842f8d78bf8c54f7747735 Mon Sep 17 00:00:00 2001 -From: Wei Liu -Date: Wed, 13 Mar 2019 13:54:48 +0000 -Subject: [PATCH] build/m4: fix python library detection on Ubuntu systems - -16cc3362aed doesn't work on Ubuntu with gcc (but it does work with -clang). Work around it by manipulating LIBS. - -Signed-off-by: Wei Liu -Acked-by: Andrew Cooper ---- - m4/python_devel.m4 | 7 +++++++ - tools/configure | 7 +++++++ - 2 files changed, 14 insertions(+) - -diff --git a/m4/python_devel.m4 b/m4/python_devel.m4 -index f9cb23aee1..7f26381376 100644 ---- a/m4/python_devel.m4 -+++ b/m4/python_devel.m4 -@@ -1,6 +1,7 @@ - AC_DEFUN([AX_CHECK_PYTHON_DEVEL], [ - ac_previous_cppflags=$CPPFLAGS - ac_previous_ldflags=$LDFLAGS -+ac_previous_libs=$LIBS - AC_PATH_PROG([pyconfig], [$PYTHON-config], [no]) - AS_IF([test x"$pyconfig" = x"no"], [ - dnl For those that don't have python-config -@@ -15,10 +16,15 @@ AS_IF([test x"$pyconfig" = x"no"], [ - print(distutils.sysconfig.get_config_var("LINKFORSHARED"))'`" - LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ - print(distutils.sysconfig.get_config_var("LDFLAGS"))'`" -+ LIBS="$LIBS `$PYTHON -c 'import distutils.sysconfig; \ -+ print(distutils.sysconfig.get_config_var("LIBS"))'`" -+ LIBS="$LIBS `$PYTHON -c 'import distutils.sysconfig; \ -+ print(distutils.sysconfig.get_config_var("SYSLIBS"))'`" - ], [ - dnl If python-config is found use it - CPPFLAGS="$CFLAGS `$PYTHON-config --cflags`" - LDFLAGS="$LDFLAGS `$PYTHON-config --ldflags`" -+ LIBS="$LIBS `$PYTHON-config --libs`" - ]) - - AC_CHECK_HEADER([Python.h], [], -@@ -28,4 +34,5 @@ AC_CHECK_FUNC([PyArg_ParseTuple], [], - - CPPFLAGS=$ac_previous_cppflags - LDFLAGS=$ac_previous_ldflags -+LIBS=$ac_previous_libs - ]) -diff --git a/tools/configure b/tools/configure -index 9ff879548a..b66d3f6fba 100755 ---- a/tools/configure -+++ b/tools/configure -@@ -7418,6 +7418,7 @@ if test "$cross_compiling" != yes; then : - - ac_previous_cppflags=$CPPFLAGS - ac_previous_ldflags=$LDFLAGS -+ac_previous_libs=$LIBS - # Extract the first word of "$PYTHON-config", so it can be a program name with args. - set dummy $PYTHON-config; ac_word=$2 - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -@@ -7472,11 +7473,16 @@ if test x"$pyconfig" = x"no"; then : - print(distutils.sysconfig.get_config_var("LINKFORSHARED"))'`" - LDFLAGS="$LDFLAGS `$PYTHON -c 'import distutils.sysconfig; \ - print(distutils.sysconfig.get_config_var("LDFLAGS"))'`" -+ LIBS="$LIBS `$PYTHON -c 'import distutils.sysconfig; \ -+ print(distutils.sysconfig.get_config_var("LIBS"))'`" -+ LIBS="$LIBS `$PYTHON -c 'import distutils.sysconfig; \ -+ print(distutils.sysconfig.get_config_var("SYSLIBS"))'`" - - else - - CPPFLAGS="$CFLAGS `$PYTHON-config --cflags`" - LDFLAGS="$LDFLAGS `$PYTHON-config --ldflags`" -+ LIBS="$LIBS `$PYTHON-config --libs`" - - fi - -@@ -7498,6 +7504,7 @@ fi - - CPPFLAGS=$ac_previous_cppflags - LDFLAGS=$ac_previous_ldflags -+LIBS=$ac_previous_libs - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether Python setup.py brokenly enables -D_FORTIFY_SOURCE" >&5 --- -2.17.2 - -From 485079e816788d70169f45579e1f5a8f909dc1b3 Mon Sep 17 00:00:00 2001 -From: Wei Liu -Date: Mon, 1 Apr 2019 11:32:35 +0100 -Subject: [PATCH 1/4] pygrub: fix message in grub parser - -The code suggests 0 is allowed. Zero is not a positive number. - -Signed-off-by: Wei Liu -Acked-by: Andrew Cooper ---- - tools/pygrub/src/GrubConf.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tools/pygrub/src/GrubConf.py b/tools/pygrub/src/GrubConf.py -index f8d3799dc0..0204d410ac 100644 ---- a/tools/pygrub/src/GrubConf.py -+++ b/tools/pygrub/src/GrubConf.py -@@ -236,7 +236,7 @@ class _GrubConfigFile(object): - self._default = val - - if self._default < 0: -- raise ValueError("default must be positive number") -+ raise ValueError("default must be non-negative number") - default = property(_get_default, _set_default) - - def set_splash(self, val): --- -2.17.2 - - -From 767ba397d34848c7e0c4e9cdfc5efa4e0cb61442 Mon Sep 17 00:00:00 2001 -From: Wei Liu -Date: Mon, 1 Apr 2019 11:32:36 +0100 -Subject: [PATCH 2/4] pygrub/grub: always use integer for default entry - -The original code set the default to either a string or an integer -(0) and relies on a Python 2 specific behaviour to work (integer is -allowed to be compared to string in Python 2 but not 3). - -Always use integer. The caller (pygrub) already has code to handle -that. - -Reported-by: M A Young -Signed-off-by: Wei Liu -Acked-by: Andrew Cooper ---- - tools/pygrub/src/GrubConf.py | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/tools/pygrub/src/GrubConf.py b/tools/pygrub/src/GrubConf.py -index 0204d410ac..594139bac7 100644 ---- a/tools/pygrub/src/GrubConf.py -+++ b/tools/pygrub/src/GrubConf.py -@@ -233,7 +233,11 @@ class _GrubConfigFile(object): - if val == "saved": - self._default = 0 - else: -- self._default = val -+ try: -+ self._default = int(val) -+ except ValueError: -+ logging.warning("Invalid value %s, setting default to 0" %(val,)) -+ self._default = 0 - - if self._default < 0: - raise ValueError("default must be non-negative number") --- -2.17.2 - - -From ff915c8cacc264ae1380d51fea07267b8308d7ba Mon Sep 17 00:00:00 2001 -From: Wei Liu -Date: Mon, 1 Apr 2019 11:32:37 +0100 -Subject: [PATCH 3/4] pygrub: encode / decode string in Python 3 - -String is unicode in 3 but bytes in 2. We need to call encode / decode -function when using Python 3. - -Reported-by: M A Young -Signed-off-by: Wei Liu -Acked-by: Andrew Cooper ---- - tools/pygrub/src/pygrub | 10 ++++++++-- - 1 file changed, 8 insertions(+), 2 deletions(-) - -diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub -index dbdce315c6..ce7ab0eb8c 100755 ---- a/tools/pygrub/src/pygrub -+++ b/tools/pygrub/src/pygrub -@@ -457,7 +457,10 @@ class Grub: - # limit read size to avoid pathological cases - buf = f.read(FS_READ_MAX) - del f -- self.cf.parse(buf) -+ if sys.version_info[0] < 3: -+ self.cf.parse(buf) -+ else: -+ self.cf.parse(buf.decode()) - - def image_index(self): - if isinstance(self.cf.default, int): -@@ -960,5 +963,8 @@ if __name__ == "__main__": - ostring = format_simple(bootcfg["kernel"], bootcfg["ramdisk"], args, "\0") - - sys.stdout.flush() -- os.write(fd, ostring) -+ if sys.version_info[0] < 3: -+ os.write(fd, ostring) -+ else: -+ os.write(fd, ostring.encode()) - --- -2.17.2 - - -From a57a1b26ec0ae31f924cf2bbcf479637d007be44 Mon Sep 17 00:00:00 2001 -From: Wei Liu -Date: Mon, 1 Apr 2019 11:32:38 +0100 -Subject: [PATCH 4/4] tools/ocaml: make python scripts 2 and 3 compatible - -1. Explicitly import reduce because that's required in 3. -2. Change print to function. -3. Eliminate invocations of has_key. - -Signed-off-by: M A Young -Signed-off-by: Wei Liu -Reviewed-by: Andrew Cooper -Acked-by: Christian Lindig ---- - tools/ocaml/libs/xentoollog/genlevels.py | 5 ++++- - tools/ocaml/libs/xl/genwrap.py | 17 ++++++++++------- - 2 files changed, 14 insertions(+), 8 deletions(-) - -diff --git a/tools/ocaml/libs/xentoollog/genlevels.py b/tools/ocaml/libs/xentoollog/genlevels.py -index 8c233c59b1..f9cf853e26 100755 ---- a/tools/ocaml/libs/xentoollog/genlevels.py -+++ b/tools/ocaml/libs/xentoollog/genlevels.py -@@ -1,6 +1,9 @@ - #!/usr/bin/python - -+from __future__ import print_function -+ - import sys -+from functools import reduce - - def read_levels(): - f = open('../../../libs/toollog/include/xentoollog.h', 'r') -@@ -93,7 +96,7 @@ def autogen_header(open_comment, close_comment): - - if __name__ == '__main__': - if len(sys.argv) < 3: -- print >>sys.stderr, "Usage: genlevels.py " -+ print("Usage: genlevels.py ", file=sys.stderr) - sys.exit(1) - - levels, olevels = read_levels() -diff --git a/tools/ocaml/libs/xl/genwrap.py b/tools/ocaml/libs/xl/genwrap.py -index 815c1cb0e3..7bf26bdcd8 100644 ---- a/tools/ocaml/libs/xl/genwrap.py -+++ b/tools/ocaml/libs/xl/genwrap.py -@@ -1,6 +1,9 @@ - #!/usr/bin/python - -+from __future__ import print_function -+ - import sys,os -+from functools import reduce - - import idl - -@@ -78,7 +81,7 @@ def ocaml_type_of(ty): - elif isinstance(ty,idl.Array): - return "%s array" % ocaml_type_of(ty.elem_type) - elif isinstance(ty,idl.Builtin): -- if not builtins.has_key(ty.typename): -+ if ty.typename not in builtins: - raise NotImplementedError("Unknown Builtin %s (%s)" % (ty.typename, type(ty))) - typename,_,_ = builtins[ty.typename] - if not typename: -@@ -251,7 +254,7 @@ def gen_ocaml_ml(ty, interface, indent=""): - else: - s += "\texternal default : ctx -> %sunit -> t = \"stub_libxl_%s_init\"\n" % (union_args, ty.rawname) - -- if functions.has_key(ty.rawname): -+ if ty.rawname in functions: - for name,args in functions[ty.rawname]: - s += "\texternal %s : " % name - s += " -> ".join(args) -@@ -278,7 +281,7 @@ def c_val(ty, c, o, indent="", parent = None): - else: - s += "%s = Int_val(%s);" % (c, o) - elif isinstance(ty,idl.Builtin): -- if not builtins.has_key(ty.typename): -+ if ty.typename not in builtins: - raise NotImplementedError("Unknown Builtin %s (%s)" % (ty.typename, type(ty))) - _,fn,_ = builtins[ty.typename] - if not fn: -@@ -375,7 +378,7 @@ def ocaml_Val(ty, o, c, indent="", parent = None): - else: - s += "%s = Val_int(%s);" % (o, c) - elif isinstance(ty,idl.Builtin): -- if not builtins.has_key(ty.typename): -+ if ty.typename not in builtins: - raise NotImplementedError("Unknown Builtin %s (%s)" % (ty.typename, type(ty))) - _,_,fn = builtins[ty.typename] - if not fn: -@@ -520,7 +523,7 @@ def autogen_header(open_comment, close_comment): - - if __name__ == '__main__': - if len(sys.argv) < 4: -- print >>sys.stderr, "Usage: genwrap.py " -+ print("Usage: genwrap.py ", file=sys.stderr) - sys.exit(1) - - (_,types) = idl.parse(sys.argv[1]) -@@ -533,7 +536,7 @@ if __name__ == '__main__': - - for t in blacklist: - if t not in [ty.rawname for ty in types]: -- print "unknown type %s in blacklist" % t -+ print("unknown type %s in blacklist" % t) - - types = [ty for ty in types if not ty.rawname in blacklist] - -@@ -564,7 +567,7 @@ if __name__ == '__main__': - cinc.write("\n") - cinc.write(gen_Val_ocaml(ty)) - cinc.write("\n") -- if functions.has_key(ty.rawname): -+ if ty.rawname in functions: - cinc.write(gen_c_stub_prototype(ty, functions[ty.rawname])) - cinc.write("\n") - if ty.init_fn is not None: --- -2.17.2 - diff --git a/xen.python38.patch b/xen.python38.patch deleted file mode 100644 index 30d5d21..0000000 --- a/xen.python38.patch +++ /dev/null @@ -1,94 +0,0 @@ ---- xen-4.12.0/m4/python_devel.m4.orig 2019-05-31 23:30:42.489738121 +0100 -+++ xen-4.12.0/m4/python_devel.m4 2019-06-01 17:02:38.886934441 +0100 -@@ -24,7 +24,8 @@ - dnl If python-config is found use it - CPPFLAGS="$CFLAGS `$PYTHON-config --cflags`" - LDFLAGS="$LDFLAGS `$PYTHON-config --ldflags`" -- LIBS="$LIBS `$PYTHON-config --libs`" -+ LIBSTMP="`$PYTHON-config --libs --embed`" || LIBSTMP="`$PYTHON-config --libs`" -+ LIBS="$LIBS $LIBSTMP" - ]) - - AC_CHECK_HEADER([Python.h], [], ---- xen-4.12.0/tools/configure.orig 2019-05-31 23:30:42.498738452 +0100 -+++ xen-4.12.0/tools/configure 2019-06-01 17:08:26.100727658 +0100 -@@ -7482,7 +7482,8 @@ - - CPPFLAGS="$CFLAGS `$PYTHON-config --cflags`" - LDFLAGS="$LDFLAGS `$PYTHON-config --ldflags`" -- LIBS="$LIBS `$PYTHON-config --libs`" -+ LIBSTMP="`$PYTHON-config --libs --embed`" || LIBSTMP="`$PYTHON-config --libs`" -+ LIBS="$LIBS $LIBSTMP" - - fi - ---- xen-4.12.0/tools/python/xen/lowlevel/xc/xc.c.orig 2019-04-01 12:03:23.000000000 +0100 -+++ xen-4.12.0/tools/python/xen/lowlevel/xc/xc.c 2019-06-01 17:58:39.567729630 +0100 -@@ -118,7 +118,8 @@ - PyObject *kwds) - { - uint32_t dom = 0, target = 0; -- int ret, i; -+ int ret; -+ unsigned int i; - PyObject *pyhandle = NULL; - struct xen_domctl_createdomain config = { - .handle = { -@@ -296,7 +297,7 @@ - - static PyObject *pyxc_domain_sethandle(XcObject *self, PyObject *args) - { -- int i; -+ unsigned int i; - uint32_t dom; - PyObject *pyhandle; - xen_domain_handle_t handle; -@@ -337,7 +338,8 @@ - PyObject *list, *info_dict, *pyhandle; - - uint32_t first_dom = 0; -- int max_doms = 1024, nr_doms, i, j; -+ int max_doms = 1024, nr_doms, i; -+ unsigned int j; - xc_dominfo_t *info; - - static char *kwd_list[] = { "first_dom", "max_doms", NULL }; -@@ -632,7 +634,8 @@ - { - uint32_t sbdf; - uint32_t max_sdevs, num_sdevs; -- int domid, seg, bus, dev, func, rc, i; -+ int domid, seg, bus, dev, func, rc; -+ unsigned int i; - PyObject *Pystr; - char *group_str; - char dev_str[9]; -@@ -972,7 +975,7 @@ - { - xc_physinfo_t pinfo; - char cpu_cap[128], virt_caps[128], *p; -- int i; -+ unsigned int i; - const char *virtcap_names[] = { "hvm", "hvm_directio" }; - - if ( xc_physinfo(self->xc_handle, &pinfo) != 0 ) ---- xen-4.12.0/tools/python/xen/lowlevel/xs/xs.c.orig 2019-04-01 12:03:23.000000000 +0100 -+++ xen-4.12.0/tools/python/xen/lowlevel/xs/xs.c 2019-06-01 18:59:46.316760561 +0100 -@@ -186,7 +186,7 @@ - Py_END_ALLOW_THREADS - - if (xsval) { -- int i; -+ unsigned int i; - PyObject *val = PyList_New(xsval_n); - for (i = 0; i < xsval_n; i++) - #if PY_MAJOR_VERSION >= 3 -@@ -276,7 +276,7 @@ - struct xs_handle *xh = xshandle(self); - struct xs_permissions *perms; - unsigned int perms_n = 0; -- int i; -+ unsigned int i; - - xs_transaction_t th; - char *thstr; diff --git a/xen.spec b/xen.spec index 3f2f4e9..9ce0255 100644 --- a/xen.spec +++ b/xen.spec @@ -53,12 +53,12 @@ %endif # Hypervisor ABI -%define hv_abi 4.12 +%define hv_abi 4.13 Summary: Xen is a virtual machine monitor Name: xen -Version: 4.12.1 -Release: 9%{?dist} +Version: 4.13.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,46 +112,6 @@ Patch39: qemu.trad.CVE-2017-9330.patch Patch40: xen.drop.brctl.patch Patch41: xen.python.env.patch Patch42: xen.gcc9.fixes.patch -Patch43: xen.python3.patch -Patch54: xen.python38.patch -Patch55: xsa296.patch -Patch56: xsa298.patch -Patch57: xsa299-4.12-0001-x86-mm-L1TF-checks-don-t-leave-a-partial-entry.patch -Patch58: xsa299-4.12-0002-x86-mm-Don-t-re-set-PGT_pinned-on-a-partially-de-val.patch -Patch59: xsa299-4.12-0003-x86-mm-Separate-out-partial_pte-tristate-into-indivi.patch -Patch60: xsa299-4.12-0004-x86-mm-Use-flags-for-_put_page_type-rather-than-a-bo.patch -Patch61: xsa299-4.12-0005-x86-mm-Rework-get_page_and_type_from_mfn-conditional.patch -Patch62: xsa299-4.12-0006-x86-mm-Have-alloc_l-23-_table-clear-partial_flags-wh.patch -Patch63: xsa299-4.12-0007-x86-mm-Always-retain-a-general-ref-on-partial.patch -Patch64: xsa299-4.12-0008-x86-mm-Collapse-PTF_partial_set-and-PTF_partial_gene.patch -Patch65: xsa299-4.12-0009-x86-mm-Properly-handle-linear-pagetable-promotion-fa.patch -Patch66: xsa299-4.12-0010-x86-mm-Fix-nested-de-validation-on-error.patch -Patch67: xsa299-4.12-0011-x86-mm-Don-t-drop-a-type-ref-unless-you-held-a-ref-t.patch -Patch68: xsa301-master-1.patch -Patch69: xsa301-master-2.patch -Patch70: xsa301-master-3.patch -Patch71: xsa302-4.12-0001-IOMMU-add-missing-HVM-check.patch -Patch72: xsa302-4.12-0002-passthrough-quarantine-PCI-devices.patch -Patch73: xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch -Patch74: xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch -Patch75: xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch -Patch76: xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch -Patch77: xsa304-4.12-1.patch -Patch78: xsa304-4.12-2.patch -Patch79: xsa304-4.12-3.patch -Patch80: xsa305-4.12-1.patch -Patch81: xsa305-4.12-2.patch -Patch82: xsa306-4.12.patch -Patch83: xsa307.patch -Patch84: xsa308.patch -Patch85: xsa309.patch -Patch86: xsa310-0001-x86-mm-Set-old_guest_table-when-destroying-vcpu-page.patch -Patch87: xsa310-0002-x86-mm-alloc-free_lN_table-Retain-partial_flags-on-E.patch -Patch88: xsa310-0003-x86-mm-relinquish_memory-Grab-an-extra-type-ref-when.patch -Patch89: xen.git-36d2ecb9991bf2d1ddb933872c3dfbd26300ca68.patch -Patch90: xen.git-1e8932f0d815bee3102ede328676e1a4ca1926c7.patch -Patch91: xsa311-4.12.patch -Patch92: xen.ocaml.patch %if %build_qemutrad @@ -348,46 +308,6 @@ manage Xen virtual machines. %patch40 -p1 %patch41 -p1 %patch42 -p1 -%patch43 -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 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -861,7 +781,6 @@ fi %{_sbindir}/xen-hvmcrash %{_sbindir}/xen-hvmctx %endif -%{_sbindir}/xen-tmem-list-parse %{_sbindir}/xenconsoled %{_sbindir}/xenlockprof %{_sbindir}/xenmon @@ -889,6 +808,9 @@ fi %endif %{_sbindir}/xen-livepatch %{_sbindir}/xen-diag +%ifnarch armv7hl aarch64 +%{_sbindir}/xen-ucode +%endif # Xen logfiles %dir %attr(0700,root,root) %{_localstatedir}/log/xen @@ -927,7 +849,7 @@ fi %dir %{_includedir}/xenstore-compat %{_includedir}/xenstore-compat/* %{_libdir}/*.so -/usr/share/pkgconfig/* +%{_libdir}/pkgconfig/* %files licenses %doc licensedir/* @@ -951,6 +873,14 @@ fi %endif %changelog +* Wed Dec 18 2019 Michael Young - 4.13.0-1 +- update to 4.13.0 + remove patches now included or superceded upstream + adjust xen.hypervisor.config + /usr/sbin/xen-tmem-list-parse has been removed + pkgconfig files have moved to %%{_libdir}/pkgconfig + /usr/sbin/xen-ucode has been added (x86 only) + * Sun Dec 15 2019 Michael Young - 4.12.1-9 - fix build with OCaml 4.09.0 diff --git a/xsa296.patch b/xsa296.patch deleted file mode 100644 index e71ea7f..0000000 --- a/xsa296.patch +++ /dev/null @@ -1,195 +0,0 @@ -From: Andrew Cooper -Subject: xen/hypercall: Don't use BUG() for parameter checking in hypercall_create_continuation() - -Since c/s 1d429034 "hypercall: update vcpu_op to take an unsigned vcpuid", -which incorrectly swapped 'i' for 'u' in the parameter type list, guests have -been able to hit the BUG() in next_args()'s default case. - -Correct these back to 'i'. - -In addition, make adjustments to prevent this class of issue from occurring in -the future - crashing Xen is not an appropriate form of parameter checking. - -Capitalise NEXT_ARG() to catch all uses, to highlight that it is a macro doing -non-function-like things behind the scenes, and undef it when appropriate. -Implement a bad_fmt: block which prints an error, asserts unreachable, and -crashes the guest. - -On the ARM side, drop all parameter checking of p. It is asymmetric with the -x86 side, and akin to expecting memcpy() or sprintf() to check their src/fmt -parameter before use. A caller passing "" or something other than a string -literal will be obvious during code review. - -This is XSA-296. - -Signed-off-by: Andrew Cooper -Acked-by: Julien Grall - -diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c -index 941bbff4fe..a3da8e9c08 100644 ---- a/xen/arch/arm/domain.c -+++ b/xen/arch/arm/domain.c -@@ -383,14 +383,15 @@ void sync_vcpu_execstate(struct vcpu *v) - /* Nothing to do -- no lazy switching */ - } - --#define next_arg(fmt, args) ({ \ -+#define NEXT_ARG(fmt, args) \ -+({ \ - unsigned long __arg; \ - switch ( *(fmt)++ ) \ - { \ - case 'i': __arg = (unsigned long)va_arg(args, unsigned int); break; \ - case 'l': __arg = (unsigned long)va_arg(args, unsigned long); break; \ - case 'h': __arg = (unsigned long)va_arg(args, void *); break; \ -- default: __arg = 0; BUG(); \ -+ default: goto bad_fmt; \ - } \ - __arg; \ - }) -@@ -405,9 +406,6 @@ unsigned long hypercall_create_continuation( - unsigned int i; - va_list args; - -- /* All hypercalls take at least one argument */ -- BUG_ON( !p || *p == '\0' ); -- - current->hcall_preempted = true; - - va_start(args, format); -@@ -415,7 +413,7 @@ unsigned long hypercall_create_continuation( - if ( mcs->flags & MCSF_in_multicall ) - { - for ( i = 0; *p != '\0'; i++ ) -- mcs->call.args[i] = next_arg(p, args); -+ mcs->call.args[i] = NEXT_ARG(p, args); - - /* Return value gets written back to mcs->call.result */ - rc = mcs->call.result; -@@ -431,7 +429,7 @@ unsigned long hypercall_create_continuation( - - for ( i = 0; *p != '\0'; i++ ) - { -- arg = next_arg(p, args); -+ arg = NEXT_ARG(p, args); - - switch ( i ) - { -@@ -454,7 +452,7 @@ unsigned long hypercall_create_continuation( - - for ( i = 0; *p != '\0'; i++ ) - { -- arg = next_arg(p, args); -+ arg = NEXT_ARG(p, args); - - switch ( i ) - { -@@ -475,8 +473,16 @@ unsigned long hypercall_create_continuation( - va_end(args); - - return rc; -+ -+ bad_fmt: -+ gprintk(XENLOG_ERR, "Bad hypercall continuation format '%c'\n", *p); -+ ASSERT_UNREACHABLE(); -+ domain_crash(current->domain); -+ return 0; - } - -+#undef NEXT_ARG -+ - void startup_cpu_idle_loop(void) - { - struct vcpu *v = current; -diff --git a/xen/arch/x86/hypercall.c b/xen/arch/x86/hypercall.c -index d483dbaa6b..4643e5eb43 100644 ---- a/xen/arch/x86/hypercall.c -+++ b/xen/arch/x86/hypercall.c -@@ -80,14 +80,15 @@ const hypercall_args_t hypercall_args_table[NR_hypercalls] = - #undef COMP - #undef ARGS - --#define next_arg(fmt, args) ({ \ -+#define NEXT_ARG(fmt, args) \ -+({ \ - unsigned long __arg; \ - switch ( *(fmt)++ ) \ - { \ - case 'i': __arg = (unsigned long)va_arg(args, unsigned int); break; \ - case 'l': __arg = (unsigned long)va_arg(args, unsigned long); break; \ - case 'h': __arg = (unsigned long)va_arg(args, void *); break; \ -- default: __arg = 0; BUG(); \ -+ default: goto bad_fmt; \ - } \ - __arg; \ - }) -@@ -109,7 +110,7 @@ unsigned long hypercall_create_continuation( - if ( mcs->flags & MCSF_in_multicall ) - { - for ( i = 0; *p != '\0'; i++ ) -- mcs->call.args[i] = next_arg(p, args); -+ mcs->call.args[i] = NEXT_ARG(p, args); - } - else - { -@@ -121,7 +122,7 @@ unsigned long hypercall_create_continuation( - { - for ( i = 0; *p != '\0'; i++ ) - { -- arg = next_arg(p, args); -+ arg = NEXT_ARG(p, args); - switch ( i ) - { - case 0: regs->rdi = arg; break; -@@ -137,7 +138,7 @@ unsigned long hypercall_create_continuation( - { - for ( i = 0; *p != '\0'; i++ ) - { -- arg = next_arg(p, args); -+ arg = NEXT_ARG(p, args); - switch ( i ) - { - case 0: regs->rbx = arg; break; -@@ -154,8 +155,16 @@ unsigned long hypercall_create_continuation( - va_end(args); - - return op; -+ -+ bad_fmt: -+ gprintk(XENLOG_ERR, "Bad hypercall continuation format '%c'\n", *p); -+ ASSERT_UNREACHABLE(); -+ domain_crash(curr->domain); -+ return 0; - } - -+#undef NEXT_ARG -+ - int hypercall_xlat_continuation(unsigned int *id, unsigned int nr, - unsigned int mask, ...) - { -diff --git a/xen/common/compat/domain.c b/xen/common/compat/domain.c -index 39877b3ab2..2531fa7421 100644 ---- a/xen/common/compat/domain.c -+++ b/xen/common/compat/domain.c -@@ -81,7 +81,7 @@ int compat_vcpu_op(int cmd, unsigned int vcpuid, XEN_GUEST_HANDLE_PARAM(void) ar - } - - if ( rc == -ERESTART ) -- rc = hypercall_create_continuation(__HYPERVISOR_vcpu_op, "iuh", -+ rc = hypercall_create_continuation(__HYPERVISOR_vcpu_op, "iih", - cmd, vcpuid, arg); - - break; -diff --git a/xen/common/domain.c b/xen/common/domain.c -index 2308588052..65bcd85e34 100644 ---- a/xen/common/domain.c -+++ b/xen/common/domain.c -@@ -1411,7 +1411,7 @@ long do_vcpu_op(int cmd, unsigned int vcpuid, XEN_GUEST_HANDLE_PARAM(void) arg) - - rc = arch_initialise_vcpu(v, arg); - if ( rc == -ERESTART ) -- rc = hypercall_create_continuation(__HYPERVISOR_vcpu_op, "iuh", -+ rc = hypercall_create_continuation(__HYPERVISOR_vcpu_op, "iih", - cmd, vcpuid, arg); - - break; diff --git a/xsa298.patch b/xsa298.patch deleted file mode 100644 index aa39042..0000000 --- a/xsa298.patch +++ /dev/null @@ -1,89 +0,0 @@ -From: Jan Beulich -Subject: x86/PV: check GDT/LDT limits during emulation - -Accesses beyond the LDT limit originating from emulation would trigger -the ASSERT() in pv_map_ldt_shadow_page(). On production builds such -accesses would cause an attempt to promote the touched page (offset from -the present LDT base address) to a segment descriptor one. If this -happens to succeed, guest user mode would be able to elevate its -privileges to that of the guest kernel. This is particularly easy when -there's no LDT at all, in which case the LDT base stored internally to -Xen is simply zero. - -Also adjust the ASSERT() that was triggering: It was off by one to -begin with, and for production builds we also better use -ASSERT_UNREACHABLE() instead with suitable recovery code afterwards. - -This is XSA-298. - -Reported-by: Andrew Cooper -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper ---- -v2: Correct 64-bit-only limit check (by folding into the common one). - ---- a/xen/arch/x86/pv/emul-gate-op.c -+++ b/xen/arch/x86/pv/emul-gate-op.c -@@ -51,7 +51,13 @@ static int read_gate_descriptor(unsigned - const seg_desc_t *pdesc = gdt_ldt_desc_ptr(gate_sel); - - if ( (gate_sel < 4) || -- ((gate_sel >= FIRST_RESERVED_GDT_BYTE) && !(gate_sel & 4)) || -+ /* -+ * We're interested in call gates only, which occupy a single -+ * seg_desc_t for 32-bit and a consecutive pair of them for 64-bit. -+ */ -+ ((gate_sel >> 3) + !is_pv_32bit_vcpu(v) >= -+ (gate_sel & 4 ? v->arch.pv.ldt_ents -+ : v->arch.pv.gdt_ents)) || - __get_user(desc, pdesc) ) - return 0; - -@@ -70,7 +76,7 @@ static int read_gate_descriptor(unsigned - if ( !is_pv_32bit_vcpu(v) ) - { - if ( (*ar & 0x1f00) != 0x0c00 || -- (gate_sel >= FIRST_RESERVED_GDT_BYTE - 8 && !(gate_sel & 4)) || -+ /* Limit check done above already. */ - __get_user(desc, pdesc + 1) || - (desc.b & 0x1f00) ) - return 0; ---- a/xen/arch/x86/pv/emulate.c -+++ b/xen/arch/x86/pv/emulate.c -@@ -31,7 +31,14 @@ int pv_emul_read_descriptor(unsigned int - { - seg_desc_t desc; - -- if ( sel < 4) -+ if ( sel < 4 || -+ /* -+ * Don't apply the GDT limit here, as the selector may be a Xen -+ * provided one. __get_user() will fail (without taking further -+ * action) for ones falling in the gap between guest populated -+ * and Xen ones. -+ */ -+ ((sel & 4) && (sel >> 3) >= v->arch.pv.ldt_ents) ) - desc.b = desc.a = 0; - else if ( __get_user(desc, gdt_ldt_desc_ptr(sel)) ) - return 0; ---- a/xen/arch/x86/pv/mm.c -+++ b/xen/arch/x86/pv/mm.c -@@ -92,12 +92,16 @@ bool pv_map_ldt_shadow_page(unsigned int - BUG_ON(unlikely(in_irq())); - - /* -- * Hardware limit checking should guarantee this property. NB. This is -+ * Prior limit checking should guarantee this property. NB. This is - * safe as updates to the LDT can only be made by MMUEXT_SET_LDT to the - * current vcpu, and vcpu_reset() will block until this vcpu has been - * descheduled before continuing. - */ -- ASSERT((offset >> 3) <= curr->arch.pv.ldt_ents); -+ if ( unlikely((offset >> 3) >= curr->arch.pv.ldt_ents) ) -+ { -+ ASSERT_UNREACHABLE(); -+ return false; -+ } - - if ( is_pv_32bit_domain(currd) ) - linear = (uint32_t)linear; diff --git a/xsa299-4.12-0001-x86-mm-L1TF-checks-don-t-leave-a-partial-entry.patch b/xsa299-4.12-0001-x86-mm-L1TF-checks-don-t-leave-a-partial-entry.patch deleted file mode 100644 index fbb9d80..0000000 --- a/xsa299-4.12-0001-x86-mm-L1TF-checks-don-t-leave-a-partial-entry.patch +++ /dev/null @@ -1,94 +0,0 @@ -From 33d051917d5ef38f678b507a3c832afde48b9b49 Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Thu, 10 Oct 2019 17:57:49 +0100 -Subject: [PATCH 01/11] x86/mm: L1TF checks don't leave a partial entry - -On detection of a potential L1TF issue, most validation code returns --ERESTART to allow the switch to shadow mode to happen and cause the -original operation to be restarted. - -However, in the validation code, the return value -ERESTART has been -repurposed to indicate 1) the function has partially completed -something which needs to be undone, and 2) calling put_page_type() -should cleanly undo it. This causes problems in several places. - -For L1 tables, on receiving an -ERESTART return from alloc_l1_table(), -alloc_page_type() will set PGT_partial on the page. If for some -reason the original operation never restarts, then on domain -destruction, relinquish_memory() will call free_page_type() on the -page. - -Unfortunately, alloc_ and free_l1_table() aren't set up to deal with -PGT_partial. When returning a failure, alloc_l1_table() always -de-validates whatever it's validated so far, and free_l1_table() -always devalidates the whole page. This means that if -relinquish_memory() calls free_page_type() on an L1 that didn't -complete due to an L1TF, it will call put_page_from_l1e() on "page -entries" that have never been validated. - -For L2+ tables, setting rc to ERESTART causes the rest of the -alloc_lN_table() function to *think* that the entry in question will -have PGT_partial set. This will cause it to set partial_pte = 1. If -relinqush_memory() then calls free_page_type() on one of those pages, -then free_lN_table() will call put_page_from_lNe() on the entry when -it shouldn't. - -Rather than indicating -ERESTART, indicate -EINTR. This is the code -to indicate that nothing has changed from when you started the call -(which is effectively how alloc_l1_table() handles errors). - -mod_lN_entry() shouldn't have any of these types of problems, so leave -potential changes there for a clean-up patch later. - -This is part of XSA-299. - -Reported-by: George Dunlap -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ---- - xen/arch/x86/mm.c | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 3557cd1178..a1b55c10ff 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1409,7 +1409,7 @@ static int alloc_l1_table(struct page_info *page) - { - if ( !(l1e_get_flags(pl1e[i]) & _PAGE_PRESENT) ) - { -- ret = pv_l1tf_check_l1e(d, pl1e[i]) ? -ERESTART : 0; -+ ret = pv_l1tf_check_l1e(d, pl1e[i]) ? -EINTR : 0; - if ( ret ) - goto out; - } -@@ -1517,7 +1517,7 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) - { - if ( !pv_l1tf_check_l2e(d, l2e) ) - continue; -- rc = -ERESTART; -+ rc = -EINTR; - } - else - rc = get_page_from_l2e(l2e, pfn, d, partial); -@@ -1603,7 +1603,7 @@ static int alloc_l3_table(struct page_info *page) - { - if ( !pv_l1tf_check_l3e(d, l3e) ) - continue; -- rc = -ERESTART; -+ rc = -EINTR; - } - else - rc = get_page_from_l3e(l3e, pfn, d, partial); -@@ -1783,7 +1783,7 @@ static int alloc_l4_table(struct page_info *page) - { - if ( !pv_l1tf_check_l4e(d, l4e) ) - continue; -- rc = -ERESTART; -+ rc = -EINTR; - } - else - rc = get_page_from_l4e(l4e, pfn, d, partial); --- -2.23.0 - diff --git a/xsa299-4.12-0002-x86-mm-Don-t-re-set-PGT_pinned-on-a-partially-de-val.patch b/xsa299-4.12-0002-x86-mm-Don-t-re-set-PGT_pinned-on-a-partially-de-val.patch deleted file mode 100644 index a74598e..0000000 --- a/xsa299-4.12-0002-x86-mm-Don-t-re-set-PGT_pinned-on-a-partially-de-val.patch +++ /dev/null @@ -1,99 +0,0 @@ -From b490792c18f74b76ec8161721c1e07f810e36309 Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Thu, 10 Oct 2019 17:57:49 +0100 -Subject: [PATCH 02/11] x86/mm: Don't re-set PGT_pinned on a partially - de-validated page - -When unpinning pagetables, if an operation is interrupted, -relinquish_memory() re-sets PGT_pinned so that the un-pin will -pickedup again when the hypercall restarts. - -This is appropriate when put_page_and_type_preemptible() returns --EINTR, which indicates that the page is back in its initial state -(i.e., completely validated). However, for -ERESTART, this leads to a -state where a page has both PGT_pinned and PGT_partial set. - -This happens to work at the moment, although it's not really a -"canonical" state; but in subsequent patches, where we need to make a -distinction in handling between PGT_validated and PGT_partial pages, -this causes issues. - -Move to a "canonical" state by: -- Only re-setting PGT_pinned on -EINTR -- Re-dropping the refcount held by PGT_pinned on -ERESTART - -In the latter case, the PGT_partial bit will be cleared further down -with the rest of the other PGT_partial pages. - -While here, clean up some trainling whitespace. - -This is part of XSA-299. - -Reported-by: George Dunlap -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ---- - xen/arch/x86/domain.c | 31 ++++++++++++++++++++++++++++--- - 1 file changed, 28 insertions(+), 3 deletions(-) - -diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c -index 2585327834..59df8a6d8d 100644 ---- a/xen/arch/x86/domain.c -+++ b/xen/arch/x86/domain.c -@@ -114,7 +114,7 @@ static void play_dead(void) - * this case, heap corruption or #PF can occur (when heap debugging is - * enabled). For example, even printk() can involve tasklet scheduling, - * which touches per-cpu vars. -- * -+ * - * Consider very carefully when adding code to *dead_idle. Most hypervisor - * subsystems are unsafe to call. - */ -@@ -1909,9 +1909,34 @@ static int relinquish_memory( - break; - case -ERESTART: - case -EINTR: -+ /* -+ * -EINTR means PGT_validated has been re-set; re-set -+ * PGT_pinned again so that it gets picked up next time -+ * around. -+ * -+ * -ERESTART, OTOH, means PGT_partial is set instead. Put -+ * it back on the list, but don't set PGT_pinned; the -+ * section below will finish off de-validation. But we do -+ * need to drop the general ref associated with -+ * PGT_pinned, since put_page_and_type_preemptible() -+ * didn't do it. -+ * -+ * NB we can do an ASSERT for PGT_validated, since we -+ * "own" the type ref; but theoretically, the PGT_partial -+ * could be cleared by someone else. -+ */ -+ if ( ret == -EINTR ) -+ { -+ ASSERT(page->u.inuse.type_info & PGT_validated); -+ set_bit(_PGT_pinned, &page->u.inuse.type_info); -+ } -+ else -+ put_page(page); -+ - ret = -ERESTART; -+ -+ /* Put the page back on the list and drop the ref we grabbed above */ - page_list_add(page, list); -- set_bit(_PGT_pinned, &page->u.inuse.type_info); - put_page(page); - goto out; - default: -@@ -2161,7 +2186,7 @@ void vcpu_kick(struct vcpu *v) - * pending flag. These values may fluctuate (after all, we hold no - * locks) but the key insight is that each change will cause - * evtchn_upcall_pending to be polled. -- * -+ * - * NB2. We save the running flag across the unblock to avoid a needless - * IPI for domains that we IPI'd to unblock. - */ --- -2.23.0 - diff --git a/xsa299-4.12-0003-x86-mm-Separate-out-partial_pte-tristate-into-indivi.patch b/xsa299-4.12-0003-x86-mm-Separate-out-partial_pte-tristate-into-indivi.patch deleted file mode 100644 index 226e548..0000000 --- a/xsa299-4.12-0003-x86-mm-Separate-out-partial_pte-tristate-into-indivi.patch +++ /dev/null @@ -1,618 +0,0 @@ -From 0f9f61e5737fdd346550ec6e30161fa99e4653fa Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Thu, 10 Oct 2019 17:57:49 +0100 -Subject: [PATCH 03/11] x86/mm: Separate out partial_pte tristate into - individual flags - -At the moment, partial_pte is a tri-state that contains two distinct bits -of information: - -1. If zero, the pte at index [nr_validated_ptes] is un-validated. If - non-zero, the pte was last seen with PGT_partial set. - -2. If positive, the pte at index [nr_validated_ptes] does not hold a - general reference count. If negative, it does. - -To make future patches more clear, separate out this functionality -into two distinct, named bits: PTF_partial_set (for #1) and -PTF_partial_general_ref (for #2). - -Additionally, a number of functions which need this information also -take other flags to control behavior (such as `preemptible` and -`defer`). These are hard to read in the caller (since you only see -'true' or 'false'), and ugly when many are added together. In -preparation for adding yet another flag in a future patch, collapse -all of these into a single `flag` variable. - -NB that this does mean checking for what was previously the '-1' -condition a bit more ugly in the put_page_from_lNe functions (since -you have to check for both partial_set and general ref); but this -clause will go away in a future patch. - -Also note that the original comment had an off-by-one error: -partial_flags (like partial_pte before it) concerns -plNe[nr_validated_ptes], not plNe[nr_validated_ptes+1]. - -No functional change intended. - -This is part of XSA-299. - -Reported-by: George Dunlap -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ---- - xen/arch/x86/mm.c | 165 ++++++++++++++++++++++++--------------- - xen/include/asm-x86/mm.h | 41 +++++++--- - 2 files changed, 128 insertions(+), 78 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index a1b55c10ff..3f6f8cc9b8 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1094,20 +1094,35 @@ get_page_from_l1e( - } - - #ifdef CONFIG_PV -+ -+/* -+ * The following flags are used to specify behavior of various get and -+ * put commands. The first two are also stored in page->partial_flags -+ * to indicate the state of the page pointed to by -+ * page->pte[page->nr_validated_entries]. See the comment in mm.h for -+ * more information. -+ */ -+#define PTF_partial_set (1 << 0) -+#define PTF_partial_general_ref (1 << 1) -+#define PTF_preemptible (1 << 2) -+#define PTF_defer (1 << 3) -+ - static int get_page_and_type_from_mfn( - mfn_t mfn, unsigned long type, struct domain *d, -- int partial, int preemptible) -+ unsigned int flags) - { - struct page_info *page = mfn_to_page(mfn); - int rc; -+ bool preemptible = flags & PTF_preemptible, -+ partial_ref = flags & PTF_partial_general_ref; - -- if ( likely(partial >= 0) && -+ if ( likely(!partial_ref) && - unlikely(!get_page_from_mfn(mfn, d)) ) - return -EINVAL; - - rc = _get_page_type(page, type, preemptible); - -- if ( unlikely(rc) && partial >= 0 && -+ if ( unlikely(rc) && !partial_ref && - (!preemptible || page != current->arch.old_guest_table) ) - put_page(page); - -@@ -1117,7 +1132,7 @@ static int get_page_and_type_from_mfn( - define_get_linear_pagetable(l2); - static int - get_page_from_l2e( -- l2_pgentry_t l2e, unsigned long pfn, struct domain *d, int partial) -+ l2_pgentry_t l2e, unsigned long pfn, struct domain *d, unsigned int flags) - { - unsigned long mfn = l2e_get_pfn(l2e); - int rc; -@@ -1129,8 +1144,9 @@ get_page_from_l2e( - return -EINVAL; - } - -- rc = get_page_and_type_from_mfn(_mfn(mfn), PGT_l1_page_table, d, -- partial, false); -+ ASSERT(!(flags & PTF_preemptible)); -+ -+ rc = get_page_and_type_from_mfn(_mfn(mfn), PGT_l1_page_table, d, flags); - if ( unlikely(rc == -EINVAL) && get_l2_linear_pagetable(l2e, pfn, d) ) - rc = 0; - -@@ -1140,7 +1156,7 @@ get_page_from_l2e( - define_get_linear_pagetable(l3); - static int - get_page_from_l3e( -- l3_pgentry_t l3e, unsigned long pfn, struct domain *d, int partial) -+ l3_pgentry_t l3e, unsigned long pfn, struct domain *d, unsigned int flags) - { - int rc; - -@@ -1152,7 +1168,7 @@ get_page_from_l3e( - } - - rc = get_page_and_type_from_mfn( -- l3e_get_mfn(l3e), PGT_l2_page_table, d, partial, 1); -+ l3e_get_mfn(l3e), PGT_l2_page_table, d, flags | PTF_preemptible); - if ( unlikely(rc == -EINVAL) && - !is_pv_32bit_domain(d) && - get_l3_linear_pagetable(l3e, pfn, d) ) -@@ -1164,7 +1180,7 @@ get_page_from_l3e( - define_get_linear_pagetable(l4); - static int - get_page_from_l4e( -- l4_pgentry_t l4e, unsigned long pfn, struct domain *d, int partial) -+ l4_pgentry_t l4e, unsigned long pfn, struct domain *d, unsigned int flags) - { - int rc; - -@@ -1176,7 +1192,7 @@ get_page_from_l4e( - } - - rc = get_page_and_type_from_mfn( -- l4e_get_mfn(l4e), PGT_l3_page_table, d, partial, 1); -+ l4e_get_mfn(l4e), PGT_l3_page_table, d, flags | PTF_preemptible); - if ( unlikely(rc == -EINVAL) && get_l4_linear_pagetable(l4e, pfn, d) ) - rc = 0; - -@@ -1277,7 +1293,7 @@ static void put_data_page(struct page_info *page, bool writeable) - * Note also that this automatically deals correctly with linear p.t.'s. - */ - static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, -- int partial, bool defer) -+ unsigned int flags) - { - int rc = 0; - -@@ -1300,12 +1316,13 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, - struct page_info *pg = l2e_get_page(l2e); - struct page_info *ptpg = mfn_to_page(_mfn(pfn)); - -- if ( unlikely(partial > 0) ) -+ if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == -+ PTF_partial_set ) - { -- ASSERT(!defer); -+ ASSERT(!(flags & PTF_defer)); - rc = _put_page_type(pg, true, ptpg); - } -- else if ( defer ) -+ else if ( flags & PTF_defer ) - { - current->arch.old_guest_ptpg = ptpg; - current->arch.old_guest_table = pg; -@@ -1322,7 +1339,7 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, - } - - static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, -- int partial, bool defer) -+ unsigned int flags) - { - struct page_info *pg; - int rc; -@@ -1345,13 +1362,14 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, - - pg = l3e_get_page(l3e); - -- if ( unlikely(partial > 0) ) -+ if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == -+ PTF_partial_set ) - { -- ASSERT(!defer); -+ ASSERT(!(flags & PTF_defer)); - return _put_page_type(pg, true, mfn_to_page(_mfn(pfn))); - } - -- if ( defer ) -+ if ( flags & PTF_defer ) - { - current->arch.old_guest_ptpg = mfn_to_page(_mfn(pfn)); - current->arch.old_guest_table = pg; -@@ -1366,7 +1384,7 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, - } - - static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, -- int partial, bool defer) -+ unsigned int flags) - { - int rc = 1; - -@@ -1375,13 +1393,14 @@ static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, - { - struct page_info *pg = l4e_get_page(l4e); - -- if ( unlikely(partial > 0) ) -+ if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == -+ PTF_partial_set ) - { -- ASSERT(!defer); -+ ASSERT(!(flags & PTF_defer)); - return _put_page_type(pg, true, mfn_to_page(_mfn(pfn))); - } - -- if ( defer ) -+ if ( flags & PTF_defer ) - { - current->arch.old_guest_ptpg = mfn_to_page(_mfn(pfn)); - current->arch.old_guest_table = pg; -@@ -1492,12 +1511,13 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) - unsigned long pfn = mfn_x(page_to_mfn(page)); - l2_pgentry_t *pl2e; - unsigned int i; -- int rc = 0, partial = page->partial_pte; -+ int rc = 0; -+ unsigned int partial_flags = page->partial_flags; - - pl2e = map_domain_page(_mfn(pfn)); - - for ( i = page->nr_validated_ptes; i < L2_PAGETABLE_ENTRIES; -- i++, partial = 0 ) -+ i++, partial_flags = 0 ) - { - l2_pgentry_t l2e; - -@@ -1520,17 +1540,18 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) - rc = -EINTR; - } - else -- rc = get_page_from_l2e(l2e, pfn, d, partial); -+ rc = get_page_from_l2e(l2e, pfn, d, partial_flags); - - if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- page->partial_pte = partial ?: 1; -+ /* Set 'set', retain 'general ref' */ -+ page->partial_flags = partial_flags | PTF_partial_set; - } - else if ( rc == -EINTR && i ) - { - page->nr_validated_ptes = i; -- page->partial_pte = 0; -+ page->partial_flags = 0; - rc = -ERESTART; - } - else if ( rc < 0 && rc != -EINTR ) -@@ -1539,7 +1560,7 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) - if ( i ) - { - page->nr_validated_ptes = i; -- page->partial_pte = 0; -+ page->partial_flags = 0; - current->arch.old_guest_ptpg = NULL; - current->arch.old_guest_table = page; - } -@@ -1563,7 +1584,8 @@ static int alloc_l3_table(struct page_info *page) - unsigned long pfn = mfn_x(page_to_mfn(page)); - l3_pgentry_t *pl3e; - unsigned int i; -- int rc = 0, partial = page->partial_pte; -+ int rc = 0; -+ unsigned int partial_flags = page->partial_flags; - - pl3e = map_domain_page(_mfn(pfn)); - -@@ -1578,7 +1600,7 @@ static int alloc_l3_table(struct page_info *page) - memset(pl3e + 4, 0, (L3_PAGETABLE_ENTRIES - 4) * sizeof(*pl3e)); - - for ( i = page->nr_validated_ptes; i < L3_PAGETABLE_ENTRIES; -- i++, partial = 0 ) -+ i++, partial_flags = 0 ) - { - l3_pgentry_t l3e = pl3e[i]; - -@@ -1597,7 +1619,8 @@ static int alloc_l3_table(struct page_info *page) - else - rc = get_page_and_type_from_mfn( - l3e_get_mfn(l3e), -- PGT_l2_page_table | PGT_pae_xen_l2, d, partial, 1); -+ PGT_l2_page_table | PGT_pae_xen_l2, d, -+ partial_flags | PTF_preemptible); - } - else if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) ) - { -@@ -1606,17 +1629,18 @@ static int alloc_l3_table(struct page_info *page) - rc = -EINTR; - } - else -- rc = get_page_from_l3e(l3e, pfn, d, partial); -+ rc = get_page_from_l3e(l3e, pfn, d, partial_flags); - - if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- page->partial_pte = partial ?: 1; -+ /* Set 'set', leave 'general ref' set if this entry was set */ -+ page->partial_flags = partial_flags | PTF_partial_set; - } - else if ( rc == -EINTR && i ) - { - page->nr_validated_ptes = i; -- page->partial_pte = 0; -+ page->partial_flags = 0; - rc = -ERESTART; - } - if ( rc < 0 ) -@@ -1633,7 +1657,7 @@ static int alloc_l3_table(struct page_info *page) - if ( i ) - { - page->nr_validated_ptes = i; -- page->partial_pte = 0; -+ page->partial_flags = 0; - current->arch.old_guest_ptpg = NULL; - current->arch.old_guest_table = page; - } -@@ -1767,10 +1791,11 @@ static int alloc_l4_table(struct page_info *page) - unsigned long pfn = mfn_x(page_to_mfn(page)); - l4_pgentry_t *pl4e = map_domain_page(_mfn(pfn)); - unsigned int i; -- int rc = 0, partial = page->partial_pte; -+ int rc = 0; -+ unsigned int partial_flags = page->partial_flags; - - for ( i = page->nr_validated_ptes; i < L4_PAGETABLE_ENTRIES; -- i++, partial = 0 ) -+ i++, partial_flags = 0 ) - { - l4_pgentry_t l4e; - -@@ -1786,12 +1811,13 @@ static int alloc_l4_table(struct page_info *page) - rc = -EINTR; - } - else -- rc = get_page_from_l4e(l4e, pfn, d, partial); -+ rc = get_page_from_l4e(l4e, pfn, d, partial_flags); - - if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- page->partial_pte = partial ?: 1; -+ /* Set 'set', leave 'general ref' set if this entry was set */ -+ page->partial_flags = partial_flags | PTF_partial_set; - } - else if ( rc < 0 ) - { -@@ -1801,7 +1827,7 @@ static int alloc_l4_table(struct page_info *page) - if ( i ) - { - page->nr_validated_ptes = i; -- page->partial_pte = 0; -+ page->partial_flags = 0; - if ( rc == -EINTR ) - rc = -ERESTART; - else -@@ -1853,19 +1879,20 @@ static int free_l2_table(struct page_info *page) - struct domain *d = page_get_owner(page); - unsigned long pfn = mfn_x(page_to_mfn(page)); - l2_pgentry_t *pl2e; -- int rc = 0, partial = page->partial_pte; -- unsigned int i = page->nr_validated_ptes - !partial; -+ int rc = 0; -+ unsigned int partial_flags = page->partial_flags, -+ i = page->nr_validated_ptes - !(partial_flags & PTF_partial_set); - - pl2e = map_domain_page(_mfn(pfn)); - - for ( ; ; ) - { - if ( is_guest_l2_slot(d, page->u.inuse.type_info, i) ) -- rc = put_page_from_l2e(pl2e[i], pfn, partial, false); -+ rc = put_page_from_l2e(pl2e[i], pfn, partial_flags); - if ( rc < 0 ) - break; - -- partial = 0; -+ partial_flags = 0; - - if ( !i-- ) - break; -@@ -1887,12 +1914,14 @@ static int free_l2_table(struct page_info *page) - else if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- page->partial_pte = partial ?: -1; -+ page->partial_flags = (partial_flags & PTF_partial_set) ? -+ partial_flags : -+ (PTF_partial_set | PTF_partial_general_ref); - } - else if ( rc == -EINTR && i < L2_PAGETABLE_ENTRIES - 1 ) - { - page->nr_validated_ptes = i + 1; -- page->partial_pte = 0; -+ page->partial_flags = 0; - rc = -ERESTART; - } - -@@ -1904,18 +1933,19 @@ static int free_l3_table(struct page_info *page) - struct domain *d = page_get_owner(page); - unsigned long pfn = mfn_x(page_to_mfn(page)); - l3_pgentry_t *pl3e; -- int rc = 0, partial = page->partial_pte; -- unsigned int i = page->nr_validated_ptes - !partial; -+ int rc = 0; -+ unsigned int partial_flags = page->partial_flags, -+ i = page->nr_validated_ptes - !(partial_flags & PTF_partial_set); - - pl3e = map_domain_page(_mfn(pfn)); - - for ( ; ; ) - { -- rc = put_page_from_l3e(pl3e[i], pfn, partial, 0); -+ rc = put_page_from_l3e(pl3e[i], pfn, partial_flags); - if ( rc < 0 ) - break; - -- partial = 0; -+ partial_flags = 0; - if ( rc == 0 ) - pl3e[i] = unadjust_guest_l3e(pl3e[i], d); - -@@ -1934,12 +1964,14 @@ static int free_l3_table(struct page_info *page) - if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- page->partial_pte = partial ?: -1; -+ page->partial_flags = (partial_flags & PTF_partial_set) ? -+ partial_flags : -+ (PTF_partial_set | PTF_partial_general_ref); - } - else if ( rc == -EINTR && i < L3_PAGETABLE_ENTRIES - 1 ) - { - page->nr_validated_ptes = i + 1; -- page->partial_pte = 0; -+ page->partial_flags = 0; - rc = -ERESTART; - } - return rc > 0 ? 0 : rc; -@@ -1950,26 +1982,29 @@ static int free_l4_table(struct page_info *page) - struct domain *d = page_get_owner(page); - unsigned long pfn = mfn_x(page_to_mfn(page)); - l4_pgentry_t *pl4e = map_domain_page(_mfn(pfn)); -- int rc = 0, partial = page->partial_pte; -- unsigned int i = page->nr_validated_ptes - !partial; -+ int rc = 0; -+ unsigned partial_flags = page->partial_flags, -+ i = page->nr_validated_ptes - !(partial_flags & PTF_partial_set); - - do { - if ( is_guest_l4_slot(d, i) ) -- rc = put_page_from_l4e(pl4e[i], pfn, partial, 0); -+ rc = put_page_from_l4e(pl4e[i], pfn, partial_flags); - if ( rc < 0 ) - break; -- partial = 0; -+ partial_flags = 0; - } while ( i-- ); - - if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- page->partial_pte = partial ?: -1; -+ page->partial_flags = (partial_flags & PTF_partial_set) ? -+ partial_flags : -+ (PTF_partial_set | PTF_partial_general_ref); - } - else if ( rc == -EINTR && i < L4_PAGETABLE_ENTRIES - 1 ) - { - page->nr_validated_ptes = i + 1; -- page->partial_pte = 0; -+ page->partial_flags = 0; - rc = -ERESTART; - } - -@@ -2247,7 +2282,7 @@ static int mod_l2_entry(l2_pgentry_t *pl2e, - return -EBUSY; - } - -- put_page_from_l2e(ol2e, pfn, 0, true); -+ put_page_from_l2e(ol2e, pfn, PTF_defer); - - return rc; - } -@@ -2315,7 +2350,7 @@ static int mod_l3_entry(l3_pgentry_t *pl3e, - if ( !create_pae_xen_mappings(d, pl3e) ) - BUG(); - -- put_page_from_l3e(ol3e, pfn, 0, 1); -+ put_page_from_l3e(ol3e, pfn, PTF_defer); - return rc; - } - -@@ -2378,7 +2413,7 @@ static int mod_l4_entry(l4_pgentry_t *pl4e, - return -EFAULT; - } - -- put_page_from_l4e(ol4e, pfn, 0, 1); -+ put_page_from_l4e(ol4e, pfn, PTF_defer); - return rc; - } - #endif /* CONFIG_PV */ -@@ -2649,7 +2684,7 @@ int free_page_type(struct page_info *page, unsigned long type, - if ( !(type & PGT_partial) ) - { - page->nr_validated_ptes = 1U << PAGETABLE_ORDER; -- page->partial_pte = 0; -+ page->partial_flags = 0; - } - - switch ( type & PGT_type_mask ) -@@ -2946,7 +2981,7 @@ static int _get_page_type(struct page_info *page, unsigned long type, - if ( !(x & PGT_partial) ) - { - page->nr_validated_ptes = 0; -- page->partial_pte = 0; -+ page->partial_flags = 0; - } - page->linear_pt_count = 0; - rc = alloc_page_type(page, type, preemptible); -@@ -3122,7 +3157,7 @@ int new_guest_cr3(mfn_t mfn) - return 0; - } - -- rc = get_page_and_type_from_mfn(mfn, PGT_root_page_table, d, 0, 1); -+ rc = get_page_and_type_from_mfn(mfn, PGT_root_page_table, d, PTF_preemptible); - switch ( rc ) - { - case 0: -@@ -3473,7 +3508,7 @@ long do_mmuext_op( - if ( op.arg1.mfn != 0 ) - { - rc = get_page_and_type_from_mfn( -- _mfn(op.arg1.mfn), PGT_root_page_table, currd, 0, 1); -+ _mfn(op.arg1.mfn), PGT_root_page_table, currd, PTF_preemptible); - - if ( unlikely(rc) ) - { -diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h -index 6faa563167..8406ac3c37 100644 ---- a/xen/include/asm-x86/mm.h -+++ b/xen/include/asm-x86/mm.h -@@ -228,19 +228,34 @@ struct page_info - * setting the flag must not drop that reference, whereas the instance - * clearing it will have to. - * -- * If @partial_pte is positive then PTE at @nr_validated_ptes+1 has -- * been partially validated. This implies that the general reference -- * to the page (acquired from get_page_from_lNe()) would be dropped -- * (again due to the apparent failure) and hence must be re-acquired -- * when resuming the validation, but must not be dropped when picking -- * up the page for invalidation. -+ * If partial_flags & PTF_partial_set is set, then the page at -+ * at @nr_validated_ptes had PGT_partial set as a result of an -+ * operation on the current page. (That page may or may not -+ * still have PGT_partial set.) - * -- * If @partial_pte is negative then PTE at @nr_validated_ptes+1 has -- * been partially invalidated. This is basically the opposite case of -- * above, i.e. the general reference to the page was not dropped in -- * put_page_from_lNe() (due to the apparent failure), and hence it -- * must be dropped when the put operation is resumed (and completes), -- * but it must not be acquired if picking up the page for validation. -+ * If PTF_partial_general_ref is set, then the PTE at -+ * @nr_validated_ptef holds a general reference count for the -+ * page. -+ * -+ * This happens: -+ * - During de-validation, if de-validation of the page was -+ * interrupted -+ * - During validation, if an invalid entry is encountered and -+ * validation is preemptible -+ * - During validation, if PTF_partial_general_ref was set on -+ * this entry to begin with (perhaps because we're picking -+ * up from a partial de-validation). -+ * -+ * When resuming validation, if PTF_partial_general_ref is clear, -+ * then a general reference must be re-acquired; if it is set, no -+ * reference should be acquired. -+ * -+ * When resuming de-validation, if PTF_partial_general_ref is -+ * clear, no reference should be dropped; if it is set, a -+ * reference should be dropped. -+ * -+ * NB that PTF_partial_set and PTF_partial_general_ref are -+ * defined in mm.c, the only place where they are used. - * - * The 3rd field, @linear_pt_count, indicates - * - by a positive value, how many same-level page table entries a page -@@ -251,7 +266,7 @@ struct page_info - struct { - u16 nr_validated_ptes:PAGETABLE_ORDER + 1; - u16 :16 - PAGETABLE_ORDER - 1 - 2; -- s16 partial_pte:2; -+ u16 partial_flags:2; - s16 linear_pt_count; - }; - --- -2.23.0 - diff --git a/xsa299-4.12-0004-x86-mm-Use-flags-for-_put_page_type-rather-than-a-bo.patch b/xsa299-4.12-0004-x86-mm-Use-flags-for-_put_page_type-rather-than-a-bo.patch deleted file mode 100644 index d07c233..0000000 --- a/xsa299-4.12-0004-x86-mm-Use-flags-for-_put_page_type-rather-than-a-bo.patch +++ /dev/null @@ -1,140 +0,0 @@ -From db1d801aa8dcb918a27486a6e8d9cf5d7307dec3 Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Thu, 10 Oct 2019 17:57:49 +0100 -Subject: [PATCH 04/11] x86/mm: Use flags for _put_page_type rather than a - boolean - -This is in mainly in preparation for _put_page_type taking the -partial_flags value in the future. It also makes it easier to read in -the caller (since you see a flag name rather than `true` or `false`). - -No functional change intended. - -This is part of XSA-299. - -Reported-by: George Dunlap -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ---- - xen/arch/x86/mm.c | 25 +++++++++++++------------ - 1 file changed, 13 insertions(+), 12 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 3f6f8cc9b8..0740b61af8 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1200,7 +1200,7 @@ get_page_from_l4e( - } - #endif /* CONFIG_PV */ - --static int _put_page_type(struct page_info *page, bool preemptible, -+static int _put_page_type(struct page_info *page, unsigned int flags, - struct page_info *ptpg); - - void put_page_from_l1e(l1_pgentry_t l1e, struct domain *l1e_owner) -@@ -1320,7 +1320,7 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, - PTF_partial_set ) - { - ASSERT(!(flags & PTF_defer)); -- rc = _put_page_type(pg, true, ptpg); -+ rc = _put_page_type(pg, PTF_preemptible, ptpg); - } - else if ( flags & PTF_defer ) - { -@@ -1329,7 +1329,7 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, - } - else - { -- rc = _put_page_type(pg, true, ptpg); -+ rc = _put_page_type(pg, PTF_preemptible, ptpg); - if ( likely(!rc) ) - put_page(pg); - } -@@ -1366,7 +1366,7 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, - PTF_partial_set ) - { - ASSERT(!(flags & PTF_defer)); -- return _put_page_type(pg, true, mfn_to_page(_mfn(pfn))); -+ return _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); - } - - if ( flags & PTF_defer ) -@@ -1376,7 +1376,7 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, - return 0; - } - -- rc = _put_page_type(pg, true, mfn_to_page(_mfn(pfn))); -+ rc = _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); - if ( likely(!rc) ) - put_page(pg); - -@@ -1397,7 +1397,7 @@ static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, - PTF_partial_set ) - { - ASSERT(!(flags & PTF_defer)); -- return _put_page_type(pg, true, mfn_to_page(_mfn(pfn))); -+ return _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); - } - - if ( flags & PTF_defer ) -@@ -1407,7 +1407,7 @@ static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, - return 0; - } - -- rc = _put_page_type(pg, true, mfn_to_page(_mfn(pfn))); -+ rc = _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); - if ( likely(!rc) ) - put_page(pg); - } -@@ -2757,10 +2757,11 @@ static int _put_final_page_type(struct page_info *page, unsigned long type, - } - - --static int _put_page_type(struct page_info *page, bool preemptible, -+static int _put_page_type(struct page_info *page, unsigned int flags, - struct page_info *ptpg) - { - unsigned long nx, x, y = page->u.inuse.type_info; -+ bool preemptible = flags & PTF_preemptible; - - ASSERT(current_locked_page_ne_check(page)); - -@@ -2969,7 +2970,7 @@ static int _get_page_type(struct page_info *page, unsigned long type, - - if ( unlikely(iommu_ret) ) - { -- _put_page_type(page, false, NULL); -+ _put_page_type(page, 0, NULL); - rc = iommu_ret; - goto out; - } -@@ -2996,7 +2997,7 @@ static int _get_page_type(struct page_info *page, unsigned long type, - - void put_page_type(struct page_info *page) - { -- int rc = _put_page_type(page, false, NULL); -+ int rc = _put_page_type(page, 0, NULL); - ASSERT(rc == 0); - (void)rc; - } -@@ -3013,7 +3014,7 @@ int get_page_type(struct page_info *page, unsigned long type) - - int put_page_type_preemptible(struct page_info *page) - { -- return _put_page_type(page, true, NULL); -+ return _put_page_type(page, PTF_preemptible, NULL); - } - - int get_page_type_preemptible(struct page_info *page, unsigned long type) -@@ -3030,7 +3031,7 @@ int put_old_guest_table(struct vcpu *v) - if ( !v->arch.old_guest_table ) - return 0; - -- switch ( rc = _put_page_type(v->arch.old_guest_table, true, -+ switch ( rc = _put_page_type(v->arch.old_guest_table, PTF_preemptible, - v->arch.old_guest_ptpg) ) - { - case -EINTR: --- -2.23.0 - diff --git a/xsa299-4.12-0005-x86-mm-Rework-get_page_and_type_from_mfn-conditional.patch b/xsa299-4.12-0005-x86-mm-Rework-get_page_and_type_from_mfn-conditional.patch deleted file mode 100644 index 9cfbb73..0000000 --- a/xsa299-4.12-0005-x86-mm-Rework-get_page_and_type_from_mfn-conditional.patch +++ /dev/null @@ -1,79 +0,0 @@ -From 6f257854c8778774210281c5c21028c4b7739b44 Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Thu, 10 Oct 2019 17:57:49 +0100 -Subject: [PATCH 05/11] x86/mm: Rework get_page_and_type_from_mfn conditional - -Make it easier to read by declaring the conditions in which we will -retain the ref, rather than the conditions under which we release it. - -The only way (page == current->arch.old_guest_table) can be true is if -preemptible is true; so remove this from the query itself, and add an -ASSERT() to that effect on the opposite path. - -No functional change intended. - -NB that alloc_lN_table() mishandle the "linear pt failure" situation -described in the comment; this will be addressed in a future patch. - -This is part of XSA-299. - -Reported-by: George Dunlap -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ---- - xen/arch/x86/mm.c | 39 +++++++++++++++++++++++++++++++++++++-- - 1 file changed, 37 insertions(+), 2 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 0740b61af8..0a4d39a2c3 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1122,8 +1122,43 @@ static int get_page_and_type_from_mfn( - - rc = _get_page_type(page, type, preemptible); - -- if ( unlikely(rc) && !partial_ref && -- (!preemptible || page != current->arch.old_guest_table) ) -+ /* -+ * Retain the refcount if: -+ * - page is fully validated (rc == 0) -+ * - page is not validated (rc < 0) but: -+ * - We came in with a reference (partial_ref) -+ * - page is partially validated but there's been an error -+ * (page == current->arch.old_guest_table) -+ * -+ * The partial_ref-on-error clause is worth an explanation. There -+ * are two scenarios where partial_ref might be true coming in: -+ * - mfn has been partially demoted as type `type`; i.e. has -+ * PGT_partial set -+ * - mfn has been partially demoted as L(type+1) (i.e., a linear -+ * page; e.g. we're being called from get_page_from_l2e with -+ * type == PGT_l1_table, but the mfn is PGT_l2_table) -+ * -+ * If there's an error, in the first case, _get_page_type will -+ * either return -ERESTART, in which case we want to retain the -+ * ref (as the caller will consider it retained), or -EINVAL, in -+ * which case old_guest_table will be set; in both cases, we need -+ * to retain the ref. -+ * -+ * In the second case, if there's an error, _get_page_type() can -+ * *only* return -EINVAL, and *never* set old_guest_table. In -+ * that case we also want to retain the reference, to allow the -+ * page to continue to be torn down (i.e., PGT_partial cleared) -+ * safely. -+ * -+ * Also note that we shouldn't be able to leave with the reference -+ * count retained unless we succeeded, or the operation was -+ * preemptible. -+ */ -+ if ( likely(!rc) || partial_ref ) -+ /* nothing */; -+ else if ( page == current->arch.old_guest_table ) -+ ASSERT(preemptible); -+ else - put_page(page); - - return rc; --- -2.23.0 - diff --git a/xsa299-4.12-0006-x86-mm-Have-alloc_l-23-_table-clear-partial_flags-wh.patch b/xsa299-4.12-0006-x86-mm-Have-alloc_l-23-_table-clear-partial_flags-wh.patch deleted file mode 100644 index 72ee3ea..0000000 --- a/xsa299-4.12-0006-x86-mm-Have-alloc_l-23-_table-clear-partial_flags-wh.patch +++ /dev/null @@ -1,111 +0,0 @@ -From 4ad70553611a7a4e4494d5a3b51b5cc295a488e0 Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Thu, 10 Oct 2019 17:57:49 +0100 -Subject: [PATCH 06/11] x86/mm: Have alloc_l[23]_table clear partial_flags when - preempting - -In order to allow recursive pagetable promotions and demotions to be -interrupted, Xen must keep track of the state of the sub-pages -promoted or demoted. This is stored in two elements in the page -struct: nr_entries_validated and partial_flags. - -The rule is that entries [0, nr_entries_validated) should always be -validated and hold a general reference count. If partial_flags is -zero, then [nr_entries_validated] is not validated and no reference -count is held. If PTF_partial_set is set, then [nr_entries_validated] -is partially validated. - -At the moment, a distinction is made between promotion and demotion -with regard to whether the entry itself "holds" a general reference -count: when entry promotion is interrupted (i.e., returns -ERESTART), -the entry is not considered to hold a reference; when entry demotion -is interrupted, the entry is still considered to hold a general -reference. - -PTF_partial_general_ref is used to distinguish between these cases. -If clear, it's a partial promotion => no general reference count held -by the entry; if set, it's partial demotion, so a general reference -count held. Because promotions and demotions can be interleaved, this -value is passed to get_page_and_type_from_mfn and put_page_from_l*e, -to be able to properly handle reference counts. - -Unfortunately, when alloc_l[23]_table check hypercall_preempt_check() -and return -ERESTART, they set nr_entries_validated, but don't clear -partial_flags. - -If we were picking up from a previously-interrupted promotion, that -means that PTF_partial_set would be set even though -[nr_entries_validated] was not partially validated. This means that -if the page in this state were de-validated, put_page_type() would -erroneously be called on that entry. - -Perhaps worse, if we were racing with a de-validation, then we might -leave both PTF_partial_set and PTF_partial_general_ref; and when -de-validation picked up again, both the type and the general ref would -be erroneously dropped from [nr_entries_validated]. - -In a sense, the real issue here is code duplication. Rather than -duplicate the interruption code, set rc to -EINTR and fall through to -the code which already handles that case correctly. - -Given the logic at this point, it should be impossible for -partial_flags to be non-zero; add an ASSERT() to catch any changes. - -This is part of XSA-299. - -Reported-by: George Dunlap -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ---- - xen/arch/x86/mm.c | 25 ++++++------------------- - 1 file changed, 6 insertions(+), 19 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 0a4d39a2c3..bbd29a68f4 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1554,21 +1554,13 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) - for ( i = page->nr_validated_ptes; i < L2_PAGETABLE_ENTRIES; - i++, partial_flags = 0 ) - { -- l2_pgentry_t l2e; -+ l2_pgentry_t l2e = pl2e[i]; - - if ( i > page->nr_validated_ptes && hypercall_preempt_check() ) -- { -- page->nr_validated_ptes = i; -- rc = -ERESTART; -- break; -- } -- -- if ( !is_guest_l2_slot(d, type, i) ) -+ rc = -EINTR; -+ else if ( !is_guest_l2_slot(d, type, i) ) - continue; -- -- l2e = pl2e[i]; -- -- if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) ) -+ else if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) ) - { - if ( !pv_l1tf_check_l2e(d, l2e) ) - continue; -@@ -1640,13 +1632,8 @@ static int alloc_l3_table(struct page_info *page) - l3_pgentry_t l3e = pl3e[i]; - - if ( i > page->nr_validated_ptes && hypercall_preempt_check() ) -- { -- page->nr_validated_ptes = i; -- rc = -ERESTART; -- break; -- } -- -- if ( is_pv_32bit_domain(d) && (i == 3) ) -+ rc = -EINTR; -+ else if ( is_pv_32bit_domain(d) && (i == 3) ) - { - if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || - (l3e_get_flags(l3e) & l3_disallow_mask(d)) ) --- -2.23.0 - diff --git a/xsa299-4.12-0007-x86-mm-Always-retain-a-general-ref-on-partial.patch b/xsa299-4.12-0007-x86-mm-Always-retain-a-general-ref-on-partial.patch deleted file mode 100644 index ef390e2..0000000 --- a/xsa299-4.12-0007-x86-mm-Always-retain-a-general-ref-on-partial.patch +++ /dev/null @@ -1,378 +0,0 @@ -From 51fe4e67d954649fcf103116be6206a769f0db1e Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Thu, 10 Oct 2019 17:57:49 +0100 -Subject: [PATCH 07/11] x86/mm: Always retain a general ref on partial - -In order to allow recursive pagetable promotions and demotions to be -interrupted, Xen must keep track of the state of the sub-pages -promoted or demoted. This is stored in two elements in the page struct: -nr_entries_validated and partial_flags. - -The rule is that entries [0, nr_entries_validated) should always be -validated and hold a general reference count. If partial_flags is -zero, then [nr_entries_validated] is not validated and no reference -count is held. If PTF_partial_set is set, then [nr_entries_validated] -is partially validated. - -At the moment, a distinction is made between promotion and demotion -with regard to whether the entry itself "holds" a general reference -count: when entry promotion is interrupted (i.e., returns -ERESTART), -the entry is not considered to hold a reference; when entry demotion -is interrupted, the entry is still considered to hold a general -reference. - -PTF_partial_general_ref is used to distinguish between these cases. -If clear, it's a partial promotion => no general reference count held -by the entry; if set, it's partial demotion, so a general reference -count held. Because promotions and demotions can be interleaved, this -value is passed to get_page_and_type_from_mfn and put_page_from_l*e, -to be able to properly handle reference counts. - -Unfortunately, because a refcount is not held, it is possible to -engineer a situation where PFT_partial_set is set but the page in -question has been assigned to another domain. A sketch is provided in -the appendix. - -Fix this by having the parent page table entry hold a general -reference count whenever PFT_partial_set is set. (For clarity of -change, keep two separate flags. These will be collapsed in a -subsequent changeset.) - -This has two basic implications. On the put_page_from_lNe() side, -this mean that the (partial_set && !partial_ref) case can never happen, -and no longer needs to be special-cased. - -Secondly, because both flags are set together, there's no need to carry over -existing bits from partial_pte. - -(NB there is still another issue with calling _put_page_type() on a -page which had PGT_partial set; that will be handled in a subsequent -patch.) - -On the get_page_and_type_from_mfn() side, we need to distinguish -between callers which hold a reference on partial (i.e., -alloc_lN_table()), and those which do not (new_cr3, PIN_LN_TABLE, and -so on): pass a flag if the type should be retained on interruption. - -NB that since l1 promotion can't be preempted, that get_page_from_l2e -can't return -ERESTART. - -This is part of XSA-299. - -Reported-by: George Dunlap -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ------ -* Appendix: Engineering PTF_partial_set while a page belongs to a - foreign domain - -Suppose A is a page which can be promoted to an l3, and B is a page -which can be promoted to an l2, and A[x] points to B. B has -PGC_allocated set but no other general references. - -V1: PIN_L3 A. - A is validated, B is validated. - A.type_count = 1 | PGT_validated | PGT_pinned - B.type_count = 1 | PGT_validated - B.count = 2 | PGC_allocated (A[x] holds a general ref) - -V1: UNPIN A. - A begins de-validation. - Arrange to be interrupted when i < x - V1->old_guest_table = A - V1->old_guest_table_ref_held = false - A.type_count = 1 | PGT_partial - A.nr_validated_entries = i < x - B.type_count = 0 - B.count = 1 | PGC_allocated - -V2: MOD_L4_ENTRY to point some l4e to A. - Picks up re-validation of A. - Arrange to be interrupted halfway through B's validation - B.type_count = 1 | PGT_partial - B.count = 2 | PGC_allocated (PGT_partial holds a general ref) - A.type_count = 1 | PGT_partial - A.nr_validated_entries = x - A.partial_pte = PTF_partial_set - -V3: MOD_L3_ENTRY to point some other l3e (not in A) to B. - Validates B. - B.type_count = 1 | PGT_validated - B.count = 2 | PGC_allocated ("other l3e" holds a general ref) - -V3: MOD_L3_ENTRY to clear l3e pointing to B. - Devalidates B. - B.type_count = 0 - B.count = 1 | PGC_allocated - -V3: decrease_reservation(B) - Clears PGC_allocated - B.count = 0 => B is freed - -B gets assigned to a different domain - -V1: Restarts UNPIN of A - put_old_guest_table(A) - ... - free_l3_table(A) - -Now since A.partial_flags has PTF_partial_set, free_l3_table() will -call put_page_from_l3e() on A[x], which points to B, while B is owned -by another domain. - -If A[x] held a general refcount for B on partial validation, as it does -for partial de-validation, then B would still have a reference count of -1 after PGC_allocated was freed; so B wouldn't be freed until after -put_page_from_l3e() had happend on A[x]. ---- - xen/arch/x86/mm.c | 84 +++++++++++++++++++++++----------------- - xen/include/asm-x86/mm.h | 15 ++++--- - 2 files changed, 58 insertions(+), 41 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index bbd29a68f4..4d3ebf341d 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1102,10 +1102,11 @@ get_page_from_l1e( - * page->pte[page->nr_validated_entries]. See the comment in mm.h for - * more information. - */ --#define PTF_partial_set (1 << 0) --#define PTF_partial_general_ref (1 << 1) --#define PTF_preemptible (1 << 2) --#define PTF_defer (1 << 3) -+#define PTF_partial_set (1 << 0) -+#define PTF_partial_general_ref (1 << 1) -+#define PTF_preemptible (1 << 2) -+#define PTF_defer (1 << 3) -+#define PTF_retain_ref_on_restart (1 << 4) - - static int get_page_and_type_from_mfn( - mfn_t mfn, unsigned long type, struct domain *d, -@@ -1114,7 +1115,11 @@ static int get_page_and_type_from_mfn( - struct page_info *page = mfn_to_page(mfn); - int rc; - bool preemptible = flags & PTF_preemptible, -- partial_ref = flags & PTF_partial_general_ref; -+ partial_ref = flags & PTF_partial_general_ref, -+ partial_set = flags & PTF_partial_set, -+ retain_ref = flags & PTF_retain_ref_on_restart; -+ -+ ASSERT(partial_ref == partial_set); - - if ( likely(!partial_ref) && - unlikely(!get_page_from_mfn(mfn, d)) ) -@@ -1127,13 +1132,15 @@ static int get_page_and_type_from_mfn( - * - page is fully validated (rc == 0) - * - page is not validated (rc < 0) but: - * - We came in with a reference (partial_ref) -+ * - page is partially validated (rc == -ERESTART), and the -+ * caller has asked the ref to be retained in that case - * - page is partially validated but there's been an error - * (page == current->arch.old_guest_table) - * - * The partial_ref-on-error clause is worth an explanation. There - * are two scenarios where partial_ref might be true coming in: -- * - mfn has been partially demoted as type `type`; i.e. has -- * PGT_partial set -+ * - mfn has been partially promoted / demoted as type `type`; -+ * i.e. has PGT_partial set - * - mfn has been partially demoted as L(type+1) (i.e., a linear - * page; e.g. we're being called from get_page_from_l2e with - * type == PGT_l1_table, but the mfn is PGT_l2_table) -@@ -1156,7 +1163,8 @@ static int get_page_and_type_from_mfn( - */ - if ( likely(!rc) || partial_ref ) - /* nothing */; -- else if ( page == current->arch.old_guest_table ) -+ else if ( page == current->arch.old_guest_table || -+ (retain_ref && rc == -ERESTART) ) - ASSERT(preemptible); - else - put_page(page); -@@ -1354,8 +1362,8 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, - if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == - PTF_partial_set ) - { -- ASSERT(!(flags & PTF_defer)); -- rc = _put_page_type(pg, PTF_preemptible, ptpg); -+ /* partial_set should always imply partial_ref */ -+ BUG(); - } - else if ( flags & PTF_defer ) - { -@@ -1400,8 +1408,8 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, - if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == - PTF_partial_set ) - { -- ASSERT(!(flags & PTF_defer)); -- return _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); -+ /* partial_set should always imply partial_ref */ -+ BUG(); - } - - if ( flags & PTF_defer ) -@@ -1431,8 +1439,8 @@ static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, - if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == - PTF_partial_set ) - { -- ASSERT(!(flags & PTF_defer)); -- return _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); -+ /* partial_set should always imply partial_ref */ -+ BUG(); - } - - if ( flags & PTF_defer ) -@@ -1569,13 +1577,22 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) - else - rc = get_page_from_l2e(l2e, pfn, d, partial_flags); - -- if ( rc == -ERESTART ) -- { -- page->nr_validated_ptes = i; -- /* Set 'set', retain 'general ref' */ -- page->partial_flags = partial_flags | PTF_partial_set; -- } -- else if ( rc == -EINTR && i ) -+ /* -+ * It shouldn't be possible for get_page_from_l2e to return -+ * -ERESTART, since we never call this with PTF_preemptible. -+ * (alloc_l1_table may return -EINTR on an L1TF-vulnerable -+ * entry.) -+ * -+ * NB that while on a "clean" promotion, we can never get -+ * PGT_partial. It is possible to arrange for an l2e to -+ * contain a partially-devalidated l2; but in that case, both -+ * of the following functions will fail anyway (the first -+ * because the page in question is not an l1; the second -+ * because the page is not fully validated). -+ */ -+ ASSERT(rc != -ERESTART); -+ -+ if ( rc == -EINTR && i ) - { - page->nr_validated_ptes = i; - page->partial_flags = 0; -@@ -1584,6 +1601,7 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) - else if ( rc < 0 && rc != -EINTR ) - { - gdprintk(XENLOG_WARNING, "Failure in alloc_l2_table: slot %#x\n", i); -+ ASSERT(current->arch.old_guest_table == NULL); - if ( i ) - { - page->nr_validated_ptes = i; -@@ -1642,7 +1660,7 @@ static int alloc_l3_table(struct page_info *page) - rc = get_page_and_type_from_mfn( - l3e_get_mfn(l3e), - PGT_l2_page_table | PGT_pae_xen_l2, d, -- partial_flags | PTF_preemptible); -+ partial_flags | PTF_preemptible | PTF_retain_ref_on_restart); - } - else if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) ) - { -@@ -1651,13 +1669,14 @@ static int alloc_l3_table(struct page_info *page) - rc = -EINTR; - } - else -- rc = get_page_from_l3e(l3e, pfn, d, partial_flags); -+ rc = get_page_from_l3e(l3e, pfn, d, -+ partial_flags | PTF_retain_ref_on_restart); - - if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; - /* Set 'set', leave 'general ref' set if this entry was set */ -- page->partial_flags = partial_flags | PTF_partial_set; -+ page->partial_flags = PTF_partial_set | PTF_partial_general_ref; - } - else if ( rc == -EINTR && i ) - { -@@ -1833,13 +1852,14 @@ static int alloc_l4_table(struct page_info *page) - rc = -EINTR; - } - else -- rc = get_page_from_l4e(l4e, pfn, d, partial_flags); -+ rc = get_page_from_l4e(l4e, pfn, d, -+ partial_flags | PTF_retain_ref_on_restart); - - if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; - /* Set 'set', leave 'general ref' set if this entry was set */ -- page->partial_flags = partial_flags | PTF_partial_set; -+ page->partial_flags = PTF_partial_set | PTF_partial_general_ref; - } - else if ( rc < 0 ) - { -@@ -1936,9 +1956,7 @@ static int free_l2_table(struct page_info *page) - else if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- page->partial_flags = (partial_flags & PTF_partial_set) ? -- partial_flags : -- (PTF_partial_set | PTF_partial_general_ref); -+ page->partial_flags = PTF_partial_set | PTF_partial_general_ref; - } - else if ( rc == -EINTR && i < L2_PAGETABLE_ENTRIES - 1 ) - { -@@ -1986,9 +2004,7 @@ static int free_l3_table(struct page_info *page) - if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- page->partial_flags = (partial_flags & PTF_partial_set) ? -- partial_flags : -- (PTF_partial_set | PTF_partial_general_ref); -+ page->partial_flags = PTF_partial_set | PTF_partial_general_ref; - } - else if ( rc == -EINTR && i < L3_PAGETABLE_ENTRIES - 1 ) - { -@@ -2019,9 +2035,7 @@ static int free_l4_table(struct page_info *page) - if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- page->partial_flags = (partial_flags & PTF_partial_set) ? -- partial_flags : -- (PTF_partial_set | PTF_partial_general_ref); -+ page->partial_flags = PTF_partial_set | PTF_partial_general_ref; - } - else if ( rc == -EINTR && i < L4_PAGETABLE_ENTRIES - 1 ) - { -diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h -index 8406ac3c37..02079e1324 100644 ---- a/xen/include/asm-x86/mm.h -+++ b/xen/include/asm-x86/mm.h -@@ -238,22 +238,25 @@ struct page_info - * page. - * - * This happens: -- * - During de-validation, if de-validation of the page was -+ * - During validation or de-validation, if the operation was - * interrupted - * - During validation, if an invalid entry is encountered and - * validation is preemptible - * - During validation, if PTF_partial_general_ref was set on -- * this entry to begin with (perhaps because we're picking -- * up from a partial de-validation). -+ * this entry to begin with (perhaps because it picked up a -+ * previous operation) - * -- * When resuming validation, if PTF_partial_general_ref is clear, -- * then a general reference must be re-acquired; if it is set, no -- * reference should be acquired. -+ * When resuming validation, if PTF_partial_general_ref is -+ * clear, then a general reference must be re-acquired; if it -+ * is set, no reference should be acquired. - * - * When resuming de-validation, if PTF_partial_general_ref is - * clear, no reference should be dropped; if it is set, a - * reference should be dropped. - * -+ * NB at the moment, PTF_partial_set should be set if and only if -+ * PTF_partial_general_ref is set. -+ * - * NB that PTF_partial_set and PTF_partial_general_ref are - * defined in mm.c, the only place where they are used. - * --- -2.23.0 - diff --git a/xsa299-4.12-0008-x86-mm-Collapse-PTF_partial_set-and-PTF_partial_gene.patch b/xsa299-4.12-0008-x86-mm-Collapse-PTF_partial_set-and-PTF_partial_gene.patch deleted file mode 100644 index 6cf41d1..0000000 --- a/xsa299-4.12-0008-x86-mm-Collapse-PTF_partial_set-and-PTF_partial_gene.patch +++ /dev/null @@ -1,227 +0,0 @@ -From 8a8d836f7f7418e659d37817a66cd7a6b115042b Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Thu, 10 Oct 2019 17:57:49 +0100 -Subject: [PATCH 08/11] x86/mm: Collapse PTF_partial_set and - PTF_partial_general_ref into one - -...now that they are equivalent. No functional change intended. - -Reported-by: George Dunlap -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ---- - xen/arch/x86/mm.c | 50 +++++++++++----------------------------- - xen/include/asm-x86/mm.h | 29 +++++++++++------------ - 2 files changed, 26 insertions(+), 53 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 4d3ebf341d..886e93b8aa 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1097,13 +1097,12 @@ get_page_from_l1e( - - /* - * The following flags are used to specify behavior of various get and -- * put commands. The first two are also stored in page->partial_flags -- * to indicate the state of the page pointed to by -+ * put commands. The first is also stored in page->partial_flags to -+ * indicate the state of the page pointed to by - * page->pte[page->nr_validated_entries]. See the comment in mm.h for - * more information. - */ - #define PTF_partial_set (1 << 0) --#define PTF_partial_general_ref (1 << 1) - #define PTF_preemptible (1 << 2) - #define PTF_defer (1 << 3) - #define PTF_retain_ref_on_restart (1 << 4) -@@ -1115,13 +1114,10 @@ static int get_page_and_type_from_mfn( - struct page_info *page = mfn_to_page(mfn); - int rc; - bool preemptible = flags & PTF_preemptible, -- partial_ref = flags & PTF_partial_general_ref, - partial_set = flags & PTF_partial_set, - retain_ref = flags & PTF_retain_ref_on_restart; - -- ASSERT(partial_ref == partial_set); -- -- if ( likely(!partial_ref) && -+ if ( likely(!partial_set) && - unlikely(!get_page_from_mfn(mfn, d)) ) - return -EINVAL; - -@@ -1131,14 +1127,14 @@ static int get_page_and_type_from_mfn( - * Retain the refcount if: - * - page is fully validated (rc == 0) - * - page is not validated (rc < 0) but: -- * - We came in with a reference (partial_ref) -+ * - We came in with a reference (partial_set) - * - page is partially validated (rc == -ERESTART), and the - * caller has asked the ref to be retained in that case - * - page is partially validated but there's been an error - * (page == current->arch.old_guest_table) - * -- * The partial_ref-on-error clause is worth an explanation. There -- * are two scenarios where partial_ref might be true coming in: -+ * The partial_set-on-error clause is worth an explanation. There -+ * are two scenarios where partial_set might be true coming in: - * - mfn has been partially promoted / demoted as type `type`; - * i.e. has PGT_partial set - * - mfn has been partially demoted as L(type+1) (i.e., a linear -@@ -1161,7 +1157,7 @@ static int get_page_and_type_from_mfn( - * count retained unless we succeeded, or the operation was - * preemptible. - */ -- if ( likely(!rc) || partial_ref ) -+ if ( likely(!rc) || partial_set ) - /* nothing */; - else if ( page == current->arch.old_guest_table || - (retain_ref && rc == -ERESTART) ) -@@ -1359,13 +1355,7 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, - struct page_info *pg = l2e_get_page(l2e); - struct page_info *ptpg = mfn_to_page(_mfn(pfn)); - -- if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == -- PTF_partial_set ) -- { -- /* partial_set should always imply partial_ref */ -- BUG(); -- } -- else if ( flags & PTF_defer ) -+ if ( flags & PTF_defer ) - { - current->arch.old_guest_ptpg = ptpg; - current->arch.old_guest_table = pg; -@@ -1405,13 +1395,6 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, - - pg = l3e_get_page(l3e); - -- if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == -- PTF_partial_set ) -- { -- /* partial_set should always imply partial_ref */ -- BUG(); -- } -- - if ( flags & PTF_defer ) - { - current->arch.old_guest_ptpg = mfn_to_page(_mfn(pfn)); -@@ -1436,13 +1419,6 @@ static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, - { - struct page_info *pg = l4e_get_page(l4e); - -- if ( (flags & (PTF_partial_set | PTF_partial_general_ref)) == -- PTF_partial_set ) -- { -- /* partial_set should always imply partial_ref */ -- BUG(); -- } -- - if ( flags & PTF_defer ) - { - current->arch.old_guest_ptpg = mfn_to_page(_mfn(pfn)); -@@ -1676,7 +1652,7 @@ static int alloc_l3_table(struct page_info *page) - { - page->nr_validated_ptes = i; - /* Set 'set', leave 'general ref' set if this entry was set */ -- page->partial_flags = PTF_partial_set | PTF_partial_general_ref; -+ page->partial_flags = PTF_partial_set; - } - else if ( rc == -EINTR && i ) - { -@@ -1859,7 +1835,7 @@ static int alloc_l4_table(struct page_info *page) - { - page->nr_validated_ptes = i; - /* Set 'set', leave 'general ref' set if this entry was set */ -- page->partial_flags = PTF_partial_set | PTF_partial_general_ref; -+ page->partial_flags = PTF_partial_set; - } - else if ( rc < 0 ) - { -@@ -1956,7 +1932,7 @@ static int free_l2_table(struct page_info *page) - else if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- page->partial_flags = PTF_partial_set | PTF_partial_general_ref; -+ page->partial_flags = PTF_partial_set; - } - else if ( rc == -EINTR && i < L2_PAGETABLE_ENTRIES - 1 ) - { -@@ -2004,7 +1980,7 @@ static int free_l3_table(struct page_info *page) - if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- page->partial_flags = PTF_partial_set | PTF_partial_general_ref; -+ page->partial_flags = PTF_partial_set; - } - else if ( rc == -EINTR && i < L3_PAGETABLE_ENTRIES - 1 ) - { -@@ -2035,7 +2011,7 @@ static int free_l4_table(struct page_info *page) - if ( rc == -ERESTART ) - { - page->nr_validated_ptes = i; -- page->partial_flags = PTF_partial_set | PTF_partial_general_ref; -+ page->partial_flags = PTF_partial_set; - } - else if ( rc == -EINTR && i < L4_PAGETABLE_ENTRIES - 1 ) - { -diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h -index 02079e1324..f0fd35bf6b 100644 ---- a/xen/include/asm-x86/mm.h -+++ b/xen/include/asm-x86/mm.h -@@ -233,7 +233,7 @@ struct page_info - * operation on the current page. (That page may or may not - * still have PGT_partial set.) - * -- * If PTF_partial_general_ref is set, then the PTE at -+ * Additionally, if PTF_partial_set is set, then the PTE at - * @nr_validated_ptef holds a general reference count for the - * page. - * -@@ -242,23 +242,20 @@ struct page_info - * interrupted - * - During validation, if an invalid entry is encountered and - * validation is preemptible -- * - During validation, if PTF_partial_general_ref was set on -- * this entry to begin with (perhaps because it picked up a -+ * - During validation, if PTF_partial_set was set on this -+ * entry to begin with (perhaps because it picked up a - * previous operation) - * -- * When resuming validation, if PTF_partial_general_ref is -- * clear, then a general reference must be re-acquired; if it -- * is set, no reference should be acquired. -+ * When resuming validation, if PTF_partial_set is clear, then -+ * a general reference must be re-acquired; if it is set, no -+ * reference should be acquired. - * -- * When resuming de-validation, if PTF_partial_general_ref is -- * clear, no reference should be dropped; if it is set, a -- * reference should be dropped. -+ * When resuming de-validation, if PTF_partial_set is clear, -+ * no reference should be dropped; if it is set, a reference -+ * should be dropped. - * -- * NB at the moment, PTF_partial_set should be set if and only if -- * PTF_partial_general_ref is set. -- * -- * NB that PTF_partial_set and PTF_partial_general_ref are -- * defined in mm.c, the only place where they are used. -+ * NB that PTF_partial_set is defined in mm.c, the only place -+ * where it is used. - * - * The 3rd field, @linear_pt_count, indicates - * - by a positive value, how many same-level page table entries a page -@@ -268,8 +265,8 @@ struct page_info - */ - struct { - u16 nr_validated_ptes:PAGETABLE_ORDER + 1; -- u16 :16 - PAGETABLE_ORDER - 1 - 2; -- u16 partial_flags:2; -+ u16 :16 - PAGETABLE_ORDER - 1 - 1; -+ u16 partial_flags:1; - s16 linear_pt_count; - }; - --- -2.23.0 - diff --git a/xsa299-4.12-0009-x86-mm-Properly-handle-linear-pagetable-promotion-fa.patch b/xsa299-4.12-0009-x86-mm-Properly-handle-linear-pagetable-promotion-fa.patch deleted file mode 100644 index bbaba79..0000000 --- a/xsa299-4.12-0009-x86-mm-Properly-handle-linear-pagetable-promotion-fa.patch +++ /dev/null @@ -1,106 +0,0 @@ -From da3d1d258e54fe600f7f75287183b74d957ec63b Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Thu, 10 Oct 2019 17:57:49 +0100 -Subject: [PATCH 09/11] x86/mm: Properly handle linear pagetable promotion - failures - -In order to allow recursive pagetable promotions and demotions to be -interrupted, Xen must keep track of the state of the sub-pages -promoted or demoted. This is stored in two elements in the page -struct: nr_entries_validated and partial_flags. - -The rule is that entries [0, nr_entries_validated) should always be -validated and hold a general reference count. If partial_flags is -zero, then [nr_entries_validated] is not validated and no reference -count is held. If PTF_partial_set is set, then [nr_entries_validated] -is partially validated, and a general reference count is held. - -Unfortunately, in cases where an entry began with PTF_partial_set set, -and get_page_from_lNe() returns -EINVAL, the PTF_partial_set bit is -erroneously dropped. (This scenario can be engineered mainly by the -use of interleaving of promoting and demoting a page which has "linear -pagetable" entries; see the appendix for a sketch.) This means that -we will "leak" a general reference count on the page in question, -preventing the page from being freed. - -Fix this by setting page->partial_flags to the partial_flags local -variable. - -This is part of XSA-299. - -Reported-by: George Dunlap -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ------ -Appendix - -Suppose A and B can both be promoted to L2 pages, and A[x] points to B. - -V1: PIN_L2 B. - B.type_count = 1 | PGT_validated - B.count = 2 | PGC_allocated - -V1: MOD_L3_ENTRY pointing something to A. - In the process of validating A[x], grab an extra type / ref on B: - B.type_count = 2 | PGT_validated - B.count = 3 | PGC_allocated - A.type_count = 1 | PGT_validated - A.count = 2 | PGC_allocated - -V1: UNPIN B. - B.type_count = 1 | PGT_validate - B.count = 2 | PGC_allocated - -V1: MOD_L3_ENTRY removing the reference to A. - De-validate A, down to A[x], which points to B. - Drop the final type on B. Arrange to be interrupted. - B.type_count = 1 | PGT_partial - B.count = 2 | PGC_allocated - A.type_count = 1 | PGT_partial - A.nr_validated_entries = x - A.partial_pte = -1 - -V2: MOD_L3_ENTRY adds a reference to A. - -At this point, get_page_from_l2e(A[x]) tries -get_page_and_type_from_mfn(), which fails because it's the wrong type; -and get_l2_linear_pagetable() also fails, because B isn't validated as -an l2 anymore. ---- - xen/arch/x86/mm.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 886e93b8aa..0a094291da 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1581,7 +1581,7 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) - if ( i ) - { - page->nr_validated_ptes = i; -- page->partial_flags = 0; -+ page->partial_flags = partial_flags; - current->arch.old_guest_ptpg = NULL; - current->arch.old_guest_table = page; - } -@@ -1674,7 +1674,7 @@ static int alloc_l3_table(struct page_info *page) - if ( i ) - { - page->nr_validated_ptes = i; -- page->partial_flags = 0; -+ page->partial_flags = partial_flags; - current->arch.old_guest_ptpg = NULL; - current->arch.old_guest_table = page; - } -@@ -1845,7 +1845,7 @@ static int alloc_l4_table(struct page_info *page) - if ( i ) - { - page->nr_validated_ptes = i; -- page->partial_flags = 0; -+ page->partial_flags = partial_flags; - if ( rc == -EINTR ) - rc = -ERESTART; - else --- -2.23.0 - diff --git a/xsa299-4.12-0010-x86-mm-Fix-nested-de-validation-on-error.patch b/xsa299-4.12-0010-x86-mm-Fix-nested-de-validation-on-error.patch deleted file mode 100644 index 7d5f022..0000000 --- a/xsa299-4.12-0010-x86-mm-Fix-nested-de-validation-on-error.patch +++ /dev/null @@ -1,166 +0,0 @@ -From b3e169dc8daeae85b0b51c25fdb142e2e552ec7f Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Thu, 10 Oct 2019 17:57:49 +0100 -Subject: [PATCH 10/11] x86/mm: Fix nested de-validation on error - -If an invalid entry is discovered when validating a page-table tree, -the entire tree which has so far been validated must be de-validated. -Since this may take a long time, alloc_l[2-4]_table() set current -vcpu's old_guest_table immediately; put_old_guest_table() will make -sure that put_page_type() will be called to finish off the -de-validation before any other MMU operations can happen on the vcpu. - -The invariant for partial pages should be: - -* Entries [0, nr_validated_ptes) should be completely validated; - put_page_type() will de-validate these. - -* If [nr_validated_ptes] is partially validated, partial_flags should - set PTF_partiaL_set. put_page_type() will be called on this page to - finish off devalidation, and the appropriate refcount adjustments - will be done. - -alloc_l[2-3]_table() indicates partial validation to its callers by -setting current->old_guest_table. - -Unfortunately, this is mishandled. - -Take the case where validating lNe[x] returns an error. - -First, alloc_l3_table() doesn't check old_guest_table at all; as a -result, partial_flags is not set when it should be. nr_validated_ptes -is set to x; and since PFT_partial_set clear, de-validation resumes at -nr_validated_ptes-1. This means that the l2 page at pl3e[x] will not -have put_page_type() called on it when de-validating the rest of the -l3: it will be stuck in the PGT_partial state until the domain is -destroyed, or until it is re-used as an l2. (Any other page type will -fail.) - -Worse, alloc_l4_table(), rather than setting PTF_partial_set as it -should, sets nr_validated_ptes to x+1. When de-validating, since -partial is 0, this will correctly resume calling put_page_type at [x]; -but, if the put_page_type() is never called, but instead -get_page_type() is called, validation will pick up at [x+1], -neglecting to validate [x]. If the rest of the validation succeeds, -the l4 will be validated even though [x] is invalid. - -Fix this in both cases by setting PTF_partial_set if old_guest_table -is set. - -While here, add some safety catches: -- old_guest_table must point to the page contained in - [nr_validated_ptes]. -- alloc_l1_page shouldn't set old_guest_table - -If we experience one of these situations in production builds, it's -safer to avoid calling put_page_type for the pages in question. If -they have PGT_partial set, they will be cleaned up on domain -destruction; if not, we have no idea whether a type count is safe to -drop. Retaining an extra type ref that should have been dropped may -trigger a BUG() on the free_domain_page() path, but dropping a type -count that shouldn't be dropped may cause a privilege escalation. - -This is part of XSA-299. - -Reported-by: George Dunlap -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ---- - xen/arch/x86/mm.c | 53 +++++++++++++++++++++++++++++++++++++++++++++-- - 1 file changed, 51 insertions(+), 2 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 0a094291da..a432e69c74 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1580,6 +1580,20 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) - ASSERT(current->arch.old_guest_table == NULL); - if ( i ) - { -+ /* -+ * alloc_l1_table() doesn't set old_guest_table; it does -+ * its own tear-down immediately on failure. If it -+ * did we'd need to check it and set partial_flags as we -+ * do in alloc_l[34]_table(). -+ * -+ * Note on the use of ASSERT: if it's non-null and -+ * hasn't been cleaned up yet, it should have -+ * PGT_partial set; and so the type will be cleaned up -+ * on domain destruction. Unfortunately, we would -+ * leak the general ref held by old_guest_table; but -+ * leaking a page is less bad than a host crash. -+ */ -+ ASSERT(current->arch.old_guest_table == NULL); - page->nr_validated_ptes = i; - page->partial_flags = partial_flags; - current->arch.old_guest_ptpg = NULL; -@@ -1607,6 +1621,7 @@ static int alloc_l3_table(struct page_info *page) - unsigned int i; - int rc = 0; - unsigned int partial_flags = page->partial_flags; -+ l3_pgentry_t l3e = l3e_empty(); - - pl3e = map_domain_page(_mfn(pfn)); - -@@ -1623,7 +1638,7 @@ static int alloc_l3_table(struct page_info *page) - for ( i = page->nr_validated_ptes; i < L3_PAGETABLE_ENTRIES; - i++, partial_flags = 0 ) - { -- l3_pgentry_t l3e = pl3e[i]; -+ l3e = pl3e[i]; - - if ( i > page->nr_validated_ptes && hypercall_preempt_check() ) - rc = -EINTR; -@@ -1675,6 +1690,24 @@ static int alloc_l3_table(struct page_info *page) - { - page->nr_validated_ptes = i; - page->partial_flags = partial_flags; -+ if ( current->arch.old_guest_table ) -+ { -+ /* -+ * We've experienced a validation failure. If -+ * old_guest_table is set, "transfer" the general -+ * reference count to pl3e[nr_validated_ptes] by -+ * setting PTF_partial_set. -+ * -+ * As a precaution, check that old_guest_table is the -+ * page pointed to by pl3e[nr_validated_ptes]. If -+ * not, it's safer to leak a type ref on production -+ * builds. -+ */ -+ if ( current->arch.old_guest_table == l3e_get_page(l3e) ) -+ page->partial_flags = PTF_partial_set; -+ else -+ ASSERT_UNREACHABLE(); -+ } - current->arch.old_guest_ptpg = NULL; - current->arch.old_guest_table = page; - } -@@ -1851,7 +1884,23 @@ static int alloc_l4_table(struct page_info *page) - else - { - if ( current->arch.old_guest_table ) -- page->nr_validated_ptes++; -+ { -+ /* -+ * We've experienced a validation failure. If -+ * old_guest_table is set, "transfer" the general -+ * reference count to pl3e[nr_validated_ptes] by -+ * setting PTF_partial_set. -+ * -+ * As a precaution, check that old_guest_table is the -+ * page pointed to by pl4e[nr_validated_ptes]. If -+ * not, it's safer to leak a type ref on production -+ * builds. -+ */ -+ if ( current->arch.old_guest_table == l4e_get_page(l4e) ) -+ page->partial_flags = PTF_partial_set; -+ else -+ ASSERT_UNREACHABLE(); -+ } - current->arch.old_guest_ptpg = NULL; - current->arch.old_guest_table = page; - } --- -2.23.0 - diff --git a/xsa299-4.12-0011-x86-mm-Don-t-drop-a-type-ref-unless-you-held-a-ref-t.patch b/xsa299-4.12-0011-x86-mm-Don-t-drop-a-type-ref-unless-you-held-a-ref-t.patch deleted file mode 100644 index ad7e6fe..0000000 --- a/xsa299-4.12-0011-x86-mm-Don-t-drop-a-type-ref-unless-you-held-a-ref-t.patch +++ /dev/null @@ -1,413 +0,0 @@ -From ea3dc624c5e6325a9c2f079e52a85965d4ab6ce8 Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Thu, 10 Oct 2019 17:57:50 +0100 -Subject: [PATCH 11/11] x86/mm: Don't drop a type ref unless you held a ref to - begin with - -Validation and de-validation of pagetable trees may take arbitrarily -large amounts of time, and so must be preemptible. This is indicated -by setting the PGT_partial bit in the type_info, and setting -nr_validated_entries and partial_flags appropriately. Specifically, -if the entry at [nr_validated_entries] is partially validated, -partial_flags should have the PGT_partial_set bit set, and the entry -should hold a general reference count. During de-validation, -put_page_type() is called on partially validated entries. - -Unfortunately, there are a number of issues with the current algorithm. - -First, doing a "normal" put_page_type() is not safe when no type ref -is held: there is nothing to stop another vcpu from coming along and -picking up validation again: at which point the put_page_type may drop -the only page ref on an in-use page. Some examples are listed in the -appendix. - -The core issue is that put_page_type() is being called both to clean -up PGT_partial, and to drop a type count; and has no way of knowing -which is which; and so if in between, PGT_partial is cleared, -put_page_type() will drop the type ref erroneously. - -What is needed is to distinguish between two states: -- Dropping a type ref which is held -- Cleaning up a page which has been partially de/validated - -Fix this by telling put_page_type() which of the two activities you -intend. - -When cleaning up a partial de/validation, take no action unless you -find a page partially validated. - -If put_page_type() is called without PTF_partial_set, and finds the -page in a PGT_partial state anyway, then there's certainly been a -misaccounting somewhere, and carrying on would almost certainly cause -a security issue, so crash the host instead. - -In put_page_from_lNe, pass partial_flags on to _put_page_type(). - -old_guest_table may be set either with a fully validated page (when -using the "deferred put" pattern), or with a partially validated page -(when a normal "de-validation" is interrupted, or when a validation -fails part-way through due to invalid entries). Add a flag, -old_guest_table_partial, to indicate which of these it is, and use -that to pass the appropriate flag to _put_page_type(). - -While here, delete stray trailing whitespace. - -This is part of XSA-299. - -Reported-by: George Dunlap -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ------ -Appendix: - -Suppose page A, when interpreted as an l3 pagetable, contains all -valid entries; and suppose A[x] points to page B, which when -interpreted as an l2 pagetable, contains all valid entries. - -P1: PIN_L3_TABLE - A -> PGT_l3_table | 1 | valid - B -> PGT_l2_table | 1 | valid - -P1: UNPIN_TABLE - > Arrange to interrupt after B has been de-validated - B: - type_info -> PGT_l2_table | 0 - A: - type_info -> PGT_l3_table | 1 | partial - nr_validated_enties -> (less than x) - -P2: mod_l4_entry to point to A - > Arrange for this to be interrupted while B is being validated - B: - type_info -> PGT_l2_table | 1 | partial - (nr_validated_entires &c set as appropriate) - A: - type_info -> PGT_l3_table | 1 | partial - nr_validated_entries -> x - partial_pte = 1 - -P3: mod_l3_entry some other unrelated l3 to point to B: - B: - type_info -> PGT_l2_table | 1 - -P1: Restart UNPIN_TABLE - -At this point, since A.nr_validate_entries == x and A.partial_pte != -0, free_l3_table() will call put_page_from_l3e() on pl3e[x], dropping -its type count to 0 while it's still being pointed to by some other l3 - -A similar issue arises with old_guest_table. Consider the following -scenario: - -Suppose A is a page which, when interpreted as an l2, has valid entries -until entry x, which is invalid. - -V1: PIN_L2_TABLE(A) - - A -> PGT_l2_table | 1 | PGT_partial - V1 -> old_guest_table = A - - -V2: PIN_L2_TABLE(A) - - A -> PGT_l2_table | 1 | PGT_partial - V2 -> old_guest_table = A - - put_old_guest_table() - _put_page_type(A) - A -> PGT_l2_table | 0 - -V1: - put_old_guest_table() - _put_page_type(A) # UNDERFLOW - -Indeed, it is possible to engineer for old_guest_table for every vcpu -a guest has to point to the same page. ---- - xen/arch/x86/domain.c | 6 +++ - xen/arch/x86/mm.c | 99 +++++++++++++++++++++++++++++++----- - xen/include/asm-x86/domain.h | 4 +- - 3 files changed, 95 insertions(+), 14 deletions(-) - -diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c -index 59df8a6d8d..f1ae5f89f5 100644 ---- a/xen/arch/x86/domain.c -+++ b/xen/arch/x86/domain.c -@@ -1104,9 +1104,15 @@ int arch_set_info_guest( - rc = -ERESTART; - /* Fallthrough */ - case -ERESTART: -+ /* -+ * NB that we're putting the kernel-mode table -+ * here, which we've already successfully -+ * validated above; hence partial = false; -+ */ - v->arch.old_guest_ptpg = NULL; - v->arch.old_guest_table = - pagetable_get_page(v->arch.guest_table); -+ v->arch.old_guest_table_partial = false; - v->arch.guest_table = pagetable_null(); - break; - default: -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index a432e69c74..81774368a0 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1359,10 +1359,11 @@ static int put_page_from_l2e(l2_pgentry_t l2e, unsigned long pfn, - { - current->arch.old_guest_ptpg = ptpg; - current->arch.old_guest_table = pg; -+ current->arch.old_guest_table_partial = false; - } - else - { -- rc = _put_page_type(pg, PTF_preemptible, ptpg); -+ rc = _put_page_type(pg, flags | PTF_preemptible, ptpg); - if ( likely(!rc) ) - put_page(pg); - } -@@ -1385,6 +1386,7 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, - unsigned long mfn = l3e_get_pfn(l3e); - bool writeable = l3e_get_flags(l3e) & _PAGE_RW; - -+ ASSERT(!(flags & PTF_partial_set)); - ASSERT(!(mfn & ((1UL << (L3_PAGETABLE_SHIFT - PAGE_SHIFT)) - 1))); - do { - put_data_page(mfn_to_page(_mfn(mfn)), writeable); -@@ -1397,12 +1399,14 @@ static int put_page_from_l3e(l3_pgentry_t l3e, unsigned long pfn, - - if ( flags & PTF_defer ) - { -+ ASSERT(!(flags & PTF_partial_set)); - current->arch.old_guest_ptpg = mfn_to_page(_mfn(pfn)); - current->arch.old_guest_table = pg; -+ current->arch.old_guest_table_partial = false; - return 0; - } - -- rc = _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); -+ rc = _put_page_type(pg, flags | PTF_preemptible, mfn_to_page(_mfn(pfn))); - if ( likely(!rc) ) - put_page(pg); - -@@ -1421,12 +1425,15 @@ static int put_page_from_l4e(l4_pgentry_t l4e, unsigned long pfn, - - if ( flags & PTF_defer ) - { -+ ASSERT(!(flags & PTF_partial_set)); - current->arch.old_guest_ptpg = mfn_to_page(_mfn(pfn)); - current->arch.old_guest_table = pg; -+ current->arch.old_guest_table_partial = false; - return 0; - } - -- rc = _put_page_type(pg, PTF_preemptible, mfn_to_page(_mfn(pfn))); -+ rc = _put_page_type(pg, flags | PTF_preemptible, -+ mfn_to_page(_mfn(pfn))); - if ( likely(!rc) ) - put_page(pg); - } -@@ -1535,6 +1542,14 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) - - pl2e = map_domain_page(_mfn(pfn)); - -+ /* -+ * NB that alloc_l2_table will never set partial_pte on an l2; but -+ * free_l2_table might if a linear_pagetable entry is interrupted -+ * partway through de-validation. In that circumstance, -+ * get_page_from_l2e() will always return -EINVAL; and we must -+ * retain the type ref by doing the normal partial_flags tracking. -+ */ -+ - for ( i = page->nr_validated_ptes; i < L2_PAGETABLE_ENTRIES; - i++, partial_flags = 0 ) - { -@@ -1598,6 +1613,7 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) - page->partial_flags = partial_flags; - current->arch.old_guest_ptpg = NULL; - current->arch.old_guest_table = page; -+ current->arch.old_guest_table_partial = true; - } - } - if ( rc < 0 ) -@@ -1704,12 +1720,16 @@ static int alloc_l3_table(struct page_info *page) - * builds. - */ - if ( current->arch.old_guest_table == l3e_get_page(l3e) ) -+ { -+ ASSERT(current->arch.old_guest_table_partial); - page->partial_flags = PTF_partial_set; -+ } - else - ASSERT_UNREACHABLE(); - } - current->arch.old_guest_ptpg = NULL; - current->arch.old_guest_table = page; -+ current->arch.old_guest_table_partial = true; - } - while ( i-- > 0 ) - pl3e[i] = unadjust_guest_l3e(pl3e[i], d); -@@ -1897,12 +1917,16 @@ static int alloc_l4_table(struct page_info *page) - * builds. - */ - if ( current->arch.old_guest_table == l4e_get_page(l4e) ) -+ { -+ ASSERT(current->arch.old_guest_table_partial); - page->partial_flags = PTF_partial_set; -+ } - else - ASSERT_UNREACHABLE(); - } - current->arch.old_guest_ptpg = NULL; - current->arch.old_guest_table = page; -+ current->arch.old_guest_table_partial = true; - } - } - } -@@ -2831,6 +2855,28 @@ static int _put_page_type(struct page_info *page, unsigned int flags, - x = y; - nx = x - 1; - -+ /* -+ * Is this expected to do a full reference drop, or only -+ * cleanup partial validation / devalidation? -+ * -+ * If the former, the caller must hold a "full" type ref; -+ * which means the page must be validated. If the page is -+ * *not* fully validated, continuing would almost certainly -+ * open up a security hole. An exception to this is during -+ * domain destruction, where PGT_validated can be dropped -+ * without dropping a type ref. -+ * -+ * If the latter, do nothing unless type PGT_partial is set. -+ * If it is set, the type count must be 1. -+ */ -+ if ( !(flags & PTF_partial_set) ) -+ BUG_ON((x & PGT_partial) || -+ !((x & PGT_validated) || page_get_owner(page)->is_dying)); -+ else if ( !(x & PGT_partial) ) -+ return 0; -+ else -+ BUG_ON((x & PGT_count_mask) != 1); -+ - ASSERT((x & PGT_count_mask) != 0); - - switch ( nx & (PGT_locked | PGT_count_mask) ) -@@ -3092,17 +3138,34 @@ int put_old_guest_table(struct vcpu *v) - if ( !v->arch.old_guest_table ) - return 0; - -- switch ( rc = _put_page_type(v->arch.old_guest_table, PTF_preemptible, -- v->arch.old_guest_ptpg) ) -+ rc = _put_page_type(v->arch.old_guest_table, -+ PTF_preemptible | -+ ( v->arch.old_guest_table_partial ? -+ PTF_partial_set : 0 ), -+ v->arch.old_guest_ptpg); -+ -+ if ( rc == -ERESTART || rc == -EINTR ) - { -- case -EINTR: -- case -ERESTART: -+ v->arch.old_guest_table_partial = (rc == -ERESTART); - return -ERESTART; -- case 0: -- put_page(v->arch.old_guest_table); - } - -+ /* -+ * It shouldn't be possible for _put_page_type() to return -+ * anything else at the moment; but if it does happen in -+ * production, leaking the type ref is probably the best thing to -+ * do. Either way, drop the general ref held by old_guest_table. -+ */ -+ ASSERT(rc == 0); -+ -+ put_page(v->arch.old_guest_table); - v->arch.old_guest_table = NULL; -+ v->arch.old_guest_ptpg = NULL; -+ /* -+ * Safest default if someone sets old_guest_table without -+ * explicitly setting old_guest_table_partial. -+ */ -+ v->arch.old_guest_table_partial = true; - - return rc; - } -@@ -3253,11 +3316,11 @@ int new_guest_cr3(mfn_t mfn) - switch ( rc = put_page_and_type_preemptible(page) ) - { - case -EINTR: -- rc = -ERESTART; -- /* fallthrough */ - case -ERESTART: - curr->arch.old_guest_ptpg = NULL; - curr->arch.old_guest_table = page; -+ curr->arch.old_guest_table_partial = (rc == -ERESTART); -+ rc = -ERESTART; - break; - default: - BUG_ON(rc); -@@ -3494,6 +3557,7 @@ long do_mmuext_op( - { - curr->arch.old_guest_ptpg = NULL; - curr->arch.old_guest_table = page; -+ curr->arch.old_guest_table_partial = false; - } - } - } -@@ -3528,6 +3592,11 @@ long do_mmuext_op( - case -ERESTART: - curr->arch.old_guest_ptpg = NULL; - curr->arch.old_guest_table = page; -+ /* -+ * EINTR means we still hold the type ref; ERESTART -+ * means PGT_partial holds the type ref -+ */ -+ curr->arch.old_guest_table_partial = (rc == -ERESTART); - rc = 0; - break; - default: -@@ -3596,11 +3665,15 @@ long do_mmuext_op( - switch ( rc = put_page_and_type_preemptible(page) ) - { - case -EINTR: -- rc = -ERESTART; -- /* fallthrough */ - case -ERESTART: - curr->arch.old_guest_ptpg = NULL; - curr->arch.old_guest_table = page; -+ /* -+ * EINTR means we still hold the type ref; -+ * ERESTART means PGT_partial holds the ref -+ */ -+ curr->arch.old_guest_table_partial = (rc == -ERESTART); -+ rc = -ERESTART; - break; - default: - BUG_ON(rc); -diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h -index 214e44ce1c..2cfce7b36b 100644 ---- a/xen/include/asm-x86/domain.h -+++ b/xen/include/asm-x86/domain.h -@@ -307,7 +307,7 @@ struct arch_domain - - struct paging_domain paging; - struct p2m_domain *p2m; -- /* To enforce lock ordering in the pod code wrt the -+ /* To enforce lock ordering in the pod code wrt the - * page_alloc lock */ - int page_alloc_unlock_level; - -@@ -581,6 +581,8 @@ struct arch_vcpu - struct page_info *old_guest_table; /* partially destructed pagetable */ - struct page_info *old_guest_ptpg; /* containing page table of the */ - /* former, if any */ -+ bool old_guest_table_partial; /* Are we dropping a type ref, or just -+ * finishing up a partial de-validation? */ - /* guest_table holds a ref to the page, and also a type-count unless - * shadow refcounts are in use */ - pagetable_t shadow_table[4]; /* (MFN) shadow(s) of guest */ --- -2.23.0 - diff --git a/xsa301-master-1.patch b/xsa301-master-1.patch deleted file mode 100644 index 54cce2c..0000000 --- a/xsa301-master-1.patch +++ /dev/null @@ -1,80 +0,0 @@ -From 19d6330f142cb941b6340a88592e8a294de0ff8c Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Tue, 15 Oct 2019 17:10:40 +0100 -Subject: [PATCH 1/3] xen/arm: p2m: Avoid aliasing guest physical frame - -The P2M helpers implementation is quite lax and will end up to ignore -the unused top bits of a guest physical frame. - -This effectively means that p2m_set_entry() will create a mapping for a -different frame (it is always equal to gfn & (mask unused bits)). Yet -p2m->max_mapped_gfn will be updated using the original frame. - -At the moment, p2m_get_entry() and p2m_resolve_translation_fault() -assume that p2m_get_root_pointer() will always return a non-NULL pointer -when the GFN is smaller than p2m->max_mapped_gfn. - -Unfortunately, because of the aliasing described above, it would be -possible to set p2m->max_mapped_gfn high enough so it covers frame that -would lead p2m_get_root_pointer() to return NULL. - -As we don't sanity check the guest physical frame provided by a guest, a -malicious guest could craft a series of hypercalls that will hit the -BUG_ON() and therefore DoS Xen. - -To prevent aliasing, the function p2m_get_root_pointer() is now reworked -to return NULL If any of the unused top bits are not zero. The caller -can then decide what's the appropriate action to do. Since the two paths -(i.e. P2M_ROOT_PAGES == 1 and P2M_ROOT_PAGES != 1) are now very -similarly, take the opportunity to consolidate them making the code a -bit simpler. - -With this change, p2m_get_entry() will not try to insert a mapping as -the root pointer is invalid. - -Note that root_table is now switch to unsigned long as unsigned int is -not enough to hold part of a GFN. - -This is part of XSA-301. - -Reported-by: Julien Grall -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini ---- - xen/arch/arm/p2m.c | 17 +++++------------ - 1 file changed, 5 insertions(+), 12 deletions(-) - -diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c -index a2749d9b6f..d0045a8b28 100644 ---- a/xen/arch/arm/p2m.c -+++ b/xen/arch/arm/p2m.c -@@ -229,21 +229,14 @@ void p2m_tlb_flush_sync(struct p2m_domain *p2m) - static lpae_t *p2m_get_root_pointer(struct p2m_domain *p2m, - gfn_t gfn) - { -- unsigned int root_table; -- -- if ( P2M_ROOT_PAGES == 1 ) -- return __map_domain_page(p2m->root); -+ unsigned long root_table; - - /* -- * Concatenated root-level tables. The table number will be the -- * offset at the previous level. It is not possible to -- * concatenate a level-0 root. -+ * While the root table index is the offset from the previous level, -+ * we can't use (P2M_ROOT_LEVEL - 1) because the root level might be -+ * 0. Yet we still want to check if all the unused bits are zeroed. - */ -- ASSERT(P2M_ROOT_LEVEL > 0); -- -- root_table = gfn_x(gfn) >> (level_orders[P2M_ROOT_LEVEL - 1]); -- root_table &= LPAE_ENTRY_MASK; -- -+ root_table = gfn_x(gfn) >> (level_orders[P2M_ROOT_LEVEL] + LPAE_SHIFT); - if ( root_table >= P2M_ROOT_PAGES ) - return NULL; - --- -2.23.0 - diff --git a/xsa301-master-2.patch b/xsa301-master-2.patch deleted file mode 100644 index baedc9c..0000000 --- a/xsa301-master-2.patch +++ /dev/null @@ -1,92 +0,0 @@ -From 3b896936f7505e929dd869d14afcb185d0ee75f8 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Tue, 15 Oct 2019 17:10:41 +0100 -Subject: [PATCH 2/3] xen/arm: p2m: Avoid off-by-one check on - p2m->max_mapped_gfn - -The code base is using inconsistently the field p2m->max_mapped_gfn. -Some of the useres expect that p2m->max_guest_gfn contain the highest -mapped GFN while others expect highest + 1. - -p2m->max_guest_gfn is set as highest + 1, because of that the sanity -check on the GFN in p2m_resolved_translation_fault() and -p2m_get_entry() can be bypassed when GFN == p2m->max_guest_gfn. - -p2m_get_root_pointer(p2m->max_guest_gfn) may return NULL if it is -outside of address range supported and therefore the BUG_ON() could be -hit. - -The current value hold in p2m->max_mapped_gfn is inconsistent with the -expectation of the common code (see domain_get_maximum_gpfn()) and also -the documentation of the field. - -Rather than changing the check in p2m_translation_fault() and -p2m_get_entry(), p2m->max_mapped_gfn is now containing the highest -mapped GFN and the callers assuming "highest + 1" are now adjusted. - -Take the opportunity to use 1UL rather than 1 as page_order could -theoritically big enough to overflow a 32-bit integer. - -Lastly, the documentation of the field max_guest_gfn to reflect how it -is computed. - -This is part of XSA-301. - -Reported-by: Julien Grall -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini ---- - xen/arch/arm/p2m.c | 6 +++--- - xen/include/asm-arm/p2m.h | 5 +---- - 2 files changed, 4 insertions(+), 7 deletions(-) - -diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c -index d0045a8b28..8d20d27961 100644 ---- a/xen/arch/arm/p2m.c -+++ b/xen/arch/arm/p2m.c -@@ -1041,7 +1041,7 @@ static int __p2m_set_entry(struct p2m_domain *p2m, - p2m_write_pte(entry, pte, p2m->clean_pte); - - p2m->max_mapped_gfn = gfn_max(p2m->max_mapped_gfn, -- gfn_add(sgfn, 1 << page_order)); -+ gfn_add(sgfn, (1UL << page_order) - 1)); - p2m->lowest_mapped_gfn = gfn_min(p2m->lowest_mapped_gfn, sgfn); - } - -@@ -1572,7 +1572,7 @@ int relinquish_p2m_mapping(struct domain *d) - p2m_write_lock(p2m); - - start = p2m->lowest_mapped_gfn; -- end = p2m->max_mapped_gfn; -+ end = gfn_add(p2m->max_mapped_gfn, 1); - - for ( ; gfn_x(start) < gfn_x(end); - start = gfn_next_boundary(start, order) ) -@@ -1641,7 +1641,7 @@ int p2m_cache_flush_range(struct domain *d, gfn_t *pstart, gfn_t end) - p2m_read_lock(p2m); - - start = gfn_max(start, p2m->lowest_mapped_gfn); -- end = gfn_min(end, p2m->max_mapped_gfn); -+ end = gfn_min(end, gfn_add(p2m->max_mapped_gfn, 1)); - - next_block_gfn = start; - -diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h -index 89f82df380..5fdb6e8183 100644 ---- a/xen/include/asm-arm/p2m.h -+++ b/xen/include/asm-arm/p2m.h -@@ -36,10 +36,7 @@ struct p2m_domain { - /* Current Translation Table Base Register for the p2m */ - uint64_t vttbr; - -- /* -- * Highest guest frame that's ever been mapped in the p2m -- * Only takes into account ram and foreign mapping -- */ -+ /* Highest guest frame that's ever been mapped in the p2m */ - gfn_t max_mapped_gfn; - - /* --- -2.23.0 - diff --git a/xsa301-master-3.patch b/xsa301-master-3.patch deleted file mode 100644 index 9f137b8..0000000 --- a/xsa301-master-3.patch +++ /dev/null @@ -1,67 +0,0 @@ -From 060c2dd3b7c2674a019d94afb2b4ebf3663f6c6e Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Tue, 15 Oct 2019 17:10:42 +0100 -Subject: [PATCH 3/3] xen/arm: p2m: Don't check the return of - p2m_get_root_pointer() with BUG_ON() - -It turns out that the BUG_ON() was actually reachable with well-crafted -hypercalls. The BUG_ON() is here to prevent catch logical error, so -crashing Xen is a bit over the top. - -While all the holes should now be fixed, it would be better to downgrade -the BUG_ON() to something less fatal to prevent any more DoS. - -The BUG_ON() in p2m_get_entry() is now replaced by ASSERT_UNREACHABLE() -to catch mistake in debug build and return INVALID_MFN for production -build. The interface also requires to set page_order to give an idea of -the size of "hole". So 'level' is now set so we report a hole of size of -the an entry of the root page-table. This stays inline with what happen -when the GFN is higher than p2m->max_mapped_gfn. - -The BUG_ON() in p2m_resolve_translation_fault() is now replaced by -ASSERT_UNREACHABLE() to catch mistake in debug build and just report a -fault for producion build. - -This is part of XSA-301. - -Reported-by: Julien Grall -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini ---- - xen/arch/arm/p2m.c | 13 +++++++++++-- - 1 file changed, 11 insertions(+), 2 deletions(-) - -diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c -index 8d20d27961..ce59f2b503 100644 ---- a/xen/arch/arm/p2m.c -+++ b/xen/arch/arm/p2m.c -@@ -395,7 +395,12 @@ mfn_t p2m_get_entry(struct p2m_domain *p2m, gfn_t gfn, - * the table should always be non-NULL because the gfn is below - * p2m->max_mapped_gfn and the root table pages are always present. - */ -- BUG_ON(table == NULL); -+ if ( !table ) -+ { -+ ASSERT_UNREACHABLE(); -+ level = P2M_ROOT_LEVEL; -+ goto out; -+ } - - for ( level = P2M_ROOT_LEVEL; level < 3; level++ ) - { -@@ -1196,7 +1201,11 @@ bool p2m_resolve_translation_fault(struct domain *d, gfn_t gfn) - * The table should always be non-NULL because the gfn is below - * p2m->max_mapped_gfn and the root table pages are always present. - */ -- BUG_ON(table == NULL); -+ if ( !table ) -+ { -+ ASSERT_UNREACHABLE(); -+ goto out; -+ } - - /* - * Go down the page-tables until an entry has the valid bit unset or --- -2.23.0 - diff --git a/xsa302-4.12-0001-IOMMU-add-missing-HVM-check.patch b/xsa302-4.12-0001-IOMMU-add-missing-HVM-check.patch deleted file mode 100644 index 5d52163..0000000 --- a/xsa302-4.12-0001-IOMMU-add-missing-HVM-check.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 0c9c0fbb356e3210cb77b3d738be50981b26058a Mon Sep 17 00:00:00 2001 -From: Jan Beulich -Date: Wed, 2 Oct 2019 13:36:59 +0200 -Subject: [PATCH 1/2] IOMMU: add missing HVM check -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Fix an unguarded d->arch.hvm access in assign_device(). - -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné -Acked-by: Andrew Cooper - -(cherry picked from commit 41fd1009cd7416b73d745a77c24b4e8d1a296fe6) -Signed-off-by: Ian Jackson ---- - xen/drivers/passthrough/pci.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c -index 8108ed5f9a..d7420bd8bf 100644 ---- a/xen/drivers/passthrough/pci.c -+++ b/xen/drivers/passthrough/pci.c -@@ -1452,7 +1452,8 @@ static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) - - /* Prevent device assign if mem paging or mem sharing have been - * enabled for this domain */ -- if ( unlikely(d->arch.hvm.mem_sharing_enabled || -+ if ( unlikely((is_hvm_domain(d) && -+ d->arch.hvm.mem_sharing_enabled) || - vm_event_check_ring(d->vm_event_paging) || - p2m_get_hostp2m(d)->global_logdirty) ) - return -EXDEV; --- -2.11.0 - diff --git a/xsa302-4.12-0002-passthrough-quarantine-PCI-devices.patch b/xsa302-4.12-0002-passthrough-quarantine-PCI-devices.patch deleted file mode 100644 index 181ece3..0000000 --- a/xsa302-4.12-0002-passthrough-quarantine-PCI-devices.patch +++ /dev/null @@ -1,499 +0,0 @@ -From 278d8e585a9f110a1af0bd92a9fc43733c9c7227 Mon Sep 17 00:00:00 2001 -From: Paul Durrant -Date: Mon, 14 Oct 2019 17:52:59 +0100 -Subject: [PATCH 2/2] passthrough: quarantine PCI devices - -When a PCI device is assigned to an untrusted domain, it is possible for -that domain to program the device to DMA to an arbitrary address. The -IOMMU is used to protect the host from malicious DMA by making sure that -the device addresses can only target memory assigned to the guest. However, -when the guest domain is torn down the device is assigned back to dom0, -thus allowing any in-flight DMA to potentially target critical host data. - -This patch introduces a 'quarantine' for PCI devices using dom_io. When -the toolstack makes a device assignable (by binding it to pciback), it -will now also assign it to DOMID_IO and the device will only be assigned -back to dom0 when the device is made unassignable again. Whilst device is -assignable it will only ever transfer between dom_io and guest domains. -dom_io is actually only used as a sentinel domain for quarantining purposes; -it is not configured with any IOMMU mappings. Assignment to dom_io simply -means that the device's initiator (requestor) identifier is not present in -the IOMMU's device table and thus any DMA transactions issued will be -terminated with a fault condition. - -In addition, a fix to assignment handling is made for VT-d. Failure -during the assignment step should not lead to a device still being -associated with its prior owner. Hand the device to DomIO temporarily, -until the assignment step has completed successfully. Remove the PI -hooks from the source domain then earlier as well. - -Failure of the recovery reassign_device_ownership() may not go silent: -There e.g. may still be left over RMRR mappings in the domain assignment -to which has failed, and hence we can't allow that domain to continue -executing. - -NOTE: This patch also includes one printk() cleanup; the - "XEN_DOMCTL_assign_device: " tag is dropped in iommu_do_pci_domctl(), - since similar printk()-s elsewhere also don't log such a tag. - -This is XSA-302. - -Signed-off-by: Paul Durrant -Signed-off-by: Jan Beulich -Signed-off-by: Ian Jackson -(cherry picked from commit ec99857f59f7f06236f11ca8b0b2303e5e745cc4) ---- - tools/libxl/libxl_pci.c | 25 +++++++++++- - xen/arch/x86/mm.c | 2 + - xen/common/domctl.c | 14 ++++++- - xen/drivers/passthrough/amd/pci_amd_iommu.c | 10 ++++- - xen/drivers/passthrough/iommu.c | 9 +++++ - xen/drivers/passthrough/pci.c | 59 ++++++++++++++++++++++------- - xen/drivers/passthrough/vtd/iommu.c | 40 ++++++++++++++++--- - xen/include/xen/pci.h | 3 ++ - 8 files changed, 138 insertions(+), 24 deletions(-) - -diff --git a/tools/libxl/libxl_pci.c b/tools/libxl/libxl_pci.c -index 88c324ea23..d6a23fb5f8 100644 ---- a/tools/libxl/libxl_pci.c -+++ b/tools/libxl/libxl_pci.c -@@ -754,6 +754,7 @@ static int libxl__device_pci_assignable_add(libxl__gc *gc, - libxl_device_pci *pcidev, - int rebind) - { -+ libxl_ctx *ctx = libxl__gc_owner(gc); - unsigned dom, bus, dev, func; - char *spath, *driver_path = NULL; - int rc; -@@ -779,7 +780,7 @@ static int libxl__device_pci_assignable_add(libxl__gc *gc, - } - if ( rc ) { - LOG(WARN, PCI_BDF" already assigned to pciback", dom, bus, dev, func); -- return 0; -+ goto quarantine; - } - - /* Check to see if there's already a driver that we need to unbind from */ -@@ -810,6 +811,19 @@ static int libxl__device_pci_assignable_add(libxl__gc *gc, - return ERROR_FAIL; - } - -+quarantine: -+ /* -+ * DOMID_IO is just a sentinel domain, without any actual mappings, -+ * so always pass XEN_DOMCTL_DEV_RDM_RELAXED to avoid assignment being -+ * unnecessarily denied. -+ */ -+ rc = xc_assign_device(ctx->xch, DOMID_IO, pcidev_encode_bdf(pcidev), -+ XEN_DOMCTL_DEV_RDM_RELAXED); -+ if ( rc < 0 ) { -+ LOG(ERROR, "failed to quarantine "PCI_BDF, dom, bus, dev, func); -+ return ERROR_FAIL; -+ } -+ - return 0; - } - -@@ -817,9 +831,18 @@ static int libxl__device_pci_assignable_remove(libxl__gc *gc, - libxl_device_pci *pcidev, - int rebind) - { -+ libxl_ctx *ctx = libxl__gc_owner(gc); - int rc; - char *driver_path; - -+ /* De-quarantine */ -+ rc = xc_deassign_device(ctx->xch, DOMID_IO, pcidev_encode_bdf(pcidev)); -+ if ( rc < 0 ) { -+ LOG(ERROR, "failed to de-quarantine "PCI_BDF, pcidev->domain, pcidev->bus, -+ pcidev->dev, pcidev->func); -+ return ERROR_FAIL; -+ } -+ - /* Unbind from pciback */ - if ( (rc=pciback_dev_is_assigned(gc, pcidev)) < 0 ) { - return ERROR_FAIL; -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 3557cd1178..11d753d8d2 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -295,9 +295,11 @@ void __init arch_init_memory(void) - * Initialise our DOMID_IO domain. - * This domain owns I/O pages that are within the range of the page_info - * array. Mappings occur at the priv of the caller. -+ * Quarantined PCI devices will be associated with this domain. - */ - dom_io = domain_create(DOMID_IO, NULL, false); - BUG_ON(IS_ERR(dom_io)); -+ INIT_LIST_HEAD(&dom_io->arch.pdev_list); - - /* - * Initialise our COW domain. -diff --git a/xen/common/domctl.c b/xen/common/domctl.c -index d08b6274e2..e3c4be2b48 100644 ---- a/xen/common/domctl.c -+++ b/xen/common/domctl.c -@@ -391,6 +391,16 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) - - switch ( op->cmd ) - { -+ case XEN_DOMCTL_assign_device: -+ case XEN_DOMCTL_deassign_device: -+ if ( op->domain == DOMID_IO ) -+ { -+ d = dom_io; -+ break; -+ } -+ else if ( op->domain == DOMID_INVALID ) -+ return -ESRCH; -+ /* fall through */ - case XEN_DOMCTL_test_assign_device: - if ( op->domain == DOMID_INVALID ) - { -@@ -412,7 +422,7 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) - - if ( !domctl_lock_acquire() ) - { -- if ( d ) -+ if ( d && d != dom_io ) - rcu_unlock_domain(d); - return hypercall_create_continuation( - __HYPERVISOR_domctl, "h", u_domctl); -@@ -1074,7 +1084,7 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) - domctl_lock_release(); - - domctl_out_unlock_domonly: -- if ( d ) -+ if ( d && d != dom_io ) - rcu_unlock_domain(d); - - if ( copyback && __copy_to_guest(u_domctl, op, 1) ) -diff --git a/xen/drivers/passthrough/amd/pci_amd_iommu.c b/xen/drivers/passthrough/amd/pci_amd_iommu.c -index 33a3798f36..15c13e1163 100644 ---- a/xen/drivers/passthrough/amd/pci_amd_iommu.c -+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c -@@ -120,6 +120,10 @@ static void amd_iommu_setup_domain_device( - u8 bus = pdev->bus; - const struct domain_iommu *hd = dom_iommu(domain); - -+ /* dom_io is used as a sentinel for quarantined devices */ -+ if ( domain == dom_io ) -+ return; -+ - BUG_ON( !hd->arch.root_table || !hd->arch.paging_mode || - !iommu->dev_table.buffer ); - -@@ -277,6 +281,10 @@ void amd_iommu_disable_domain_device(struct domain *domain, - int req_id; - u8 bus = pdev->bus; - -+ /* dom_io is used as a sentinel for quarantined devices */ -+ if ( domain == dom_io ) -+ return; -+ - BUG_ON ( iommu->dev_table.buffer == NULL ); - req_id = get_dma_requestor_id(iommu->seg, PCI_BDF2(bus, devfn)); - dte = iommu->dev_table.buffer + (req_id * IOMMU_DEV_TABLE_ENTRY_SIZE); -@@ -363,7 +371,7 @@ static int amd_iommu_assign_device(struct domain *d, u8 devfn, - ivrs_mappings[req_id].read_permission); - } - -- return reassign_device(hardware_domain, d, devfn, pdev); -+ return reassign_device(pdev->domain, d, devfn, pdev); - } - - static void deallocate_next_page_table(struct page_info *pg, int level) -diff --git a/xen/drivers/passthrough/iommu.c b/xen/drivers/passthrough/iommu.c -index a6697d58fb..2762e1342f 100644 ---- a/xen/drivers/passthrough/iommu.c -+++ b/xen/drivers/passthrough/iommu.c -@@ -232,6 +232,9 @@ void iommu_teardown(struct domain *d) - { - struct domain_iommu *hd = dom_iommu(d); - -+ if ( d == dom_io ) -+ return; -+ - hd->status = IOMMU_STATUS_disabled; - hd->platform_ops->teardown(d); - tasklet_schedule(&iommu_pt_cleanup_tasklet); -@@ -241,6 +244,9 @@ int iommu_construct(struct domain *d) - { - struct domain_iommu *hd = dom_iommu(d); - -+ if ( d == dom_io ) -+ return 0; -+ - if ( hd->status == IOMMU_STATUS_initialized ) - return 0; - -@@ -521,6 +527,9 @@ int __init iommu_setup(void) - printk("I/O virtualisation %sabled\n", iommu_enabled ? "en" : "dis"); - if ( iommu_enabled ) - { -+ if ( iommu_domain_init(dom_io) ) -+ panic("Could not set up quarantine\n"); -+ - printk(" - Dom0 mode: %s\n", - iommu_hwdom_passthrough ? "Passthrough" : - iommu_hwdom_strict ? "Strict" : "Relaxed"); -diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c -index d7420bd8bf..d66a8a1daf 100644 ---- a/xen/drivers/passthrough/pci.c -+++ b/xen/drivers/passthrough/pci.c -@@ -1426,19 +1426,29 @@ static int iommu_remove_device(struct pci_dev *pdev) - return hd->platform_ops->remove_device(pdev->devfn, pci_to_dev(pdev)); - } - --/* -- * If the device isn't owned by the hardware domain, it means it already -- * has been assigned to other domain, or it doesn't exist. -- */ - static int device_assigned(u16 seg, u8 bus, u8 devfn) - { - struct pci_dev *pdev; -+ int rc = 0; - - pcidevs_lock(); -- pdev = pci_get_pdev_by_domain(hardware_domain, seg, bus, devfn); -+ -+ pdev = pci_get_pdev(seg, bus, devfn); -+ -+ if ( !pdev ) -+ rc = -ENODEV; -+ /* -+ * If the device exists and it is not owned by either the hardware -+ * domain or dom_io then it must be assigned to a guest, or be -+ * hidden (owned by dom_xen). -+ */ -+ else if ( pdev->domain != hardware_domain && -+ pdev->domain != dom_io ) -+ rc = -EBUSY; -+ - pcidevs_unlock(); - -- return pdev ? 0 : -EBUSY; -+ return rc; - } - - static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) -@@ -1452,7 +1462,8 @@ static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) - - /* Prevent device assign if mem paging or mem sharing have been - * enabled for this domain */ -- if ( unlikely((is_hvm_domain(d) && -+ if ( d != dom_io && -+ unlikely((is_hvm_domain(d) && - d->arch.hvm.mem_sharing_enabled) || - vm_event_check_ring(d->vm_event_paging) || - p2m_get_hostp2m(d)->global_logdirty) ) -@@ -1468,12 +1479,20 @@ static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) - return rc; - } - -- pdev = pci_get_pdev_by_domain(hardware_domain, seg, bus, devfn); -+ pdev = pci_get_pdev(seg, bus, devfn); -+ -+ rc = -ENODEV; - if ( !pdev ) -- { -- rc = pci_get_pdev(seg, bus, devfn) ? -EBUSY : -ENODEV; - goto done; -- } -+ -+ rc = 0; -+ if ( d == pdev->domain ) -+ goto done; -+ -+ rc = -EBUSY; -+ if ( pdev->domain != hardware_domain && -+ pdev->domain != dom_io ) -+ goto done; - - if ( pdev->msix ) - msixtbl_init(d); -@@ -1496,6 +1515,10 @@ static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn, u32 flag) - } - - done: -+ /* The device is assigned to dom_io so mark it as quarantined */ -+ if ( !rc && d == dom_io ) -+ pdev->quarantine = true; -+ - if ( !has_arch_pdevs(d) && has_iommu_pt(d) ) - iommu_teardown(d); - pcidevs_unlock(); -@@ -1508,6 +1531,7 @@ int deassign_device(struct domain *d, u16 seg, u8 bus, u8 devfn) - { - const struct domain_iommu *hd = dom_iommu(d); - struct pci_dev *pdev = NULL; -+ struct domain *target; - int ret = 0; - - if ( !iommu_enabled || !hd->platform_ops ) -@@ -1518,12 +1542,16 @@ int deassign_device(struct domain *d, u16 seg, u8 bus, u8 devfn) - if ( !pdev ) - return -ENODEV; - -+ /* De-assignment from dom_io should de-quarantine the device */ -+ target = (pdev->quarantine && pdev->domain != dom_io) ? -+ dom_io : hardware_domain; -+ - while ( pdev->phantom_stride ) - { - devfn += pdev->phantom_stride; - if ( PCI_SLOT(devfn) != PCI_SLOT(pdev->devfn) ) - break; -- ret = hd->platform_ops->reassign_device(d, hardware_domain, devfn, -+ ret = hd->platform_ops->reassign_device(d, target, devfn, - pci_to_dev(pdev)); - if ( !ret ) - continue; -@@ -1534,7 +1562,7 @@ int deassign_device(struct domain *d, u16 seg, u8 bus, u8 devfn) - } - - devfn = pdev->devfn; -- ret = hd->platform_ops->reassign_device(d, hardware_domain, devfn, -+ ret = hd->platform_ops->reassign_device(d, target, devfn, - pci_to_dev(pdev)); - if ( ret ) - { -@@ -1544,6 +1572,9 @@ int deassign_device(struct domain *d, u16 seg, u8 bus, u8 devfn) - return ret; - } - -+ if ( pdev->domain == hardware_domain ) -+ pdev->quarantine = false; -+ - pdev->fault.count = 0; - - if ( !has_arch_pdevs(d) && has_iommu_pt(d) ) -@@ -1722,7 +1753,7 @@ int iommu_do_pci_domctl( - ret = hypercall_create_continuation(__HYPERVISOR_domctl, - "h", u_domctl); - else if ( ret ) -- printk(XENLOG_G_ERR "XEN_DOMCTL_assign_device: " -+ printk(XENLOG_G_ERR - "assign %04x:%02x:%02x.%u to dom%d failed (%d)\n", - seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), - d->domain_id, ret); -diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c -index 1db1cd9f2d..a8d1baa064 100644 ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -1338,6 +1338,10 @@ int domain_context_mapping_one( - int agaw, rc, ret; - bool_t flush_dev_iotlb; - -+ /* dom_io is used as a sentinel for quarantined devices */ -+ if ( domain == dom_io ) -+ return 0; -+ - ASSERT(pcidevs_locked()); - spin_lock(&iommu->lock); - maddr = bus_to_context_maddr(iommu, bus); -@@ -1573,6 +1577,10 @@ int domain_context_unmap_one( - int iommu_domid, rc, ret; - bool_t flush_dev_iotlb; - -+ /* dom_io is used as a sentinel for quarantined devices */ -+ if ( domain == dom_io ) -+ return 0; -+ - ASSERT(pcidevs_locked()); - spin_lock(&iommu->lock); - -@@ -1705,6 +1713,10 @@ static int domain_context_unmap(struct domain *domain, u8 devfn, - goto out; - } - -+ /* dom_io is used as a sentinel for quarantined devices */ -+ if ( domain == dom_io ) -+ 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 -@@ -2441,6 +2453,15 @@ static int reassign_device_ownership( - if ( ret ) - return ret; - -+ if ( devfn == pdev->devfn ) -+ { -+ list_move(&pdev->domain_list, &dom_io->arch.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); - -@@ -2459,15 +2480,13 @@ static int reassign_device_ownership( - pdev->domain = target; - } - -- if ( !has_arch_pdevs(source) ) -- vmx_pi_hooks_deassign(source); -- - return ret; - } - - static int intel_iommu_assign_device( - struct domain *d, u8 devfn, struct pci_dev *pdev, u32 flag) - { -+ struct domain *s = pdev->domain; - struct acpi_rmrr_unit *rmrr; - int ret = 0, i; - u16 bdf, seg; -@@ -2510,8 +2529,8 @@ static int intel_iommu_assign_device( - } - } - -- ret = reassign_device_ownership(hardware_domain, d, devfn, pdev); -- if ( ret ) -+ ret = reassign_device_ownership(s, d, devfn, pdev); -+ if ( ret || d == dom_io ) - return ret; - - /* Setup rmrr identity mapping */ -@@ -2524,11 +2543,20 @@ static int intel_iommu_assign_device( - ret = rmrr_identity_mapping(d, 1, rmrr, flag); - if ( ret ) - { -- reassign_device_ownership(d, hardware_domain, devfn, pdev); -+ 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 %04x:%02x:%02x.%u from %pd (%d)\n", -+ seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn), d, rc); -+ domain_crash(d); -+ } - break; - } - } -diff --git a/xen/include/xen/pci.h b/xen/include/xen/pci.h -index 8b21e8dc84..a031fd6020 100644 ---- a/xen/include/xen/pci.h -+++ b/xen/include/xen/pci.h -@@ -88,6 +88,9 @@ struct pci_dev { - - nodeid_t node; /* NUMA node */ - -+ /* Device to be quarantined, don't automatically re-assign to dom0 */ -+ bool quarantine; -+ - /* Device with errata, ignore the BARs. */ - bool ignore_bars; - --- -2.11.0 - diff --git a/xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch b/xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch deleted file mode 100644 index afb1096..0000000 --- a/xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch +++ /dev/null @@ -1,74 +0,0 @@ -From c8cb33fa64c9ccbfa2a494a9dad2e0a763c09176 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Tue, 1 Oct 2019 13:07:53 +0100 -Subject: [PATCH 1/4] xen/arm32: entry: Split __DEFINE_ENTRY_TRAP in two - -The preprocessing macro __DEFINE_ENTRY_TRAP is used to generate trap -entry function. While the macro is fairly small today, follow-up patches -will increase the size signicantly. - -In general, assembly macros are more readable as they allow you to name -parameters and avoid '\'. So the actual implementation of the trap is -now switched to an assembly macro. - -This is part of XSA-303. - -Reported-by: Julien Grall -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini -Reviewed-by: Andre Przywara ---- - xen/arch/arm/arm32/entry.S | 34 +++++++++++++++++++--------------- - 1 file changed, 19 insertions(+), 15 deletions(-) - -diff --git a/xen/arch/arm/arm32/entry.S b/xen/arch/arm/arm32/entry.S -index 0b4cd19abd..4a762e04f1 100644 ---- a/xen/arch/arm/arm32/entry.S -+++ b/xen/arch/arm/arm32/entry.S -@@ -126,24 +126,28 @@ abort_guest_exit_end: - skip_check: - mov pc, lr - --/* -- * Macro to define trap entry. The iflags corresponds to the list of -- * interrupts (Asynchronous Abort, IRQ, FIQ) to unmask. -- */ -+ /* -+ * Macro to define trap entry. The iflags corresponds to the list of -+ * interrupts (Asynchronous Abort, IRQ, FIQ) to unmask. -+ */ -+ .macro vector trap, iflags -+ SAVE_ALL -+ cpsie \iflags -+ adr lr, return_from_trap -+ mov r0, sp -+ /* -+ * Save the stack pointer in r11. It will be restored after the -+ * trap has been handled (see return_from_trap). -+ */ -+ mov r11, sp -+ bic sp, #7 /* Align the stack pointer (noop on guest trap) */ -+ b do_trap_\trap -+ .endm -+ - #define __DEFINE_TRAP_ENTRY(trap, iflags) \ - ALIGN; \ - trap_##trap: \ -- SAVE_ALL; \ -- cpsie iflags; \ -- adr lr, return_from_trap; \ -- mov r0, sp; \ -- /* \ -- * Save the stack pointer in r11. It will be restored after the \ -- * trap has been handled (see return_from_trap). \ -- */ \ -- mov r11, sp; \ -- bic sp, #7; /* Align the stack pointer (noop on guest trap) */ \ -- b do_trap_##trap -+ vector trap, iflags - - /* Trap handler which unmask IRQ/Abort, keep FIQ masked */ - #define DEFINE_TRAP_ENTRY(trap) __DEFINE_TRAP_ENTRY(trap, ai) --- -2.11.0 - diff --git a/xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch b/xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch deleted file mode 100644 index 35f9c04..0000000 --- a/xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch +++ /dev/null @@ -1,97 +0,0 @@ -From be7379207c83fa74f8a6c22a8ea213f02714776f Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Tue, 1 Oct 2019 13:15:48 +0100 -Subject: [PATCH 2/4] xen/arm32: entry: Fold the macro SAVE_ALL in the macro - vector - -Follow-up rework will require the macro vector to distinguish between -a trap from a guest vs while in the hypervisor. - -The macro SAVE_ALL already has code to distinguish between the two and -it is only called by the vector macro. So fold the former into the -latter. This will help to avoid duplicating the check. - -This is part of XSA-303. - -Reported-by: Julien Grall -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini -Reviewed-by: Andre Przywara ---- - xen/arch/arm/arm32/entry.S | 46 +++++++++++++++++++++++----------------------- - 1 file changed, 23 insertions(+), 23 deletions(-) - -diff --git a/xen/arch/arm/arm32/entry.S b/xen/arch/arm/arm32/entry.S -index 4a762e04f1..150cbc0b4b 100644 ---- a/xen/arch/arm/arm32/entry.S -+++ b/xen/arch/arm/arm32/entry.S -@@ -13,27 +13,6 @@ - #define RESTORE_BANKED(mode) \ - RESTORE_ONE_BANKED(SP_##mode) ; RESTORE_ONE_BANKED(LR_##mode) ; RESTORE_ONE_BANKED(SPSR_##mode) - --#define SAVE_ALL \ -- sub sp, #(UREGS_SP_usr - UREGS_sp); /* SP, LR, SPSR, PC */ \ -- push {r0-r12}; /* Save R0-R12 */ \ -- \ -- mrs r11, ELR_hyp; /* ELR_hyp is return address. */\ -- str r11, [sp, #UREGS_pc]; \ -- \ -- str lr, [sp, #UREGS_lr]; \ -- \ -- add r11, sp, #UREGS_kernel_sizeof+4; \ -- str r11, [sp, #UREGS_sp]; \ -- \ -- mrc CP32(r11, HSR); /* Save exception syndrome */ \ -- str r11, [sp, #UREGS_hsr]; \ -- \ -- mrs r11, SPSR_hyp; \ -- str r11, [sp, #UREGS_cpsr]; \ -- and r11, #PSR_MODE_MASK; \ -- cmp r11, #PSR_MODE_HYP; \ -- blne save_guest_regs -- - save_guest_regs: - #ifdef CONFIG_ARM32_HARDEN_BRANCH_PREDICTOR - /* -@@ -52,7 +31,7 @@ save_guest_regs: - ldr r11, =0xffffffff /* Clobber SP which is only valid for hypervisor frames. */ - str r11, [sp, #UREGS_sp] - SAVE_ONE_BANKED(SP_usr) -- /* LR_usr is the same physical register as lr and is saved in SAVE_ALL */ -+ /* LR_usr is the same physical register as lr and is saved by the caller */ - SAVE_BANKED(svc) - SAVE_BANKED(abt) - SAVE_BANKED(und) -@@ -131,7 +110,28 @@ skip_check: - * interrupts (Asynchronous Abort, IRQ, FIQ) to unmask. - */ - .macro vector trap, iflags -- SAVE_ALL -+ /* Save registers in the stack */ -+ sub sp, #(UREGS_SP_usr - UREGS_sp) /* SP, LR, SPSR, PC */ -+ push {r0-r12} /* Save R0-R12 */ -+ mrs r11, ELR_hyp /* ELR_hyp is return address */ -+ str r11, [sp, #UREGS_pc] -+ -+ str lr, [sp, #UREGS_lr] -+ -+ add r11, sp, #(UREGS_kernel_sizeof + 4) -+ -+ str r11, [sp, #UREGS_sp] -+ -+ mrc CP32(r11, HSR) /* Save exception syndrome */ -+ str r11, [sp, #UREGS_hsr] -+ -+ mrs r11, SPSR_hyp -+ str r11, [sp, #UREGS_cpsr] -+ and r11, #PSR_MODE_MASK -+ cmp r11, #PSR_MODE_HYP -+ blne save_guest_regs -+ -+ /* We are ready to handle the trap, setup the registers and jump. */ - cpsie \iflags - adr lr, return_from_trap - mov r0, sp --- -2.11.0 - diff --git a/xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch b/xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch deleted file mode 100644 index 5168452..0000000 --- a/xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch +++ /dev/null @@ -1,226 +0,0 @@ -From 098fe877967870ffda2dfd9629a5fd272f6aacdc Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Fri, 11 Oct 2019 17:49:28 +0100 -Subject: [PATCH 3/4] xen/arm32: Don't blindly unmask interrupts on trap - without a change of level - -Exception vectors will unmask interrupts regardless the state of them in -the interrupted context. - -One of the consequences is IRQ will be unmasked when receiving an -undefined instruction exception (used by WARN*) from the hypervisor. -This could result to unexpected behavior such as deadlock (if a lock was -shared with interrupts). - -In a nutshell, interrupts should only be unmasked when it is safe to do. -Xen only unmask IRQ and Abort interrupts, so the logic can stay simple. - -As vectors exceptions may be shared between guest and hypervisor, we now -need to have a different policy for the interrupts. - -On exception from hypervisor, each vector will select the list of -interrupts to inherit from the interrupted context. Any interrupts not -listed will be kept masked. - -On exception from the guest, the Abort and IRQ will be unmasked -depending on the exact vector. - -The interrupts will be kept unmasked when the vector cannot used by -either guest or hypervisor. - -Note that each vector is not anymore preceded by ALIGN. This is fine -because the alignment is already bigger than what we need. - -This is part of XSA-303. - -Reported-by: Julien Grall -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini -Reviewed-by: Andre Przywara ---- - xen/arch/arm/arm32/entry.S | 138 +++++++++++++++++++++++++++++++++++---------- - 1 file changed, 109 insertions(+), 29 deletions(-) - -diff --git a/xen/arch/arm/arm32/entry.S b/xen/arch/arm/arm32/entry.S -index 150cbc0b4b..ec90cca093 100644 ---- a/xen/arch/arm/arm32/entry.S -+++ b/xen/arch/arm/arm32/entry.S -@@ -4,6 +4,17 @@ - #include - #include - -+/* -+ * Short-hands to defined the interrupts (A, I, F) -+ * -+ * _ means the interrupt state will not change -+ * X means the state of interrupt X will change -+ * -+ * To be used with msr cpsr_* only -+ */ -+#define IFLAGS_AIF PSR_ABT_MASK | PSR_IRQ_MASK | PSR_FIQ_MASK -+#define IFLAGS_A_F PSR_ABT_MASK | PSR_FIQ_MASK -+ - #define SAVE_ONE_BANKED(reg) mrs r11, reg; str r11, [sp, #UREGS_##reg] - #define RESTORE_ONE_BANKED(reg) ldr r11, [sp, #UREGS_##reg]; msr reg, r11 - -@@ -106,10 +117,18 @@ skip_check: - mov pc, lr - - /* -- * Macro to define trap entry. The iflags corresponds to the list of -- * interrupts (Asynchronous Abort, IRQ, FIQ) to unmask. -+ * Macro to define a trap entry. -+ * -+ * @guest_iflags: Optional list of interrupts to unmask when -+ * entering from guest context. As this is used with cpsie, -+ * the letter (a, i, f) should be used. -+ * -+ * @hyp_iflags: Optional list of interrupts to inherit when -+ * entering from hypervisor context. Any interrupts not -+ * listed will be kept unchanged. As this is used with cpsr_*, -+ * IFLAGS_* short-hands should be used. - */ -- .macro vector trap, iflags -+ .macro vector trap, guest_iflags=n, hyp_iflags=0 - /* Save registers in the stack */ - sub sp, #(UREGS_SP_usr - UREGS_sp) /* SP, LR, SPSR, PC */ - push {r0-r12} /* Save R0-R12 */ -@@ -127,12 +146,39 @@ skip_check: - - mrs r11, SPSR_hyp - str r11, [sp, #UREGS_cpsr] -- and r11, #PSR_MODE_MASK -- cmp r11, #PSR_MODE_HYP -- blne save_guest_regs - -+ /* -+ * We need to distinguish whether we came from guest or -+ * hypervisor context. -+ */ -+ and r0, r11, #PSR_MODE_MASK -+ cmp r0, #PSR_MODE_HYP -+ -+ bne 1f -+ /* -+ * Trap from the hypervisor -+ * -+ * Inherit the state of the interrupts from the hypervisor -+ * context. For that we need to use SPSR (stored in r11) and -+ * modify CPSR accordingly. -+ * -+ * CPSR = (CPSR & ~hyp_iflags) | (SPSR & hyp_iflags) -+ */ -+ mrs r10, cpsr -+ bic r10, r10, #\hyp_iflags -+ and r11, r11, #\hyp_iflags -+ orr r10, r10, r11 -+ msr cpsr_cx, r10 -+ b 2f -+ -+1: -+ /* Trap from the guest */ -+ bl save_guest_regs -+ .if \guest_iflags != n -+ cpsie \guest_iflags -+ .endif -+2: - /* We are ready to handle the trap, setup the registers and jump. */ -- cpsie \iflags - adr lr, return_from_trap - mov r0, sp - /* -@@ -144,20 +190,6 @@ skip_check: - b do_trap_\trap - .endm - --#define __DEFINE_TRAP_ENTRY(trap, iflags) \ -- ALIGN; \ --trap_##trap: \ -- vector trap, iflags -- --/* Trap handler which unmask IRQ/Abort, keep FIQ masked */ --#define DEFINE_TRAP_ENTRY(trap) __DEFINE_TRAP_ENTRY(trap, ai) -- --/* Trap handler which unmask Abort, keep IRQ/FIQ masked */ --#define DEFINE_TRAP_ENTRY_NOIRQ(trap) __DEFINE_TRAP_ENTRY(trap, a) -- --/* Trap handler which unmask IRQ, keep Abort/FIQ masked */ --#define DEFINE_TRAP_ENTRY_NOABORT(trap) __DEFINE_TRAP_ENTRY(trap, i) -- - .align 5 - GLOBAL(hyp_traps_vector) - b trap_reset /* 0x00 - Reset */ -@@ -228,14 +260,62 @@ decode_vectors: - - #endif /* CONFIG_HARDEN_BRANCH_PREDICTOR */ - --DEFINE_TRAP_ENTRY(reset) --DEFINE_TRAP_ENTRY(undefined_instruction) --DEFINE_TRAP_ENTRY(hypervisor_call) --DEFINE_TRAP_ENTRY(prefetch_abort) --DEFINE_TRAP_ENTRY(guest_sync) --DEFINE_TRAP_ENTRY_NOIRQ(irq) --DEFINE_TRAP_ENTRY_NOIRQ(fiq) --DEFINE_TRAP_ENTRY_NOABORT(data_abort) -+/* Vector not used by the Hypervisor. */ -+trap_reset: -+ vector reset -+ -+/* -+ * Vector only used by the Hypervisor. -+ * -+ * While the exception can be executed with all the interrupts (e.g. -+ * IRQ) unmasked, the interrupted context may have purposefully masked -+ * some of them. So we want to inherit the state from the interrupted -+ * context. -+ */ -+trap_undefined_instruction: -+ vector undefined_instruction, hyp_iflags=IFLAGS_AIF -+ -+/* We should never reach this trap */ -+trap_hypervisor_call: -+ vector hypervisor_call -+ -+/* -+ * Vector only used by the hypervisor. -+ * -+ * While the exception can be executed with all the interrupts (e.g. -+ * IRQ) unmasked, the interrupted context may have purposefully masked -+ * some of them. So we want to inherit the state from the interrupted -+ * context. -+ */ -+trap_prefetch_abort: -+ vector prefetch_abort, hyp_iflags=IFLAGS_AIF -+ -+/* -+ * Vector only used by the hypervisor. -+ * -+ * Data Abort should be rare and most likely fatal. It is best to not -+ * unmask any interrupts to limit the amount of code that can run before -+ * the Data Abort is treated. -+ */ -+trap_data_abort: -+ vector data_abort -+ -+/* Vector only used by the guest. We can unmask Abort/IRQ. */ -+trap_guest_sync: -+ vector guest_sync, guest_iflags=ai -+ -+ -+/* Vector used by the hypervisor and the guest. */ -+trap_irq: -+ vector irq, guest_iflags=a, hyp_iflags=IFLAGS_A_F -+ -+/* -+ * Vector used by the hypervisor and the guest. -+ * -+ * FIQ are not meant to happen, so we don't unmask any interrupts. -+ */ -+trap_fiq: -+ vector fiq - - return_from_trap: - /* --- -2.11.0 - diff --git a/xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch b/xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch deleted file mode 100644 index 106cbf9..0000000 --- a/xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch +++ /dev/null @@ -1,114 +0,0 @@ -From c6d290ce157a044dec417fdda8db71e41a37d744 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 7 Oct 2019 18:10:56 +0100 -Subject: [PATCH 4/4] xen/arm64: Don't blindly unmask interrupts on trap - without a change of level - -Some of the traps without a change of the level (i.e. hypervisor -> -hypervisor) will unmask interrupts regardless the state of them in the -interrupted context. - -One of the consequences is IRQ will be unmasked when receiving a -synchronous exception (used by WARN*()). This could result to unexpected -behavior such as deadlock (if a lock was shared with interrupts). - -In a nutshell, interrupts should only be unmasked when it is safe to -do. Xen only unmask IRQ and Abort interrupts, so the logic can stay -simple: - - hyp_error: All the interrupts are now kept masked. SError should - be pretty rare and if ever happen then we most likely want to - avoid any other interrupts to be generated. The potential main - "caller" is during virtual SError synchronization on the exit - path from the guest (see check_pending_vserror). - - - hyp_sync: The interrupts state is inherited from the interrupted - context. - - - hyp_irq: All the interrupts but IRQ state are inherited from the - interrupted context. IRQ is kept masked. - -This is part of XSA-303. - -Reported-by: Julien Grall -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini -Reviewed-by: Andre Przywara ---- - xen/arch/arm/arm64/entry.S | 47 ++++++++++++++++++++++++++++++++++++++++++---- - 1 file changed, 43 insertions(+), 4 deletions(-) - -diff --git a/xen/arch/arm/arm64/entry.S b/xen/arch/arm/arm64/entry.S -index 2d9a2713a1..3e41ba65b6 100644 ---- a/xen/arch/arm/arm64/entry.S -+++ b/xen/arch/arm/arm64/entry.S -@@ -188,24 +188,63 @@ hyp_error_invalid: - entry hyp=1 - invalid BAD_ERROR - -+/* -+ * SError received while running in the hypervisor mode. -+ * -+ * Technically, we could unmask the IRQ if it were unmasked in the -+ * interrupted context. However, this require to check the PSTATE. For -+ * simplicity, as SError should be rare and potentially fatal, -+ * all interrupts are kept masked. -+ */ - hyp_error: - entry hyp=1 -- msr daifclr, #2 - mov x0, sp - bl do_trap_hyp_serror - exit hyp=1 - --/* Traps taken in Current EL with SP_ELx */ -+/* -+ * Synchronous exception received while running in the hypervisor mode. -+ * -+ * While the exception could be executed with all the interrupts (e.g. -+ * IRQ) unmasked, the interrupted context may have purposefully masked -+ * some of them. So we want to inherit the state from the interrupted -+ * context. -+ */ - hyp_sync: - entry hyp=1 -- msr daifclr, #6 -+ -+ /* Inherit interrupts */ -+ mrs x0, SPSR_el2 -+ and x0, x0, #(PSR_DBG_MASK | PSR_ABT_MASK | PSR_IRQ_MASK | PSR_FIQ_MASK) -+ msr daif, x0 -+ - mov x0, sp - bl do_trap_hyp_sync - exit hyp=1 - -+/* -+ * IRQ received while running in the hypervisor mode. -+ * -+ * While the exception could be executed with all the interrupts but IRQ -+ * unmasked, the interrupted context may have purposefully masked some -+ * of them. So we want to inherit the state from the interrupt context -+ * and keep IRQ masked. -+ * -+ * XXX: We may want to consider an ordering between interrupts (e.g. if -+ * SError are masked, then IRQ should be masked too). However, this -+ * would require some rework in some paths (e.g. panic, livepatch) to -+ * ensure the ordering is enforced everywhere. -+ */ - hyp_irq: - entry hyp=1 -- msr daifclr, #4 -+ -+ /* Inherit D, A, F interrupts and keep I masked */ -+ mrs x0, SPSR_el2 -+ mov x1, #(PSR_DBG_MASK | PSR_ABT_MASK | PSR_FIQ_MASK) -+ and x0, x0, x1 -+ orr x0, x0, #PSR_IRQ_MASK -+ msr daif, x0 -+ - mov x0, sp - bl do_trap_irq - exit hyp=1 --- -2.11.0 - diff --git a/xsa304-4.12-1.patch b/xsa304-4.12-1.patch deleted file mode 100644 index c2ed2c2..0000000 --- a/xsa304-4.12-1.patch +++ /dev/null @@ -1,71 +0,0 @@ -From: Andrew Cooper -Subject: x86/vtd: Hide superpage support for SandyBridge IOMMUs - -Something causes SandyBridge IOMMUs to choke when sharing EPT pagetables, and -an EPT superpage gets shattered. The root cause is still under investigation, -but the end result is unusable in combination with CVE-2018-12207 protections. - -This is part of XSA-304 / CVE-2018-12207 - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/drivers/passthrough/vtd/extern.h b/xen/drivers/passthrough/vtd/extern.h -index 16eada9fa2..a71c8b0f84 100644 ---- a/xen/drivers/passthrough/vtd/extern.h -+++ b/xen/drivers/passthrough/vtd/extern.h -@@ -97,6 +97,8 @@ void vtd_ops_postamble_quirk(struct iommu* iommu); - int __must_check me_wifi_quirk(struct domain *domain, - u8 bus, u8 devfn, int map); - void pci_vtd_quirk(const struct pci_dev *); -+void quirk_iommu_caps(struct iommu *iommu); -+ - bool_t platform_supports_intremap(void); - bool_t platform_supports_x2apic(void); - -diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c -index b3664ecbe0..5d34f75306 100644 ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -1215,6 +1215,8 @@ int __init iommu_alloc(struct acpi_drhd_unit *drhd) - if ( !(iommu->cap + 1) || !(iommu->ecap + 1) ) - return -ENODEV; - -+ quirk_iommu_caps(iommu); -+ - if ( cap_fault_reg_offset(iommu->cap) + - cap_num_fault_regs(iommu->cap) * PRIMARY_FAULT_REG_LEN >= PAGE_SIZE || - ecap_iotlb_offset(iommu->ecap) >= PAGE_SIZE ) -diff --git a/xen/drivers/passthrough/vtd/quirks.c b/xen/drivers/passthrough/vtd/quirks.c -index d6db862678..b02688e316 100644 ---- a/xen/drivers/passthrough/vtd/quirks.c -+++ b/xen/drivers/passthrough/vtd/quirks.c -@@ -540,3 +540,28 @@ void pci_vtd_quirk(const struct pci_dev *pdev) - break; - } - } -+ -+void __init quirk_iommu_caps(struct iommu *iommu) -+{ -+ /* -+ * IOMMU Quirks: -+ * -+ * SandyBridge IOMMUs claim support for 2M and 1G superpages, but don't -+ * implement superpages internally. -+ * -+ * There are issues changing the walk length under in-flight DMA, which -+ * has manifested as incompatibility between EPT/IOMMU sharing and the -+ * workaround for CVE-2018-12207 / XSA-304. Hide the superpages -+ * capabilities in the IOMMU, which will prevent Xen from sharing the EPT -+ * and IOMMU pagetables. -+ * -+ * Detection of SandyBridge unfortunately has to be done by processor -+ * model because the client parts don't expose their IOMMUs as PCI devices -+ * we could match with a Device ID. -+ */ -+ if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && -+ boot_cpu_data.x86 == 6 && -+ (boot_cpu_data.x86_model == 0x2a || -+ boot_cpu_data.x86_model == 0x2d) ) -+ iommu->cap &= ~(0xful << 34); -+} diff --git a/xsa304-4.12-2.patch b/xsa304-4.12-2.patch deleted file mode 100644 index 66d4301..0000000 --- a/xsa304-4.12-2.patch +++ /dev/null @@ -1,272 +0,0 @@ -From: Andrew Cooper -Subject: x86/vtx: Disable executable EPT superpages to work around - CVE-2018-12207 - -CVE-2018-12207 covers a set of errata on various Intel processors, whereby a -machine check exception can be generated in a corner case when an executable -mapping changes size or cacheability without TLB invalidation. HVM guest -kernels can trigger this to DoS the host. - -To mitigate, in affected hardware, all EPT superpages are marked NX. When an -instruction fetch violation is observed against the superpage, the superpage -is shattered to 4k and has execute permissions restored. This prevents the -guest kernel from being able to create the necessary preconditions in the iTLB -to exploit the vulnerability. - -This does come with a workload-dependent performance overhead, caused by -increased TLB pressure. Performance can be restored, if guest kernels are -trusted not to mount an attack, by specifying ept=exec-sp on the command line. - -This is part of XSA-304 / CVE-2018-12207 - -Signed-off-by: Andrew Cooper -Acked-by: George Dunlap -Reviewed-by: Jan Beulich - -diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc -index 85081fdc94..e283017015 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -895,7 +895,7 @@ Controls for interacting with the system Extended Firmware Interface. - uncacheable. - - ### ept --> `= List of [ ad=, pml= ]` -+> `= List of [ ad=, pml=, exec-sp= ]` - - > Applicability: Intel - -@@ -926,6 +926,16 @@ introduced with the Nehalem architecture. - disable PML. `pml=0` can be used to prevent the use of PML on otherwise - capable hardware. - -+* The `exec-sp` boolean controls whether EPT superpages with execute -+ permissions are permitted. In general this is good for performance. -+ -+ However, on processors vulnerable CVE-2018-12207, HVM guest kernels can -+ use executable superpages to crash the host. By default, executable -+ superpages are disabled on affected hardware. -+ -+ If HVM guest kernels are trusted not to mount a DoS against the system, -+ this option can enabled to regain performance. -+ - ### extra_guest_irqs - > `= [][,]` - -diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c -index 2089a77270..84191d4e4b 100644 ---- a/xen/arch/x86/hvm/hvm.c -+++ b/xen/arch/x86/hvm/hvm.c -@@ -1814,6 +1814,24 @@ int hvm_hap_nested_page_fault(paddr_t gpa, unsigned long gla, - break; - } - -+ /* -+ * Workaround for XSA-304 / CVE-2018-12207. If we take an execution -+ * fault against a non-executable superpage, shatter it to regain -+ * execute permissions. -+ */ -+ if ( page_order > 0 && npfec.insn_fetch && npfec.present && !violation ) -+ { -+ int res = p2m_set_entry(p2m, _gfn(gfn), mfn, PAGE_ORDER_4K, -+ p2mt, p2ma); -+ -+ if ( res ) -+ printk(XENLOG_ERR "Failed to shatter gfn %"PRI_gfn": %d\n", -+ gfn, res); -+ -+ rc = !res; -+ goto out_put_gfn; -+ } -+ - if ( violation ) - { - /* Should #VE be emulated for this fault? */ -diff --git a/xen/arch/x86/hvm/vmx/vmcs.c b/xen/arch/x86/hvm/vmx/vmcs.c -index 56519fee84..ec5ab860ad 100644 ---- a/xen/arch/x86/hvm/vmx/vmcs.c -+++ b/xen/arch/x86/hvm/vmx/vmcs.c -@@ -67,6 +67,7 @@ integer_param("ple_window", ple_window); - - static bool __read_mostly opt_ept_pml = true; - static s8 __read_mostly opt_ept_ad = -1; -+int8_t __read_mostly opt_ept_exec_sp = -1; - - static int __init parse_ept_param(const char *s) - { -@@ -82,6 +83,8 @@ static int __init parse_ept_param(const char *s) - opt_ept_ad = val; - else if ( (val = parse_boolean("pml", s, ss)) >= 0 ) - opt_ept_pml = val; -+ else if ( (val = parse_boolean("exec-sp", s, ss)) >= 0 ) -+ opt_ept_exec_sp = val; - else - rc = -EINVAL; - -diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c -index 26b7ddb5fe..28cba8ec28 100644 ---- a/xen/arch/x86/hvm/vmx/vmx.c -+++ b/xen/arch/x86/hvm/vmx/vmx.c -@@ -2445,6 +2445,102 @@ static void pi_notification_interrupt(struct cpu_user_regs *regs) - static void __init lbr_tsx_fixup_check(void); - static void __init bdw_erratum_bdf14_fixup_check(void); - -+/* -+ * Calculate whether the CPU is vulnerable to Instruction Fetch page -+ * size-change MCEs. -+ */ -+static bool __init has_if_pschange_mc(void) -+{ -+ uint64_t caps = 0; -+ -+ /* -+ * If we are virtualised, there is nothing we can do. Our EPT tables are -+ * shadowed by our hypervisor, and not walked by hardware. -+ */ -+ if ( cpu_has_hypervisor ) -+ return false; -+ -+ if ( boot_cpu_has(X86_FEATURE_ARCH_CAPS) ) -+ rdmsrl(MSR_ARCH_CAPABILITIES, caps); -+ -+ if ( caps & ARCH_CAPS_IF_PSCHANGE_MC_NO ) -+ return false; -+ -+ /* -+ * IF_PSCHANGE_MC 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 false; -+ -+ switch ( boot_cpu_data.x86_model ) -+ { -+ /* -+ * Core processors since at least Nehalem are vulnerable. -+ */ -+ case 0x1f: /* Auburndale / Havendale */ -+ case 0x1e: /* Nehalem */ -+ case 0x1a: /* Nehalem EP */ -+ case 0x2e: /* Nehalem EX */ -+ case 0x25: /* Westmere */ -+ case 0x2c: /* Westmere EP */ -+ case 0x2f: /* Westmere EX */ -+ case 0x2a: /* SandyBridge */ -+ case 0x2d: /* SandyBridge EP/EX */ -+ case 0x3a: /* IvyBridge */ -+ case 0x3e: /* IvyBridge EP/EX */ -+ case 0x3c: /* Haswell */ -+ case 0x3f: /* Haswell EX/EP */ -+ case 0x45: /* Haswell D */ -+ case 0x46: /* Haswell H */ -+ case 0x3d: /* Broadwell */ -+ case 0x47: /* Broadwell H */ -+ case 0x4f: /* Broadwell EP/EX */ -+ case 0x56: /* Broadwell D */ -+ case 0x4e: /* Skylake M */ -+ case 0x5e: /* Skylake D */ -+ case 0x55: /* Skylake-X / Cascade Lake */ -+ case 0x8e: /* Kaby / Coffee / Whiskey Lake M */ -+ case 0x9e: /* Kaby / Coffee / Whiskey Lake D */ -+ return true; -+ -+ /* -+ * Atom processors are not vulnerable. -+ */ -+ case 0x1c: /* Pineview */ -+ case 0x26: /* Lincroft */ -+ case 0x27: /* Penwell */ -+ case 0x35: /* Cloverview */ -+ case 0x36: /* Cedarview */ -+ case 0x37: /* Baytrail / Valleyview (Silvermont) */ -+ case 0x4d: /* Avaton / Rangely (Silvermont) */ -+ case 0x4c: /* Cherrytrail / Brasswell */ -+ case 0x4a: /* Merrifield */ -+ case 0x5a: /* Moorefield */ -+ case 0x5c: /* Goldmont */ -+ case 0x5d: /* SoFIA 3G Granite/ES2.1 */ -+ case 0x65: /* SoFIA LTE AOSP */ -+ case 0x5f: /* Denverton */ -+ case 0x6e: /* Cougar Mountain */ -+ case 0x75: /* Lightning Mountain */ -+ case 0x7a: /* Gemini Lake */ -+ case 0x86: /* Jacobsville */ -+ -+ /* -+ * Knights processors are not vulnerable. -+ */ -+ case 0x57: /* Knights Landing */ -+ case 0x85: /* Knights Mill */ -+ return false; -+ -+ default: -+ printk("Unrecognised CPU model %#x - assuming vulnerable to IF_PSCHANGE_MC\n", -+ boot_cpu_data.x86_model); -+ return true; -+ } -+} -+ - const struct hvm_function_table * __init start_vmx(void) - { - set_in_cr4(X86_CR4_VMXE); -@@ -2465,6 +2561,17 @@ const struct hvm_function_table * __init start_vmx(void) - */ - if ( cpu_has_vmx_ept && (cpu_has_vmx_pat || opt_force_ept) ) - { -+ bool cpu_has_bug_pschange_mc = has_if_pschange_mc(); -+ -+ if ( opt_ept_exec_sp == -1 ) -+ { -+ /* Default to non-executable superpages on vulnerable hardware. */ -+ opt_ept_exec_sp = !cpu_has_bug_pschange_mc; -+ -+ if ( cpu_has_bug_pschange_mc ) -+ printk("VMX: Disabling executable EPT superpages due to CVE-2018-12207\n"); -+ } -+ - vmx_function_table.hap_supported = 1; - vmx_function_table.altp2m_supported = 1; - -diff --git a/xen/arch/x86/mm/p2m-ept.c b/xen/arch/x86/mm/p2m-ept.c -index 952ebad82f..834d4798c8 100644 ---- a/xen/arch/x86/mm/p2m-ept.c -+++ b/xen/arch/x86/mm/p2m-ept.c -@@ -174,6 +174,12 @@ static void ept_p2m_type_to_flags(struct p2m_domain *p2m, ept_entry_t *entry, - break; - } - -+ /* -+ * Don't create executable superpages if we need to shatter them to -+ * protect against CVE-2018-12207. -+ */ -+ if ( !opt_ept_exec_sp && is_epte_superpage(entry) ) -+ entry->x = 0; - } - - #define GUEST_TABLE_MAP_FAILED 0 -diff --git a/xen/include/asm-x86/hvm/vmx/vmx.h b/xen/include/asm-x86/hvm/vmx/vmx.h -index ebaa74449b..371b912887 100644 ---- a/xen/include/asm-x86/hvm/vmx/vmx.h -+++ b/xen/include/asm-x86/hvm/vmx/vmx.h -@@ -28,6 +28,8 @@ - #include - #include - -+extern int8_t opt_ept_exec_sp; -+ - typedef union { - struct { - u64 r : 1, /* bit 0 - Read permission */ -diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h -index 637259bd1f..32746aa8ae 100644 ---- a/xen/include/asm-x86/msr-index.h -+++ b/xen/include/asm-x86/msr-index.h -@@ -52,6 +52,7 @@ - #define ARCH_CAPS_SKIP_L1DFL (_AC(1, ULL) << 3) - #define ARCH_CAPS_SSB_NO (_AC(1, ULL) << 4) - #define ARCH_CAPS_MDS_NO (_AC(1, ULL) << 5) -+#define ARCH_CAPS_IF_PSCHANGE_MC_NO (_AC(1, ULL) << 6) - - #define MSR_FLUSH_CMD 0x0000010b - #define FLUSH_CMD_L1D (_AC(1, ULL) << 0) diff --git a/xsa304-4.12-3.patch b/xsa304-4.12-3.patch deleted file mode 100644 index 04b4c45..0000000 --- a/xsa304-4.12-3.patch +++ /dev/null @@ -1,108 +0,0 @@ -From: Andrew Cooper -Subject: x86/vtx: Allow runtime modification of the exec-sp setting - -See patch for details. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -Reviewed-by: George Dunlap - -diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc -index e283017015..84221fe60a 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -936,6 +936,21 @@ introduced with the Nehalem architecture. - If HVM guest kernels are trusted not to mount a DoS against the system, - this option can enabled to regain performance. - -+ This boolean may be modified at runtime using `xl set-parameters -+ ept=[no-]exec-sp` to switch between fast and secure. -+ -+ * When switching from secure to fast, preexisting HVM domains will run -+ at their current performance until they are rebooted; new domains will -+ run without any overhead. -+ -+ * When switching from fast to secure, all HVM domains will immediately -+ suffer a performance penalty. -+ -+ **Warning: No guarantee is made that this runtime option will be retained -+ indefinitely, or that it will retain this exact behaviour. It is -+ intended as an emergency option for people who first chose fast, then -+ change their minds to secure, and wish not to reboot.** -+ - ### extra_guest_irqs - > `= [][,]` - -diff --git a/xen/arch/x86/hvm/vmx/vmcs.c b/xen/arch/x86/hvm/vmx/vmcs.c -index ec5ab860ad..c4d8a5ba78 100644 ---- a/xen/arch/x86/hvm/vmx/vmcs.c -+++ b/xen/arch/x86/hvm/vmx/vmcs.c -@@ -95,6 +95,41 @@ static int __init parse_ept_param(const char *s) - } - custom_param("ept", parse_ept_param); - -+static int parse_ept_param_runtime(const char *s) -+{ -+ int val; -+ -+ if ( !cpu_has_vmx_ept || !hvm_funcs.hap_supported || -+ !(hvm_funcs.hap_capabilities & -+ (HVM_HAP_SUPERPAGE_2MB | HVM_HAP_SUPERPAGE_1GB)) ) -+ { -+ printk("VMX: EPT not available, or not in use - ignoring\n"); -+ return 0; -+ } -+ -+ if ( (val = parse_boolean("exec-sp", s, NULL)) < 0 ) -+ return -EINVAL; -+ -+ if ( val != opt_ept_exec_sp ) -+ { -+ struct domain *d; -+ -+ opt_ept_exec_sp = val; -+ -+ rcu_read_lock(&domlist_read_lock); -+ for_each_domain ( d ) -+ if ( paging_mode_hap(d) ) -+ p2m_change_entry_type_global(d, p2m_ram_rw, p2m_ram_rw); -+ rcu_read_unlock(&domlist_read_lock); -+ } -+ -+ printk("VMX: EPT executable superpages %sabled\n", -+ val ? "en" : "dis"); -+ -+ return 0; -+} -+custom_runtime_only_param("ept", parse_ept_param_runtime); -+ - /* Dynamic (run-time adjusted) execution control flags. */ - u32 vmx_pin_based_exec_control __read_mostly; - u32 vmx_cpu_based_exec_control __read_mostly; -diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c -index f518f86493..16608098b1 100644 ---- a/xen/arch/x86/mm/p2m.c -+++ b/xen/arch/x86/mm/p2m.c -@@ -289,15 +289,20 @@ static void change_entry_type_global(struct p2m_domain *p2m, - p2m_type_t ot, p2m_type_t nt) - { - p2m->change_entry_type_global(p2m, ot, nt); -- p2m->global_logdirty = (nt == p2m_ram_logdirty); -+ /* Don't allow 'recalculate' operations to change the logdirty state. */ -+ if ( ot != nt ) -+ p2m->global_logdirty = (nt == p2m_ram_logdirty); - } - -+/* -+ * May be called with ot = nt = p2m_ram_rw for its side effect of -+ * recalculating all PTEs in the p2m. -+ */ - void p2m_change_entry_type_global(struct domain *d, - p2m_type_t ot, p2m_type_t nt) - { - struct p2m_domain *hostp2m = p2m_get_hostp2m(d); - -- ASSERT(ot != nt); - ASSERT(p2m_is_changeable(ot) && p2m_is_changeable(nt)); - - p2m_lock(hostp2m); diff --git a/xsa305-4.12-1.patch b/xsa305-4.12-1.patch deleted file mode 100644 index e1a91a5..0000000 --- a/xsa305-4.12-1.patch +++ /dev/null @@ -1,288 +0,0 @@ -From: Andrew Cooper -Subject: x86/tsx: Introduce tsx= to use MSR_TSX_CTRL when available - -To protect against the TSX Async Abort speculative vulnerability, Intel have -released new microcode for affected parts which introduce the MSR_TSX_CTRL -control, which allows TSX to be turned off. This will be architectural on -future parts. - -Introduce tsx= to provide a global on/off for TSX, including its enumeration -via CPUID. Provide stub virtualisation of this MSR, as it is not exposed to -guests at the moment. - -VMs may have booted before microcode is loaded, or before hosts have rebooted, -and they still want to migrate freely. A VM which booted seeing TSX can -migrate safely to hosts with TSX disabled - TSX will start unconditionally -aborting, but still behave in a manner compatible with the ABI. - -The guest-visible behaviour is equivalent to late loading the microcode and -setting the RTM_DISABLE bit in the course of live patching. - -This is part of XSA-305 / CVE-2019-11135 - -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 e283017015..b7e1bf8e8b 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -2033,6 +2033,20 @@ Xen version. - ### tsc (x86) - > `= unstable | skewed | stable:socket` - -+### tsx -+ = -+ -+ Applicability: x86 -+ Default: true -+ -+Controls for the use of Transactional Synchronization eXtensions. -+ -+On Intel parts released in Q3 2019 (with updated microcode), and future parts, -+a control has been introduced which allows TSX to be turned off. -+ -+On systems with the ability to turn TSX off, this boolean offers system wide -+control of whether TSX is enabled or disabled. -+ - ### ucode (x86) - > `= [ | scan]` - -diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile -index 8a8d8f060f..9b9a4435fb 100644 ---- a/xen/arch/x86/Makefile -+++ b/xen/arch/x86/Makefile -@@ -66,6 +66,7 @@ obj-y += sysctl.o - obj-y += time.o - obj-y += trace.o - obj-y += traps.o -+obj-y += tsx.o - obj-y += usercopy.o - obj-y += x86_emulate.o - obj-$(CONFIG_TBOOT) += tboot.o -diff --git a/xen/arch/x86/cpuid.c b/xen/arch/x86/cpuid.c -index 57e80694f2..1727497459 100644 ---- a/xen/arch/x86/cpuid.c -+++ b/xen/arch/x86/cpuid.c -@@ -524,6 +524,20 @@ void recalculate_cpuid_policy(struct domain *d) - if ( cpu_has_itsc && (d->disable_migrate || d->arch.vtsc) ) - __set_bit(X86_FEATURE_ITSC, max_fs); - -+ /* -+ * On hardware with MSR_TSX_CTRL, the admin may have elected to disable -+ * TSX and hide the feature bits. Migrating-in VMs may have been booted -+ * pre-mitigation when the TSX features were visbile. -+ * -+ * This situation is compatible (albeit with a perf hit to any TSX code in -+ * the guest), so allow the feature bits to remain set. -+ */ -+ if ( cpu_has_tsx_ctrl ) -+ { -+ __set_bit(X86_FEATURE_HLE, max_fs); -+ __set_bit(X86_FEATURE_RTM, max_fs); -+ } -+ - /* Clamp the toolstacks choices to reality. */ - for ( i = 0; i < ARRAY_SIZE(fs); i++ ) - fs[i] &= max_fs[i]; -diff --git a/xen/arch/x86/msr.c b/xen/arch/x86/msr.c -index 56de0fe9e1..c2722d7c73 100644 ---- a/xen/arch/x86/msr.c -+++ b/xen/arch/x86/msr.c -@@ -132,6 +132,7 @@ int guest_rdmsr(const struct vcpu *v, uint32_t msr, uint64_t *val) - case MSR_FLUSH_CMD: - /* Write-only */ - case MSR_TSX_FORCE_ABORT: -+ case MSR_TSX_CTRL: - /* Not offered to guests. */ - goto gp_fault; - -@@ -260,6 +261,7 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) - case MSR_ARCH_CAPABILITIES: - /* Read-only */ - case MSR_TSX_FORCE_ABORT: -+ case MSR_TSX_CTRL: - /* Not offered to guests. */ - goto gp_fault; - -diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c -index cf790f36ef..c1c7c44000 100644 ---- a/xen/arch/x86/setup.c -+++ b/xen/arch/x86/setup.c -@@ -1594,6 +1594,8 @@ void __init noreturn __start_xen(unsigned long mbi_p) - - early_microcode_init(); - -+ tsx_init(); /* Needs microcode. May change HLE/RTM feature bits. */ -+ - identify_cpu(&boot_cpu_data); - - set_in_cr4(X86_CR4_OSFXSR | X86_CR4_OSXMMEXCPT); -diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c -index 737a44f055..e21cf0a310 100644 ---- a/xen/arch/x86/smpboot.c -+++ b/xen/arch/x86/smpboot.c -@@ -376,6 +376,8 @@ void start_secondary(void *unused) - if ( boot_cpu_has(X86_FEATURE_IBRSB) ) - wrmsrl(MSR_SPEC_CTRL, default_xen_spec_ctrl); - -+ tsx_init(); /* Needs microcode. May change HLE/RTM feature bits. */ -+ - if ( xen_guest ) - hypervisor_ap_setup(); - -diff --git a/xen/arch/x86/tsx.c b/xen/arch/x86/tsx.c -new file mode 100644 -index 0000000000..a8ec2ccc69 ---- /dev/null -+++ b/xen/arch/x86/tsx.c -@@ -0,0 +1,74 @@ -+#include -+#include -+ -+/* -+ * Valid values: -+ * 1 => Explicit tsx=1 -+ * 0 => Explicit tsx=0 -+ * -1 => Default, implicit tsx=1 -+ * -+ * This is arranged such that the bottom bit encodes whether TSX is actually -+ * disabled, while identifying various explicit (>=0) and implicit (<0) -+ * conditions. -+ */ -+int8_t __read_mostly opt_tsx = -1; -+int8_t __read_mostly cpu_has_tsx_ctrl = -1; -+ -+static int __init parse_tsx(const char *s) -+{ -+ int rc = 0, val = parse_bool(s, NULL); -+ -+ if ( val >= 0 ) -+ opt_tsx = val; -+ else -+ rc = -EINVAL; -+ -+ return rc; -+} -+custom_param("tsx", parse_tsx); -+ -+void tsx_init(void) -+{ -+ /* -+ * This function is first called between microcode being loaded, and CPUID -+ * being scanned generally. Calculate from raw data whether MSR_TSX_CTRL -+ * is available. -+ */ -+ if ( unlikely(cpu_has_tsx_ctrl < 0) ) -+ { -+ uint64_t caps = 0; -+ -+ if ( boot_cpu_data.cpuid_level >= 7 && -+ (cpuid_count_edx(7, 0) & cpufeat_mask(X86_FEATURE_ARCH_CAPS)) ) -+ rdmsrl(MSR_ARCH_CAPABILITIES, caps); -+ -+ cpu_has_tsx_ctrl = !!(caps & ARCH_CAPS_TSX_CTRL); -+ } -+ -+ if ( cpu_has_tsx_ctrl ) -+ { -+ uint64_t val; -+ -+ rdmsrl(MSR_TSX_CTRL, val); -+ -+ val &= ~(TSX_CTRL_RTM_DISABLE | TSX_CTRL_CPUID_CLEAR); -+ /* Check bottom bit only. Higher bits are various sentinals. */ -+ if ( !(opt_tsx & 1) ) -+ val |= TSX_CTRL_RTM_DISABLE | TSX_CTRL_CPUID_CLEAR; -+ -+ wrmsrl(MSR_TSX_CTRL, val); -+ } -+ else if ( opt_tsx >= 0 ) -+ printk_once(XENLOG_WARNING -+ "MSR_TSX_CTRL not available - Ignoring tsx= setting\n"); -+} -+ -+/* -+ * Local variables: -+ * mode: C -+ * c-file-style: "BSD" -+ * c-basic-offset: 4 -+ * tab-width: 4 -+ * indent-tabs-mode: nil -+ * End: -+ */ -diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h -index 32746aa8ae..d5f3899f73 100644 ---- a/xen/include/asm-x86/msr-index.h -+++ b/xen/include/asm-x86/msr-index.h -@@ -53,6 +53,7 @@ - #define ARCH_CAPS_SSB_NO (_AC(1, ULL) << 4) - #define ARCH_CAPS_MDS_NO (_AC(1, ULL) << 5) - #define ARCH_CAPS_IF_PSCHANGE_MC_NO (_AC(1, ULL) << 6) -+#define ARCH_CAPS_TSX_CTRL (_AC(1, ULL) << 7) - - #define MSR_FLUSH_CMD 0x0000010b - #define FLUSH_CMD_L1D (_AC(1, ULL) << 0) -@@ -60,6 +61,10 @@ - #define MSR_TSX_FORCE_ABORT 0x0000010f - #define TSX_FORCE_ABORT_RTM (_AC(1, ULL) << 0) - -+#define MSR_TSX_CTRL 0x00000122 -+#define TSX_CTRL_RTM_DISABLE (_AC(1, ULL) << 0) -+#define TSX_CTRL_CPUID_CLEAR (_AC(1, ULL) << 1) -+ - /* Intel MSRs. Some also available on other CPUs */ - #define MSR_IA32_PERFCTR0 0x000000c1 - #define MSR_IA32_A_PERFCTR0 0x000004c1 -diff --git a/xen/include/asm-x86/processor.h b/xen/include/asm-x86/processor.h -index d33ac34d29..1b52712180 100644 ---- a/xen/include/asm-x86/processor.h -+++ b/xen/include/asm-x86/processor.h -@@ -263,6 +263,16 @@ static always_inline unsigned int cpuid_count_ebx( - return ebx; - } - -+static always_inline unsigned int cpuid_count_edx( -+ unsigned int leaf, unsigned int subleaf) -+{ -+ unsigned int edx, tmp; -+ -+ cpuid_count(leaf, subleaf, &tmp, &tmp, &tmp, &edx); -+ -+ return edx; -+} -+ - static inline unsigned long read_cr0(void) - { - unsigned long cr0; -@@ -609,6 +619,9 @@ static inline uint8_t get_cpu_family(uint32_t raw, uint8_t *model, - return fam; - } - -+extern int8_t opt_tsx, cpu_has_tsx_ctrl; -+void tsx_init(void); -+ - #endif /* !__ASSEMBLY__ */ - - #endif /* __ASM_X86_PROCESSOR_H */ -diff --git a/xen/include/xen/lib.h b/xen/include/xen/lib.h -index 89939f43c8..6529f12dae 100644 ---- a/xen/include/xen/lib.h -+++ b/xen/include/xen/lib.h -@@ -114,6 +114,16 @@ extern int printk_ratelimit(void); - #define gprintk(lvl, fmt, args...) \ - printk(XENLOG_GUEST lvl "%pv " fmt, current, ## args) - -+#define printk_once(fmt, args...) \ -+({ \ -+ static bool __read_mostly once_; \ -+ if ( unlikely(!once_) ) \ -+ { \ -+ once_ = true; \ -+ printk(fmt, ## args); \ -+ } \ -+}) -+ - #ifdef NDEBUG - - static inline void diff --git a/xsa305-4.12-2.patch b/xsa305-4.12-2.patch deleted file mode 100644 index 07fba86..0000000 --- a/xsa305-4.12-2.patch +++ /dev/null @@ -1,192 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Mitigate the TSX Asynchronous Abort sidechannel - -See patch documentation and comments. - -This is part of XSA-305 / CVE-2019-11135 - -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 b7e1bf8e8b..74e1e35b88 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -1920,7 +1920,7 @@ extreme care.** - An overall boolean value, `spec-ctrl=no`, can be specified to turn off all - mitigations, including pieces of infrastructure used to virtualise certain - mitigation features for guests. This also includes settings which `xpti`, --`smt`, `pv-l1tf` control, unless the respective option(s) have been -+`smt`, `pv-l1tf`, `tsx` control, unless the respective option(s) have been - specified earlier on the command line. - - Alternatively, a slightly more restricted `spec-ctrl=no-xen` can be used to -@@ -2037,7 +2037,7 @@ Xen version. - = - - Applicability: x86 -- Default: true -+ Default: false on parts vulnerable to TAA, true otherwise - - Controls for the use of Transactional Synchronization eXtensions. - -@@ -2047,6 +2047,19 @@ a control has been introduced which allows TSX to be turned off. - On systems with the ability to turn TSX off, this boolean offers system wide - control of whether TSX is enabled or disabled. - -+On parts vulnerable to CVE-2019-11135 / TSX Asynchronous Abort, the following -+logic applies: -+ -+ * An explicit `tsx=` choice is honoured, even if it is `true` and would -+ result in a vulnerable system. -+ -+ * When no explicit `tsx=` choice is given, parts vulnerable to TAA will be -+ mitigated by disabling TSX, as this is the lowest overhead option. -+ -+ * If the use of TSX is important, the more expensive TAA mitigations can be -+ opted in to with `smt=0 spec-ctrl=md-clear`, at which point TSX will remain -+ active by default. -+ - ### ucode (x86) - > `= [ | scan]` - -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index b37d40e643..800139d79c 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -96,6 +96,9 @@ static int __init parse_spec_ctrl(const char *s) - if ( opt_pv_l1tf_domu < 0 ) - opt_pv_l1tf_domu = 0; - -+ if ( opt_tsx == -1 ) -+ opt_tsx = -3; -+ - disable_common: - opt_rsb_pv = false; - opt_rsb_hvm = false; -@@ -306,7 +309,7 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - printk("Speculative mitigation facilities:\n"); - - /* Hardware features which pertain to speculative mitigations. */ -- printk(" Hardware features:%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", - (_7d0 & cpufeat_mask(X86_FEATURE_IBRSB)) ? " IBRS/IBPB" : "", - (_7d0 & cpufeat_mask(X86_FEATURE_STIBP)) ? " STIBP" : "", - (_7d0 & cpufeat_mask(X86_FEATURE_L1D_FLUSH)) ? " L1D_FLUSH" : "", -@@ -318,7 +321,9 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - (caps & ARCH_CAPS_RSBA) ? " RSBA" : "", - (caps & ARCH_CAPS_SKIP_L1DFL) ? " SKIP_L1DFL": "", - (caps & ARCH_CAPS_SSB_NO) ? " SSB_NO" : "", -- (caps & ARCH_CAPS_MDS_NO) ? " MDS_NO" : ""); -+ (caps & ARCH_CAPS_MDS_NO) ? " MDS_NO" : "", -+ (caps & ARCH_CAPS_TSX_CTRL) ? " TSX_CTRL" : "", -+ (caps & ARCH_CAPS_TAA_NO) ? " TAA_NO" : ""); - - /* Compiled-in support which pertains to mitigations. */ - if ( IS_ENABLED(CONFIG_INDIRECT_THUNK) || IS_ENABLED(CONFIG_SHADOW_PAGING) ) -@@ -332,7 +337,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, Other:%s%s%s\n", -+ printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s, Other:%s%s%s\n", - thunk == THUNK_NONE ? "N/A" : - thunk == THUNK_RETPOLINE ? "RETPOLINE" : - thunk == THUNK_LFENCE ? "LFENCE" : -@@ -341,6 +346,8 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - (default_xen_spec_ctrl & SPEC_CTRL_IBRS) ? "IBRS+" : "IBRS-", - !boot_cpu_has(X86_FEATURE_SSBD) ? "" : - (default_xen_spec_ctrl & SPEC_CTRL_SSBD) ? " SSBD+" : " SSBD-", -+ !(caps & ARCH_CAPS_TSX_CTRL) ? "" : -+ (opt_tsx & 1) ? " TSX+" : " TSX-", - opt_ibpb ? " IBPB" : "", - opt_l1d_flush ? " L1D_FLUSH" : "", - opt_md_clear_pv || opt_md_clear_hvm ? " VERW" : ""); -@@ -862,6 +869,7 @@ void __init init_speculation_mitigations(void) - { - enum ind_thunk thunk = THUNK_DEFAULT; - bool use_spec_ctrl = false, ibrs = false, hw_smt_enabled; -+ bool cpu_has_bug_taa; - uint64_t caps = 0; - - if ( boot_cpu_has(X86_FEATURE_ARCH_CAPS) ) -@@ -1086,6 +1094,53 @@ void __init init_speculation_mitigations(void) - "enabled. Mitigations will not be fully effective. Please\n" - "choose an explicit smt= setting. See XSA-297.\n"); - -+ /* -+ * Vulnerability to TAA is a little complicated to quantify. -+ * -+ * In the pipeline, it is just another way to get speculative access to -+ * stale load port, store buffer or fill buffer data, and therefore can be -+ * considered a superset of MDS (on TSX-capable parts). On parts which -+ * predate MDS_NO, the existing VERW flushing will mitigate this -+ * sidechannel as well. -+ * -+ * On parts which contain MDS_NO, the lack of VERW flushing means that an -+ * attacker can still use TSX to target microarchitectural buffers to leak -+ * secrets. Therefore, we consider TAA to be the set of TSX-capable parts -+ * which have MDS_NO but lack TAA_NO. -+ * -+ * Note: cpu_has_rtm (== hle) could already be hidden by `tsx=0` on the -+ * cmdline. MSR_TSX_CTRL will only appear on TSX-capable parts, so -+ * we check both to spot TSX in a microcode/cmdline independent way. -+ */ -+ cpu_has_bug_taa = -+ (cpu_has_rtm || (caps & ARCH_CAPS_TSX_CTRL)) && -+ (caps & (ARCH_CAPS_MDS_NO | ARCH_CAPS_TAA_NO)) == ARCH_CAPS_MDS_NO; -+ -+ /* -+ * On TAA-affected hardware, disabling TSX is the preferred mitigation, vs -+ * the MDS mitigation of disabling HT and using VERW flushing. -+ * -+ * On CPUs which advertise MDS_NO, VERW has no flushing side effect until -+ * the TSX_CTRL microcode is loaded, despite the MD_CLEAR CPUID bit being -+ * advertised, and there isn't a MD_CLEAR_2 flag to use... -+ * -+ * If we're on affected hardware, able to do something about it (which -+ * implies that VERW now works), no explicit TSX choice and traditional -+ * MDS mitigations (no-SMT, VERW) not obviosuly in use (someone might -+ * plausibly value TSX higher than Hyperthreading...), disable TSX to -+ * mitigate TAA. -+ */ -+ if ( opt_tsx == -1 && cpu_has_bug_taa && (caps & ARCH_CAPS_TSX_CTRL) && -+ ((hw_smt_enabled && opt_smt) || -+ !boot_cpu_has(X86_FEATURE_SC_VERW_IDLE)) ) -+ { -+ setup_clear_cpu_cap(X86_FEATURE_HLE); -+ setup_clear_cpu_cap(X86_FEATURE_RTM); -+ -+ opt_tsx = 0; -+ tsx_init(); -+ } -+ - print_details(thunk, caps); - - /* -diff --git a/xen/arch/x86/tsx.c b/xen/arch/x86/tsx.c -index a8ec2ccc69..2d202a0d4e 100644 ---- a/xen/arch/x86/tsx.c -+++ b/xen/arch/x86/tsx.c -@@ -5,7 +5,8 @@ - * Valid values: - * 1 => Explicit tsx=1 - * 0 => Explicit tsx=0 -- * -1 => Default, implicit tsx=1 -+ * -1 => Default, implicit tsx=1, may change to 0 to mitigate TAA -+ * -3 => Implicit tsx=1 (feed-through from spec-ctrl=0) - * - * This is arranged such that the bottom bit encodes whether TSX is actually - * disabled, while identifying various explicit (>=0) and implicit (<0) -diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h -index d5f3899f73..3971b992d3 100644 ---- a/xen/include/asm-x86/msr-index.h -+++ b/xen/include/asm-x86/msr-index.h -@@ -54,6 +54,7 @@ - #define ARCH_CAPS_MDS_NO (_AC(1, ULL) << 5) - #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 MSR_FLUSH_CMD 0x0000010b - #define FLUSH_CMD_L1D (_AC(1, ULL) << 0) diff --git a/xsa306-4.12.patch b/xsa306-4.12.patch deleted file mode 100644 index 13147b5..0000000 --- a/xsa306-4.12.patch +++ /dev/null @@ -1,91 +0,0 @@ -From: Jan Beulich -Subject: IOMMU: default to always quarantining PCI devices - -XSA-302 relies on the use of libxl's "assignable-add" feature to prepare -devices to be assigned to untrusted guests. - -Unfortunately, this is not considered a strictly required step for -device assignment. The PCI passthrough documentation on the wiki -describes alternate ways of preparing devices for assignment, and -libvirt uses its own ways as well. Hosts where these alternate methods -are used will still leave the system in a vulnerable state after the -device comes back from a guest. - -Default to always quarantining PCI devices, but provide a command line -option to revert back to prior behavior (such that people who both -sufficiently trust their guests and want to be able to use devices in -Dom0 again after they had been in use by a guest wouldn't need to -"manually" move such devices back from DomIO to Dom0). - -This is XSA-306. - -Reported-by: Marek Marczykowski-Górecki -Signed-off-by: Jan Beulich -Reviewed-by: Wei Liu - ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -1171,7 +1171,7 @@ detection of systems known to misbehave - > Default: `new` unless directed-EOI is supported - - ### iommu -- = List of [ , verbose, debug, force, required, -+ = List of [ , verbose, debug, force, required, quarantine, - sharept, intremap, intpost, crash-disable, - snoop, qinval, igfx, amd-iommu-perdev-intremap, - dom0-{passthrough,strict} ] -@@ -1209,6 +1209,12 @@ boolean (e.g. `iommu=no`) can override t - will prevent Xen from booting if IOMMUs aren't discovered and enabled - successfully. - -+* The `quarantine` boolean can be used to control Xen's behavior when -+ de-assigning devices from guests. If enabled (the default), Xen always -+ quarantines such devices; they must be explicitly assigned back to Dom0 -+ before they can be used there again. If disabled, Xen will only -+ quarantine devices the toolstack hass arranged for getting quarantined. -+ - * The `sharept` boolean controls whether the IOMMU pagetables are shared - with the CPU-side HAP pagetables, or allocated separately. Sharing - reduces the memory overhead, but doesn't work in combination with CPU-side ---- a/xen/drivers/passthrough/iommu.c -+++ b/xen/drivers/passthrough/iommu.c -@@ -30,6 +30,7 @@ bool_t __initdata iommu_enable = 1; - bool_t __read_mostly iommu_enabled; - bool_t __read_mostly force_iommu; - bool_t __read_mostly iommu_verbose; -+bool __read_mostly iommu_quarantine = true; - bool_t __read_mostly iommu_igfx = 1; - bool_t __read_mostly iommu_snoop = 1; - bool_t __read_mostly iommu_qinval = 1; -@@ -74,6 +75,8 @@ static int __init parse_iommu_param(cons - else if ( (val = parse_boolean("force", s, ss)) >= 0 || - (val = parse_boolean("required", s, ss)) >= 0 ) - force_iommu = val; -+ else if ( (val = parse_boolean("quarantine", s, ss)) >= 0 ) -+ iommu_quarantine = val; - else if ( (val = parse_boolean("igfx", s, ss)) >= 0 ) - iommu_igfx = val; - else if ( (val = parse_boolean("verbose", s, ss)) >= 0 ) ---- a/xen/drivers/passthrough/pci.c -+++ b/xen/drivers/passthrough/pci.c -@@ -1548,7 +1548,8 @@ int deassign_device(struct domain *d, u1 - return -ENODEV; - - /* De-assignment from dom_io should de-quarantine the device */ -- target = (pdev->quarantine && pdev->domain != dom_io) ? -+ target = ((pdev->quarantine || iommu_quarantine) && -+ pdev->domain != dom_io) ? - dom_io : hardware_domain; - - while ( pdev->phantom_stride ) ---- a/xen/include/xen/iommu.h -+++ b/xen/include/xen/iommu.h -@@ -53,7 +53,7 @@ static inline bool_t dfn_eq(dfn_t x, dfn - } - - extern bool_t iommu_enable, iommu_enabled; --extern bool_t force_iommu, iommu_verbose, iommu_igfx; -+extern bool force_iommu, iommu_quarantine, iommu_verbose, iommu_igfx; - extern bool_t iommu_snoop, iommu_qinval, iommu_intremap, iommu_intpost; - extern bool_t iommu_hap_pt_share; - extern bool_t iommu_debug; diff --git a/xsa307.patch b/xsa307.patch deleted file mode 100644 index 82b4ada..0000000 --- a/xsa307.patch +++ /dev/null @@ -1,99 +0,0 @@ -From: Jan Beulich -Subject: x86+Arm32: make find_next_{,zero_}bit() have well defined behavior - -These functions getting used with the 2nd and 3rd arguments being equal -wasn't well defined: Arm64 reliably returns the value of the 2nd -argument in this case, while on x86 for bitmaps up to 64 bits wide the -return value was undefined (due to the undefined behavior of a shift of -a value by the number of bits it's wide) when the incoming value was 64. -On Arm32 an actual out of bounds access would happen when the -size/offset value is a multiple of 32; if this access doesn't fault, the -return value would have been sufficiently correct afaict. - -Make the functions consistently tolerate the last two arguments being -equal (and in fact the 3rd argument being greater or equal to the 2nd), -in favor of finding and fixing all the use sites that violate the -original more strict assumption. - -This is XSA-307. - -Signed-off-by: Jan Beulich -Acked-by: Julien Grall ---- -The most obvious (albeit still indirect) exposure to guests is -evtchn_check_pollers(), which imo makes this a security issue at least -for Arm32. - -This was originally already discussed between (at least) Andrew and me, -and I don't really recall who brought up the issue first. - -Note that Arm's Linux origin of the code may call for syncing -publication with them. Then again I don't want to tell them just to see -them go public ahead of us. - ---- a/xen/arch/arm/arm32/lib/findbit.S -+++ b/xen/arch/arm/arm32/lib/findbit.S -@@ -42,8 +42,8 @@ ENDPROC(_find_first_zero_bit_le) - * Prototype: int find_next_zero_bit(void *addr, unsigned int maxbit, int offset) - */ - ENTRY(_find_next_zero_bit_le) -- teq r1, #0 -- beq 3b -+ cmp r1, r2 -+ bls 3b - ands ip, r2, #7 - beq 1b @ If new byte, goto old routine - ARM( ldrb r3, [r0, r2, lsr #3] ) -@@ -83,8 +83,8 @@ ENDPROC(_find_first_bit_le) - * Prototype: int find_next_zero_bit(void *addr, unsigned int maxbit, int offset) - */ - ENTRY(_find_next_bit_le) -- teq r1, #0 -- beq 3b -+ cmp r1, r2 -+ bls 3b - ands ip, r2, #7 - beq 1b @ If new byte, goto old routine - ARM( ldrb r3, [r0, r2, lsr #3] ) -@@ -117,8 +117,8 @@ ENTRY(_find_first_zero_bit_be) - ENDPROC(_find_first_zero_bit_be) - - ENTRY(_find_next_zero_bit_be) -- teq r1, #0 -- beq 3b -+ cmp r1, r2 -+ bls 3b - ands ip, r2, #7 - beq 1b @ If new byte, goto old routine - eor r3, r2, #0x18 @ big endian byte ordering -@@ -151,8 +151,8 @@ ENTRY(_find_first_bit_be) - ENDPROC(_find_first_bit_be) - - ENTRY(_find_next_bit_be) -- teq r1, #0 -- beq 3b -+ cmp r1, r2 -+ bls 3b - ands ip, r2, #7 - beq 1b @ If new byte, goto old routine - eor r3, r2, #0x18 @ big endian byte ordering ---- a/xen/include/asm-x86/bitops.h -+++ b/xen/include/asm-x86/bitops.h -@@ -358,7 +358,7 @@ static always_inline unsigned int __scan - const unsigned long *a__ = (addr); \ - unsigned int s__ = (size); \ - unsigned int o__ = (off); \ -- if ( __builtin_constant_p(size) && !s__ ) \ -+ if ( o__ >= s__ ) \ - r__ = s__; \ - else if ( __builtin_constant_p(size) && s__ <= BITS_PER_LONG ) \ - r__ = o__ + __scanbit(*(const unsigned long *)(a__) >> o__, s__); \ -@@ -390,7 +390,7 @@ static always_inline unsigned int __scan - const unsigned long *a__ = (addr); \ - unsigned int s__ = (size); \ - unsigned int o__ = (off); \ -- if ( __builtin_constant_p(size) && !s__ ) \ -+ if ( o__ >= s__ ) \ - r__ = s__; \ - else if ( __builtin_constant_p(size) && s__ <= BITS_PER_LONG ) \ - r__ = o__ + __scanbit(~*(const unsigned long *)(a__) >> o__, s__); \ diff --git a/xsa308.patch b/xsa308.patch deleted file mode 100644 index 7abe3ef..0000000 --- a/xsa308.patch +++ /dev/null @@ -1,74 +0,0 @@ -From: Andrew Cooper -Subject: x86/vtx: Work around SingleStep + STI/MovSS VMEntry failures - -See patch comment for technical details. - -Concerning the timeline, this was first discovered in the aftermath of -XSA-156 which caused #DB to be intercepted unconditionally, but only in -its SingleStep + STI form which is restricted to privileged software. - -After working with Intel and identifying the problematic vmentry check, -this workaround was suggested, and the patch was posted in an RFC -series. Outstanding work for that series (not breaking Introspection) -is still pending, and this fix from it (which wouldn't have been good -enough in its original form) wasn't committed. - -A vmentry failure was reported to xen-devel, and debugging identified -this bug in its SingleStep + MovSS form by way of INT1, which does not -involve the use of any privileged instructions, and proving this to be a -security issue. - -This is XSA-308 - -Reported-by: Håkon Alstadheim -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -Acked-by: Kevin Tian - -diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c -index 6a5eeb5c13..59b836f43f 100644 ---- a/xen/arch/x86/hvm/vmx/vmx.c -+++ b/xen/arch/x86/hvm/vmx/vmx.c -@@ -3816,6 +3816,42 @@ void vmx_vmexit_handler(struct cpu_user_regs *regs) - HVMTRACE_1D(TRAP_DEBUG, exit_qualification); - __restore_debug_registers(v); - write_debugreg(6, exit_qualification | DR_STATUS_RESERVED_ONE); -+ -+ /* -+ * Work around SingleStep + STI/MovSS VMEntry failures. -+ * -+ * We intercept #DB unconditionally to work around CVE-2015-8104 / -+ * XSA-156 (guest-kernel induced host DoS). -+ * -+ * STI/MovSS shadows block/defer interrupts/exceptions (exact -+ * details are complicated and poorly documented). Debug -+ * exceptions delayed for any reason are stored in the -+ * PENDING_DBG_EXCEPTIONS field. -+ * -+ * The falling edge of PENDING_DBG causes #DB to be delivered, -+ * resulting in a VMExit, as #DB is intercepted. The VMCS still -+ * reports blocked-by-STI/MovSS. -+ * -+ * The VMEntry checks when EFLAGS.TF is set don't like a VMCS in -+ * this state. Despite a #DB queued in VMENTRY_INTR_INFO, the -+ * state is rejected as DR6.BS isn't pending. Fix this up. -+ */ -+ if ( unlikely(regs->eflags & X86_EFLAGS_TF) ) -+ { -+ unsigned long int_info; -+ -+ __vmread(GUEST_INTERRUPTIBILITY_INFO, &int_info); -+ -+ if ( int_info & (VMX_INTR_SHADOW_STI | VMX_INTR_SHADOW_MOV_SS) ) -+ { -+ unsigned long pending_dbg; -+ -+ __vmread(GUEST_PENDING_DBG_EXCEPTIONS, &pending_dbg); -+ __vmwrite(GUEST_PENDING_DBG_EXCEPTIONS, -+ pending_dbg | DR_STEP); -+ } -+ } -+ - if ( !v->domain->debugger_attached ) - { - unsigned long insn_len = 0; diff --git a/xsa309.patch b/xsa309.patch deleted file mode 100644 index 8bd9237..0000000 --- a/xsa309.patch +++ /dev/null @@ -1,58 +0,0 @@ -From 523e3974ed2213719a19218f5b246e382ceef18a Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Wed, 30 Oct 2019 17:05:28 +0000 -Subject: [PATCH] x86/mm: Don't reset linear_pt_count on partial validation - -"Linear pagetables" is a technique which involves either pointing a -pagetable at itself, or to another pagetable the same or higher level. -Xen has limited support for linear pagetables: A page may either point -to itself, or point to another page of the same level (i.e., L2 to L2, -L3 to L3, and so on). - -XSA-240 introduced an additional restriction that limited the "depth" -of such chains by allowing pages to either *point to* other pages of -the same level, or *be pointed to* by other pages of the same level, -but not both. To implement this, we keep track of the number of -outstanding times a page points to or is pointed to another page -table, to prevent both from happening at the same time. - -Unfortunately, the original commit introducing this reset this count -when resuming validation of a partially-validated pagetable, dropping -some "linear_pt_entry" counts. - -On debug builds on systems where guests used this feature, this might -lead to crashes that look like this: - - Assertion 'oc > 0' failed at mm.c:874 - -Worse, if an attacker could engineer such a situation to occur, they -might be able to make loops or other abitrary chains of linear -pagetables, leading to the denial-of-service situation outlined in -XSA-240. - -This is XSA-309. - -Reported-by: Manuel Bouyer -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ---- - xen/arch/x86/mm.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 7d4dd80a85..01393fb0da 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -3059,8 +3059,8 @@ static int _get_page_type(struct page_info *page, unsigned long type, - { - page->nr_validated_ptes = 0; - page->partial_flags = 0; -+ page->linear_pt_count = 0; - } -- page->linear_pt_count = 0; - rc = alloc_page_type(page, type, preemptible); - } - --- -2.24.0 - diff --git a/xsa310-0001-x86-mm-Set-old_guest_table-when-destroying-vcpu-page.patch b/xsa310-0001-x86-mm-Set-old_guest_table-when-destroying-vcpu-page.patch deleted file mode 100644 index 3eb3533..0000000 --- a/xsa310-0001-x86-mm-Set-old_guest_table-when-destroying-vcpu-page.patch +++ /dev/null @@ -1,167 +0,0 @@ -From 7c537dc8d28a03064a14171ed5c6fc329531816a Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Tue, 19 Nov 2019 11:40:34 +0000 -Subject: [PATCH 1/3] x86/mm: Set old_guest_table when destroying vcpu - pagetables - -Changeset 6c4efc1eba ("x86/mm: Don't drop a type ref unless you held a -ref to begin with"), part of XSA-299, changed the calling discipline -of put_page_type() such that if put_page_type() returned -ERESTART -(indicating a partially de-validated page), subsequent calls to -put_page_type() must be called with PTF_partial_set. If called on a -partially de-validated page but without PTF_partial_set, Xen will -BUG(), because to do otherwise would risk opening up the kind of -privilege escalation bug described in XSA-299. - -One place this was missed was in vcpu_destroy_pagetables(). -put_page_and_type_preemptible() is called, but on -ERESTART, the -entire operation is simply restarted, causing put_page_type() to be -called on a partially de-validated page without PTF_partial_set. The -result was that if such an operation were interrupted, Xen would hit a -BUG(). - -Fix this by having vcpu_destroy_pagetables() consistently pass off -interrupted de-validations to put_old_page_type(): -- Unconditionally clear references to the page, even if - put_page_and_type failed -- Set old_guest_table and old_guest_table_partial appropriately - -While here, do some refactoring: - - - Move clearing of arch.cr3 to the top of the function - - - Now that clearing is unconditional, move the unmap to the same - conditional as the l4tab mapping. This also allows us to reduce - the scope of the l4tab variable. - - - Avoid code duplication by looping to drop references on - guest_table_user - -This is part of XSA-310. - -Reported-by: Sarah Newman -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ---- -Added in v2. - -Changes in v3: -- Minor comment / whitespace fixes ---- - xen/arch/x86/mm.c | 75 +++++++++++++++++++++++++++++------------------ - 1 file changed, 47 insertions(+), 28 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 01393fb0da..a759afc9e3 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -3142,40 +3142,36 @@ int put_old_guest_table(struct vcpu *v) - int vcpu_destroy_pagetables(struct vcpu *v) - { - unsigned long mfn = pagetable_get_pfn(v->arch.guest_table); -- struct page_info *page; -- l4_pgentry_t *l4tab = NULL; -+ struct page_info *page = NULL; - int rc = put_old_guest_table(v); -+ bool put_guest_table_user = false; - - if ( rc ) - return rc; - -+ v->arch.cr3 = 0; -+ -+ /* -+ * Get the top-level guest page; either the guest_table itself, for -+ * 64-bit, or the top-level l4 entry for 32-bit. Either way, remove -+ * the reference to that page. -+ */ - if ( is_pv_32bit_vcpu(v) ) - { -- l4tab = map_domain_page(_mfn(mfn)); -- mfn = l4e_get_pfn(*l4tab); -- } -+ l4_pgentry_t *l4tab = map_domain_page(_mfn(mfn)); - -- if ( mfn ) -- { -- page = mfn_to_page(_mfn(mfn)); -- if ( paging_mode_refcounts(v->domain) ) -- put_page(page); -- else -- rc = put_page_and_type_preemptible(page); -- } -- -- if ( l4tab ) -- { -- if ( !rc ) -- l4e_write(l4tab, l4e_empty()); -+ mfn = l4e_get_pfn(*l4tab); -+ l4e_write(l4tab, l4e_empty()); - unmap_domain_page(l4tab); - } -- else if ( !rc ) -+ else - { - v->arch.guest_table = pagetable_null(); -+ put_guest_table_user = true; -+ } - -- /* Drop ref to guest_table_user (from MMUEXT_NEW_USER_BASEPTR) */ -- mfn = pagetable_get_pfn(v->arch.guest_table_user); -+ /* Free that page if non-zero */ -+ do { - if ( mfn ) - { - page = mfn_to_page(_mfn(mfn)); -@@ -3183,18 +3179,41 @@ int vcpu_destroy_pagetables(struct vcpu *v) - put_page(page); - else - rc = put_page_and_type_preemptible(page); -+ mfn = 0; - } -- if ( !rc ) -- v->arch.guest_table_user = pagetable_null(); -- } - -- v->arch.cr3 = 0; -+ if ( !rc && put_guest_table_user ) -+ { -+ /* Drop ref to guest_table_user (from MMUEXT_NEW_USER_BASEPTR) */ -+ mfn = pagetable_get_pfn(v->arch.guest_table_user); -+ v->arch.guest_table_user = pagetable_null(); -+ put_guest_table_user = false; -+ } -+ } while ( mfn ); - - /* -- * put_page_and_type_preemptible() is liable to return -EINTR. The -- * callers of us expect -ERESTART so convert it over. -+ * If a "put" operation was interrupted, finish things off in -+ * put_old_guest_table() when the operation is restarted. - */ -- return rc != -EINTR ? rc : -ERESTART; -+ switch ( rc ) -+ { -+ case -EINTR: -+ case -ERESTART: -+ v->arch.old_guest_ptpg = NULL; -+ v->arch.old_guest_table = page; -+ v->arch.old_guest_table_partial = (rc == -ERESTART); -+ rc = -ERESTART; -+ break; -+ default: -+ /* -+ * Failure to 'put' a page may cause it to leak, but that's -+ * less bad than a crash. -+ */ -+ ASSERT(rc == 0); -+ break; -+ } -+ -+ return rc; - } - - int new_guest_cr3(mfn_t mfn) --- -2.24.0 - diff --git a/xsa310-0002-x86-mm-alloc-free_lN_table-Retain-partial_flags-on-E.patch b/xsa310-0002-x86-mm-alloc-free_lN_table-Retain-partial_flags-on-E.patch deleted file mode 100644 index 12c04e4..0000000 --- a/xsa310-0002-x86-mm-alloc-free_lN_table-Retain-partial_flags-on-E.patch +++ /dev/null @@ -1,104 +0,0 @@ -From 128cb126aee9b4a2855ab898fdfbfe7009fbf1f5 Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Thu, 31 Oct 2019 11:17:38 +0000 -Subject: [PATCH 2/3] x86/mm: alloc/free_lN_table: Retain partial_flags on - -EINTR - -When validating or de-validating pages (in alloc_lN_table and -free_lN_table respectively), the `partial_flags` local variable is -used to keep track of whether the "current" PTE started the entire -operation in a "may be partial" state. - -One of the patches in XSA-299 addressed the fact that it is possible -for a previously-partially-validated entry to subsequently be found to -have invalid entries (indicated by returning -EINVAL); in which case -page->partial_flags needs to be set to indicate that the current PTE -may have the partial bit set (and thus _put_page_type() should be -called with PTF_partial_set). - -Unfortunately, the patches in XSA-299 assumed that once -put_page_from_lNe() returned -ERESTART on a page, it was not possible -for it to return -EINTR. This turns out to be true for -alloc_lN_table() and free_lN_table, but not for _get_page_type() and -_put_page_type(): both can return -EINTR when called on pages with -PGT_partial set. In these cases, the pages PGT_partial will still be -set; failing to set partial_flags appropriately may allow an attacker -to do a privilege escalation similar to those described in XSA-299. - -Fix this by always copying the local partial_flags variable into -page->partial_flags when exiting early. - -NB that on the "get" side, no adjustment to nr_validated_entries is -needed: whether pte[i] is partially validated or entirely -un-validated, we want nr_validated_entries = i. On the "put" side, -however, we need to adjust nr_validated_entries appropriately: if -pte[i] is entirely validated, we want nr_validated_entries = i + 1; if -pte[i] is partially validated, we want nr_validated_entries = i. - -This is part of XSA-310. - -Reported-by: Sarah Newman -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ---- - xen/arch/x86/mm.c | 16 ++++++++-------- - 1 file changed, 8 insertions(+), 8 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index a759afc9e3..97c8d73b7b 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1557,7 +1557,7 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) - if ( rc == -EINTR && i ) - { - page->nr_validated_ptes = i; -- page->partial_flags = 0; -+ page->partial_flags = partial_flags;; - rc = -ERESTART; - } - else if ( rc < 0 && rc != -EINTR ) -@@ -1660,7 +1660,7 @@ static int alloc_l3_table(struct page_info *page) - else if ( rc == -EINTR && i ) - { - page->nr_validated_ptes = i; -- page->partial_flags = 0; -+ page->partial_flags = partial_flags; - rc = -ERESTART; - } - if ( rc < 0 ) -@@ -1982,8 +1982,8 @@ static int free_l2_table(struct page_info *page) - } - else if ( rc == -EINTR && i < L2_PAGETABLE_ENTRIES - 1 ) - { -- page->nr_validated_ptes = i + 1; -- page->partial_flags = 0; -+ page->nr_validated_ptes = i + !(partial_flags & PTF_partial_set); -+ page->partial_flags = partial_flags; - rc = -ERESTART; - } - -@@ -2030,8 +2030,8 @@ static int free_l3_table(struct page_info *page) - } - else if ( rc == -EINTR && i < L3_PAGETABLE_ENTRIES - 1 ) - { -- page->nr_validated_ptes = i + 1; -- page->partial_flags = 0; -+ page->nr_validated_ptes = i + !(partial_flags & PTF_partial_set); -+ page->partial_flags = partial_flags; - rc = -ERESTART; - } - return rc > 0 ? 0 : rc; -@@ -2061,8 +2061,8 @@ static int free_l4_table(struct page_info *page) - } - else if ( rc == -EINTR && i < L4_PAGETABLE_ENTRIES - 1 ) - { -- page->nr_validated_ptes = i + 1; -- page->partial_flags = 0; -+ page->nr_validated_ptes = i + !(partial_flags & PTF_partial_set); -+ page->partial_flags = partial_flags; - rc = -ERESTART; - } - --- -2.24.0 - diff --git a/xsa310-0003-x86-mm-relinquish_memory-Grab-an-extra-type-ref-when.patch b/xsa310-0003-x86-mm-relinquish_memory-Grab-an-extra-type-ref-when.patch deleted file mode 100644 index 9ee4238..0000000 --- a/xsa310-0003-x86-mm-relinquish_memory-Grab-an-extra-type-ref-when.patch +++ /dev/null @@ -1,75 +0,0 @@ -From e9f835982a726ae16997c566b5eafab74f8b4cb7 Mon Sep 17 00:00:00 2001 -From: George Dunlap -Date: Mon, 28 Oct 2019 14:33:51 +0000 -Subject: [PATCH 3/3] x86/mm: relinquish_memory: Grab an extra type ref when - setting PGT_partial - -The PGT_partial bit in page->type_info holds both a type count and a -general ref count. During domain tear-down, when free_page_type() -returns -ERESTART, relinquish_memory() correctly handles the general -ref count, but fails to grab an extra type count when setting -PGT_partial. When this bit is eventually cleared, type_count underflows -and triggers the following BUG in page_alloc.c:free_domheap_pages(): - - BUG_ON((pg[i].u.inuse.type_info & PGT_count_mask) != 0); - -As far as we can tell, this page underflow cannot be exploited any any -other way: The page can't be used as a pagetable by the dying domain -because it's dying; it can't be used as a pagetable by any other -domain since it belongs to the dying domain; and ownership can't -transfer to any other domain without hitting the BUG_ON() in -free_domheap_pages(). - -(steal_page() won't work on a page in this state, since it requires -PGC_allocated to be set, and PGC_allocated will already have been -cleared.) - -Fix this by grabbing an extra type ref if setting PGT_partial in -relinquish_memory. - -This is part of XSA-310. - -Reported-by: Sarah Newman -Signed-off-by: George Dunlap -Acked-by: Jan Beulich ---- -v2: -- Move discussion of potential exploits into the commit message -- Keep PGT_partial and put_page() ordering ---- - xen/arch/x86/domain.c | 19 +++++++++++++++++++ - 1 file changed, 19 insertions(+) - -diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c -index f1dd86e12e..51880fc50d 100644 ---- a/xen/arch/x86/domain.c -+++ b/xen/arch/x86/domain.c -@@ -2049,6 +2049,25 @@ static int relinquish_memory( - goto out; - case -ERESTART: - page_list_add(page, list); -+ /* -+ * PGT_partial holds a type ref and a general ref. -+ * If we came in with PGT_partial set, then we 1) -+ * don't need to grab an extra type count, and 2) -+ * do need to drop the extra page ref we grabbed -+ * at the top of the loop. If we didn't come in -+ * with PGT_partial set, we 1) do need to drab an -+ * extra type count, but 2) can transfer the page -+ * ref we grabbed above to it. -+ * -+ * Note that we must increment type_info before -+ * setting PGT_partial. Theoretically it should -+ * be safe to drop the page ref before setting -+ * PGT_partial, but do it afterwards just to be -+ * extra safe. -+ */ -+ if ( !(x & PGT_partial) ) -+ page->u.inuse.type_info++; -+ smp_wmb(); - page->u.inuse.type_info |= PGT_partial; - if ( x & PGT_partial ) - put_page(page); --- -2.24.0 - diff --git a/xsa311-4.12.patch b/xsa311-4.12.patch deleted file mode 100644 index 6c18188..0000000 --- a/xsa311-4.12.patch +++ /dev/null @@ -1,194 +0,0 @@ -From: Andrew Cooper -Subject: AMD/IOMMU: Cease using a dynamic height for the IOMMU pagetables - -update_paging_mode() has multiple bugs: - - 1) Booting with iommu=debug will cause it to inform you that that it called - without the pdev_list lock held. - 2) When growing by more than a single level, it leaks the newly allocated - table(s) in the case of a further error. - -Furthermore, the choice of default level for a domain has issues: - - 1) All HVM guests grow from 2 to 3 levels during construction because of the - position of the VRAM just below the 4G boundary, so defaulting to 2 is a - waste of effort. - 2) The limit for PV guests doesn't take memory hotplug into account, and - isn't dynamic at runtime like HVM guests. This means that a PV guest may - get RAM which it can't map in the IOMMU. - -The dynamic height is a property unique to AMD, and adds a substantial -quantity of complexity for what is a marginal performance improvement. Remove -the complexity by removing the dynamic height. - -PV guests now get 3 or 4 levels based on any hotplug regions in the host. -This only makes a difference for hardware which previously had all RAM below -the 512G boundary, and a hotplug region above. - -HVM guests now get 4 levels (which will be sufficient until 256TB guests -become a thing), because we don't currently have the information to know when -3 would be safe to use. - -The overhead of this extra level is not expected to be noticeable. It costs -one page (4k) per domain, and one extra IO-TLB paging structure cache entry -which is very hot and less likely to be evicted. - -This is XSA-311. - -Reported-by: XXX PERSON 3 -Signed-off-by: Andrew Cooper -Signed-off-by: Andrew Cooper -Acked-by: Jan Beulich - -diff --git a/xen/drivers/passthrough/amd/iommu_map.c b/xen/drivers/passthrough/amd/iommu_map.c -index 628aa60230..9a222c95e1 100644 ---- a/xen/drivers/passthrough/amd/iommu_map.c -+++ b/xen/drivers/passthrough/amd/iommu_map.c -@@ -455,100 +455,6 @@ static int iommu_pde_from_dfn(struct domain *d, unsigned long dfn, - return 0; - } - --static int update_paging_mode(struct domain *d, unsigned long dfn) --{ -- uint16_t bdf; -- void *device_entry; -- unsigned int req_id, level, offset; -- unsigned long flags; -- struct pci_dev *pdev; -- struct amd_iommu *iommu = NULL; -- struct page_info *new_root = NULL; -- struct page_info *old_root = NULL; -- void *new_root_vaddr; -- unsigned long old_root_mfn; -- struct domain_iommu *hd = dom_iommu(d); -- -- if ( dfn == dfn_x(INVALID_DFN) ) -- return -EADDRNOTAVAIL; -- ASSERT(!(dfn >> DEFAULT_DOMAIN_ADDRESS_WIDTH)); -- -- level = hd->arch.paging_mode; -- old_root = hd->arch.root_table; -- offset = dfn >> (PTE_PER_TABLE_SHIFT * (level - 1)); -- -- ASSERT(spin_is_locked(&hd->arch.mapping_lock) && is_hvm_domain(d)); -- -- while ( offset >= PTE_PER_TABLE_SIZE ) -- { -- /* Allocate and install a new root table. -- * Only upper I/O page table grows, no need to fix next level bits */ -- new_root = alloc_amd_iommu_pgtable(); -- if ( new_root == NULL ) -- { -- AMD_IOMMU_DEBUG("%s Cannot allocate I/O page table\n", -- __func__); -- return -ENOMEM; -- } -- -- new_root_vaddr = __map_domain_page(new_root); -- old_root_mfn = mfn_x(page_to_mfn(old_root)); -- set_iommu_pde_present(new_root_vaddr, old_root_mfn, level, -- !!IOMMUF_writable, !!IOMMUF_readable); -- level++; -- old_root = new_root; -- offset >>= PTE_PER_TABLE_SHIFT; -- unmap_domain_page(new_root_vaddr); -- } -- -- if ( new_root != NULL ) -- { -- hd->arch.paging_mode = level; -- hd->arch.root_table = new_root; -- -- if ( !pcidevs_locked() ) -- AMD_IOMMU_DEBUG("%s Try to access pdev_list " -- "without aquiring pcidevs_lock.\n", __func__); -- -- /* Update device table entries using new root table and paging mode */ -- for_each_pdev( d, pdev ) -- { -- if ( pdev->type == DEV_TYPE_PCI_HOST_BRIDGE ) -- continue; -- -- bdf = PCI_BDF2(pdev->bus, pdev->devfn); -- iommu = find_iommu_for_device(pdev->seg, bdf); -- if ( !iommu ) -- { -- AMD_IOMMU_DEBUG("%s Fail to find iommu.\n", __func__); -- return -ENODEV; -- } -- -- spin_lock_irqsave(&iommu->lock, flags); -- do { -- req_id = get_dma_requestor_id(pdev->seg, bdf); -- device_entry = iommu->dev_table.buffer + -- (req_id * IOMMU_DEV_TABLE_ENTRY_SIZE); -- -- /* valid = 0 only works for dom0 passthrough mode */ -- amd_iommu_set_root_page_table((uint32_t *)device_entry, -- page_to_maddr(hd->arch.root_table), -- d->domain_id, -- hd->arch.paging_mode, 1); -- -- amd_iommu_flush_device(iommu, req_id); -- bdf += pdev->phantom_stride; -- } while ( PCI_DEVFN2(bdf) != pdev->devfn && -- PCI_SLOT(bdf) == PCI_SLOT(pdev->devfn) ); -- spin_unlock_irqrestore(&iommu->lock, flags); -- } -- -- /* For safety, invalidate all entries */ -- amd_iommu_flush_all_pages(d); -- } -- return 0; --} -- - int amd_iommu_map_page(struct domain *d, dfn_t dfn, mfn_t mfn, - unsigned int flags, unsigned int *flush_flags) - { -@@ -573,20 +479,6 @@ int amd_iommu_map_page(struct domain *d, dfn_t dfn, mfn_t mfn, - return rc; - } - -- /* Since HVM domain is initialized with 2 level IO page table, -- * we might need a deeper page table for wider dfn now */ -- if ( is_hvm_domain(d) ) -- { -- if ( update_paging_mode(d, dfn_x(dfn)) ) -- { -- spin_unlock(&hd->arch.mapping_lock); -- AMD_IOMMU_DEBUG("Update page mode failed dfn = %"PRI_dfn"\n", -- dfn_x(dfn)); -- domain_crash(d); -- return -EFAULT; -- } -- } -- - if ( iommu_pde_from_dfn(d, dfn_x(dfn), pt_mfn, true) || (pt_mfn[1] == 0) ) - { - spin_unlock(&hd->arch.mapping_lock); -diff --git a/xen/drivers/passthrough/amd/pci_amd_iommu.c b/xen/drivers/passthrough/amd/pci_amd_iommu.c -index 15c13e1163..57dc2c5f20 100644 ---- a/xen/drivers/passthrough/amd/pci_amd_iommu.c -+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c -@@ -242,10 +242,17 @@ static int amd_iommu_domain_init(struct domain *d) - { - struct domain_iommu *hd = dom_iommu(d); - -- /* For pv and dom0, stick with get_paging_mode(max_page) -- * For HVM dom0, use 2 level page table at first */ -- hd->arch.paging_mode = is_hvm_domain(d) ? -- 2 : amd_iommu_get_paging_mode(max_page); -+ /* -+ * Choose the number of levels for the IOMMU page tables. -+ * - PV needs 3 or 4, depending on whether there is RAM (including hotplug -+ * RAM) above the 512G boundary. -+ * - 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. -+ */ -+ hd->arch.paging_mode = is_hvm_domain(d) -+ ? 4 : amd_iommu_get_paging_mode(get_upper_mfn_bound()); -+ - return 0; - } - From b1a2540d9f3a446aa6aacfc26683a5dd8334ce97 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 14 Jan 2020 19:51:54 +0000 Subject: [PATCH 030/194] arm: a CPU may speculate past the ERET instruction [XSA-312] use more explicit library names add weak requires for perl (/etc/xen/scripts/locking.sh) --- xen.spec | 35 ++++++++++++++++++-- xsa312.patch | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 xsa312.patch diff --git a/xen.spec b/xen.spec index 9ce0255..1be5740 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.13.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 @@ Patch39: qemu.trad.CVE-2017-9330.patch Patch40: xen.drop.brctl.patch Patch41: xen.python.env.patch Patch42: xen.gcc9.fixes.patch +Patch43: xsa312.patch %if %build_qemutrad @@ -203,6 +204,8 @@ Requires: /usr/bin/qemu-img # Ensure we at least have a suitable kernel installed, though we can't # force user to actually boot it. Requires: xen-hypervisor-abi = %{hv_abi} +# perl is used in /etc/xen/scripts/locking.sh +Recommends: perl %description runtime This package contains the runtime programs and daemons which @@ -308,6 +311,7 @@ manage Xen virtual machines. %patch40 -p1 %patch41 -p1 %patch42 -p1 +%patch43 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -661,7 +665,29 @@ fi %{_unitdir}/xendomains.service %files libs -%{_libdir}/*.so.* +%{_libdir}/libxencall.so.1 +%{_libdir}/libxencall.so.1.2 +%{_libdir}/libxenctrl.so.4.* +%{_libdir}/libxendevicemodel.so.1 +%{_libdir}/libxendevicemodel.so.1.3 +%{_libdir}/libxenevtchn.so.1 +%{_libdir}/libxenevtchn.so.1.1 +%{_libdir}/libxenforeignmemory.so.1 +%{_libdir}/libxenforeignmemory.so.1.3 +%{_libdir}/libxenfsimage.so.4.* +%{_libdir}/libxengnttab.so.1 +%{_libdir}/libxengnttab.so.1.2 +%{_libdir}/libxenguest.so.4.* +%{_libdir}/libxenlight.so.4.* +%{_libdir}/libxenstat.so.4.* +%{_libdir}/libxenstore.so.3.0 +%{_libdir}/libxenstore.so.3.0.3 +%{_libdir}/libxentoolcore.so.1 +%{_libdir}/libxentoolcore.so.1.0 +%{_libdir}/libxentoollog.so.1 +%{_libdir}/libxentoollog.so.1.0 +%{_libdir}/libxenvchan.so.4.* +%{_libdir}/libxlutil.so.4.* %{_libdir}/xenfsimage # All runtime stuff except for XenD/xm python stuff @@ -873,6 +899,11 @@ fi %endif %changelog +* Tue Jan 14 2020 Michael Young - 4.13.0-2 +- arm: a CPU may speculate past the ERET instruction [XSA-312] +- use more explicit library names +- add weak requires for perl (/etc/xen/scripts/locking.sh) + * Wed Dec 18 2019 Michael Young - 4.13.0-1 - update to 4.13.0 remove patches now included or superceded upstream diff --git a/xsa312.patch b/xsa312.patch new file mode 100644 index 0000000..ae3fa40 --- /dev/null +++ b/xsa312.patch @@ -0,0 +1,93 @@ +From 9f807cf84a9a7a011cf1df7895c54d6031a7596d Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Thu, 19 Dec 2019 08:12:21 +0000 +Subject: [PATCH] xen/arm: Place a speculation barrier sequence following an + eret instruction + +Some CPUs can speculate past an ERET instruction and potentially perform +speculative accesses to memory before processing the exception return. +Since the register state is often controlled by lower privilege level +at the point of an ERET, this could potentially be used as part of a +side-channel attack. + +Newer CPUs may implement a new SB barrier instruction which acts +as an architected speculation barrier. For current CPUs, the sequence +DSB; ISB is known to prevent speculation. + +The latter sequence is heavier than SB but it would never be executed +(this is speculation after all!). + +Introduce a new macro 'sb' that could be used when a speculation barrier +is required. For now it is using dsb; isb but this could easily be +updated to cater SB in the future. + +This is XSA-312. + +Signed-off-by: Julien Grall +--- + xen/arch/arm/arm32/entry.S | 1 + + xen/arch/arm/arm64/entry.S | 3 +++ + xen/include/asm-arm/macros.h | 9 +++++++++ + 3 files changed, 13 insertions(+) + +diff --git a/xen/arch/arm/arm32/entry.S b/xen/arch/arm/arm32/entry.S +index 31ccfb2631..b228d44b19 100644 +--- a/xen/arch/arm/arm32/entry.S ++++ b/xen/arch/arm/arm32/entry.S +@@ -426,6 +426,7 @@ return_to_hypervisor: + add sp, #(UREGS_SP_usr - UREGS_sp); /* SP, LR, SPSR, PC */ + clrex + eret ++ sb + + /* + * struct vcpu *__context_switch(struct vcpu *prev, struct vcpu *next) +diff --git a/xen/arch/arm/arm64/entry.S b/xen/arch/arm/arm64/entry.S +index d35855af96..175ea2981e 100644 +--- a/xen/arch/arm/arm64/entry.S ++++ b/xen/arch/arm/arm64/entry.S +@@ -354,6 +354,7 @@ guest_sync: + */ + mov x1, xzr + eret ++ sb + + check_wa2: + /* ARM_SMCCC_ARCH_WORKAROUND_2 handling */ +@@ -393,6 +394,7 @@ wa2_end: + #endif /* !CONFIG_ARM_SSBD */ + mov x0, xzr + eret ++ sb + guest_sync_slowpath: + /* + * x0/x1 may have been scratch by the fast path above, so avoid +@@ -457,6 +459,7 @@ return_from_trap: + ldr lr, [sp], #(UREGS_SPSR_el1 - UREGS_LR) /* CPSR, PC, SP, LR */ + + eret ++ sb + + /* + * Consume pending SError generated by the guest if any. +diff --git a/xen/include/asm-arm/macros.h b/xen/include/asm-arm/macros.h +index 91ea3505e4..4833671f4c 100644 +--- a/xen/include/asm-arm/macros.h ++++ b/xen/include/asm-arm/macros.h +@@ -20,4 +20,13 @@ + .endr + .endm + ++ /* ++ * Speculative barrier ++ * XXX: Add support for the 'sb' instruction ++ */ ++ .macro sb ++ dsb nsh ++ isb ++ .endm ++ + #endif /* __ASM_ARM_MACROS_H */ +-- +2.17.1 + From 05b5d868bc3c93227ebc8cc2a0fadba27a83bfce Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 22 Jan 2020 22:40:26 +0000 Subject: [PATCH 031/194] build fixes for OCaml 4.10.0 and gcc 10 --- xen.gcc10.fixes.patch | 65 ++++++++++++++++++++++++++++++++++++ xen.ocaml.4.10.patch | 78 +++++++++++++++++++++++++++++++++++++++++++ xen.spec | 9 ++++- 3 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 xen.gcc10.fixes.patch create mode 100644 xen.ocaml.4.10.patch diff --git a/xen.gcc10.fixes.patch b/xen.gcc10.fixes.patch new file mode 100644 index 0000000..3159bd7 --- /dev/null +++ b/xen.gcc10.fixes.patch @@ -0,0 +1,65 @@ +--- xen-4.13.0/tools/xenstore/utils.h.orig 2019-12-17 14:23:09.000000000 +0000 ++++ xen-4.13.0/tools/xenstore/utils.h 2020-01-21 21:13:05.108957447 +0000 +@@ -24,7 +24,7 @@ + void barf(const char *fmt, ...) __attribute__((noreturn)); + void barf_perror(const char *fmt, ...) __attribute__((noreturn)); + +-void (*xprintf)(const char *fmt, ...); ++extern void (*xprintf)(const char *fmt, ...); + + #define eprintf(_fmt, _args...) xprintf("[ERR] %s" _fmt, __FUNCTION__, ##_args) + +--- xen-4.13.0/tools/xenstore/xenstored_core.h.orig 2020-01-21 21:15:19.243931307 +0000 ++++ xen-4.13.0/tools/xenstore/xenstored_core.h 2020-01-21 21:38:35.340617819 +0000 +@@ -204,7 +204,7 @@ + /* Open a pipe for signal handling */ + void init_pipe(int reopen_log_pipe[2]); + +-xengnttab_handle **xgt_handle; ++extern xengnttab_handle **xgt_handle; + + int remember_string(struct hashtable *hash, const char *str); + +--- xen-4.13.0/tools/libxl/libxlu_pci.c.orig 2019-12-17 14:23:09.000000000 +0000 ++++ xen-4.13.0/tools/libxl/libxlu_pci.c 2020-01-21 21:56:26.812212916 +0000 +@@ -48,7 +48,7 @@ + int xlu_pci_parse_bdf(XLU_Config *cfg, libxl_device_pci *pcidev, const char *str) + { + unsigned state = STATE_DOMAIN; +- unsigned dom, bus, dev, func, vslot = 0; ++ unsigned dom = 0, bus = 0, dev = 0, func = 0, vslot = 0; + char *buf2, *tok, *ptr, *end, *optkey = NULL; + + if ( NULL == (buf2 = ptr = strdup(str)) ) +--- xen-4.13.0/tools/libxl/libxl_utils.c.orig 2019-12-17 14:23:09.000000000 +0000 ++++ xen-4.13.0/tools/libxl/libxl_utils.c 2020-01-21 22:34:52.096300774 +0000 +@@ -1259,7 +1259,7 @@ + } + memset(un, 0, sizeof(struct sockaddr_un)); + un->sun_family = AF_UNIX; +- strncpy(un->sun_path, path, sizeof(un->sun_path)); ++ strncpy(un->sun_path, path, sizeof(un->sun_path)-1); + return 0; + } + +--- xen-4.13.0/tools/debugger/kdd/kdd.h.orig 2019-12-17 14:23:09.000000000 +0000 ++++ xen-4.13.0/tools/debugger/kdd/kdd.h 2020-01-21 23:35:55.458605582 +0000 +@@ -323,7 +323,7 @@ + kdd_msg msg; + kdd_reg reg; + kdd_stc stc; +- uint8_t payload[0]; ++ uint8_t payload[65535]; + }; + } PACKED kdd_pkt; + +--- 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.ocaml.4.10.patch b/xen.ocaml.4.10.patch new file mode 100644 index 0000000..a24ffdd --- /dev/null +++ b/xen.ocaml.4.10.patch @@ -0,0 +1,78 @@ +--- xen-4.13.0/tools/ocaml/libs/xc/xenctrl_stubs.c.orig 2019-12-17 14:23:09.000000000 +0000 ++++ xen-4.13.0/tools/ocaml/libs/xc/xenctrl_stubs.c 2020-01-21 19:24:49.508024245 +0000 +@@ -46,7 +46,7 @@ + #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))) ++ ((Field(array, index) == Val_none) ? NULL : (char *) Bp_val(Field(Field(array, index), 0))) + + /* maybe here we should check the range of the input instead of blindly + * casting it to uint32 */ +@@ -505,7 +505,7 @@ + ret = xc_vcpu_getcontext(_H(xch), _D(domid), Int_val(cpu), &ctxt); + + context = caml_alloc_string(sizeof(ctxt)); +- memcpy(String_val(context), (char *) &ctxt.c, sizeof(ctxt.c)); ++ memcpy((char *) Bp_val(context), (char *) &ctxt.c, sizeof(ctxt.c)); + + CAMLreturn(context); + } +@@ -684,7 +684,7 @@ + conring_size = size; + + ring = caml_alloc_string(count); +- memcpy(String_val(ring), str, count); ++ memcpy((char *) Bp_val(ring), str, count); + free(str); + + CAMLreturn(ring); +@@ -695,7 +695,7 @@ + CAMLparam2(xch, keys); + int r; + +- r = xc_send_debug_keys(_H(xch), String_val(keys)); ++ r = xc_send_debug_keys(_H(xch), (char *) Bp_val(keys)); + if (r) + failwith_xc(_H(xch)); + CAMLreturn(Val_unit); +@@ -855,7 +855,7 @@ + } + + for (r = 0; r < 4; r++) +- out_config[r] = (c_config[r]) ? String_val(Field(Field(array, r), 0)) : NULL; ++ out_config[r] = (c_config[r]) ? (char *) Bp_val(Field(Field(array, r), 0)) : NULL; + + r = xc_cpuid_set(_H(xch), _D(domid), + c_input, (const char **)c_config, out_config); +--- xen-4.13.0/tools/ocaml/libs/xb/xs_ring_stubs.c.orig 2019-12-17 14:23:09.000000000 +0000 ++++ xen-4.13.0/tools/ocaml/libs/xb/xs_ring_stubs.c 2020-01-21 23:51:35.473330934 +0000 +@@ -44,7 +44,7 @@ + CAMLlocal1(ml_result); + + struct mmap_interface *interface = GET_C_STRUCT(ml_interface); +- char *buffer = String_val(ml_buffer); ++ char *buffer = (char *) Bp_val(ml_buffer); + int len = Int_val(ml_len); + int result; + +@@ -103,7 +103,7 @@ + CAMLlocal1(ml_result); + + struct mmap_interface *interface = GET_C_STRUCT(ml_interface); +- char *buffer = String_val(ml_buffer); ++ char *buffer = (char *) Bp_val(ml_buffer); + int len = Int_val(ml_len); + int result; + +--- xen-4.13.0/tools/ocaml/libs/xb/xenbus_stubs.c.orig 2019-12-17 14:23:09.000000000 +0000 ++++ xen-4.13.0/tools/ocaml/libs/xb/xenbus_stubs.c 2020-01-22 00:04:09.443168991 +0000 +@@ -65,7 +65,7 @@ + }; + + ret = caml_alloc_string(sizeof(struct xsd_sockmsg)); +- memcpy(String_val(ret), &xsd, sizeof(struct xsd_sockmsg)); ++ memcpy((char *) Bp_val(ret), &xsd, sizeof(struct xsd_sockmsg)); + + CAMLreturn(ret); + } diff --git a/xen.spec b/xen.spec index 1be5740..0475846 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.13.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,8 @@ Patch40: xen.drop.brctl.patch Patch41: xen.python.env.patch Patch42: xen.gcc9.fixes.patch Patch43: xsa312.patch +Patch44: xen.ocaml.4.10.patch +Patch45: xen.gcc10.fixes.patch %if %build_qemutrad @@ -312,6 +314,8 @@ manage Xen virtual machines. %patch41 -p1 %patch42 -p1 %patch43 -p1 +%patch44 -p1 +%patch45 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -899,6 +903,9 @@ fi %endif %changelog +* Wed Jan 22 2020 Michael Young - 4.13.0-3 +- build fixes for OCaml 4.10.0 and gcc 10 + * Tue Jan 14 2020 Michael Young - 4.13.0-2 - arm: a CPU may speculate past the ERET instruction [XSA-312] - use more explicit library names From 1e9fc415c661ad1ac8b115af262530475d13c29a Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Fri, 31 Jan 2020 04:16:50 +0000 Subject: [PATCH 032/194] - Rebuilt for https://fedoraproject.org/wiki/Fedora_32_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 0475846..7a8435f 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.13.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 @@ -903,6 +903,9 @@ fi %endif %changelog +* Fri Jan 31 2020 Fedora Release Engineering - 4.13.0-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + * Wed Jan 22 2020 Michael Young - 4.13.0-3 - build fixes for OCaml 4.10.0 and gcc 10 From fa37c9fcb0ce8fe0605ce89e18dd7f87e2a18753 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 10 Mar 2020 10:09:58 +0000 Subject: [PATCH 033/194] try setting --with-system-ipxe to a rom file (dir doesn't work) add weak depends on ipxe-roms-qemu and qemu-system-x86-core --- xen.spec | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/xen.spec b/xen.spec index 7a8435f..3d0a9ab 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.13.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 @@ -208,6 +208,12 @@ 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 +# 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 %description runtime This package contains the runtime programs and daemons which @@ -384,7 +390,7 @@ CONFIG_EXTRA="" CONFIG_EXTRA="$CONFIG_EXTRA --with-system-ovmf=%{_libexecdir}/%{name}/boot/ovmf.bin" %endif %ifnarch armv7hl aarch64 -CONFIG_EXTRA="$CONFIG_EXTRA --with-system-ipxe=/usr/share/ipxe" +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 make %{?_smp_mflags} %{?ocaml_flags} prefix=/usr tools @@ -903,6 +909,10 @@ fi %endif %changelog +* Tue Mar 10 2020 Michael Young - 4.13.0-5 +- setting for --with-system-ipxe should be a rom file (#1778516) +- add weak depends on ipxe-roms-qemu and qemu-system-x86-core + * Fri Jan 31 2020 Fedora Release Engineering - 4.13.0-4 - Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild From b64e04acdfdadc67a8d5fcef103af0c63939fc84 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 17 Mar 2020 19:50:04 +0000 Subject: [PATCH 034/194] fix issues in pygrub dependency found by python 3.8 --- xen.pygrubfix.patch | 72 +++++++++++++++++++++++++++++++++++++++++++++ xen.spec | 7 ++++- 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 xen.pygrubfix.patch diff --git a/xen.pygrubfix.patch b/xen.pygrubfix.patch new file mode 100644 index 0000000..ffcd74e --- /dev/null +++ b/xen.pygrubfix.patch @@ -0,0 +1,72 @@ +--- xen-4.13.0/tools/python/xen/lowlevel/xc/xc.c.orig 2019-12-17 14:23:09.000000000 +0000 ++++ xen-4.13.0/tools/python/xen/lowlevel/xc/xc.c 2020-03-15 21:58:58.073272499 +0000 +@@ -2106,7 +2106,7 @@ + + { "gnttab_hvm_seed", + (PyCFunction)pyxc_gnttab_hvm_seed, +- METH_KEYWORDS, "\n" ++ METH_VARARGS | METH_KEYWORDS, "\n" + "Initialise HVM guest grant table.\n" + " dom [int]: Identifier of domain to build into.\n" + " console_gmfn [int]: \n" +@@ -2175,7 +2175,7 @@ + + { "sched_credit_domain_set", + (PyCFunction)pyxc_sched_credit_domain_set, +- METH_KEYWORDS, "\n" ++ METH_VARARGS | METH_KEYWORDS, "\n" + "Set the scheduling parameters for a domain when running with the\n" + "SMP credit scheduler.\n" + " domid [int]: domain id to set\n" +@@ -2193,7 +2193,7 @@ + + { "sched_credit2_domain_set", + (PyCFunction)pyxc_sched_credit2_domain_set, +- METH_KEYWORDS, "\n" ++ METH_VARARGS | METH_KEYWORDS, "\n" + "Set the scheduling parameters for a domain when running with the\n" + "SMP credit2 scheduler.\n" + " domid [int]: domain id to set\n" +@@ -2491,14 +2491,14 @@ + + { "flask_context_to_sid", + (PyCFunction)pyflask_context_to_sid, +- METH_KEYWORDS, "\n" ++ METH_VARARGS | METH_KEYWORDS, "\n" + "Convert a context string to a dynamic SID.\n" + " context [str]: String specifying context to be converted\n" + "Returns: [int]: Numeric SID on success; -1 on error.\n" }, + + { "flask_sid_to_context", + (PyCFunction)pyflask_sid_to_context, +- METH_KEYWORDS, "\n" ++ METH_VARARGS | METH_KEYWORDS, "\n" + "Convert a dynamic SID to context string.\n" + " context [int]: SID to be converted\n" + "Returns: [str]: Numeric SID on success; -1 on error.\n" }, +@@ -2505,7 +2505,7 @@ + + { "flask_load", + (PyCFunction)pyflask_load, +- METH_KEYWORDS, "\n" ++ METH_VARARGS | METH_KEYWORDS, "\n" + "Loads a policy into the hypervisor.\n" + " policy [str]: policy to be load\n" + "Returns: [int]: 0 on success; -1 on failure.\n" }, +@@ -2518,14 +2518,14 @@ + + { "flask_setenforce", + (PyCFunction)pyflask_setenforce, +- METH_KEYWORDS, "\n" ++ METH_VARARGS | METH_KEYWORDS, "\n" + "Modifies the current mode for the Flask XSM module.\n" + " mode [int]: mode to change to\n" + "Returns: [int]: 0 on success; -1 on failure.\n" }, + + { "flask_access", + (PyCFunction)pyflask_access, +- METH_KEYWORDS, "\n" ++ METH_VARARGS | METH_KEYWORDS, "\n" + "Returns whether a source context has access to target context based on \ + class and permissions requested.\n" + " scon [str]: source context\n" diff --git a/xen.spec b/xen.spec index 3d0a9ab..a37353e 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.13.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 @@ -115,6 +115,7 @@ Patch42: xen.gcc9.fixes.patch Patch43: xsa312.patch Patch44: xen.ocaml.4.10.patch Patch45: xen.gcc10.fixes.patch +Patch46: xen.pygrubfix.patch %if %build_qemutrad @@ -322,6 +323,7 @@ manage Xen virtual machines. %patch43 -p1 %patch44 -p1 %patch45 -p1 +%patch46 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -909,6 +911,9 @@ fi %endif %changelog +* Tue Mar 17 2020 Michael Young - 4.13.0-6 +- fix issues in pygrub dependency found by python 3.8 + * Tue Mar 10 2020 Michael Young - 4.13.0-5 - setting for --with-system-ipxe should be a rom file (#1778516) - add weak depends on ipxe-roms-qemu and qemu-system-x86-core From e6b8299a36ffcc76da755a9a808432f9a40487b6 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 14 Apr 2020 22:37:54 +0100 Subject: [PATCH 035/194] 4 security updates multiple xenoprof issues [XSA-313, CVE-2020-11740, CVE-2020-11741] (#1823912, #1823914) Missing memory barriers in read-write unlock paths [XSA-314, CVE-2020-11739] (#1823784) Bad error path in GNTTABOP_map_grant [XSA-316, CVE-2020-11743] (#1823926) Bad continuation handling in GNTTABOP_copy [XSA-318, CVE-2020-11742] (#1823943) --- xen.spec | 21 +++++++- xsa313-1.patch | 26 +++++++++ xsa313-2.patch | 132 ++++++++++++++++++++++++++++++++++++++++++++++ xsa314-4.13.patch | 121 ++++++++++++++++++++++++++++++++++++++++++ xsa316-xen.patch | 30 +++++++++++ xsa318.patch | 39 ++++++++++++++ 6 files changed, 368 insertions(+), 1 deletion(-) create mode 100644 xsa313-1.patch create mode 100644 xsa313-2.patch create mode 100644 xsa314-4.13.patch create mode 100644 xsa316-xen.patch create mode 100644 xsa318.patch diff --git a/xen.spec b/xen.spec index a37353e..e451783 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.13.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 @@ -116,6 +116,11 @@ Patch43: xsa312.patch Patch44: xen.ocaml.4.10.patch Patch45: xen.gcc10.fixes.patch Patch46: xen.pygrubfix.patch +Patch47: xsa313-1.patch +Patch48: xsa313-2.patch +Patch49: xsa314-4.13.patch +Patch50: xsa316-xen.patch +Patch51: xsa318.patch %if %build_qemutrad @@ -324,6 +329,11 @@ manage Xen virtual machines. %patch44 -p1 %patch45 -p1 %patch46 -p1 +%patch47 -p1 +%patch48 -p1 +%patch49 -p1 +%patch50 -p1 +%patch51 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -911,6 +921,15 @@ fi %endif %changelog +* Tue Apr 14 2020 Michael Young - 4.13.0-7 +- multiple xenoprof issues [XSA-313, CVE-2020-11740, CVE-2020-11741] + (#1823912, #1823914) +- Missing memory barriers in read-write unlock paths [XSA-314, + CVE-2020-11739] (#1823784) +- Bad error path in GNTTABOP_map_grant [XSA-316, CVE-2020-11743] (#1823926) +- Bad continuation handling in GNTTABOP_copy [XSA-318, CVE-2020-11742] + (#1823943) + * Tue Mar 17 2020 Michael Young - 4.13.0-6 - fix issues in pygrub dependency found by python 3.8 diff --git a/xsa313-1.patch b/xsa313-1.patch new file mode 100644 index 0000000..95fde7e --- /dev/null +++ b/xsa313-1.patch @@ -0,0 +1,26 @@ +From: Jan Beulich +Subject: xenoprof: clear buffer intended to be shared with guests + +alloc_xenheap_pages() making use of MEMF_no_scrub is fine for Xen +internally used allocations, but buffers allocated to be shared with +(unpriviliged) guests need to be zapped of their prior content. + +This is part of XSA-313. + +Reported-by: Ilja Van Sprundel +Signed-off-by: Jan Beulich +Reviewed-by: Andrew Cooper +Reviewed-by: Wei Liu + +--- a/xen/common/xenoprof.c ++++ b/xen/common/xenoprof.c +@@ -253,6 +253,9 @@ static int alloc_xenoprof_struct( + return -ENOMEM; + } + ++ for ( i = 0; i < npages; ++i ) ++ clear_page(d->xenoprof->rawbuf + i * PAGE_SIZE); ++ + d->xenoprof->npages = npages; + d->xenoprof->nbuf = nvcpu; + d->xenoprof->bufsize = bufsize; diff --git a/xsa313-2.patch b/xsa313-2.patch new file mode 100644 index 0000000..d81b823 --- /dev/null +++ b/xsa313-2.patch @@ -0,0 +1,132 @@ +From: Jan Beulich +Subject: xenoprof: limit consumption of shared buffer data + +Since a shared buffer can be written to by the guest, we may only read +the head and tail pointers from there (all other fields should only ever +be written to). Furthermore, for any particular operation the two values +must be read exactly once, with both checks and consumption happening +with the thus read values. (The backtrace related xenoprof_buf_space() +use in xenoprof_log_event() is an exception: The values used there get +re-checked by every subsequent xenoprof_add_sample().) + +Since that code needed touching, also fix the double increment of the +lost samples count in case the backtrace related xenoprof_add_sample() +invocation in xenoprof_log_event() fails. + +Where code is being touched anyway, add const as appropriate, but take +the opportunity to entirely drop the now unused domain parameter of +xenoprof_buf_space(). + +This is part of XSA-313. + +Reported-by: Ilja Van Sprundel +Signed-off-by: Jan Beulich +Reviewed-by: George Dunlap +Reviewed-by: Wei Liu + +--- a/xen/common/xenoprof.c ++++ b/xen/common/xenoprof.c +@@ -479,25 +479,22 @@ static int add_passive_list(XEN_GUEST_HA + + + /* Get space in the buffer */ +-static int xenoprof_buf_space(struct domain *d, xenoprof_buf_t * buf, int size) ++static int xenoprof_buf_space(int head, int tail, int size) + { +- int head, tail; +- +- head = xenoprof_buf(d, buf, event_head); +- tail = xenoprof_buf(d, buf, event_tail); +- + return ((tail > head) ? 0 : size) + tail - head - 1; + } + + /* Check for space and add a sample. Return 1 if successful, 0 otherwise. */ +-static int xenoprof_add_sample(struct domain *d, xenoprof_buf_t *buf, ++static int xenoprof_add_sample(const struct domain *d, ++ const struct xenoprof_vcpu *v, + uint64_t eip, int mode, int event) + { ++ xenoprof_buf_t *buf = v->buffer; + int head, tail, size; + + head = xenoprof_buf(d, buf, event_head); + tail = xenoprof_buf(d, buf, event_tail); +- size = xenoprof_buf(d, buf, event_size); ++ size = v->event_size; + + /* make sure indexes in shared buffer are sane */ + if ( (head < 0) || (head >= size) || (tail < 0) || (tail >= size) ) +@@ -506,7 +503,7 @@ static int xenoprof_add_sample(struct do + return 0; + } + +- if ( xenoprof_buf_space(d, buf, size) > 0 ) ++ if ( xenoprof_buf_space(head, tail, size) > 0 ) + { + xenoprof_buf(d, buf, event_log[head].eip) = eip; + xenoprof_buf(d, buf, event_log[head].mode) = mode; +@@ -530,7 +527,6 @@ static int xenoprof_add_sample(struct do + int xenoprof_add_trace(struct vcpu *vcpu, uint64_t pc, int mode) + { + struct domain *d = vcpu->domain; +- xenoprof_buf_t *buf = d->xenoprof->vcpu[vcpu->vcpu_id].buffer; + + /* Do not accidentally write an escape code due to a broken frame. */ + if ( pc == XENOPROF_ESCAPE_CODE ) +@@ -539,7 +535,8 @@ int xenoprof_add_trace(struct vcpu *vcpu + return 0; + } + +- return xenoprof_add_sample(d, buf, pc, mode, 0); ++ return xenoprof_add_sample(d, &d->xenoprof->vcpu[vcpu->vcpu_id], ++ pc, mode, 0); + } + + void xenoprof_log_event(struct vcpu *vcpu, const struct cpu_user_regs *regs, +@@ -570,17 +567,22 @@ void xenoprof_log_event(struct vcpu *vcp + /* Provide backtrace if requested. */ + if ( backtrace_depth > 0 ) + { +- if ( (xenoprof_buf_space(d, buf, v->event_size) < 2) || +- !xenoprof_add_sample(d, buf, XENOPROF_ESCAPE_CODE, mode, +- XENOPROF_TRACE_BEGIN) ) ++ if ( xenoprof_buf_space(xenoprof_buf(d, buf, event_head), ++ xenoprof_buf(d, buf, event_tail), ++ v->event_size) < 2 ) + { + xenoprof_buf(d, buf, lost_samples)++; + lost_samples++; + return; + } ++ ++ /* xenoprof_add_sample() will increment lost_samples on failure */ ++ if ( !xenoprof_add_sample(d, v, XENOPROF_ESCAPE_CODE, mode, ++ XENOPROF_TRACE_BEGIN) ) ++ return; + } + +- if ( xenoprof_add_sample(d, buf, pc, mode, event) ) ++ if ( xenoprof_add_sample(d, v, pc, mode, event) ) + { + if ( is_active(vcpu->domain) ) + active_samples++; +--- a/xen/include/xen/xenoprof.h ++++ b/xen/include/xen/xenoprof.h +@@ -61,12 +61,12 @@ struct xenoprof { + + #ifndef CONFIG_COMPAT + #define XENOPROF_COMPAT(x) 0 +-#define xenoprof_buf(d, b, field) ((b)->field) ++#define xenoprof_buf(d, b, field) ACCESS_ONCE((b)->field) + #else + #define XENOPROF_COMPAT(x) ((x)->is_compat) +-#define xenoprof_buf(d, b, field) (*(!(d)->xenoprof->is_compat ? \ +- &(b)->native.field : \ +- &(b)->compat.field)) ++#define xenoprof_buf(d, b, field) ACCESS_ONCE(*(!(d)->xenoprof->is_compat \ ++ ? &(b)->native.field \ ++ : &(b)->compat.field)) + #endif + + struct domain; diff --git a/xsa314-4.13.patch b/xsa314-4.13.patch new file mode 100644 index 0000000..67e0066 --- /dev/null +++ b/xsa314-4.13.patch @@ -0,0 +1,121 @@ +From ab49f005f7d01d4004d76f2e295d31aca7d4f93a Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Thu, 20 Feb 2020 20:54:40 +0000 +Subject: [PATCH] xen/rwlock: Add missing memory barrier in the unlock path of + rwlock + +The rwlock unlock paths are using atomic_sub() to release the lock. +However the implementation of atomic_sub() rightfully doesn't contain a +memory barrier. On Arm, this means a processor is allowed to re-order +the memory access with the preceeding access. + +In other words, the unlock may be seen by another processor before all +the memory accesses within the "critical" section. + +The rwlock paths already contains barrier indirectly, but they are not +very useful without the counterpart in the unlock paths. + +The memory barriers are not necessary on x86 because loads/stores are +not re-ordered with lock instructions. + +So add arch_lock_release_barrier() in the unlock paths that will only +add memory barrier on Arm. + +Take the opportunity to document each lock paths explaining why a +barrier is not necessary. + +This is XSA-314. + +Signed-off-by: Julien Grall +Reviewed-by: Jan Beulich +Reviewed-by: Stefano Stabellini + +--- + xen/include/xen/rwlock.h | 29 ++++++++++++++++++++++++++++- + 1 file changed, 28 insertions(+), 1 deletion(-) + +diff --git a/xen/include/xen/rwlock.h b/xen/include/xen/rwlock.h +index 3dfea1ac2a..516486306f 100644 +--- a/xen/include/xen/rwlock.h ++++ b/xen/include/xen/rwlock.h +@@ -48,6 +48,10 @@ static inline int _read_trylock(rwlock_t *lock) + if ( likely(!(cnts & _QW_WMASK)) ) + { + cnts = (u32)atomic_add_return(_QR_BIAS, &lock->cnts); ++ /* ++ * atomic_add_return() is a full barrier so no need for an ++ * arch_lock_acquire_barrier(). ++ */ + if ( likely(!(cnts & _QW_WMASK)) ) + return 1; + atomic_sub(_QR_BIAS, &lock->cnts); +@@ -64,11 +68,19 @@ static inline void _read_lock(rwlock_t *lock) + u32 cnts; + + cnts = atomic_add_return(_QR_BIAS, &lock->cnts); ++ /* ++ * atomic_add_return() is a full barrier so no need for an ++ * arch_lock_acquire_barrier(). ++ */ + if ( likely(!(cnts & _QW_WMASK)) ) + return; + + /* The slowpath will decrement the reader count, if necessary. */ + queue_read_lock_slowpath(lock); ++ /* ++ * queue_read_lock_slowpath() is using spinlock and therefore is a ++ * full barrier. So no need for an arch_lock_acquire_barrier(). ++ */ + } + + static inline void _read_lock_irq(rwlock_t *lock) +@@ -92,6 +104,7 @@ static inline unsigned long _read_lock_irqsave(rwlock_t *lock) + */ + static inline void _read_unlock(rwlock_t *lock) + { ++ arch_lock_release_barrier(); + /* + * Atomically decrement the reader count + */ +@@ -121,11 +134,20 @@ static inline int _rw_is_locked(rwlock_t *lock) + */ + static inline void _write_lock(rwlock_t *lock) + { +- /* Optimize for the unfair lock case where the fair flag is 0. */ ++ /* ++ * Optimize for the unfair lock case where the fair flag is 0. ++ * ++ * atomic_cmpxchg() is a full barrier so no need for an ++ * arch_lock_acquire_barrier(). ++ */ + if ( atomic_cmpxchg(&lock->cnts, 0, _QW_LOCKED) == 0 ) + return; + + queue_write_lock_slowpath(lock); ++ /* ++ * queue_write_lock_slowpath() is using spinlock and therefore is a ++ * full barrier. So no need for an arch_lock_acquire_barrier(). ++ */ + } + + static inline void _write_lock_irq(rwlock_t *lock) +@@ -157,11 +179,16 @@ static inline int _write_trylock(rwlock_t *lock) + if ( unlikely(cnts) ) + return 0; + ++ /* ++ * atomic_cmpxchg() is a full barrier so no need for an ++ * arch_lock_acquire_barrier(). ++ */ + return likely(atomic_cmpxchg(&lock->cnts, 0, _QW_LOCKED) == 0); + } + + static inline void _write_unlock(rwlock_t *lock) + { ++ arch_lock_release_barrier(); + /* + * If the writer field is atomic, it can be cleared directly. + * Otherwise, an atomic subtraction will be used to clear it. +-- +2.17.1 + diff --git a/xsa316-xen.patch b/xsa316-xen.patch new file mode 100644 index 0000000..4962b4e --- /dev/null +++ b/xsa316-xen.patch @@ -0,0 +1,30 @@ +From: Ross Lagerwall +Subject: xen/gnttab: Fix error path in map_grant_ref() + +Part of XSA-295 (c/s 863e74eb2cffb) inadvertently re-positioned the brackets, +changing the logic. If the _set_status() call fails, the grant_map hypercall +would fail with a status of 1 (rc != GNTST_okay) instead of the expected +negative GNTST_* error. + +This error path can be taken due to bad guest state, and causes net/blk-back +in Linux to crash. + +This is XSA-316. + +Signed-off-by: Ross Lagerwall +Reviewed-by: Andrew Cooper +Reviewed-by: Julien Grall + +diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c +index 9fd6e60416..4b5344dc21 100644 +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -1031,7 +1031,7 @@ map_grant_ref( + { + if ( (rc = _set_status(shah, status, rd, rgt->gt_version, act, + op->flags & GNTMAP_readonly, 1, +- ld->domain_id) != GNTST_okay) ) ++ ld->domain_id)) != GNTST_okay ) + goto act_release_out; + + if ( !act->pin ) diff --git a/xsa318.patch b/xsa318.patch new file mode 100644 index 0000000..f4becdf --- /dev/null +++ b/xsa318.patch @@ -0,0 +1,39 @@ +From: Jan Beulich +Subject: gnttab: fix GNTTABOP_copy continuation handling + +The XSA-226 fix was flawed - the backwards transformation on rc was done +too early, causing a continuation to not get invoked when the need for +preemption was determined at the very first iteration of the request. +This in particular means that all of the status fields of the individual +operations would be left untouched, i.e. set to whatever the caller may +or may not have initialized them to. + +This is part of XSA-318. + +Reported-by: Pawel Wieczorkiewicz +Tested-by: Pawel Wieczorkiewicz +Signed-off-by: Jan Beulich +Reviewed-by: Juergen Gross + +--- a/xen/common/grant_table.c ++++ b/xen/common/grant_table.c +@@ -3576,8 +3576,7 @@ do_grant_table_op( + rc = gnttab_copy(copy, count); + if ( rc > 0 ) + { +- rc = count - rc; +- guest_handle_add_offset(copy, rc); ++ guest_handle_add_offset(copy, count - rc); + uop = guest_handle_cast(copy, void); + } + break; +@@ -3644,6 +3643,9 @@ do_grant_table_op( + out: + if ( rc > 0 || opaque_out != 0 ) + { ++ /* Adjust rc, see gnttab_copy() for why this is needed. */ ++ if ( cmd == GNTTABOP_copy ) ++ rc = count - rc; + ASSERT(rc < count); + ASSERT((opaque_out & GNTTABOP_CMD_MASK) == 0); + rc = hypercall_create_continuation(__HYPERVISOR_grant_table_op, "ihi", From b801938d789777d0713539d8967b5a36cef20b79 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 5 May 2020 21:55:53 +0100 Subject: [PATCH 036/194] build aarch64 hypervisor with -mno-outline-atomics to fix gcc 10 build --- xen.spec | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/xen.spec b/xen.spec index e451783..5e25c78 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.13.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 @@ -386,7 +386,7 @@ XEN_TARGET_ARCH=x86_64 make %{?_smp_mflags} prefix=/usr xen CC="/usr/bin/x86_64- make %{?_smp_mflags} 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 %{?_smp_mflags} prefix=/usr xen CC="gcc $RPM_OPT_FLAGS" +make %{?_smp_mflags} prefix=/usr xen CC="gcc $RPM_OPT_FLAGS -mno-outline-atomics" %else make %{?_smp_mflags} 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 @@ -921,6 +921,9 @@ fi %endif %changelog +* Tue May 05 2020 Michael Young - 4.13.0-8 +- build aarch64 hypervisor with -mno-outline-atomics to fix gcc 10 build + * Tue Apr 14 2020 Michael Young - 4.13.0-7 - multiple xenoprof issues [XSA-313, CVE-2020-11740, CVE-2020-11741] (#1823912, #1823914) From 5b7f356ae7c34055bed79439b65c2b5bccd38ef0 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 19 May 2020 19:10:34 +0100 Subject: [PATCH 037/194] update to xen-4.13.1 --- .gitignore | 2 +- sources | 2 +- xen.pygrubfix.patch | 72 ------------------------ xen.spec | 22 ++------ xsa312.patch | 93 ------------------------------- xsa313-1.patch | 26 --------- xsa313-2.patch | 132 -------------------------------------------- xsa314-4.13.patch | 121 ---------------------------------------- xsa316-xen.patch | 30 ---------- xsa318.patch | 39 ------------- 10 files changed, 8 insertions(+), 531 deletions(-) delete mode 100644 xen.pygrubfix.patch delete mode 100644 xsa312.patch delete mode 100644 xsa313-1.patch delete mode 100644 xsa313-2.patch delete mode 100644 xsa314-4.13.patch delete mode 100644 xsa316-xen.patch delete mode 100644 xsa318.patch diff --git a/.gitignore b/.gitignore index 65b3a89..c542b51 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.13.0.tar.gz +/xen-4.13.1.tar.gz diff --git a/sources b/sources index 5f3458b..0fe92bd 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.13.0.tar.gz) = 5b2ded9a2fe3f7ddf40eed1fa9858baead06233a01eb6099cc45b3c78b6c3823acfe7b731910733e87125dfa49d08c53f74c215fb1b320a92b44b87a0a105225 +SHA512 (xen-4.13.1.tar.gz) = b56d20704155d98d803496cba83eb928e0f986a750831cd5600fc88d0ae772fe1456571654375054043d2da8daca255cc98385ebf08b1b1a75ecf7f4b7a0ee90 diff --git a/xen.pygrubfix.patch b/xen.pygrubfix.patch deleted file mode 100644 index ffcd74e..0000000 --- a/xen.pygrubfix.patch +++ /dev/null @@ -1,72 +0,0 @@ ---- xen-4.13.0/tools/python/xen/lowlevel/xc/xc.c.orig 2019-12-17 14:23:09.000000000 +0000 -+++ xen-4.13.0/tools/python/xen/lowlevel/xc/xc.c 2020-03-15 21:58:58.073272499 +0000 -@@ -2106,7 +2106,7 @@ - - { "gnttab_hvm_seed", - (PyCFunction)pyxc_gnttab_hvm_seed, -- METH_KEYWORDS, "\n" -+ METH_VARARGS | METH_KEYWORDS, "\n" - "Initialise HVM guest grant table.\n" - " dom [int]: Identifier of domain to build into.\n" - " console_gmfn [int]: \n" -@@ -2175,7 +2175,7 @@ - - { "sched_credit_domain_set", - (PyCFunction)pyxc_sched_credit_domain_set, -- METH_KEYWORDS, "\n" -+ METH_VARARGS | METH_KEYWORDS, "\n" - "Set the scheduling parameters for a domain when running with the\n" - "SMP credit scheduler.\n" - " domid [int]: domain id to set\n" -@@ -2193,7 +2193,7 @@ - - { "sched_credit2_domain_set", - (PyCFunction)pyxc_sched_credit2_domain_set, -- METH_KEYWORDS, "\n" -+ METH_VARARGS | METH_KEYWORDS, "\n" - "Set the scheduling parameters for a domain when running with the\n" - "SMP credit2 scheduler.\n" - " domid [int]: domain id to set\n" -@@ -2491,14 +2491,14 @@ - - { "flask_context_to_sid", - (PyCFunction)pyflask_context_to_sid, -- METH_KEYWORDS, "\n" -+ METH_VARARGS | METH_KEYWORDS, "\n" - "Convert a context string to a dynamic SID.\n" - " context [str]: String specifying context to be converted\n" - "Returns: [int]: Numeric SID on success; -1 on error.\n" }, - - { "flask_sid_to_context", - (PyCFunction)pyflask_sid_to_context, -- METH_KEYWORDS, "\n" -+ METH_VARARGS | METH_KEYWORDS, "\n" - "Convert a dynamic SID to context string.\n" - " context [int]: SID to be converted\n" - "Returns: [str]: Numeric SID on success; -1 on error.\n" }, -@@ -2505,7 +2505,7 @@ - - { "flask_load", - (PyCFunction)pyflask_load, -- METH_KEYWORDS, "\n" -+ METH_VARARGS | METH_KEYWORDS, "\n" - "Loads a policy into the hypervisor.\n" - " policy [str]: policy to be load\n" - "Returns: [int]: 0 on success; -1 on failure.\n" }, -@@ -2518,14 +2518,14 @@ - - { "flask_setenforce", - (PyCFunction)pyflask_setenforce, -- METH_KEYWORDS, "\n" -+ METH_VARARGS | METH_KEYWORDS, "\n" - "Modifies the current mode for the Flask XSM module.\n" - " mode [int]: mode to change to\n" - "Returns: [int]: 0 on success; -1 on failure.\n" }, - - { "flask_access", - (PyCFunction)pyflask_access, -- METH_KEYWORDS, "\n" -+ METH_VARARGS | METH_KEYWORDS, "\n" - "Returns whether a source context has access to target context based on \ - class and permissions requested.\n" - " scon [str]: source context\n" diff --git a/xen.spec b/xen.spec index 5e25c78..6cb93b6 100644 --- a/xen.spec +++ b/xen.spec @@ -57,8 +57,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.13.0 -Release: 8%{?dist} +Version: 4.13.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,15 +112,8 @@ Patch39: qemu.trad.CVE-2017-9330.patch Patch40: xen.drop.brctl.patch Patch41: xen.python.env.patch Patch42: xen.gcc9.fixes.patch -Patch43: xsa312.patch Patch44: xen.ocaml.4.10.patch Patch45: xen.gcc10.fixes.patch -Patch46: xen.pygrubfix.patch -Patch47: xsa313-1.patch -Patch48: xsa313-2.patch -Patch49: xsa314-4.13.patch -Patch50: xsa316-xen.patch -Patch51: xsa318.patch %if %build_qemutrad @@ -325,15 +318,8 @@ manage Xen virtual machines. %patch40 -p1 %patch41 -p1 %patch42 -p1 -%patch43 -p1 %patch44 -p1 %patch45 -p1 -%patch46 -p1 -%patch47 -p1 -%patch48 -p1 -%patch49 -p1 -%patch50 -p1 -%patch51 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -921,6 +907,10 @@ fi %endif %changelog +* Tue May 19 2020 Michael Young - 4.13.1-1 +- update to 4.13.1 + remove patches now included or superceded upstream + * Tue May 05 2020 Michael Young - 4.13.0-8 - build aarch64 hypervisor with -mno-outline-atomics to fix gcc 10 build diff --git a/xsa312.patch b/xsa312.patch deleted file mode 100644 index ae3fa40..0000000 --- a/xsa312.patch +++ /dev/null @@ -1,93 +0,0 @@ -From 9f807cf84a9a7a011cf1df7895c54d6031a7596d Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Thu, 19 Dec 2019 08:12:21 +0000 -Subject: [PATCH] xen/arm: Place a speculation barrier sequence following an - eret instruction - -Some CPUs can speculate past an ERET instruction and potentially perform -speculative accesses to memory before processing the exception return. -Since the register state is often controlled by lower privilege level -at the point of an ERET, this could potentially be used as part of a -side-channel attack. - -Newer CPUs may implement a new SB barrier instruction which acts -as an architected speculation barrier. For current CPUs, the sequence -DSB; ISB is known to prevent speculation. - -The latter sequence is heavier than SB but it would never be executed -(this is speculation after all!). - -Introduce a new macro 'sb' that could be used when a speculation barrier -is required. For now it is using dsb; isb but this could easily be -updated to cater SB in the future. - -This is XSA-312. - -Signed-off-by: Julien Grall ---- - xen/arch/arm/arm32/entry.S | 1 + - xen/arch/arm/arm64/entry.S | 3 +++ - xen/include/asm-arm/macros.h | 9 +++++++++ - 3 files changed, 13 insertions(+) - -diff --git a/xen/arch/arm/arm32/entry.S b/xen/arch/arm/arm32/entry.S -index 31ccfb2631..b228d44b19 100644 ---- a/xen/arch/arm/arm32/entry.S -+++ b/xen/arch/arm/arm32/entry.S -@@ -426,6 +426,7 @@ return_to_hypervisor: - add sp, #(UREGS_SP_usr - UREGS_sp); /* SP, LR, SPSR, PC */ - clrex - eret -+ sb - - /* - * struct vcpu *__context_switch(struct vcpu *prev, struct vcpu *next) -diff --git a/xen/arch/arm/arm64/entry.S b/xen/arch/arm/arm64/entry.S -index d35855af96..175ea2981e 100644 ---- a/xen/arch/arm/arm64/entry.S -+++ b/xen/arch/arm/arm64/entry.S -@@ -354,6 +354,7 @@ guest_sync: - */ - mov x1, xzr - eret -+ sb - - check_wa2: - /* ARM_SMCCC_ARCH_WORKAROUND_2 handling */ -@@ -393,6 +394,7 @@ wa2_end: - #endif /* !CONFIG_ARM_SSBD */ - mov x0, xzr - eret -+ sb - guest_sync_slowpath: - /* - * x0/x1 may have been scratch by the fast path above, so avoid -@@ -457,6 +459,7 @@ return_from_trap: - ldr lr, [sp], #(UREGS_SPSR_el1 - UREGS_LR) /* CPSR, PC, SP, LR */ - - eret -+ sb - - /* - * Consume pending SError generated by the guest if any. -diff --git a/xen/include/asm-arm/macros.h b/xen/include/asm-arm/macros.h -index 91ea3505e4..4833671f4c 100644 ---- a/xen/include/asm-arm/macros.h -+++ b/xen/include/asm-arm/macros.h -@@ -20,4 +20,13 @@ - .endr - .endm - -+ /* -+ * Speculative barrier -+ * XXX: Add support for the 'sb' instruction -+ */ -+ .macro sb -+ dsb nsh -+ isb -+ .endm -+ - #endif /* __ASM_ARM_MACROS_H */ --- -2.17.1 - diff --git a/xsa313-1.patch b/xsa313-1.patch deleted file mode 100644 index 95fde7e..0000000 --- a/xsa313-1.patch +++ /dev/null @@ -1,26 +0,0 @@ -From: Jan Beulich -Subject: xenoprof: clear buffer intended to be shared with guests - -alloc_xenheap_pages() making use of MEMF_no_scrub is fine for Xen -internally used allocations, but buffers allocated to be shared with -(unpriviliged) guests need to be zapped of their prior content. - -This is part of XSA-313. - -Reported-by: Ilja Van Sprundel -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper -Reviewed-by: Wei Liu - ---- a/xen/common/xenoprof.c -+++ b/xen/common/xenoprof.c -@@ -253,6 +253,9 @@ static int alloc_xenoprof_struct( - return -ENOMEM; - } - -+ for ( i = 0; i < npages; ++i ) -+ clear_page(d->xenoprof->rawbuf + i * PAGE_SIZE); -+ - d->xenoprof->npages = npages; - d->xenoprof->nbuf = nvcpu; - d->xenoprof->bufsize = bufsize; diff --git a/xsa313-2.patch b/xsa313-2.patch deleted file mode 100644 index d81b823..0000000 --- a/xsa313-2.patch +++ /dev/null @@ -1,132 +0,0 @@ -From: Jan Beulich -Subject: xenoprof: limit consumption of shared buffer data - -Since a shared buffer can be written to by the guest, we may only read -the head and tail pointers from there (all other fields should only ever -be written to). Furthermore, for any particular operation the two values -must be read exactly once, with both checks and consumption happening -with the thus read values. (The backtrace related xenoprof_buf_space() -use in xenoprof_log_event() is an exception: The values used there get -re-checked by every subsequent xenoprof_add_sample().) - -Since that code needed touching, also fix the double increment of the -lost samples count in case the backtrace related xenoprof_add_sample() -invocation in xenoprof_log_event() fails. - -Where code is being touched anyway, add const as appropriate, but take -the opportunity to entirely drop the now unused domain parameter of -xenoprof_buf_space(). - -This is part of XSA-313. - -Reported-by: Ilja Van Sprundel -Signed-off-by: Jan Beulich -Reviewed-by: George Dunlap -Reviewed-by: Wei Liu - ---- a/xen/common/xenoprof.c -+++ b/xen/common/xenoprof.c -@@ -479,25 +479,22 @@ static int add_passive_list(XEN_GUEST_HA - - - /* Get space in the buffer */ --static int xenoprof_buf_space(struct domain *d, xenoprof_buf_t * buf, int size) -+static int xenoprof_buf_space(int head, int tail, int size) - { -- int head, tail; -- -- head = xenoprof_buf(d, buf, event_head); -- tail = xenoprof_buf(d, buf, event_tail); -- - return ((tail > head) ? 0 : size) + tail - head - 1; - } - - /* Check for space and add a sample. Return 1 if successful, 0 otherwise. */ --static int xenoprof_add_sample(struct domain *d, xenoprof_buf_t *buf, -+static int xenoprof_add_sample(const struct domain *d, -+ const struct xenoprof_vcpu *v, - uint64_t eip, int mode, int event) - { -+ xenoprof_buf_t *buf = v->buffer; - int head, tail, size; - - head = xenoprof_buf(d, buf, event_head); - tail = xenoprof_buf(d, buf, event_tail); -- size = xenoprof_buf(d, buf, event_size); -+ size = v->event_size; - - /* make sure indexes in shared buffer are sane */ - if ( (head < 0) || (head >= size) || (tail < 0) || (tail >= size) ) -@@ -506,7 +503,7 @@ static int xenoprof_add_sample(struct do - return 0; - } - -- if ( xenoprof_buf_space(d, buf, size) > 0 ) -+ if ( xenoprof_buf_space(head, tail, size) > 0 ) - { - xenoprof_buf(d, buf, event_log[head].eip) = eip; - xenoprof_buf(d, buf, event_log[head].mode) = mode; -@@ -530,7 +527,6 @@ static int xenoprof_add_sample(struct do - int xenoprof_add_trace(struct vcpu *vcpu, uint64_t pc, int mode) - { - struct domain *d = vcpu->domain; -- xenoprof_buf_t *buf = d->xenoprof->vcpu[vcpu->vcpu_id].buffer; - - /* Do not accidentally write an escape code due to a broken frame. */ - if ( pc == XENOPROF_ESCAPE_CODE ) -@@ -539,7 +535,8 @@ int xenoprof_add_trace(struct vcpu *vcpu - return 0; - } - -- return xenoprof_add_sample(d, buf, pc, mode, 0); -+ return xenoprof_add_sample(d, &d->xenoprof->vcpu[vcpu->vcpu_id], -+ pc, mode, 0); - } - - void xenoprof_log_event(struct vcpu *vcpu, const struct cpu_user_regs *regs, -@@ -570,17 +567,22 @@ void xenoprof_log_event(struct vcpu *vcp - /* Provide backtrace if requested. */ - if ( backtrace_depth > 0 ) - { -- if ( (xenoprof_buf_space(d, buf, v->event_size) < 2) || -- !xenoprof_add_sample(d, buf, XENOPROF_ESCAPE_CODE, mode, -- XENOPROF_TRACE_BEGIN) ) -+ if ( xenoprof_buf_space(xenoprof_buf(d, buf, event_head), -+ xenoprof_buf(d, buf, event_tail), -+ v->event_size) < 2 ) - { - xenoprof_buf(d, buf, lost_samples)++; - lost_samples++; - return; - } -+ -+ /* xenoprof_add_sample() will increment lost_samples on failure */ -+ if ( !xenoprof_add_sample(d, v, XENOPROF_ESCAPE_CODE, mode, -+ XENOPROF_TRACE_BEGIN) ) -+ return; - } - -- if ( xenoprof_add_sample(d, buf, pc, mode, event) ) -+ if ( xenoprof_add_sample(d, v, pc, mode, event) ) - { - if ( is_active(vcpu->domain) ) - active_samples++; ---- a/xen/include/xen/xenoprof.h -+++ b/xen/include/xen/xenoprof.h -@@ -61,12 +61,12 @@ struct xenoprof { - - #ifndef CONFIG_COMPAT - #define XENOPROF_COMPAT(x) 0 --#define xenoprof_buf(d, b, field) ((b)->field) -+#define xenoprof_buf(d, b, field) ACCESS_ONCE((b)->field) - #else - #define XENOPROF_COMPAT(x) ((x)->is_compat) --#define xenoprof_buf(d, b, field) (*(!(d)->xenoprof->is_compat ? \ -- &(b)->native.field : \ -- &(b)->compat.field)) -+#define xenoprof_buf(d, b, field) ACCESS_ONCE(*(!(d)->xenoprof->is_compat \ -+ ? &(b)->native.field \ -+ : &(b)->compat.field)) - #endif - - struct domain; diff --git a/xsa314-4.13.patch b/xsa314-4.13.patch deleted file mode 100644 index 67e0066..0000000 --- a/xsa314-4.13.patch +++ /dev/null @@ -1,121 +0,0 @@ -From ab49f005f7d01d4004d76f2e295d31aca7d4f93a Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Thu, 20 Feb 2020 20:54:40 +0000 -Subject: [PATCH] xen/rwlock: Add missing memory barrier in the unlock path of - rwlock - -The rwlock unlock paths are using atomic_sub() to release the lock. -However the implementation of atomic_sub() rightfully doesn't contain a -memory barrier. On Arm, this means a processor is allowed to re-order -the memory access with the preceeding access. - -In other words, the unlock may be seen by another processor before all -the memory accesses within the "critical" section. - -The rwlock paths already contains barrier indirectly, but they are not -very useful without the counterpart in the unlock paths. - -The memory barriers are not necessary on x86 because loads/stores are -not re-ordered with lock instructions. - -So add arch_lock_release_barrier() in the unlock paths that will only -add memory barrier on Arm. - -Take the opportunity to document each lock paths explaining why a -barrier is not necessary. - -This is XSA-314. - -Signed-off-by: Julien Grall -Reviewed-by: Jan Beulich -Reviewed-by: Stefano Stabellini - ---- - xen/include/xen/rwlock.h | 29 ++++++++++++++++++++++++++++- - 1 file changed, 28 insertions(+), 1 deletion(-) - -diff --git a/xen/include/xen/rwlock.h b/xen/include/xen/rwlock.h -index 3dfea1ac2a..516486306f 100644 ---- a/xen/include/xen/rwlock.h -+++ b/xen/include/xen/rwlock.h -@@ -48,6 +48,10 @@ static inline int _read_trylock(rwlock_t *lock) - if ( likely(!(cnts & _QW_WMASK)) ) - { - cnts = (u32)atomic_add_return(_QR_BIAS, &lock->cnts); -+ /* -+ * atomic_add_return() is a full barrier so no need for an -+ * arch_lock_acquire_barrier(). -+ */ - if ( likely(!(cnts & _QW_WMASK)) ) - return 1; - atomic_sub(_QR_BIAS, &lock->cnts); -@@ -64,11 +68,19 @@ static inline void _read_lock(rwlock_t *lock) - u32 cnts; - - cnts = atomic_add_return(_QR_BIAS, &lock->cnts); -+ /* -+ * atomic_add_return() is a full barrier so no need for an -+ * arch_lock_acquire_barrier(). -+ */ - if ( likely(!(cnts & _QW_WMASK)) ) - return; - - /* The slowpath will decrement the reader count, if necessary. */ - queue_read_lock_slowpath(lock); -+ /* -+ * queue_read_lock_slowpath() is using spinlock and therefore is a -+ * full barrier. So no need for an arch_lock_acquire_barrier(). -+ */ - } - - static inline void _read_lock_irq(rwlock_t *lock) -@@ -92,6 +104,7 @@ static inline unsigned long _read_lock_irqsave(rwlock_t *lock) - */ - static inline void _read_unlock(rwlock_t *lock) - { -+ arch_lock_release_barrier(); - /* - * Atomically decrement the reader count - */ -@@ -121,11 +134,20 @@ static inline int _rw_is_locked(rwlock_t *lock) - */ - static inline void _write_lock(rwlock_t *lock) - { -- /* Optimize for the unfair lock case where the fair flag is 0. */ -+ /* -+ * Optimize for the unfair lock case where the fair flag is 0. -+ * -+ * atomic_cmpxchg() is a full barrier so no need for an -+ * arch_lock_acquire_barrier(). -+ */ - if ( atomic_cmpxchg(&lock->cnts, 0, _QW_LOCKED) == 0 ) - return; - - queue_write_lock_slowpath(lock); -+ /* -+ * queue_write_lock_slowpath() is using spinlock and therefore is a -+ * full barrier. So no need for an arch_lock_acquire_barrier(). -+ */ - } - - static inline void _write_lock_irq(rwlock_t *lock) -@@ -157,11 +179,16 @@ static inline int _write_trylock(rwlock_t *lock) - if ( unlikely(cnts) ) - return 0; - -+ /* -+ * atomic_cmpxchg() is a full barrier so no need for an -+ * arch_lock_acquire_barrier(). -+ */ - return likely(atomic_cmpxchg(&lock->cnts, 0, _QW_LOCKED) == 0); - } - - static inline void _write_unlock(rwlock_t *lock) - { -+ arch_lock_release_barrier(); - /* - * If the writer field is atomic, it can be cleared directly. - * Otherwise, an atomic subtraction will be used to clear it. --- -2.17.1 - diff --git a/xsa316-xen.patch b/xsa316-xen.patch deleted file mode 100644 index 4962b4e..0000000 --- a/xsa316-xen.patch +++ /dev/null @@ -1,30 +0,0 @@ -From: Ross Lagerwall -Subject: xen/gnttab: Fix error path in map_grant_ref() - -Part of XSA-295 (c/s 863e74eb2cffb) inadvertently re-positioned the brackets, -changing the logic. If the _set_status() call fails, the grant_map hypercall -would fail with a status of 1 (rc != GNTST_okay) instead of the expected -negative GNTST_* error. - -This error path can be taken due to bad guest state, and causes net/blk-back -in Linux to crash. - -This is XSA-316. - -Signed-off-by: Ross Lagerwall -Reviewed-by: Andrew Cooper -Reviewed-by: Julien Grall - -diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c -index 9fd6e60416..4b5344dc21 100644 ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -1031,7 +1031,7 @@ map_grant_ref( - { - if ( (rc = _set_status(shah, status, rd, rgt->gt_version, act, - op->flags & GNTMAP_readonly, 1, -- ld->domain_id) != GNTST_okay) ) -+ ld->domain_id)) != GNTST_okay ) - goto act_release_out; - - if ( !act->pin ) diff --git a/xsa318.patch b/xsa318.patch deleted file mode 100644 index f4becdf..0000000 --- a/xsa318.patch +++ /dev/null @@ -1,39 +0,0 @@ -From: Jan Beulich -Subject: gnttab: fix GNTTABOP_copy continuation handling - -The XSA-226 fix was flawed - the backwards transformation on rc was done -too early, causing a continuation to not get invoked when the need for -preemption was determined at the very first iteration of the request. -This in particular means that all of the status fields of the individual -operations would be left untouched, i.e. set to whatever the caller may -or may not have initialized them to. - -This is part of XSA-318. - -Reported-by: Pawel Wieczorkiewicz -Tested-by: Pawel Wieczorkiewicz -Signed-off-by: Jan Beulich -Reviewed-by: Juergen Gross - ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -3576,8 +3576,7 @@ do_grant_table_op( - rc = gnttab_copy(copy, count); - if ( rc > 0 ) - { -- rc = count - rc; -- guest_handle_add_offset(copy, rc); -+ guest_handle_add_offset(copy, count - rc); - uop = guest_handle_cast(copy, void); - } - break; -@@ -3644,6 +3643,9 @@ do_grant_table_op( - out: - if ( rc > 0 || opaque_out != 0 ) - { -+ /* Adjust rc, see gnttab_copy() for why this is needed. */ -+ if ( cmd == GNTTABOP_copy ) -+ rc = count - rc; - ASSERT(rc < count); - ASSERT((opaque_out & GNTTABOP_CMD_MASK) == 0); - rc = hypercall_create_continuation(__HYPERVISOR_grant_table_op, "ihi", From 4dbcf4d3f7954de0a13cb68c3f523e9e38a60b95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Tue, 26 May 2020 03:55:27 +0200 Subject: [PATCH 038/194] Rebuilt for Python 3.9 --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 6cb93b6..038690b 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.13.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 @@ -907,6 +907,9 @@ fi %endif %changelog +* Tue May 26 2020 Miro Hrončok - 4.13.1-2 +- Rebuilt for Python 3.9 + * Tue May 19 2020 Michael Young - 4.13.1-1 - update to 4.13.1 remove patches now included or superceded upstream From a23d05f0ceb5026f7fe1b8420ff593ad5e025841 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 10 Jun 2020 19:03:53 +0100 Subject: [PATCH 039/194] Special Register Buffer speculative side channel [XSA-320] --- xen.spec | 9 ++- xsa320-4.13-1.patch | 117 +++++++++++++++++++++++++++++ xsa320-4.13-2.patch | 179 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 xsa320-4.13-1.patch create mode 100644 xsa320-4.13-2.patch diff --git a/xen.spec b/xen.spec index 038690b..0cf0351 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.13.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 @@ -114,6 +114,8 @@ Patch41: xen.python.env.patch Patch42: xen.gcc9.fixes.patch Patch44: xen.ocaml.4.10.patch Patch45: xen.gcc10.fixes.patch +Patch46: xsa320-4.13-1.patch +Patch47: xsa320-4.13-2.patch %if %build_qemutrad @@ -320,6 +322,8 @@ manage Xen virtual machines. %patch42 -p1 %patch44 -p1 %patch45 -p1 +%patch46 -p1 +%patch47 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -907,6 +911,9 @@ fi %endif %changelog +* Wed Jun 10 2020 Michael Young - 4.13.1-3 +- Special Register Buffer speculative side channel [XSA-320] + * Tue May 26 2020 Miro Hrončok - 4.13.1-2 - Rebuilt for Python 3.9 diff --git a/xsa320-4.13-1.patch b/xsa320-4.13-1.patch new file mode 100644 index 0000000..09eb8ea --- /dev/null +++ b/xsa320-4.13-1.patch @@ -0,0 +1,117 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: CPUID/MSR definitions for Special Register Buffer Data Sampling + +This is part of XSA-320 / CVE-2020-0543 + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +Acked-by: Wei Liu + +diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc +index 1d9d816622..9268454297 100644 +--- a/docs/misc/xen-command-line.pandoc ++++ b/docs/misc/xen-command-line.pandoc +@@ -483,10 +483,10 @@ accounting for hardware capabilities as enumerated via CPUID. + + Currently accepted: + +-The Speculation Control hardware features `md-clear`, `ibrsb`, `stibp`, `ibpb`, +-`l1d-flush` and `ssbd` are used by default if available and applicable. They can +-be ignored, e.g. `no-ibrsb`, at which point Xen won't use them itself, and +-won't offer them to guests. ++The Speculation Control hardware features `srbds-ctrl`, `md-clear`, `ibrsb`, ++`stibp`, `ibpb`, `l1d-flush` and `ssbd` are used by default if available and ++applicable. They can be ignored, e.g. `no-ibrsb`, at which point Xen won't ++use them itself, and won't offer them to guests. + + ### cpuid_mask_cpu + > `= fam_0f_rev_[cdefg] | fam_10_rev_[bc] | fam_11_rev_b` +diff --git a/tools/libxl/libxl_cpuid.c b/tools/libxl/libxl_cpuid.c +index 6cea4227ba..a78f08b927 100644 +--- a/tools/libxl/libxl_cpuid.c ++++ b/tools/libxl/libxl_cpuid.c +@@ -213,6 +213,7 @@ int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str) + + {"avx512-4vnniw",0x00000007, 0, CPUID_REG_EDX, 2, 1}, + {"avx512-4fmaps",0x00000007, 0, CPUID_REG_EDX, 3, 1}, ++ {"srbds-ctrl", 0x00000007, 0, CPUID_REG_EDX, 9, 1}, + {"md-clear", 0x00000007, 0, CPUID_REG_EDX, 10, 1}, + {"cet-ibt", 0x00000007, 0, CPUID_REG_EDX, 20, 1}, + {"ibrsb", 0x00000007, 0, CPUID_REG_EDX, 26, 1}, +diff --git a/tools/misc/xen-cpuid.c b/tools/misc/xen-cpuid.c +index 603e1d65fd..a09440813b 100644 +--- a/tools/misc/xen-cpuid.c ++++ b/tools/misc/xen-cpuid.c +@@ -157,6 +157,7 @@ static const char *const str_7d0[32] = + [ 2] = "avx512_4vnniw", [ 3] = "avx512_4fmaps", + [ 4] = "fsrm", + ++ /* 8 */ [ 9] = "srbds-ctrl", + [10] = "md-clear", + /* 12 */ [13] = "tsx-force-abort", + +diff --git a/xen/arch/x86/msr.c b/xen/arch/x86/msr.c +index 4b12103482..0cded3c0ad 100644 +--- a/xen/arch/x86/msr.c ++++ b/xen/arch/x86/msr.c +@@ -134,6 +134,7 @@ int guest_rdmsr(struct vcpu *v, uint32_t msr, uint64_t *val) + /* Write-only */ + case MSR_TSX_FORCE_ABORT: + case MSR_TSX_CTRL: ++ case MSR_MCU_OPT_CTRL: + case MSR_U_CET: + case MSR_S_CET: + case MSR_PL0_SSP ... MSR_INTERRUPT_SSP_TABLE: +@@ -288,6 +289,7 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) + /* Read-only */ + case MSR_TSX_FORCE_ABORT: + case MSR_TSX_CTRL: ++ case MSR_MCU_OPT_CTRL: + case MSR_U_CET: + case MSR_S_CET: + case MSR_PL0_SSP ... MSR_INTERRUPT_SSP_TABLE: +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 6656c44aec..5fc1c6827e 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -312,12 +312,13 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) + printk("Speculative mitigation facilities:\n"); + + /* Hardware features which pertain to speculative mitigations. */ +- printk(" Hardware features:%s%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%s\n", + (_7d0 & cpufeat_mask(X86_FEATURE_IBRSB)) ? " IBRS/IBPB" : "", + (_7d0 & cpufeat_mask(X86_FEATURE_STIBP)) ? " STIBP" : "", + (_7d0 & cpufeat_mask(X86_FEATURE_L1D_FLUSH)) ? " L1D_FLUSH" : "", + (_7d0 & cpufeat_mask(X86_FEATURE_SSBD)) ? " SSBD" : "", + (_7d0 & cpufeat_mask(X86_FEATURE_MD_CLEAR)) ? " MD_CLEAR" : "", ++ (_7d0 & cpufeat_mask(X86_FEATURE_SRBDS_CTRL)) ? " SRBDS_CTRL" : "", + (e8b & cpufeat_mask(X86_FEATURE_IBPB)) ? " IBPB" : "", + (caps & ARCH_CAPS_IBRS_ALL) ? " IBRS_ALL" : "", + (caps & ARCH_CAPS_RDCL_NO) ? " RDCL_NO" : "", +diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h +index 7693c4a71a..91994669e1 100644 +--- a/xen/include/asm-x86/msr-index.h ++++ b/xen/include/asm-x86/msr-index.h +@@ -179,6 +179,9 @@ + #define MSR_IA32_VMX_TRUE_ENTRY_CTLS 0x490 + #define MSR_IA32_VMX_VMFUNC 0x491 + ++#define MSR_MCU_OPT_CTRL 0x00000123 ++#define MCU_OPT_CTRL_RNGDS_MITG_DIS (_AC(1, ULL) << 0) ++ + #define MSR_U_CET 0x000006a0 + #define MSR_S_CET 0x000006a2 + #define MSR_PL0_SSP 0x000006a4 +diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h +index 2835688f1c..a2482c3627 100644 +--- a/xen/include/public/arch-x86/cpufeatureset.h ++++ b/xen/include/public/arch-x86/cpufeatureset.h +@@ -252,6 +252,7 @@ XEN_CPUFEATURE(IBPB, 8*32+12) /*A IBPB support only (no IBRS, used by + /* Intel-defined CPU features, CPUID level 0x00000007:0.edx, word 9 */ + XEN_CPUFEATURE(AVX512_4VNNIW, 9*32+ 2) /*A AVX512 Neural Network Instructions */ + XEN_CPUFEATURE(AVX512_4FMAPS, 9*32+ 3) /*A AVX512 Multiply Accumulation Single Precision */ ++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(TSX_FORCE_ABORT, 9*32+13) /* MSR_TSX_FORCE_ABORT.RTM_ABORT */ + XEN_CPUFEATURE(CET_IBT, 9*32+20) /* CET - Indirect Branch Tracking */ diff --git a/xsa320-4.13-2.patch b/xsa320-4.13-2.patch new file mode 100644 index 0000000..8a8080a --- /dev/null +++ b/xsa320-4.13-2.patch @@ -0,0 +1,179 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Mitigate the Special Register Buffer Data Sampling sidechannel + +See patch documentation and comments. + +This is part of XSA-320 / CVE-2020-0543 + +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 9268454297..c780312531 100644 +--- a/docs/misc/xen-command-line.pandoc ++++ b/docs/misc/xen-command-line.pandoc +@@ -1991,7 +1991,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}= ]` ++> l1d-flush,branch-harden,srb-lock}= ]` + + Controls for speculative execution sidechannel mitigations. By default, Xen + will pick the most appropriate mitigations based on compiled in support, +@@ -2068,6 +2068,12 @@ If Xen is compiled with `CONFIG_SPECULATIVE_HARDEN_BRANCH`, the + speculation barriers to protect selected conditional branches. By default, + 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). ++ + ### sync_console + > `= ` + +diff --git a/xen/arch/x86/acpi/power.c b/xen/arch/x86/acpi/power.c +index feb0f6ce20..75c6e34164 100644 +--- a/xen/arch/x86/acpi/power.c ++++ b/xen/arch/x86/acpi/power.c +@@ -295,6 +295,9 @@ static int enter_state(u32 state) + ci->spec_ctrl_flags |= (default_spec_ctrl_flags & SCF_ist_wrmsr); + spec_ctrl_exit_idle(ci); + ++ if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) ++ wrmsrl(MSR_MCU_OPT_CTRL, default_xen_mcu_opt_ctrl); ++ + done: + spin_debug_enable(); + local_irq_restore(flags); +diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c +index dc8fdac1a1..b1e51b3aff 100644 +--- a/xen/arch/x86/smpboot.c ++++ b/xen/arch/x86/smpboot.c +@@ -361,12 +361,14 @@ void start_secondary(void *unused) + microcode_update_one(false); + + /* +- * If MSR_SPEC_CTRL is available, apply Xen's default setting and discard +- * any firmware settings. Note: MSR_SPEC_CTRL may only become available +- * after loading microcode. ++ * If any speculative control MSRs are available, apply Xen's default ++ * settings. Note: These MSRs may only become available after loading ++ * microcode. + */ + 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); + + 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 5fc1c6827e..33343062a7 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -65,6 +65,9 @@ static unsigned int __initdata l1d_maxphysaddr; + 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) + { + const char *ss; +@@ -112,6 +115,7 @@ static int __init parse_spec_ctrl(const char *s) + opt_ssbd = false; + opt_l1d_flush = 0; + opt_branch_harden = false; ++ opt_srb_lock = 0; + } + else if ( val > 0 ) + rc = -EINVAL; +@@ -178,6 +182,8 @@ static int __init parse_spec_ctrl(const char *s) + opt_l1d_flush = val; + else if ( (val = parse_boolean("branch-harden", s, ss)) >= 0 ) + opt_branch_harden = val; ++ else if ( (val = parse_boolean("srb-lock", s, ss)) >= 0 ) ++ opt_srb_lock = val; + else + rc = -EINVAL; + +@@ -341,7 +347,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, Other:%s%s%s%s\n", ++ printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s, Other:%s%s%s%s%s\n", + thunk == THUNK_NONE ? "N/A" : + thunk == THUNK_RETPOLINE ? "RETPOLINE" : + thunk == THUNK_LFENCE ? "LFENCE" : +@@ -352,6 +358,8 @@ 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) ? "" : ++ opt_srb_lock ? " SRB_LOCK+" : " SRB_LOCK-", + opt_ibpb ? " IBPB" : "", + opt_l1d_flush ? " L1D_FLUSH" : "", + opt_md_clear_pv || opt_md_clear_hvm ? " VERW" : "", +@@ -1149,6 +1157,34 @@ void __init init_speculation_mitigations(void) + tsx_init(); + } + ++ /* Calculate suitable defaults for MSR_MCU_OPT_CTRL */ ++ if ( boot_cpu_has(X86_FEATURE_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) && opt_tsx == 0)) ) ++ 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; ++ } ++ + print_details(thunk, caps); + + /* +@@ -1180,6 +1216,9 @@ void __init init_speculation_mitigations(void) + + wrmsrl(MSR_SPEC_CTRL, bsp_delay_spec_ctrl ? 0 : default_xen_spec_ctrl); + } ++ ++ 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/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h +index 9caecddfec..b252bb8631 100644 +--- a/xen/include/asm-x86/spec_ctrl.h ++++ b/xen/include/asm-x86/spec_ctrl.h +@@ -54,6 +54,8 @@ 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(); From 0e913fb9ea109e91a51bb59a0696cb7d4a7ef667 Mon Sep 17 00:00:00 2001 From: Jeff Law Date: Tue, 30 Jun 2020 14:09:07 -0600 Subject: [PATCH 040/194] Disable LTO --- xen.spec | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 0cf0351..17e6980 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.13.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 @@ -349,6 +349,11 @@ cp -v %{SOURCE21} xen/.config %build +# This package calls binutils components directly and would need to pass +# in flags to enable the LTO plugins +# Disable LTO +%define _lto_cflags %{nil} + %if !%build_ocaml %define ocaml_flags OCAML_TOOLS=n %endif @@ -911,6 +916,9 @@ fi %endif %changelog +* Tue Jun 30 2020 Jeff Law - 4.13.1-4 +Disable LTO + * Wed Jun 10 2020 Michael Young - 4.13.1-3 - Special Register Buffer speculative side channel [XSA-320] From 9d8391a4722bb3ba590c27418b4c76b70bc1b09c Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 7 Jul 2020 19:01:41 +0100 Subject: [PATCH 041/194] 5 security updates incorrect error handling in event channel port allocation leads to DoS [XSA-317, CVE-2020-15566] (#1854465) inverted code paths in x86 dirty VRAM tracking leads to DoS [XSA-319, CVE-2020-15563] (#1854463) xen: insufficient cache write-back under VT-d leads to DoS [XSA-321, CVE-2020-15565] (#1854467) missing alignment check in VCPUOP_register_vcpu_info leads to DoS [XSA-327, CVE-2020-15564] (#1854458) non-atomic modification of live EPT PTE leads to DoS [XSA-328, CVE-2020-15567] (#1854464) --- xen.spec | 38 +++++++++- xsa317.patch | 50 +++++++++++++ xsa319.patch | 27 +++++++ xsa321-4.13-1.patch | 31 ++++++++ xsa321-4.13-2.patch | 175 ++++++++++++++++++++++++++++++++++++++++++++ xsa321-4.13-3.patch | 82 +++++++++++++++++++++ xsa321-4.13-4.patch | 36 +++++++++ xsa321-4.13-5.patch | 24 ++++++ xsa321-4.13-6.patch | 91 +++++++++++++++++++++++ xsa321-4.13-7.patch | 153 ++++++++++++++++++++++++++++++++++++++ xsa327.patch | 63 ++++++++++++++++ xsa328-4.13-1.patch | 118 +++++++++++++++++++++++++++++ xsa328-4.13-2.patch | 48 ++++++++++++ 13 files changed, 935 insertions(+), 1 deletion(-) create mode 100644 xsa317.patch create mode 100644 xsa319.patch create mode 100644 xsa321-4.13-1.patch create mode 100644 xsa321-4.13-2.patch create mode 100644 xsa321-4.13-3.patch create mode 100644 xsa321-4.13-4.patch create mode 100644 xsa321-4.13-5.patch create mode 100644 xsa321-4.13-6.patch create mode 100644 xsa321-4.13-7.patch create mode 100644 xsa327.patch create mode 100644 xsa328-4.13-1.patch create mode 100644 xsa328-4.13-2.patch diff --git a/xen.spec b/xen.spec index 17e6980..60b3c61 100644 --- a/xen.spec +++ b/xen.spec @@ -116,6 +116,18 @@ Patch44: xen.ocaml.4.10.patch Patch45: xen.gcc10.fixes.patch Patch46: xsa320-4.13-1.patch Patch47: xsa320-4.13-2.patch +Patch48: xsa317.patch +Patch49: xsa319.patch +Patch50: xsa328-4.13-1.patch +Patch51: xsa328-4.13-2.patch +Patch52: xsa321-4.13-1.patch +Patch53: xsa321-4.13-2.patch +Patch54: xsa321-4.13-3.patch +Patch55: xsa321-4.13-4.patch +Patch56: xsa321-4.13-5.patch +Patch57: xsa321-4.13-6.patch +Patch58: xsa321-4.13-7.patch +Patch59: xsa327.patch %if %build_qemutrad @@ -324,6 +336,18 @@ manage Xen virtual machines. %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 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -916,7 +940,19 @@ fi %endif %changelog -* Tue Jun 30 2020 Jeff Law - 4.13.1-4 +* Tue Jul 07 2020 Michael Young - 4.13.1-4 +- incorrect error handling in event channel port allocation leads to + DoS [XSA-317, CVE-2020-15566] (#1854465) +- inverted code paths in x86 dirty VRAM tracking leads to DoS + [XSA-319, CVE-2020-15563] (#1854463) +- xen: insufficient cache write-back under VT-d leads to DoS + [XSA-321, CVE-2020-15565] (#1854467) +- missing alignment check in VCPUOP_register_vcpu_info leads to DoS + [XSA-327, CVE-2020-15564] (#1854458) +- non-atomic modification of live EPT PTE leads to DoS + [XSA-328, CVE-2020-15567] (#1854464) + +* Tue Jun 30 2020 Jeff Law Disable LTO * Wed Jun 10 2020 Michael Young - 4.13.1-3 diff --git a/xsa317.patch b/xsa317.patch new file mode 100644 index 0000000..20e2c64 --- /dev/null +++ b/xsa317.patch @@ -0,0 +1,50 @@ +From aeb46e92f915f19a61d5a8a1f4b696793f64e6fb Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Thu, 19 Mar 2020 13:17:31 +0000 +Subject: [PATCH] xen/common: event_channel: Don't ignore error in + get_free_port() + +Currently, get_free_port() is assuming that the port has been allocated +when evtchn_allocate_port() is not return -EBUSY. + +However, the function may return an error when: + - We exhausted all the event channels. This can happen if the limit + configured by the administrator for the guest ('max_event_channels' + in xl cfg) is higher than the ABI used by the guest. For instance, + if the guest is using 2L, the limit should not be higher than 4095. + - We cannot allocate memory (e.g Xen has not more memory). + +Users of get_free_port() (such as EVTCHNOP_alloc_unbound) will validly +assuming the port was valid and will next call evtchn_from_port(). This +will result to a crash as the memory backing the event channel structure +is not present. + +Fixes: 368ae9a05fe ("xen/pvshim: forward evtchn ops between L0 Xen and L2 DomU") +Signed-off-by: Julien Grall +Reviewed-by: Jan Beulich +--- + xen/common/event_channel.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/xen/common/event_channel.c b/xen/common/event_channel.c +index e86e2bfab0..a8d182b584 100644 +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -195,10 +195,10 @@ static int get_free_port(struct domain *d) + { + int rc = evtchn_allocate_port(d, port); + +- if ( rc == -EBUSY ) +- continue; +- +- return port; ++ if ( rc == 0 ) ++ return port; ++ else if ( rc != -EBUSY ) ++ return rc; + } + + return -ENOSPC; +-- +2.17.1 + diff --git a/xsa319.patch b/xsa319.patch new file mode 100644 index 0000000..769443c --- /dev/null +++ b/xsa319.patch @@ -0,0 +1,27 @@ +From: Jan Beulich +Subject: x86/shadow: correct an inverted conditional in dirty VRAM tracking + +This originally was "mfn_x(mfn) == INVALID_MFN". Make it like this +again, taking the opportunity to also drop the unnecessary nearby +braces. + +This is XSA-319. + +Fixes: 246a5a3377c2 ("xen: Use a typesafe to define INVALID_MFN") +Signed-off-by: Jan Beulich +Reviewed-by: Andrew Cooper + +--- a/xen/arch/x86/mm/shadow/common.c ++++ b/xen/arch/x86/mm/shadow/common.c +@@ -3252,10 +3252,8 @@ int shadow_track_dirty_vram(struct domai + int dirty = 0; + paddr_t sl1ma = dirty_vram->sl1ma[i]; + +- if ( !mfn_eq(mfn, INVALID_MFN) ) +- { ++ if ( mfn_eq(mfn, INVALID_MFN) ) + dirty = 1; +- } + else + { + page = mfn_to_page(mfn); diff --git a/xsa321-4.13-1.patch b/xsa321-4.13-1.patch new file mode 100644 index 0000000..9a08ab2 --- /dev/null +++ b/xsa321-4.13-1.patch @@ -0,0 +1,31 @@ +From: Jan Beulich +Subject: vtd: improve IOMMU TLB flush + +Do not limit PSI flushes to order 0 pages, in order to avoid doing a +full TLB flush if the passed in page has an order greater than 0 and +is aligned. Should increase the performance of IOMMU TLB flushes when +dealing with page orders greater than 0. + +This is part of XSA-321. + +Signed-off-by: Jan Beulich + +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -570,13 +570,14 @@ static int __must_check iommu_flush_iotl + if ( iommu_domid == -1 ) + continue; + +- if ( page_count != 1 || dfn_eq(dfn, INVALID_DFN) ) ++ if ( !page_count || (page_count & (page_count - 1)) || ++ dfn_eq(dfn, INVALID_DFN) || !IS_ALIGNED(dfn_x(dfn), page_count) ) + rc = iommu_flush_iotlb_dsi(iommu, iommu_domid, + 0, flush_dev_iotlb); + else + rc = iommu_flush_iotlb_psi(iommu, iommu_domid, + dfn_to_daddr(dfn), +- PAGE_ORDER_4K, ++ get_order_from_pages(page_count), + !dma_old_pte_present, + flush_dev_iotlb); + diff --git a/xsa321-4.13-2.patch b/xsa321-4.13-2.patch new file mode 100644 index 0000000..1e48615 --- /dev/null +++ b/xsa321-4.13-2.patch @@ -0,0 +1,175 @@ +From: +Subject: vtd: prune (and rename) cache flush functions + +Rename __iommu_flush_cache to iommu_sync_cache and remove +iommu_flush_cache_page. Also remove the iommu_flush_cache_entry +wrapper and just use iommu_sync_cache instead. Note the _entry suffix +was meaningless as the wrapper was already taking a size parameter in +bytes. While there also constify the addr parameter. + +No functional change intended. + +This is part of XSA-321. + +Reviewed-by: Jan Beulich + +--- a/xen/drivers/passthrough/vtd/extern.h ++++ b/xen/drivers/passthrough/vtd/extern.h +@@ -43,8 +43,7 @@ void disable_qinval(struct vtd_iommu *io + int enable_intremap(struct vtd_iommu *iommu, int eim); + void disable_intremap(struct vtd_iommu *iommu); + +-void iommu_flush_cache_entry(void *addr, unsigned int size); +-void iommu_flush_cache_page(void *addr, unsigned long npages); ++void iommu_sync_cache(const void *addr, unsigned int size); + int iommu_alloc(struct acpi_drhd_unit *drhd); + void iommu_free(struct acpi_drhd_unit *drhd); + +--- a/xen/drivers/passthrough/vtd/intremap.c ++++ b/xen/drivers/passthrough/vtd/intremap.c +@@ -230,7 +230,7 @@ static void free_remap_entry(struct vtd_ + iremap_entries, iremap_entry); + + update_irte(iommu, iremap_entry, &new_ire, false); +- iommu_flush_cache_entry(iremap_entry, sizeof(*iremap_entry)); ++ iommu_sync_cache(iremap_entry, sizeof(*iremap_entry)); + iommu_flush_iec_index(iommu, 0, index); + + unmap_vtd_domain_page(iremap_entries); +@@ -406,7 +406,7 @@ static int ioapic_rte_to_remap_entry(str + } + + update_irte(iommu, iremap_entry, &new_ire, !init); +- iommu_flush_cache_entry(iremap_entry, sizeof(*iremap_entry)); ++ iommu_sync_cache(iremap_entry, sizeof(*iremap_entry)); + iommu_flush_iec_index(iommu, 0, index); + + unmap_vtd_domain_page(iremap_entries); +@@ -695,7 +695,7 @@ static int msi_msg_to_remap_entry( + update_irte(iommu, iremap_entry, &new_ire, msi_desc->irte_initialized); + msi_desc->irte_initialized = true; + +- iommu_flush_cache_entry(iremap_entry, sizeof(*iremap_entry)); ++ iommu_sync_cache(iremap_entry, sizeof(*iremap_entry)); + iommu_flush_iec_index(iommu, 0, index); + + unmap_vtd_domain_page(iremap_entries); +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -140,7 +140,8 @@ static int context_get_domain_id(struct + } + + static int iommus_incoherent; +-static void __iommu_flush_cache(void *addr, unsigned int size) ++ ++void iommu_sync_cache(const void *addr, unsigned int size) + { + int i; + static unsigned int clflush_size = 0; +@@ -155,16 +156,6 @@ static void __iommu_flush_cache(void *ad + cacheline_flush((char *)addr + i); + } + +-void iommu_flush_cache_entry(void *addr, unsigned int size) +-{ +- __iommu_flush_cache(addr, size); +-} +- +-void iommu_flush_cache_page(void *addr, unsigned long npages) +-{ +- __iommu_flush_cache(addr, PAGE_SIZE * npages); +-} +- + /* Allocate page table, return its machine address */ + uint64_t alloc_pgtable_maddr(unsigned long npages, nodeid_t node) + { +@@ -183,7 +174,7 @@ uint64_t alloc_pgtable_maddr(unsigned lo + vaddr = __map_domain_page(cur_pg); + memset(vaddr, 0, PAGE_SIZE); + +- iommu_flush_cache_page(vaddr, 1); ++ iommu_sync_cache(vaddr, PAGE_SIZE); + unmap_domain_page(vaddr); + cur_pg++; + } +@@ -216,7 +207,7 @@ static u64 bus_to_context_maddr(struct v + } + set_root_value(*root, maddr); + set_root_present(*root); +- iommu_flush_cache_entry(root, sizeof(struct root_entry)); ++ iommu_sync_cache(root, sizeof(struct root_entry)); + } + maddr = (u64) get_context_addr(*root); + unmap_vtd_domain_page(root_entries); +@@ -263,7 +254,7 @@ static u64 addr_to_dma_page_maddr(struct + */ + dma_set_pte_readable(*pte); + dma_set_pte_writable(*pte); +- iommu_flush_cache_entry(pte, sizeof(struct dma_pte)); ++ iommu_sync_cache(pte, sizeof(struct dma_pte)); + } + + if ( level == 2 ) +@@ -640,7 +631,7 @@ static int __must_check dma_pte_clear_on + *flush_flags |= IOMMU_FLUSHF_modified; + + spin_unlock(&hd->arch.mapping_lock); +- iommu_flush_cache_entry(pte, sizeof(struct dma_pte)); ++ iommu_sync_cache(pte, sizeof(struct dma_pte)); + + unmap_vtd_domain_page(page); + +@@ -679,7 +670,7 @@ static void iommu_free_page_table(struct + iommu_free_pagetable(dma_pte_addr(*pte), next_level); + + dma_clear_pte(*pte); +- iommu_flush_cache_entry(pte, sizeof(struct dma_pte)); ++ iommu_sync_cache(pte, sizeof(struct dma_pte)); + } + + unmap_vtd_domain_page(pt_vaddr); +@@ -1400,7 +1391,7 @@ int domain_context_mapping_one( + context_set_address_width(*context, agaw); + context_set_fault_enable(*context); + context_set_present(*context); +- iommu_flush_cache_entry(context, sizeof(struct context_entry)); ++ iommu_sync_cache(context, sizeof(struct context_entry)); + spin_unlock(&iommu->lock); + + /* Context entry was previously non-present (with domid 0). */ +@@ -1564,7 +1555,7 @@ int domain_context_unmap_one( + + context_clear_present(*context); + context_clear_entry(*context); +- iommu_flush_cache_entry(context, sizeof(struct context_entry)); ++ iommu_sync_cache(context, sizeof(struct context_entry)); + + iommu_domid= domain_iommu_domid(domain, iommu); + if ( iommu_domid == -1 ) +@@ -1791,7 +1782,7 @@ static int __must_check intel_iommu_map_ + + *pte = new; + +- iommu_flush_cache_entry(pte, sizeof(struct dma_pte)); ++ iommu_sync_cache(pte, sizeof(struct dma_pte)); + spin_unlock(&hd->arch.mapping_lock); + unmap_vtd_domain_page(page); + +@@ -1866,7 +1857,7 @@ int iommu_pte_flush(struct domain *d, ui + int iommu_domid; + int rc = 0; + +- iommu_flush_cache_entry(pte, sizeof(struct dma_pte)); ++ iommu_sync_cache(pte, sizeof(struct dma_pte)); + + for_each_drhd_unit ( drhd ) + { +@@ -2724,7 +2715,7 @@ static int __init intel_iommu_quarantine + dma_set_pte_addr(*pte, maddr); + dma_set_pte_readable(*pte); + } +- iommu_flush_cache_page(parent, 1); ++ iommu_sync_cache(parent, PAGE_SIZE); + + unmap_vtd_domain_page(parent); + parent = map_vtd_domain_page(maddr); diff --git a/xsa321-4.13-3.patch b/xsa321-4.13-3.patch new file mode 100644 index 0000000..c141c4b --- /dev/null +++ b/xsa321-4.13-3.patch @@ -0,0 +1,82 @@ +From: +Subject: x86/iommu: introduce a cache sync hook + +The hook is only implemented for VT-d and it uses the already existing +iommu_sync_cache function present in VT-d code. The new hook is +added so that the cache can be flushed by code outside of VT-d when +using shared page tables. + +Note that alloc_pgtable_maddr must use the now locally defined +sync_cache function, because IOMMU ops are not yet setup the first +time the function gets called during IOMMU initialization. + +No functional change intended. + +This is part of XSA-321. + +Reviewed-by: Jan Beulich + +--- a/xen/drivers/passthrough/vtd/extern.h ++++ b/xen/drivers/passthrough/vtd/extern.h +@@ -43,7 +43,6 @@ void disable_qinval(struct vtd_iommu *io + int enable_intremap(struct vtd_iommu *iommu, int eim); + void disable_intremap(struct vtd_iommu *iommu); + +-void iommu_sync_cache(const void *addr, unsigned int size); + int iommu_alloc(struct acpi_drhd_unit *drhd); + void iommu_free(struct acpi_drhd_unit *drhd); + +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -141,7 +141,7 @@ static int context_get_domain_id(struct + + static int iommus_incoherent; + +-void iommu_sync_cache(const void *addr, unsigned int size) ++static void sync_cache(const void *addr, unsigned int size) + { + int i; + static unsigned int clflush_size = 0; +@@ -174,7 +174,7 @@ uint64_t alloc_pgtable_maddr(unsigned lo + vaddr = __map_domain_page(cur_pg); + memset(vaddr, 0, PAGE_SIZE); + +- iommu_sync_cache(vaddr, PAGE_SIZE); ++ sync_cache(vaddr, PAGE_SIZE); + unmap_domain_page(vaddr); + cur_pg++; + } +@@ -2763,6 +2763,7 @@ const struct iommu_ops __initconstrel in + .iotlb_flush_all = iommu_flush_iotlb_all, + .get_reserved_device_memory = intel_iommu_get_reserved_device_memory, + .dump_p2m_table = vtd_dump_p2m_table, ++ .sync_cache = sync_cache, + }; + + const struct iommu_init_ops __initconstrel intel_iommu_init_ops = { +--- a/xen/include/asm-x86/iommu.h ++++ b/xen/include/asm-x86/iommu.h +@@ -121,6 +121,13 @@ extern bool untrusted_msi; + int pi_update_irte(const struct pi_desc *pi_desc, const struct pirq *pirq, + const uint8_t gvec); + ++#define iommu_sync_cache(addr, size) ({ \ ++ const struct iommu_ops *ops = iommu_get_ops(); \ ++ \ ++ if ( ops->sync_cache ) \ ++ iommu_vcall(ops, sync_cache, addr, size); \ ++}) ++ + #endif /* !__ARCH_X86_IOMMU_H__ */ + /* + * Local variables: +--- a/xen/include/xen/iommu.h ++++ b/xen/include/xen/iommu.h +@@ -250,6 +250,7 @@ struct iommu_ops { + int (*setup_hpet_msi)(struct msi_desc *); + + int (*adjust_irq_affinities)(void); ++ void (*sync_cache)(const void *addr, unsigned int size); + #endif /* CONFIG_X86 */ + + int __must_check (*suspend)(void); diff --git a/xsa321-4.13-4.patch b/xsa321-4.13-4.patch new file mode 100644 index 0000000..62bbcc7 --- /dev/null +++ b/xsa321-4.13-4.patch @@ -0,0 +1,36 @@ +From: +Subject: vtd: don't assume addresses are aligned in sync_cache + +Current code in sync_cache assume that the address passed in is +aligned to a cache line size. Fix the code to support passing in +arbitrary addresses not necessarily aligned to a cache line size. + +This is part of XSA-321. + +Reviewed-by: Jan Beulich + +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -143,8 +143,8 @@ static int iommus_incoherent; + + static void sync_cache(const void *addr, unsigned int size) + { +- int i; +- static unsigned int clflush_size = 0; ++ static unsigned long clflush_size = 0; ++ const void *end = addr + size; + + if ( !iommus_incoherent ) + return; +@@ -152,8 +152,9 @@ static void sync_cache(const void *addr, + if ( clflush_size == 0 ) + clflush_size = get_cache_line_size(); + +- for ( i = 0; i < size; i += clflush_size ) +- cacheline_flush((char *)addr + i); ++ addr -= (unsigned long)addr & (clflush_size - 1); ++ for ( ; addr < end; addr += clflush_size ) ++ cacheline_flush((char *)addr); + } + + /* Allocate page table, return its machine address */ diff --git a/xsa321-4.13-5.patch b/xsa321-4.13-5.patch new file mode 100644 index 0000000..60cfe6c --- /dev/null +++ b/xsa321-4.13-5.patch @@ -0,0 +1,24 @@ +From: +Subject: x86/alternative: introduce alternative_2 + +It's based on alternative_io_2 without inputs or outputs but with an +added memory clobber. + +This is part of XSA-321. + +Acked-by: Jan Beulich + +--- a/xen/include/asm-x86/alternative.h ++++ b/xen/include/asm-x86/alternative.h +@@ -114,6 +114,11 @@ extern void alternative_branches(void); + #define alternative(oldinstr, newinstr, feature) \ + asm volatile (ALTERNATIVE(oldinstr, newinstr, feature) : : : "memory") + ++#define alternative_2(oldinstr, newinstr1, feature1, newinstr2, feature2) \ ++ asm volatile (ALTERNATIVE_2(oldinstr, newinstr1, feature1, \ ++ newinstr2, feature2) \ ++ : : : "memory") ++ + /* + * Alternative inline assembly with input. + * diff --git a/xsa321-4.13-6.patch b/xsa321-4.13-6.patch new file mode 100644 index 0000000..4c5c5ab --- /dev/null +++ b/xsa321-4.13-6.patch @@ -0,0 +1,91 @@ +From: +Subject: vtd: optimize CPU cache sync + +Some VT-d IOMMUs are non-coherent, which requires a cache write back +in order for the changes made by the CPU to be visible to the IOMMU. +This cache write back was unconditionally done using clflush, but there are +other more efficient instructions to do so, hence implement support +for them using the alternative framework. + +This is part of XSA-321. + +Reviewed-by: Jan Beulich + +--- a/xen/drivers/passthrough/vtd/extern.h ++++ b/xen/drivers/passthrough/vtd/extern.h +@@ -68,7 +68,6 @@ int __must_check qinval_device_iotlb_syn + u16 did, u16 size, u64 addr); + + unsigned int get_cache_line_size(void); +-void cacheline_flush(char *); + void flush_all_cache(void); + + uint64_t alloc_pgtable_maddr(unsigned long npages, nodeid_t node); +--- 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 +@@ -154,7 +155,42 @@ static void sync_cache(const void *addr, + + addr -= (unsigned long)addr & (clflush_size - 1); + for ( ; addr < end; addr += clflush_size ) +- cacheline_flush((char *)addr); ++/* ++ * 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 */ +--- a/xen/drivers/passthrough/vtd/x86/vtd.c ++++ b/xen/drivers/passthrough/vtd/x86/vtd.c +@@ -51,11 +51,6 @@ unsigned int get_cache_line_size(void) + return ((cpuid_ebx(1) >> 8) & 0xff) * 8; + } + +-void cacheline_flush(char * addr) +-{ +- clflush(addr); +-} +- + void flush_all_cache() + { + wbinvd(); diff --git a/xsa321-4.13-7.patch b/xsa321-4.13-7.patch new file mode 100644 index 0000000..0bd018f --- /dev/null +++ b/xsa321-4.13-7.patch @@ -0,0 +1,153 @@ +From: +Subject: x86/ept: flush cache when modifying PTEs and sharing page tables + +Modifications made to the page tables by EPT code need to be written +to memory when the page tables are shared with the IOMMU, as Intel +IOMMUs can be non-coherent and thus require changes to be written to +memory in order to be visible to the IOMMU. + +In order to achieve this make sure data is written back to memory +after writing an EPT entry when the recalc bit is not set in +atomic_write_ept_entry. If such bit is set, the entry will be +adjusted and atomic_write_ept_entry will be called a second time +without the recalc bit set. Note that when splitting a super page the +new tables resulting of the split should also be written back. + +Failure to do so can allow devices behind the IOMMU access to the +stale super page, or cause coherency issues as changes made by the +processor to the page tables are not visible to the IOMMU. + +This allows to remove the VT-d specific iommu_pte_flush helper, since +the cache write back is now performed by atomic_write_ept_entry, and +hence iommu_iotlb_flush can be used to flush the IOMMU TLB. The newly +used method (iommu_iotlb_flush) can result in less flushes, since it +might sometimes be called rightly with 0 flags, in which case it +becomes a no-op. + +This is part of XSA-321. + +Reviewed-by: Jan Beulich + +--- a/xen/arch/x86/mm/p2m-ept.c ++++ b/xen/arch/x86/mm/p2m-ept.c +@@ -58,6 +58,19 @@ static int atomic_write_ept_entry(struct + + write_atomic(&entryptr->epte, new.epte); + ++ /* ++ * The recalc field on the EPT is used to signal either that a ++ * recalculation of the EMT field is required (which doesn't effect the ++ * IOMMU), or a type change. Type changes can only be between ram_rw, ++ * logdirty and ioreq_server: changes to/from logdirty won't work well with ++ * an IOMMU anyway, as IOMMU #PFs are not synchronous and will lead to ++ * aborts, and changes to/from ioreq_server are already fully flushed ++ * before returning to guest context (see ++ * XEN_DMOP_map_mem_type_to_ioreq_server). ++ */ ++ if ( !new.recalc && iommu_use_hap_pt(p2m->domain) ) ++ iommu_sync_cache(entryptr, sizeof(*entryptr)); ++ + return 0; + } + +@@ -278,6 +291,9 @@ static bool_t ept_split_super_page(struc + break; + } + ++ if ( iommu_use_hap_pt(p2m->domain) ) ++ iommu_sync_cache(table, EPT_PAGETABLE_ENTRIES * sizeof(ept_entry_t)); ++ + unmap_domain_page(table); + + /* Even failed we should install the newly allocated ept page. */ +@@ -337,6 +353,9 @@ static int ept_next_level(struct p2m_dom + if ( !next ) + return GUEST_TABLE_MAP_FAILED; + ++ if ( iommu_use_hap_pt(p2m->domain) ) ++ iommu_sync_cache(next, EPT_PAGETABLE_ENTRIES * sizeof(ept_entry_t)); ++ + rc = atomic_write_ept_entry(p2m, ept_entry, e, next_level); + ASSERT(rc == 0); + } +@@ -821,7 +840,10 @@ out: + need_modify_vtd_table ) + { + if ( iommu_use_hap_pt(d) ) +- rc = iommu_pte_flush(d, gfn, &ept_entry->epte, order, vtd_pte_present); ++ rc = iommu_iotlb_flush(d, _dfn(gfn), (1u << order), ++ (iommu_flags ? IOMMU_FLUSHF_added : 0) | ++ (vtd_pte_present ? IOMMU_FLUSHF_modified ++ : 0)); + else if ( need_iommu_pt_sync(d) ) + rc = iommu_flags ? + iommu_legacy_map(d, _dfn(gfn), mfn, order, iommu_flags) : +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -1884,53 +1884,6 @@ static int intel_iommu_lookup_page(struc + return 0; + } + +-int iommu_pte_flush(struct domain *d, uint64_t dfn, uint64_t *pte, +- int order, int present) +-{ +- struct acpi_drhd_unit *drhd; +- struct vtd_iommu *iommu = NULL; +- struct domain_iommu *hd = dom_iommu(d); +- bool_t flush_dev_iotlb; +- int iommu_domid; +- int rc = 0; +- +- iommu_sync_cache(pte, sizeof(struct dma_pte)); +- +- for_each_drhd_unit ( drhd ) +- { +- iommu = drhd->iommu; +- if ( !test_bit(iommu->index, &hd->arch.iommu_bitmap) ) +- continue; +- +- flush_dev_iotlb = !!find_ats_dev_drhd(iommu); +- iommu_domid= domain_iommu_domid(d, iommu); +- if ( iommu_domid == -1 ) +- continue; +- +- rc = iommu_flush_iotlb_psi(iommu, iommu_domid, +- __dfn_to_daddr(dfn), +- order, !present, flush_dev_iotlb); +- if ( rc > 0 ) +- { +- iommu_flush_write_buffer(iommu); +- rc = 0; +- } +- } +- +- if ( unlikely(rc) ) +- { +- if ( !d->is_shutting_down && printk_ratelimit() ) +- printk(XENLOG_ERR VTDPREFIX +- " d%d: IOMMU pages flush failed: %d\n", +- d->domain_id, rc); +- +- if ( !is_hardware_domain(d) ) +- domain_crash(d); +- } +- +- return rc; +-} +- + static int __init vtd_ept_page_compatible(struct vtd_iommu *iommu) + { + u64 ept_cap, vtd_cap = iommu->cap; +--- a/xen/include/asm-x86/iommu.h ++++ b/xen/include/asm-x86/iommu.h +@@ -97,10 +97,6 @@ static inline int iommu_adjust_irq_affin + : 0; + } + +-/* While VT-d specific, this must get declared in a generic header. */ +-int __must_check iommu_pte_flush(struct domain *d, u64 gfn, u64 *pte, +- int order, int present); +- + static inline bool iommu_supports_x2apic(void) + { + return iommu_init_ops && iommu_init_ops->supports_x2apic diff --git a/xsa327.patch b/xsa327.patch new file mode 100644 index 0000000..0541cfa --- /dev/null +++ b/xsa327.patch @@ -0,0 +1,63 @@ +From 030300ebbb86c40c12db038714479d746167c767 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Tue, 26 May 2020 18:31:33 +0100 +Subject: [PATCH] xen: Check the alignment of the offset pased via + VCPUOP_register_vcpu_info + +Currently a guest is able to register any guest physical address to use +for the vcpu_info structure as long as the structure can fits in the +rest of the frame. + +This means a guest can provide an address that is not aligned to the +natural alignment of the structure. + +On Arm 32-bit, unaligned access are completely forbidden by the +hypervisor. This will result to a data abort which is fatal. + +On Arm 64-bit, unaligned access are only forbidden when used for atomic +access. As the structure contains fields (such as evtchn_pending_self) +that are updated using atomic operations, any unaligned access will be +fatal as well. + +While the misalignment is only fatal on Arm, a generic check is added +as an x86 guest shouldn't sensibly pass an unaligned address (this +would result to a split lock). + +This is XSA-327. + +Reported-by: Julien Grall +Signed-off-by: Julien Grall +Reviewed-by: Andrew Cooper +Reviewed-by: Stefano Stabellini +--- + xen/common/domain.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/xen/common/domain.c b/xen/common/domain.c +index 7cc9526139a6..e9be05f1d05f 100644 +--- a/xen/common/domain.c ++++ b/xen/common/domain.c +@@ -1227,10 +1227,20 @@ int map_vcpu_info(struct vcpu *v, unsigned long gfn, unsigned offset) + void *mapping; + vcpu_info_t *new_info; + struct page_info *page; ++ unsigned int align; + + if ( offset > (PAGE_SIZE - sizeof(vcpu_info_t)) ) + return -EINVAL; + ++#ifdef CONFIG_COMPAT ++ if ( has_32bit_shinfo(d) ) ++ align = alignof(new_info->compat); ++ else ++#endif ++ align = alignof(*new_info); ++ if ( offset & (align - 1) ) ++ return -EINVAL; ++ + if ( !mfn_eq(v->vcpu_info_mfn, INVALID_MFN) ) + return -EINVAL; + +-- +2.17.1 + diff --git a/xsa328-4.13-1.patch b/xsa328-4.13-1.patch new file mode 100644 index 0000000..56e48de --- /dev/null +++ b/xsa328-4.13-1.patch @@ -0,0 +1,118 @@ +From: Jan Beulich +Subject: x86/EPT: ept_set_middle_entry() related adjustments + +ept_split_super_page() wants to further modify the newly allocated +table, so have ept_set_middle_entry() return the mapped pointer rather +than tearing it down and then getting re-established right again. + +Similarly ept_next_level() wants to hand back a mapped pointer of +the next level page, so re-use the one established by +ept_set_middle_entry() in case that path was taken. + +Pull the setting of suppress_ve ahead of insertion into the higher level +table, and don't have ept_split_super_page() set the field a 2nd time. + +This is part of XSA-328. + +Signed-off-by: Jan Beulich + +--- a/xen/arch/x86/mm/p2m-ept.c ++++ b/xen/arch/x86/mm/p2m-ept.c +@@ -187,8 +187,9 @@ static void ept_p2m_type_to_flags(struct + #define GUEST_TABLE_SUPER_PAGE 2 + #define GUEST_TABLE_POD_PAGE 3 + +-/* Fill in middle levels of ept table */ +-static int ept_set_middle_entry(struct p2m_domain *p2m, ept_entry_t *ept_entry) ++/* Fill in middle level of ept table; return pointer to mapped new table. */ ++static ept_entry_t *ept_set_middle_entry(struct p2m_domain *p2m, ++ ept_entry_t *ept_entry) + { + mfn_t mfn; + ept_entry_t *table; +@@ -196,7 +197,12 @@ static int ept_set_middle_entry(struct p + + mfn = p2m_alloc_ptp(p2m, 0); + if ( mfn_eq(mfn, INVALID_MFN) ) +- return 0; ++ return NULL; ++ ++ table = map_domain_page(mfn); ++ ++ for ( i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) ++ table[i].suppress_ve = 1; + + ept_entry->epte = 0; + ept_entry->mfn = mfn_x(mfn); +@@ -208,14 +214,7 @@ static int ept_set_middle_entry(struct p + + ept_entry->suppress_ve = 1; + +- table = map_domain_page(mfn); +- +- for ( i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) +- table[i].suppress_ve = 1; +- +- unmap_domain_page(table); +- +- return 1; ++ return table; + } + + /* free ept sub tree behind an entry */ +@@ -253,10 +252,10 @@ static bool_t ept_split_super_page(struc + + ASSERT(is_epte_superpage(ept_entry)); + +- if ( !ept_set_middle_entry(p2m, &new_ept) ) ++ table = ept_set_middle_entry(p2m, &new_ept); ++ if ( !table ) + return 0; + +- table = map_domain_page(_mfn(new_ept.mfn)); + trunk = 1UL << ((level - 1) * EPT_TABLE_ORDER); + + for ( i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) +@@ -267,7 +266,6 @@ static bool_t ept_split_super_page(struc + epte->sp = (level > 1); + epte->mfn += i * trunk; + epte->snp = is_iommu_enabled(p2m->domain) && iommu_snoop; +- epte->suppress_ve = 1; + + ept_p2m_type_to_flags(p2m, epte, epte->sa_p2mt, epte->access); + +@@ -306,8 +304,7 @@ static int ept_next_level(struct p2m_dom + ept_entry_t **table, unsigned long *gfn_remainder, + int next_level) + { +- unsigned long mfn; +- ept_entry_t *ept_entry, e; ++ ept_entry_t *ept_entry, *next = NULL, e; + u32 shift, index; + + shift = next_level * EPT_TABLE_ORDER; +@@ -332,19 +329,17 @@ static int ept_next_level(struct p2m_dom + if ( read_only ) + return GUEST_TABLE_MAP_FAILED; + +- if ( !ept_set_middle_entry(p2m, ept_entry) ) ++ next = ept_set_middle_entry(p2m, ept_entry); ++ if ( !next ) + return GUEST_TABLE_MAP_FAILED; +- else +- e = atomic_read_ept_entry(ept_entry); /* Refresh */ ++ /* e is now stale and hence may not be used anymore below. */ + } +- + /* The only time sp would be set here is if we had hit a superpage */ +- if ( is_epte_superpage(&e) ) ++ else if ( is_epte_superpage(&e) ) + return GUEST_TABLE_SUPER_PAGE; + +- mfn = e.mfn; + unmap_domain_page(*table); +- *table = map_domain_page(_mfn(mfn)); ++ *table = next ?: map_domain_page(_mfn(e.mfn)); + *gfn_remainder &= (1UL << shift) - 1; + return GUEST_TABLE_NORMAL_PAGE; + } diff --git a/xsa328-4.13-2.patch b/xsa328-4.13-2.patch new file mode 100644 index 0000000..c4f437f --- /dev/null +++ b/xsa328-4.13-2.patch @@ -0,0 +1,48 @@ +From: +Subject: x86/ept: atomically modify entries in ept_next_level + +ept_next_level was passing a live PTE pointer to ept_set_middle_entry, +which was then modified without taking into account that the PTE could +be part of a live EPT table. This wasn't a security issue because the +pages returned by p2m_alloc_ptp are zeroed, so adding such an entry +before actually initializing it didn't allow a guest to access +physical memory addresses it wasn't supposed to access. + +This is part of XSA-328. + +Reviewed-by: Jan Beulich + +--- a/xen/arch/x86/mm/p2m-ept.c ++++ b/xen/arch/x86/mm/p2m-ept.c +@@ -307,6 +307,8 @@ static int ept_next_level(struct p2m_dom + ept_entry_t *ept_entry, *next = NULL, e; + u32 shift, index; + ++ ASSERT(next_level); ++ + shift = next_level * EPT_TABLE_ORDER; + + index = *gfn_remainder >> shift; +@@ -323,16 +325,20 @@ static int ept_next_level(struct p2m_dom + + if ( !is_epte_present(&e) ) + { ++ int rc; ++ + if ( e.sa_p2mt == p2m_populate_on_demand ) + return GUEST_TABLE_POD_PAGE; + + if ( read_only ) + return GUEST_TABLE_MAP_FAILED; + +- next = ept_set_middle_entry(p2m, ept_entry); ++ next = ept_set_middle_entry(p2m, &e); + if ( !next ) + return GUEST_TABLE_MAP_FAILED; +- /* e is now stale and hence may not be used anymore below. */ ++ ++ rc = atomic_write_ept_entry(p2m, ept_entry, e, next_level); ++ ASSERT(rc == 0); + } + /* The only time sp would be set here is if we had hit a superpage */ + else if ( is_epte_superpage(&e) ) From 5c5498aae848beccdd4411329d3b624f7c8c00f7 Mon Sep 17 00:00:00 2001 From: Tom Stellard Date: Tue, 14 Jul 2020 14:55:13 +0000 Subject: [PATCH 042/194] Use make macros https://fedoraproject.org/wiki/Changes/UseMakeBuildInstallMacro --- xen.spec | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/xen.spec b/xen.spec index 60b3c61..bde45c8 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.13.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 @@ -399,15 +399,15 @@ 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 %{?_smp_mflags} 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'`" +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 %{?_smp_mflags} prefix=/usr xen CC="gcc `echo $RPM_OPT_FLAGS | sed -e 's/-mfloat-abi=hard//g' -e 's/-march=armv7-a//g'`" +%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 %{?_smp_mflags} prefix=/usr xen CC="gcc $RPM_OPT_FLAGS -mno-outline-atomics" +%make_build prefix=/usr xen CC="gcc $RPM_OPT_FLAGS -mno-outline-atomics" %else -make %{?_smp_mflags} 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'`" +%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 @@ -424,7 +424,7 @@ CONFIG_EXTRA="$CONFIG_EXTRA --with-system-ovmf=%{_libexecdir}/%{name}/boot/ovmf. 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 -make %{?_smp_mflags} %{?ocaml_flags} prefix=/usr tools +%make_build %{?ocaml_flags} prefix=/usr tools %if %build_docs make prefix=/usr docs %endif @@ -940,6 +940,10 @@ fi %endif %changelog +* Tue Jul 14 2020 Tom Stellard - 4.13.1-5 +- Use make macros +- https://fedoraproject.org/wiki/Changes/UseMakeBuildInstallMacro + * Tue Jul 07 2020 Michael Young - 4.13.1-4 - incorrect error handling in event channel port allocation leads to DoS [XSA-317, CVE-2020-15566] (#1854465) From f15c128730c9c7ee4445ec461af9c0b1edbee1ad Mon Sep 17 00:00:00 2001 From: Michael Young Date: Sun, 26 Jul 2020 17:17:25 +0100 Subject: [PATCH 043/194] update to xen-4.14.0 --- .gitignore | 2 +- sources | 2 +- xen-net-disable-iptables-on-bridge.patch | 27 ---- xen.drop.brctl.patch | 92 ------------ xen.gcc10.fixes.patch | 34 +---- xen.hypervisor.config | 32 ++-- xen.ocaml.4.10.patch | 78 ---------- xen.python.env.patch | 43 ------ xen.spec | 75 ++++------ xsa317.patch | 50 ------- xsa319.patch | 27 ---- xsa320-4.13-1.patch | 117 --------------- xsa320-4.13-2.patch | 179 ----------------------- xsa321-4.13-1.patch | 31 ---- xsa321-4.13-2.patch | 175 ---------------------- xsa321-4.13-3.patch | 82 ----------- xsa321-4.13-4.patch | 36 ----- xsa321-4.13-5.patch | 24 --- xsa321-4.13-6.patch | 91 ------------ xsa321-4.13-7.patch | 153 ------------------- xsa327.patch | 63 -------- xsa328-4.13-1.patch | 118 --------------- xsa328-4.13-2.patch | 48 ------ 23 files changed, 54 insertions(+), 1525 deletions(-) delete mode 100644 xen-net-disable-iptables-on-bridge.patch delete mode 100644 xen.ocaml.4.10.patch delete mode 100644 xen.python.env.patch delete mode 100644 xsa317.patch delete mode 100644 xsa319.patch delete mode 100644 xsa320-4.13-1.patch delete mode 100644 xsa320-4.13-2.patch delete mode 100644 xsa321-4.13-1.patch delete mode 100644 xsa321-4.13-2.patch delete mode 100644 xsa321-4.13-3.patch delete mode 100644 xsa321-4.13-4.patch delete mode 100644 xsa321-4.13-5.patch delete mode 100644 xsa321-4.13-6.patch delete mode 100644 xsa321-4.13-7.patch delete mode 100644 xsa327.patch delete mode 100644 xsa328-4.13-1.patch delete mode 100644 xsa328-4.13-2.patch diff --git a/.gitignore b/.gitignore index c542b51..f112132 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.13.1.tar.gz +/xen-4.14.0.tar.gz diff --git a/sources b/sources index 0fe92bd..660c881 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.13.1.tar.gz) = b56d20704155d98d803496cba83eb928e0f986a750831cd5600fc88d0ae772fe1456571654375054043d2da8daca255cc98385ebf08b1b1a75ecf7f4b7a0ee90 +SHA512 (xen-4.14.0.tar.gz) = ebce47a2f754955d8517123d69f62006634d97620fbbe3784869a0667466e586a249f57ffaf7846d5bcb45d69377cde43354c82c233fbb5407e55653b9a33ac0 diff --git a/xen-net-disable-iptables-on-bridge.patch b/xen-net-disable-iptables-on-bridge.patch deleted file mode 100644 index bc2de21..0000000 --- a/xen-net-disable-iptables-on-bridge.patch +++ /dev/null @@ -1,27 +0,0 @@ ---- xen-4.1.0-orig/tools/hotplug/Linux/vif-bridge 2008-08-22 10:49:07.000000000 +0100 -+++ xen-4.1.0-new/tools/hotplug/Linux/vif-bridge 2008-08-29 11:29:38.000000000 +0100 -@@ -96,8 +96,6 @@ case "$command" in - ;; - esac - --handle_iptable -- - call_hooks vif post - - log debug "Successful vif-bridge $command for $dev, bridge $bridge." ---- xen-3.3.0-orig/tools/hotplug/Linux/xen-network-common.sh 2008-08-22 10:49:07.000000000 +0100 -+++ xen-3.3.0-new/tools/hotplug/Linux/xen-network-common.sh 2008-08-29 11:29:38.000000000 +0100 -@@ -99,6 +99,13 @@ create_bridge () { - brctl addbr ${bridge} - brctl stp ${bridge} off - brctl setfd ${bridge} 0 -+ # Setting these to zero stops guest<->LAN traffic -+ # traversing the bridge from hitting the *tables -+ # rulesets. guest<->host traffic still gets processed -+ # by the host's iptables rules so this isn't a hole -+ sysctl -q -w "net.bridge.bridge-nf-call-arptables=0" -+ sysctl -q -w "net.bridge.bridge-nf-call-ip6tables=0" -+ sysctl -q -w "net.bridge.bridge-nf-call-iptables=0" - fi - } - diff --git a/xen.drop.brctl.patch b/xen.drop.brctl.patch index 8d51b1e..f420b8e 100644 --- a/xen.drop.brctl.patch +++ b/xen.drop.brctl.patch @@ -1,95 +1,3 @@ ---- xen-4.11.0-rc7/tools/hotplug/Linux/colo-proxy-setup.orig 2018-06-28 08:39:45.000000000 +0100 -+++ xen-4.11.0-rc7/tools/hotplug/Linux/colo-proxy-setup 2018-07-03 20:09:26.637017216 +0100 -@@ -76,10 +76,10 @@ - - function setup_secondary() - { -- do_without_error brctl delif $bridge $vifname -- do_without_error brctl addbr $forwardbr -- do_without_error brctl addif $forwardbr $vifname -- do_without_error brctl addif $forwardbr $forwarddev -+ do_without_error ip link set $vifname nomaster -+ do_without_error ip link add name $forwardbr type bridge -+ do_without_error ip link set $vifname master $forwardbr -+ do_without_error ip link set $forwarddev master $forwardbr - do_without_error ip link set dev $forwardbr up - do_without_error modprobe xt_SECCOLO - -@@ -91,10 +91,10 @@ - - function teardown_secondary() - { -- do_without_error brctl delif $forwardbr $forwarddev -- do_without_error brctl delif $forwardbr $vifname -- do_without_error brctl delbr $forwardbr -- do_without_error brctl addif $bridge $vifname -+ do_without_error ip link set $forwarddev nomaster -+ do_without_error ip link set $vifname nomaster -+ do_without_error ip link delete $forwardbr type bridge -+ do_without_error ip link set $vifname master $bridge - - do_without_error iptables -t mangle -D PREROUTING -m physdev --physdev-in \ - $vifname -j SECCOLO --index $index ---- xen-4.11.0-rc7/tools/hotplug/Linux/vif2.orig 2018-06-28 08:39:45.000000000 +0100 -+++ xen-4.11.0-rc7/tools/hotplug/Linux/vif2 2018-07-03 20:11:07.558757301 +0100 -@@ -7,13 +7,12 @@ - bridge=$(xenstore_read_default "$XENBUS_PATH/bridge" "$bridge") - if [ -z "$bridge" ] - then -- nr_bridges=$(($(brctl show | cut -f 1 | grep -v "^$" | wc -l) - 1)) -+ nr_bridges=$(bridge link | wc -l) - if [ "$nr_bridges" != 1 ] - then - fatal "no bridge specified, and don't know which one to use ($nr_bridges found)" - fi -- bridge=$(brctl show | cut -d " --" -f 2 | cut -f 1) -+ bridge=$(bridge link | cut -d" " -f10) - fi - - command="$1" ---- xen-4.11.0-rc7/tools/hotplug/Linux/vif-bridge.orig 2018-07-03 19:59:18.499474117 +0100 -+++ xen-4.11.0-rc7/tools/hotplug/Linux/vif-bridge 2018-07-03 20:12:31.088852864 +0100 -@@ -33,7 +33,7 @@ - - if [ -z "$bridge" ] - then -- bridge=$(brctl show | awk 'NR==2{print$1}') -+ bridge=$(bridge link | cut -d" " -f10) - - if [ -z "$bridge" ] - then -@@ -82,7 +82,7 @@ - ;; - - offline) -- do_without_error brctl delif "$bridge" "$dev" -+ do_without_error ip link set "$dev" nomaster - do_without_error ifconfig "$dev" down - ;; - ---- xen-4.11.0-rc7/tools/hotplug/Linux/xen-network-common.sh.orig 2018-07-03 19:59:18.500474154 +0100 -+++ xen-4.11.0-rc7/tools/hotplug/Linux/xen-network-common.sh 2018-07-03 20:16:16.466205182 +0100 -@@ -111,9 +111,7 @@ - - # Don't create the bridge if it already exists. - if [ ! -e "/sys/class/net/${bridge}/bridge" ]; then -- brctl addbr ${bridge} -- brctl stp ${bridge} off -- brctl setfd ${bridge} 0 -+ ip link add name ${bridge} type bridge stp_state 0 forward_delay 0 - # Setting these to zero stops guest<->LAN traffic - # traversing the bridge from hitting the *tables - # rulesets. guest<->host traffic still gets processed -@@ -134,7 +132,7 @@ - ip link set dev ${dev} up || true - return - fi -- brctl addif ${bridge} ${dev} -+ ip link set ${dev} master ${bridge} - ip link set dev ${dev} up - } - --- 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 @@ diff --git a/xen.gcc10.fixes.patch b/xen.gcc10.fixes.patch index 3159bd7..0bd895f 100644 --- a/xen.gcc10.fixes.patch +++ b/xen.gcc10.fixes.patch @@ -1,25 +1,3 @@ ---- xen-4.13.0/tools/xenstore/utils.h.orig 2019-12-17 14:23:09.000000000 +0000 -+++ xen-4.13.0/tools/xenstore/utils.h 2020-01-21 21:13:05.108957447 +0000 -@@ -24,7 +24,7 @@ - void barf(const char *fmt, ...) __attribute__((noreturn)); - void barf_perror(const char *fmt, ...) __attribute__((noreturn)); - --void (*xprintf)(const char *fmt, ...); -+extern void (*xprintf)(const char *fmt, ...); - - #define eprintf(_fmt, _args...) xprintf("[ERR] %s" _fmt, __FUNCTION__, ##_args) - ---- xen-4.13.0/tools/xenstore/xenstored_core.h.orig 2020-01-21 21:15:19.243931307 +0000 -+++ xen-4.13.0/tools/xenstore/xenstored_core.h 2020-01-21 21:38:35.340617819 +0000 -@@ -204,7 +204,7 @@ - /* Open a pipe for signal handling */ - void init_pipe(int reopen_log_pipe[2]); - --xengnttab_handle **xgt_handle; -+extern xengnttab_handle **xgt_handle; - - int remember_string(struct hashtable *hash, const char *str); - --- xen-4.13.0/tools/libxl/libxlu_pci.c.orig 2019-12-17 14:23:09.000000000 +0000 +++ xen-4.13.0/tools/libxl/libxlu_pci.c 2020-01-21 21:56:26.812212916 +0000 @@ -48,7 +48,7 @@ @@ -42,17 +20,7 @@ return 0; } ---- xen-4.13.0/tools/debugger/kdd/kdd.h.orig 2019-12-17 14:23:09.000000000 +0000 -+++ xen-4.13.0/tools/debugger/kdd/kdd.h 2020-01-21 23:35:55.458605582 +0000 -@@ -323,7 +323,7 @@ - kdd_msg msg; - kdd_reg reg; - kdd_stc stc; -- uint8_t payload[0]; -+ uint8_t payload[65535]; - }; - } PACKED kdd_pkt; - + --- 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 @@ diff --git a/xen.hypervisor.config b/xen.hypervisor.config index cf6aa02..a498382 100644 --- a/xen.hypervisor.config +++ b/xen.hypervisor.config @@ -1,42 +1,51 @@ # # Automatically generated file; DO NOT EDIT. -# Xen/x86 4.13.0 Configuration +# Xen/x86 4.14.0-rc Configuration # +CONFIG_CC_IS_GCC=y +CONFIG_GCC_VERSION=100101 +CONFIG_CLANG_VERSION=0 +CONFIG_CC_HAS_VISIBILITY_ATTRIBUTE=y CONFIG_X86_64=y CONFIG_X86=y CONFIG_ARCH_DEFCONFIG="arch/x86/configs/x86_64_defconfig" +CONFIG_INDIRECT_THUNK=y +CONFIG_HAS_AS_CET_SS=y # # Architecture Features # CONFIG_NR_CPUS=256 CONFIG_PV=y +CONFIG_PV32=y CONFIG_PV_LINEAR_PT=y CONFIG_HVM=y CONFIG_SHADOW_PAGING=y # CONFIG_BIGMEM is not set -# CONFIG_HVM_FEP is not set CONFIG_TBOOT=y CONFIG_XEN_ALIGN_DEFAULT=y # CONFIG_XEN_ALIGN_2M is not set # CONFIG_XEN_GUEST is not set +# CONFIG_HYPERV_GUEST is not set +# end of Architecture Features # # Common Features # CONFIG_COMPAT=y CONFIG_CORE_PARKING=y +CONFIG_GRANT_TABLE=y CONFIG_HAS_ALTERNATIVE=y CONFIG_HAS_EX_TABLE=y +CONFIG_HAS_FAST_MULTIPLY=y CONFIG_MEM_ACCESS_ALWAYS_ON=y CONFIG_MEM_ACCESS=y CONFIG_HAS_MEM_PAGING=y -CONFIG_HAS_MEM_SHARING=y CONFIG_HAS_PDX=y CONFIG_HAS_UBSAN=y CONFIG_HAS_KEXEC=y -CONFIG_HAS_GDBSX=y CONFIG_HAS_IOPORTS=y +CONFIG_HAS_SCHED_GRANULARITY=y CONFIG_NEEDS_LIBELF=y # @@ -44,13 +53,16 @@ CONFIG_NEEDS_LIBELF=y # CONFIG_SPECULATIVE_HARDEN_ARRAY=y CONFIG_SPECULATIVE_HARDEN_BRANCH=y +# end of Speculative hardening + +CONFIG_HYPFS=y +CONFIG_HYPFS_CONFIG=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 is not set CONFIG_SCHED_NULL=y CONFIG_SCHED_DEFAULT="credit2" CONFIG_CRYPTO=y @@ -59,6 +71,8 @@ CONFIG_FAST_SYMBOL_LOOKUP=y CONFIG_ENFORCE_UNIQUE_SYMBOLS=y CONFIG_CMDLINE="" CONFIG_DOM0_MEM="" +CONFIG_TRACEBUFFER=y +# end of Common Features # # Device Drivers @@ -74,18 +88,16 @@ CONFIG_HAS_PCI=y CONFIG_VIDEO=y CONFIG_VGA=y CONFIG_HAS_VPCI=y +# end of Device Drivers -# -# Deprecated Functionality -# -# CONFIG_PV_LDT_PAGING is not set -CONFIG_DEFCONFIG_LIST="$ARCH_DEFCONFIG" +# CONFIG_EXPERT is not set CONFIG_ARCH_SUPPORTS_INT128=y # # Debugging Options # # CONFIG_DEBUG is not set +# end of Debugging Options # ARM64 settings CONFIG_64BIT=y diff --git a/xen.ocaml.4.10.patch b/xen.ocaml.4.10.patch deleted file mode 100644 index a24ffdd..0000000 --- a/xen.ocaml.4.10.patch +++ /dev/null @@ -1,78 +0,0 @@ ---- xen-4.13.0/tools/ocaml/libs/xc/xenctrl_stubs.c.orig 2019-12-17 14:23:09.000000000 +0000 -+++ xen-4.13.0/tools/ocaml/libs/xc/xenctrl_stubs.c 2020-01-21 19:24:49.508024245 +0000 -@@ -46,7 +46,7 @@ - #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))) -+ ((Field(array, index) == Val_none) ? NULL : (char *) Bp_val(Field(Field(array, index), 0))) - - /* maybe here we should check the range of the input instead of blindly - * casting it to uint32 */ -@@ -505,7 +505,7 @@ - ret = xc_vcpu_getcontext(_H(xch), _D(domid), Int_val(cpu), &ctxt); - - context = caml_alloc_string(sizeof(ctxt)); -- memcpy(String_val(context), (char *) &ctxt.c, sizeof(ctxt.c)); -+ memcpy((char *) Bp_val(context), (char *) &ctxt.c, sizeof(ctxt.c)); - - CAMLreturn(context); - } -@@ -684,7 +684,7 @@ - conring_size = size; - - ring = caml_alloc_string(count); -- memcpy(String_val(ring), str, count); -+ memcpy((char *) Bp_val(ring), str, count); - free(str); - - CAMLreturn(ring); -@@ -695,7 +695,7 @@ - CAMLparam2(xch, keys); - int r; - -- r = xc_send_debug_keys(_H(xch), String_val(keys)); -+ r = xc_send_debug_keys(_H(xch), (char *) Bp_val(keys)); - if (r) - failwith_xc(_H(xch)); - CAMLreturn(Val_unit); -@@ -855,7 +855,7 @@ - } - - for (r = 0; r < 4; r++) -- out_config[r] = (c_config[r]) ? String_val(Field(Field(array, r), 0)) : NULL; -+ out_config[r] = (c_config[r]) ? (char *) Bp_val(Field(Field(array, r), 0)) : NULL; - - r = xc_cpuid_set(_H(xch), _D(domid), - c_input, (const char **)c_config, out_config); ---- xen-4.13.0/tools/ocaml/libs/xb/xs_ring_stubs.c.orig 2019-12-17 14:23:09.000000000 +0000 -+++ xen-4.13.0/tools/ocaml/libs/xb/xs_ring_stubs.c 2020-01-21 23:51:35.473330934 +0000 -@@ -44,7 +44,7 @@ - CAMLlocal1(ml_result); - - struct mmap_interface *interface = GET_C_STRUCT(ml_interface); -- char *buffer = String_val(ml_buffer); -+ char *buffer = (char *) Bp_val(ml_buffer); - int len = Int_val(ml_len); - int result; - -@@ -103,7 +103,7 @@ - CAMLlocal1(ml_result); - - struct mmap_interface *interface = GET_C_STRUCT(ml_interface); -- char *buffer = String_val(ml_buffer); -+ char *buffer = (char *) Bp_val(ml_buffer); - int len = Int_val(ml_len); - int result; - ---- xen-4.13.0/tools/ocaml/libs/xb/xenbus_stubs.c.orig 2019-12-17 14:23:09.000000000 +0000 -+++ xen-4.13.0/tools/ocaml/libs/xb/xenbus_stubs.c 2020-01-22 00:04:09.443168991 +0000 -@@ -65,7 +65,7 @@ - }; - - ret = caml_alloc_string(sizeof(struct xsd_sockmsg)); -- memcpy(String_val(ret), &xsd, sizeof(struct xsd_sockmsg)); -+ memcpy((char *) Bp_val(ret), &xsd, sizeof(struct xsd_sockmsg)); - - CAMLreturn(ret); - } diff --git a/xen.python.env.patch b/xen.python.env.patch deleted file mode 100644 index 3b8c1e6..0000000 --- a/xen.python.env.patch +++ /dev/null @@ -1,43 +0,0 @@ ---- xen-4.11.0/tools/xenmon/Makefile.orig 2018-07-09 14:47:19.000000000 +0100 -+++ xen-4.11.0/tools/xenmon/Makefile 2018-09-10 21:13:15.200655105 +0100 -@@ -32,7 +32,7 @@ - $(INSTALL_DIR) $(DESTDIR)$(sbindir) - $(INSTALL_PROG) xenbaked $(DESTDIR)$(sbindir)/xenbaked - $(INSTALL_PROG) xentrace_setmask $(DESTDIR)$(sbindir)/xentrace_setmask -- $(INSTALL_PROG) xenmon.py $(DESTDIR)$(sbindir)/xenmon -+ $(INSTALL_PYTHON_PROG) xenmon.py $(DESTDIR)$(sbindir)/xenmon - - .PHONY: uninstall - uninstall: ---- xen-4.11.0/tools/python/Makefile.orig 2018-07-09 14:47:19.000000000 +0100 -+++ xen-4.11.0/tools/python/Makefile 2018-09-10 21:21:07.097979007 +0100 -@@ -20,8 +20,8 @@ - setup.py install --record $(INSTALL_LOG) $(PYTHON_PREFIX_ARG) \ - --root="$(DESTDIR)" --force - -- $(INSTALL_PROG) scripts/convert-legacy-stream $(DESTDIR)$(LIBEXEC_BIN) -- $(INSTALL_PROG) scripts/verify-stream-v2 $(DESTDIR)$(LIBEXEC_BIN) -+ $(INSTALL_PYTHON_PROG) scripts/convert-legacy-stream $(DESTDIR)$(LIBEXEC_BIN) -+ $(INSTALL_PYTHON_PROG) scripts/verify-stream-v2 $(DESTDIR)$(LIBEXEC_BIN) - - .PHONY: uninstall - uninstall: ---- xen-4.11.0/tools/python/install-wrap.orig 2018-07-09 14:47:19.000000000 +0100 -+++ xen-4.11.0/tools/python/install-wrap 2018-09-11 20:09:57.803655357 +0100 -@@ -44,7 +44,7 @@ - destf="$dest" - for srcf in ${srcs}; do - if test -d "$dest"; then -- destf="$dest/${srcf%%*/}" -+ destf="$dest/${srcf##*/}" - fi - org="$(sed -n '2q; /^#! *\/usr\/bin\/env python *$/p' $srcf)" - if test "x$org" = x; then ---- xen-4.11.0/tools/misc/xencov_split.orig 2018-07-09 14:47:19.000000000 +0100 -+++ xen-4.11.0/tools/misc/xencov_split 2018-09-18 21:56:07.397893895 +0100 -@@ -1,4 +1,4 @@ --#!/usr/bin/python -+#!/usr/bin/python3 - - import sys, os, os.path as path, struct, errno - from optparse import OptionParser diff --git a/xen.spec b/xen.spec index bde45c8..01c3b2a 100644 --- a/xen.spec +++ b/xen.spec @@ -53,12 +53,12 @@ %endif # Hypervisor ABI -%define hv_abi 4.13 +%define hv_abi 4.14 Summary: Xen is a virtual machine monitor Name: xen -Version: 4.13.1 -Release: 5%{?dist} +Version: 4.14.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 +73,6 @@ Source15: polarssl-1.1.4-gpl.tgz # .config file for xen hypervisor Source21: xen.hypervisor.config -Patch1: xen-net-disable-iptables-on-bridge.patch Patch3: xen.fedora.efi.build.patch Patch4: CVE-2014-0150.patch Patch5: xen.fedora.systemd.patch @@ -110,24 +109,8 @@ Patch37: droplibvirtconflict.patch Patch38: qemu.trad.CVE-2017-8309.patch Patch39: qemu.trad.CVE-2017-9330.patch Patch40: xen.drop.brctl.patch -Patch41: xen.python.env.patch Patch42: xen.gcc9.fixes.patch -Patch44: xen.ocaml.4.10.patch Patch45: xen.gcc10.fixes.patch -Patch46: xsa320-4.13-1.patch -Patch47: xsa320-4.13-2.patch -Patch48: xsa317.patch -Patch49: xsa319.patch -Patch50: xsa328-4.13-1.patch -Patch51: xsa328-4.13-2.patch -Patch52: xsa321-4.13-1.patch -Patch53: xsa321-4.13-2.patch -Patch54: xsa321-4.13-3.patch -Patch55: xsa321-4.13-4.patch -Patch56: xsa321-4.13-5.patch -Patch57: xsa321-4.13-6.patch -Patch58: xsa321-4.13-7.patch -Patch59: xsa327.patch %if %build_qemutrad @@ -183,6 +166,7 @@ ExclusiveArch: %{ix86} x86_64 armv7hl aarch64 #ExclusiveArch: %#{ix86} x86_64 ia64 noarch %if %with_ocaml BuildRequires: ocaml, ocaml-findlib +BuildRequires: perl(Data::Dumper) %endif %if %with_systemd_presets Requires(post): systemd @@ -196,6 +180,9 @@ BuildRequires: libfdt-devel %if %build_ovmf BuildRequires: edk2-ovmf %endif +%if %build_hyp +BuildRequires: bison flex +%endif %description This package contains the XenD daemon and xm command line @@ -248,14 +235,8 @@ Summary: Xen documentation BuildArch: noarch Requires: xen-licenses # for the docs -%if "%dist" >= ".fc18" -BuildRequires: texlive-times texlive-courier texlive-helvetic texlive-ntgclass -%endif -BuildRequires: transfig texi2html ghostscript texlive-latex -BuildRequires: perl(Pod::Man) perl(Pod::Text) texinfo graphviz -# optional requires for more documentation -#BuildRequires: pandoc discount -BuildRequires: discount +BuildRequires: perl(Pod::Man) perl(Pod::Text) perl(File::Find) +BuildRequires: transfig pandoc perl(Pod::Html) %description doc This package contains the Xen documentation. @@ -302,7 +283,6 @@ manage Xen virtual machines. %prep %setup -q -%patch1 -p1 %patch4 -p1 %patch5 -p1 %patch6 -p1 @@ -330,24 +310,8 @@ manage Xen virtual machines. %patch37 -p1 %patch3 -p1 %patch40 -p1 -%patch41 -p1 %patch42 -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 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -482,7 +446,6 @@ rm -rf %{buildroot}/boot # silly doc dir fun rm -fr %{buildroot}%{_datadir}/doc/xen -rm -rf %{buildroot}%{_datadir}/doc/qemu # Pointless helper rm -f %{buildroot}%{_sbindir}/xen-python-path @@ -730,6 +693,8 @@ fi %{_libdir}/libxenvchan.so.4.* %{_libdir}/libxlutil.so.4.* %{_libdir}/xenfsimage +%{_libdir}/libxenhypfs.so.1 +%{_libdir}/libxenhypfs.so.1.0 # All runtime stuff except for XenD/xm python stuff %files runtime @@ -789,6 +754,8 @@ fi %{_mandir}/man5/xl-network-configuration.5.gz %{_mandir}/man7/xen-pv-channel.7.gz %{_mandir}/man7/xl-numa-placement.7.gz +%{_mandir}/man1/xenhypfs.1.gz +%{_mandir}/man7/xen-vbd-interface.7.gz %endif %{python3_sitearch}/xenfsimage*.so @@ -878,6 +845,8 @@ fi %ifnarch armv7hl aarch64 %{_sbindir}/xen-ucode %endif +%{_bindir}/vchan-socket-proxy +%{_sbindir}/xenhypfs # Xen logfiles %dir %attr(0700,root,root) %{_localstatedir}/log/xen @@ -940,6 +909,20 @@ fi %endif %changelog +* Sun Jul 26 2020 Michael Young - 4.14.0-1 +- update to 4.14.0 + remove or adjust patches now included or superceded upstream + adjust xen.hypervisor.config + bison and flex packages now needed for hypervisor build + /usr/bin/vchan-socket-proxy and /usr/sbin/xenhypfs have been added + with associated libraries and man page +- re-enable pandoc for more documentation + adding xen-vbd-interface.7.gz +- revise documentation build dependencies + drop tex, texinfo, ghostscript, graphviz, discount + add perl(Pod::Html) perl(File::Find) +- additional build dependency for ocaml on perl(Data::Dumper) + * Tue Jul 14 2020 Tom Stellard - 4.13.1-5 - Use make macros - https://fedoraproject.org/wiki/Changes/UseMakeBuildInstallMacro diff --git a/xsa317.patch b/xsa317.patch deleted file mode 100644 index 20e2c64..0000000 --- a/xsa317.patch +++ /dev/null @@ -1,50 +0,0 @@ -From aeb46e92f915f19a61d5a8a1f4b696793f64e6fb Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Thu, 19 Mar 2020 13:17:31 +0000 -Subject: [PATCH] xen/common: event_channel: Don't ignore error in - get_free_port() - -Currently, get_free_port() is assuming that the port has been allocated -when evtchn_allocate_port() is not return -EBUSY. - -However, the function may return an error when: - - We exhausted all the event channels. This can happen if the limit - configured by the administrator for the guest ('max_event_channels' - in xl cfg) is higher than the ABI used by the guest. For instance, - if the guest is using 2L, the limit should not be higher than 4095. - - We cannot allocate memory (e.g Xen has not more memory). - -Users of get_free_port() (such as EVTCHNOP_alloc_unbound) will validly -assuming the port was valid and will next call evtchn_from_port(). This -will result to a crash as the memory backing the event channel structure -is not present. - -Fixes: 368ae9a05fe ("xen/pvshim: forward evtchn ops between L0 Xen and L2 DomU") -Signed-off-by: Julien Grall -Reviewed-by: Jan Beulich ---- - xen/common/event_channel.c | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) - -diff --git a/xen/common/event_channel.c b/xen/common/event_channel.c -index e86e2bfab0..a8d182b584 100644 ---- a/xen/common/event_channel.c -+++ b/xen/common/event_channel.c -@@ -195,10 +195,10 @@ static int get_free_port(struct domain *d) - { - int rc = evtchn_allocate_port(d, port); - -- if ( rc == -EBUSY ) -- continue; -- -- return port; -+ if ( rc == 0 ) -+ return port; -+ else if ( rc != -EBUSY ) -+ return rc; - } - - return -ENOSPC; --- -2.17.1 - diff --git a/xsa319.patch b/xsa319.patch deleted file mode 100644 index 769443c..0000000 --- a/xsa319.patch +++ /dev/null @@ -1,27 +0,0 @@ -From: Jan Beulich -Subject: x86/shadow: correct an inverted conditional in dirty VRAM tracking - -This originally was "mfn_x(mfn) == INVALID_MFN". Make it like this -again, taking the opportunity to also drop the unnecessary nearby -braces. - -This is XSA-319. - -Fixes: 246a5a3377c2 ("xen: Use a typesafe to define INVALID_MFN") -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper - ---- a/xen/arch/x86/mm/shadow/common.c -+++ b/xen/arch/x86/mm/shadow/common.c -@@ -3252,10 +3252,8 @@ int shadow_track_dirty_vram(struct domai - int dirty = 0; - paddr_t sl1ma = dirty_vram->sl1ma[i]; - -- if ( !mfn_eq(mfn, INVALID_MFN) ) -- { -+ if ( mfn_eq(mfn, INVALID_MFN) ) - dirty = 1; -- } - else - { - page = mfn_to_page(mfn); diff --git a/xsa320-4.13-1.patch b/xsa320-4.13-1.patch deleted file mode 100644 index 09eb8ea..0000000 --- a/xsa320-4.13-1.patch +++ /dev/null @@ -1,117 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: CPUID/MSR definitions for Special Register Buffer Data Sampling - -This is part of XSA-320 / CVE-2020-0543 - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -Acked-by: Wei Liu - -diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc -index 1d9d816622..9268454297 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -483,10 +483,10 @@ accounting for hardware capabilities as enumerated via CPUID. - - Currently accepted: - --The Speculation Control hardware features `md-clear`, `ibrsb`, `stibp`, `ibpb`, --`l1d-flush` and `ssbd` are used by default if available and applicable. They can --be ignored, e.g. `no-ibrsb`, at which point Xen won't use them itself, and --won't offer them to guests. -+The Speculation Control hardware features `srbds-ctrl`, `md-clear`, `ibrsb`, -+`stibp`, `ibpb`, `l1d-flush` and `ssbd` are used by default if available and -+applicable. They can be ignored, e.g. `no-ibrsb`, at which point Xen won't -+use them itself, and won't offer them to guests. - - ### cpuid_mask_cpu - > `= fam_0f_rev_[cdefg] | fam_10_rev_[bc] | fam_11_rev_b` -diff --git a/tools/libxl/libxl_cpuid.c b/tools/libxl/libxl_cpuid.c -index 6cea4227ba..a78f08b927 100644 ---- a/tools/libxl/libxl_cpuid.c -+++ b/tools/libxl/libxl_cpuid.c -@@ -213,6 +213,7 @@ int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str) - - {"avx512-4vnniw",0x00000007, 0, CPUID_REG_EDX, 2, 1}, - {"avx512-4fmaps",0x00000007, 0, CPUID_REG_EDX, 3, 1}, -+ {"srbds-ctrl", 0x00000007, 0, CPUID_REG_EDX, 9, 1}, - {"md-clear", 0x00000007, 0, CPUID_REG_EDX, 10, 1}, - {"cet-ibt", 0x00000007, 0, CPUID_REG_EDX, 20, 1}, - {"ibrsb", 0x00000007, 0, CPUID_REG_EDX, 26, 1}, -diff --git a/tools/misc/xen-cpuid.c b/tools/misc/xen-cpuid.c -index 603e1d65fd..a09440813b 100644 ---- a/tools/misc/xen-cpuid.c -+++ b/tools/misc/xen-cpuid.c -@@ -157,6 +157,7 @@ static const char *const str_7d0[32] = - [ 2] = "avx512_4vnniw", [ 3] = "avx512_4fmaps", - [ 4] = "fsrm", - -+ /* 8 */ [ 9] = "srbds-ctrl", - [10] = "md-clear", - /* 12 */ [13] = "tsx-force-abort", - -diff --git a/xen/arch/x86/msr.c b/xen/arch/x86/msr.c -index 4b12103482..0cded3c0ad 100644 ---- a/xen/arch/x86/msr.c -+++ b/xen/arch/x86/msr.c -@@ -134,6 +134,7 @@ int guest_rdmsr(struct vcpu *v, uint32_t msr, uint64_t *val) - /* Write-only */ - case MSR_TSX_FORCE_ABORT: - case MSR_TSX_CTRL: -+ case MSR_MCU_OPT_CTRL: - case MSR_U_CET: - case MSR_S_CET: - case MSR_PL0_SSP ... MSR_INTERRUPT_SSP_TABLE: -@@ -288,6 +289,7 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) - /* Read-only */ - case MSR_TSX_FORCE_ABORT: - case MSR_TSX_CTRL: -+ case MSR_MCU_OPT_CTRL: - case MSR_U_CET: - case MSR_S_CET: - case MSR_PL0_SSP ... MSR_INTERRUPT_SSP_TABLE: -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 6656c44aec..5fc1c6827e 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -312,12 +312,13 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - printk("Speculative mitigation facilities:\n"); - - /* Hardware features which pertain to speculative mitigations. */ -- printk(" Hardware features:%s%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%s\n", - (_7d0 & cpufeat_mask(X86_FEATURE_IBRSB)) ? " IBRS/IBPB" : "", - (_7d0 & cpufeat_mask(X86_FEATURE_STIBP)) ? " STIBP" : "", - (_7d0 & cpufeat_mask(X86_FEATURE_L1D_FLUSH)) ? " L1D_FLUSH" : "", - (_7d0 & cpufeat_mask(X86_FEATURE_SSBD)) ? " SSBD" : "", - (_7d0 & cpufeat_mask(X86_FEATURE_MD_CLEAR)) ? " MD_CLEAR" : "", -+ (_7d0 & cpufeat_mask(X86_FEATURE_SRBDS_CTRL)) ? " SRBDS_CTRL" : "", - (e8b & cpufeat_mask(X86_FEATURE_IBPB)) ? " IBPB" : "", - (caps & ARCH_CAPS_IBRS_ALL) ? " IBRS_ALL" : "", - (caps & ARCH_CAPS_RDCL_NO) ? " RDCL_NO" : "", -diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h -index 7693c4a71a..91994669e1 100644 ---- a/xen/include/asm-x86/msr-index.h -+++ b/xen/include/asm-x86/msr-index.h -@@ -179,6 +179,9 @@ - #define MSR_IA32_VMX_TRUE_ENTRY_CTLS 0x490 - #define MSR_IA32_VMX_VMFUNC 0x491 - -+#define MSR_MCU_OPT_CTRL 0x00000123 -+#define MCU_OPT_CTRL_RNGDS_MITG_DIS (_AC(1, ULL) << 0) -+ - #define MSR_U_CET 0x000006a0 - #define MSR_S_CET 0x000006a2 - #define MSR_PL0_SSP 0x000006a4 -diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h -index 2835688f1c..a2482c3627 100644 ---- a/xen/include/public/arch-x86/cpufeatureset.h -+++ b/xen/include/public/arch-x86/cpufeatureset.h -@@ -252,6 +252,7 @@ XEN_CPUFEATURE(IBPB, 8*32+12) /*A IBPB support only (no IBRS, used by - /* Intel-defined CPU features, CPUID level 0x00000007:0.edx, word 9 */ - XEN_CPUFEATURE(AVX512_4VNNIW, 9*32+ 2) /*A AVX512 Neural Network Instructions */ - XEN_CPUFEATURE(AVX512_4FMAPS, 9*32+ 3) /*A AVX512 Multiply Accumulation Single Precision */ -+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(TSX_FORCE_ABORT, 9*32+13) /* MSR_TSX_FORCE_ABORT.RTM_ABORT */ - XEN_CPUFEATURE(CET_IBT, 9*32+20) /* CET - Indirect Branch Tracking */ diff --git a/xsa320-4.13-2.patch b/xsa320-4.13-2.patch deleted file mode 100644 index 8a8080a..0000000 --- a/xsa320-4.13-2.patch +++ /dev/null @@ -1,179 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Mitigate the Special Register Buffer Data Sampling sidechannel - -See patch documentation and comments. - -This is part of XSA-320 / CVE-2020-0543 - -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 9268454297..c780312531 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -1991,7 +1991,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}= ]` -+> l1d-flush,branch-harden,srb-lock}= ]` - - Controls for speculative execution sidechannel mitigations. By default, Xen - will pick the most appropriate mitigations based on compiled in support, -@@ -2068,6 +2068,12 @@ If Xen is compiled with `CONFIG_SPECULATIVE_HARDEN_BRANCH`, the - speculation barriers to protect selected conditional branches. By default, - 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). -+ - ### sync_console - > `= ` - -diff --git a/xen/arch/x86/acpi/power.c b/xen/arch/x86/acpi/power.c -index feb0f6ce20..75c6e34164 100644 ---- a/xen/arch/x86/acpi/power.c -+++ b/xen/arch/x86/acpi/power.c -@@ -295,6 +295,9 @@ static int enter_state(u32 state) - ci->spec_ctrl_flags |= (default_spec_ctrl_flags & SCF_ist_wrmsr); - spec_ctrl_exit_idle(ci); - -+ if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) -+ wrmsrl(MSR_MCU_OPT_CTRL, default_xen_mcu_opt_ctrl); -+ - done: - spin_debug_enable(); - local_irq_restore(flags); -diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c -index dc8fdac1a1..b1e51b3aff 100644 ---- a/xen/arch/x86/smpboot.c -+++ b/xen/arch/x86/smpboot.c -@@ -361,12 +361,14 @@ void start_secondary(void *unused) - microcode_update_one(false); - - /* -- * If MSR_SPEC_CTRL is available, apply Xen's default setting and discard -- * any firmware settings. Note: MSR_SPEC_CTRL may only become available -- * after loading microcode. -+ * If any speculative control MSRs are available, apply Xen's default -+ * settings. Note: These MSRs may only become available after loading -+ * microcode. - */ - 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); - - 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 5fc1c6827e..33343062a7 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -65,6 +65,9 @@ static unsigned int __initdata l1d_maxphysaddr; - 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) - { - const char *ss; -@@ -112,6 +115,7 @@ static int __init parse_spec_ctrl(const char *s) - opt_ssbd = false; - opt_l1d_flush = 0; - opt_branch_harden = false; -+ opt_srb_lock = 0; - } - else if ( val > 0 ) - rc = -EINVAL; -@@ -178,6 +182,8 @@ static int __init parse_spec_ctrl(const char *s) - opt_l1d_flush = val; - else if ( (val = parse_boolean("branch-harden", s, ss)) >= 0 ) - opt_branch_harden = val; -+ else if ( (val = parse_boolean("srb-lock", s, ss)) >= 0 ) -+ opt_srb_lock = val; - else - rc = -EINVAL; - -@@ -341,7 +347,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, Other:%s%s%s%s\n", -+ printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s, Other:%s%s%s%s%s\n", - thunk == THUNK_NONE ? "N/A" : - thunk == THUNK_RETPOLINE ? "RETPOLINE" : - thunk == THUNK_LFENCE ? "LFENCE" : -@@ -352,6 +358,8 @@ 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) ? "" : -+ opt_srb_lock ? " SRB_LOCK+" : " SRB_LOCK-", - opt_ibpb ? " IBPB" : "", - opt_l1d_flush ? " L1D_FLUSH" : "", - opt_md_clear_pv || opt_md_clear_hvm ? " VERW" : "", -@@ -1149,6 +1157,34 @@ void __init init_speculation_mitigations(void) - tsx_init(); - } - -+ /* Calculate suitable defaults for MSR_MCU_OPT_CTRL */ -+ if ( boot_cpu_has(X86_FEATURE_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) && opt_tsx == 0)) ) -+ 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; -+ } -+ - print_details(thunk, caps); - - /* -@@ -1180,6 +1216,9 @@ void __init init_speculation_mitigations(void) - - wrmsrl(MSR_SPEC_CTRL, bsp_delay_spec_ctrl ? 0 : default_xen_spec_ctrl); - } -+ -+ 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/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h -index 9caecddfec..b252bb8631 100644 ---- a/xen/include/asm-x86/spec_ctrl.h -+++ b/xen/include/asm-x86/spec_ctrl.h -@@ -54,6 +54,8 @@ 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/xsa321-4.13-1.patch b/xsa321-4.13-1.patch deleted file mode 100644 index 9a08ab2..0000000 --- a/xsa321-4.13-1.patch +++ /dev/null @@ -1,31 +0,0 @@ -From: Jan Beulich -Subject: vtd: improve IOMMU TLB flush - -Do not limit PSI flushes to order 0 pages, in order to avoid doing a -full TLB flush if the passed in page has an order greater than 0 and -is aligned. Should increase the performance of IOMMU TLB flushes when -dealing with page orders greater than 0. - -This is part of XSA-321. - -Signed-off-by: Jan Beulich - ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -570,13 +570,14 @@ static int __must_check iommu_flush_iotl - if ( iommu_domid == -1 ) - continue; - -- if ( page_count != 1 || dfn_eq(dfn, INVALID_DFN) ) -+ if ( !page_count || (page_count & (page_count - 1)) || -+ dfn_eq(dfn, INVALID_DFN) || !IS_ALIGNED(dfn_x(dfn), page_count) ) - rc = iommu_flush_iotlb_dsi(iommu, iommu_domid, - 0, flush_dev_iotlb); - else - rc = iommu_flush_iotlb_psi(iommu, iommu_domid, - dfn_to_daddr(dfn), -- PAGE_ORDER_4K, -+ get_order_from_pages(page_count), - !dma_old_pte_present, - flush_dev_iotlb); - diff --git a/xsa321-4.13-2.patch b/xsa321-4.13-2.patch deleted file mode 100644 index 1e48615..0000000 --- a/xsa321-4.13-2.patch +++ /dev/null @@ -1,175 +0,0 @@ -From: -Subject: vtd: prune (and rename) cache flush functions - -Rename __iommu_flush_cache to iommu_sync_cache and remove -iommu_flush_cache_page. Also remove the iommu_flush_cache_entry -wrapper and just use iommu_sync_cache instead. Note the _entry suffix -was meaningless as the wrapper was already taking a size parameter in -bytes. While there also constify the addr parameter. - -No functional change intended. - -This is part of XSA-321. - -Reviewed-by: Jan Beulich - ---- a/xen/drivers/passthrough/vtd/extern.h -+++ b/xen/drivers/passthrough/vtd/extern.h -@@ -43,8 +43,7 @@ void disable_qinval(struct vtd_iommu *io - int enable_intremap(struct vtd_iommu *iommu, int eim); - void disable_intremap(struct vtd_iommu *iommu); - --void iommu_flush_cache_entry(void *addr, unsigned int size); --void iommu_flush_cache_page(void *addr, unsigned long npages); -+void iommu_sync_cache(const void *addr, unsigned int size); - int iommu_alloc(struct acpi_drhd_unit *drhd); - void iommu_free(struct acpi_drhd_unit *drhd); - ---- a/xen/drivers/passthrough/vtd/intremap.c -+++ b/xen/drivers/passthrough/vtd/intremap.c -@@ -230,7 +230,7 @@ static void free_remap_entry(struct vtd_ - iremap_entries, iremap_entry); - - update_irte(iommu, iremap_entry, &new_ire, false); -- iommu_flush_cache_entry(iremap_entry, sizeof(*iremap_entry)); -+ iommu_sync_cache(iremap_entry, sizeof(*iremap_entry)); - iommu_flush_iec_index(iommu, 0, index); - - unmap_vtd_domain_page(iremap_entries); -@@ -406,7 +406,7 @@ static int ioapic_rte_to_remap_entry(str - } - - update_irte(iommu, iremap_entry, &new_ire, !init); -- iommu_flush_cache_entry(iremap_entry, sizeof(*iremap_entry)); -+ iommu_sync_cache(iremap_entry, sizeof(*iremap_entry)); - iommu_flush_iec_index(iommu, 0, index); - - unmap_vtd_domain_page(iremap_entries); -@@ -695,7 +695,7 @@ static int msi_msg_to_remap_entry( - update_irte(iommu, iremap_entry, &new_ire, msi_desc->irte_initialized); - msi_desc->irte_initialized = true; - -- iommu_flush_cache_entry(iremap_entry, sizeof(*iremap_entry)); -+ iommu_sync_cache(iremap_entry, sizeof(*iremap_entry)); - iommu_flush_iec_index(iommu, 0, index); - - unmap_vtd_domain_page(iremap_entries); ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -140,7 +140,8 @@ static int context_get_domain_id(struct - } - - static int iommus_incoherent; --static void __iommu_flush_cache(void *addr, unsigned int size) -+ -+void iommu_sync_cache(const void *addr, unsigned int size) - { - int i; - static unsigned int clflush_size = 0; -@@ -155,16 +156,6 @@ static void __iommu_flush_cache(void *ad - cacheline_flush((char *)addr + i); - } - --void iommu_flush_cache_entry(void *addr, unsigned int size) --{ -- __iommu_flush_cache(addr, size); --} -- --void iommu_flush_cache_page(void *addr, unsigned long npages) --{ -- __iommu_flush_cache(addr, PAGE_SIZE * npages); --} -- - /* Allocate page table, return its machine address */ - uint64_t alloc_pgtable_maddr(unsigned long npages, nodeid_t node) - { -@@ -183,7 +174,7 @@ uint64_t alloc_pgtable_maddr(unsigned lo - vaddr = __map_domain_page(cur_pg); - memset(vaddr, 0, PAGE_SIZE); - -- iommu_flush_cache_page(vaddr, 1); -+ iommu_sync_cache(vaddr, PAGE_SIZE); - unmap_domain_page(vaddr); - cur_pg++; - } -@@ -216,7 +207,7 @@ static u64 bus_to_context_maddr(struct v - } - set_root_value(*root, maddr); - set_root_present(*root); -- iommu_flush_cache_entry(root, sizeof(struct root_entry)); -+ iommu_sync_cache(root, sizeof(struct root_entry)); - } - maddr = (u64) get_context_addr(*root); - unmap_vtd_domain_page(root_entries); -@@ -263,7 +254,7 @@ static u64 addr_to_dma_page_maddr(struct - */ - dma_set_pte_readable(*pte); - dma_set_pte_writable(*pte); -- iommu_flush_cache_entry(pte, sizeof(struct dma_pte)); -+ iommu_sync_cache(pte, sizeof(struct dma_pte)); - } - - if ( level == 2 ) -@@ -640,7 +631,7 @@ static int __must_check dma_pte_clear_on - *flush_flags |= IOMMU_FLUSHF_modified; - - spin_unlock(&hd->arch.mapping_lock); -- iommu_flush_cache_entry(pte, sizeof(struct dma_pte)); -+ iommu_sync_cache(pte, sizeof(struct dma_pte)); - - unmap_vtd_domain_page(page); - -@@ -679,7 +670,7 @@ static void iommu_free_page_table(struct - iommu_free_pagetable(dma_pte_addr(*pte), next_level); - - dma_clear_pte(*pte); -- iommu_flush_cache_entry(pte, sizeof(struct dma_pte)); -+ iommu_sync_cache(pte, sizeof(struct dma_pte)); - } - - unmap_vtd_domain_page(pt_vaddr); -@@ -1400,7 +1391,7 @@ int domain_context_mapping_one( - context_set_address_width(*context, agaw); - context_set_fault_enable(*context); - context_set_present(*context); -- iommu_flush_cache_entry(context, sizeof(struct context_entry)); -+ iommu_sync_cache(context, sizeof(struct context_entry)); - spin_unlock(&iommu->lock); - - /* Context entry was previously non-present (with domid 0). */ -@@ -1564,7 +1555,7 @@ int domain_context_unmap_one( - - context_clear_present(*context); - context_clear_entry(*context); -- iommu_flush_cache_entry(context, sizeof(struct context_entry)); -+ iommu_sync_cache(context, sizeof(struct context_entry)); - - iommu_domid= domain_iommu_domid(domain, iommu); - if ( iommu_domid == -1 ) -@@ -1791,7 +1782,7 @@ static int __must_check intel_iommu_map_ - - *pte = new; - -- iommu_flush_cache_entry(pte, sizeof(struct dma_pte)); -+ iommu_sync_cache(pte, sizeof(struct dma_pte)); - spin_unlock(&hd->arch.mapping_lock); - unmap_vtd_domain_page(page); - -@@ -1866,7 +1857,7 @@ int iommu_pte_flush(struct domain *d, ui - int iommu_domid; - int rc = 0; - -- iommu_flush_cache_entry(pte, sizeof(struct dma_pte)); -+ iommu_sync_cache(pte, sizeof(struct dma_pte)); - - for_each_drhd_unit ( drhd ) - { -@@ -2724,7 +2715,7 @@ static int __init intel_iommu_quarantine - dma_set_pte_addr(*pte, maddr); - dma_set_pte_readable(*pte); - } -- iommu_flush_cache_page(parent, 1); -+ iommu_sync_cache(parent, PAGE_SIZE); - - unmap_vtd_domain_page(parent); - parent = map_vtd_domain_page(maddr); diff --git a/xsa321-4.13-3.patch b/xsa321-4.13-3.patch deleted file mode 100644 index c141c4b..0000000 --- a/xsa321-4.13-3.patch +++ /dev/null @@ -1,82 +0,0 @@ -From: -Subject: x86/iommu: introduce a cache sync hook - -The hook is only implemented for VT-d and it uses the already existing -iommu_sync_cache function present in VT-d code. The new hook is -added so that the cache can be flushed by code outside of VT-d when -using shared page tables. - -Note that alloc_pgtable_maddr must use the now locally defined -sync_cache function, because IOMMU ops are not yet setup the first -time the function gets called during IOMMU initialization. - -No functional change intended. - -This is part of XSA-321. - -Reviewed-by: Jan Beulich - ---- a/xen/drivers/passthrough/vtd/extern.h -+++ b/xen/drivers/passthrough/vtd/extern.h -@@ -43,7 +43,6 @@ void disable_qinval(struct vtd_iommu *io - int enable_intremap(struct vtd_iommu *iommu, int eim); - void disable_intremap(struct vtd_iommu *iommu); - --void iommu_sync_cache(const void *addr, unsigned int size); - int iommu_alloc(struct acpi_drhd_unit *drhd); - void iommu_free(struct acpi_drhd_unit *drhd); - ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -141,7 +141,7 @@ static int context_get_domain_id(struct - - static int iommus_incoherent; - --void iommu_sync_cache(const void *addr, unsigned int size) -+static void sync_cache(const void *addr, unsigned int size) - { - int i; - static unsigned int clflush_size = 0; -@@ -174,7 +174,7 @@ uint64_t alloc_pgtable_maddr(unsigned lo - vaddr = __map_domain_page(cur_pg); - memset(vaddr, 0, PAGE_SIZE); - -- iommu_sync_cache(vaddr, PAGE_SIZE); -+ sync_cache(vaddr, PAGE_SIZE); - unmap_domain_page(vaddr); - cur_pg++; - } -@@ -2763,6 +2763,7 @@ const struct iommu_ops __initconstrel in - .iotlb_flush_all = iommu_flush_iotlb_all, - .get_reserved_device_memory = intel_iommu_get_reserved_device_memory, - .dump_p2m_table = vtd_dump_p2m_table, -+ .sync_cache = sync_cache, - }; - - const struct iommu_init_ops __initconstrel intel_iommu_init_ops = { ---- a/xen/include/asm-x86/iommu.h -+++ b/xen/include/asm-x86/iommu.h -@@ -121,6 +121,13 @@ extern bool untrusted_msi; - int pi_update_irte(const struct pi_desc *pi_desc, const struct pirq *pirq, - const uint8_t gvec); - -+#define iommu_sync_cache(addr, size) ({ \ -+ const struct iommu_ops *ops = iommu_get_ops(); \ -+ \ -+ if ( ops->sync_cache ) \ -+ iommu_vcall(ops, sync_cache, addr, size); \ -+}) -+ - #endif /* !__ARCH_X86_IOMMU_H__ */ - /* - * Local variables: ---- a/xen/include/xen/iommu.h -+++ b/xen/include/xen/iommu.h -@@ -250,6 +250,7 @@ struct iommu_ops { - int (*setup_hpet_msi)(struct msi_desc *); - - int (*adjust_irq_affinities)(void); -+ void (*sync_cache)(const void *addr, unsigned int size); - #endif /* CONFIG_X86 */ - - int __must_check (*suspend)(void); diff --git a/xsa321-4.13-4.patch b/xsa321-4.13-4.patch deleted file mode 100644 index 62bbcc7..0000000 --- a/xsa321-4.13-4.patch +++ /dev/null @@ -1,36 +0,0 @@ -From: -Subject: vtd: don't assume addresses are aligned in sync_cache - -Current code in sync_cache assume that the address passed in is -aligned to a cache line size. Fix the code to support passing in -arbitrary addresses not necessarily aligned to a cache line size. - -This is part of XSA-321. - -Reviewed-by: Jan Beulich - ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -143,8 +143,8 @@ static int iommus_incoherent; - - static void sync_cache(const void *addr, unsigned int size) - { -- int i; -- static unsigned int clflush_size = 0; -+ static unsigned long clflush_size = 0; -+ const void *end = addr + size; - - if ( !iommus_incoherent ) - return; -@@ -152,8 +152,9 @@ static void sync_cache(const void *addr, - if ( clflush_size == 0 ) - clflush_size = get_cache_line_size(); - -- for ( i = 0; i < size; i += clflush_size ) -- cacheline_flush((char *)addr + i); -+ addr -= (unsigned long)addr & (clflush_size - 1); -+ for ( ; addr < end; addr += clflush_size ) -+ cacheline_flush((char *)addr); - } - - /* Allocate page table, return its machine address */ diff --git a/xsa321-4.13-5.patch b/xsa321-4.13-5.patch deleted file mode 100644 index 60cfe6c..0000000 --- a/xsa321-4.13-5.patch +++ /dev/null @@ -1,24 +0,0 @@ -From: -Subject: x86/alternative: introduce alternative_2 - -It's based on alternative_io_2 without inputs or outputs but with an -added memory clobber. - -This is part of XSA-321. - -Acked-by: Jan Beulich - ---- a/xen/include/asm-x86/alternative.h -+++ b/xen/include/asm-x86/alternative.h -@@ -114,6 +114,11 @@ extern void alternative_branches(void); - #define alternative(oldinstr, newinstr, feature) \ - asm volatile (ALTERNATIVE(oldinstr, newinstr, feature) : : : "memory") - -+#define alternative_2(oldinstr, newinstr1, feature1, newinstr2, feature2) \ -+ asm volatile (ALTERNATIVE_2(oldinstr, newinstr1, feature1, \ -+ newinstr2, feature2) \ -+ : : : "memory") -+ - /* - * Alternative inline assembly with input. - * diff --git a/xsa321-4.13-6.patch b/xsa321-4.13-6.patch deleted file mode 100644 index 4c5c5ab..0000000 --- a/xsa321-4.13-6.patch +++ /dev/null @@ -1,91 +0,0 @@ -From: -Subject: vtd: optimize CPU cache sync - -Some VT-d IOMMUs are non-coherent, which requires a cache write back -in order for the changes made by the CPU to be visible to the IOMMU. -This cache write back was unconditionally done using clflush, but there are -other more efficient instructions to do so, hence implement support -for them using the alternative framework. - -This is part of XSA-321. - -Reviewed-by: Jan Beulich - ---- a/xen/drivers/passthrough/vtd/extern.h -+++ b/xen/drivers/passthrough/vtd/extern.h -@@ -68,7 +68,6 @@ int __must_check qinval_device_iotlb_syn - u16 did, u16 size, u64 addr); - - unsigned int get_cache_line_size(void); --void cacheline_flush(char *); - void flush_all_cache(void); - - uint64_t alloc_pgtable_maddr(unsigned long npages, nodeid_t node); ---- 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 -@@ -154,7 +155,42 @@ static void sync_cache(const void *addr, - - addr -= (unsigned long)addr & (clflush_size - 1); - for ( ; addr < end; addr += clflush_size ) -- cacheline_flush((char *)addr); -+/* -+ * 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 */ ---- a/xen/drivers/passthrough/vtd/x86/vtd.c -+++ b/xen/drivers/passthrough/vtd/x86/vtd.c -@@ -51,11 +51,6 @@ unsigned int get_cache_line_size(void) - return ((cpuid_ebx(1) >> 8) & 0xff) * 8; - } - --void cacheline_flush(char * addr) --{ -- clflush(addr); --} -- - void flush_all_cache() - { - wbinvd(); diff --git a/xsa321-4.13-7.patch b/xsa321-4.13-7.patch deleted file mode 100644 index 0bd018f..0000000 --- a/xsa321-4.13-7.patch +++ /dev/null @@ -1,153 +0,0 @@ -From: -Subject: x86/ept: flush cache when modifying PTEs and sharing page tables - -Modifications made to the page tables by EPT code need to be written -to memory when the page tables are shared with the IOMMU, as Intel -IOMMUs can be non-coherent and thus require changes to be written to -memory in order to be visible to the IOMMU. - -In order to achieve this make sure data is written back to memory -after writing an EPT entry when the recalc bit is not set in -atomic_write_ept_entry. If such bit is set, the entry will be -adjusted and atomic_write_ept_entry will be called a second time -without the recalc bit set. Note that when splitting a super page the -new tables resulting of the split should also be written back. - -Failure to do so can allow devices behind the IOMMU access to the -stale super page, or cause coherency issues as changes made by the -processor to the page tables are not visible to the IOMMU. - -This allows to remove the VT-d specific iommu_pte_flush helper, since -the cache write back is now performed by atomic_write_ept_entry, and -hence iommu_iotlb_flush can be used to flush the IOMMU TLB. The newly -used method (iommu_iotlb_flush) can result in less flushes, since it -might sometimes be called rightly with 0 flags, in which case it -becomes a no-op. - -This is part of XSA-321. - -Reviewed-by: Jan Beulich - ---- a/xen/arch/x86/mm/p2m-ept.c -+++ b/xen/arch/x86/mm/p2m-ept.c -@@ -58,6 +58,19 @@ static int atomic_write_ept_entry(struct - - write_atomic(&entryptr->epte, new.epte); - -+ /* -+ * The recalc field on the EPT is used to signal either that a -+ * recalculation of the EMT field is required (which doesn't effect the -+ * IOMMU), or a type change. Type changes can only be between ram_rw, -+ * logdirty and ioreq_server: changes to/from logdirty won't work well with -+ * an IOMMU anyway, as IOMMU #PFs are not synchronous and will lead to -+ * aborts, and changes to/from ioreq_server are already fully flushed -+ * before returning to guest context (see -+ * XEN_DMOP_map_mem_type_to_ioreq_server). -+ */ -+ if ( !new.recalc && iommu_use_hap_pt(p2m->domain) ) -+ iommu_sync_cache(entryptr, sizeof(*entryptr)); -+ - return 0; - } - -@@ -278,6 +291,9 @@ static bool_t ept_split_super_page(struc - break; - } - -+ if ( iommu_use_hap_pt(p2m->domain) ) -+ iommu_sync_cache(table, EPT_PAGETABLE_ENTRIES * sizeof(ept_entry_t)); -+ - unmap_domain_page(table); - - /* Even failed we should install the newly allocated ept page. */ -@@ -337,6 +353,9 @@ static int ept_next_level(struct p2m_dom - if ( !next ) - return GUEST_TABLE_MAP_FAILED; - -+ if ( iommu_use_hap_pt(p2m->domain) ) -+ iommu_sync_cache(next, EPT_PAGETABLE_ENTRIES * sizeof(ept_entry_t)); -+ - rc = atomic_write_ept_entry(p2m, ept_entry, e, next_level); - ASSERT(rc == 0); - } -@@ -821,7 +840,10 @@ out: - need_modify_vtd_table ) - { - if ( iommu_use_hap_pt(d) ) -- rc = iommu_pte_flush(d, gfn, &ept_entry->epte, order, vtd_pte_present); -+ rc = iommu_iotlb_flush(d, _dfn(gfn), (1u << order), -+ (iommu_flags ? IOMMU_FLUSHF_added : 0) | -+ (vtd_pte_present ? IOMMU_FLUSHF_modified -+ : 0)); - else if ( need_iommu_pt_sync(d) ) - rc = iommu_flags ? - iommu_legacy_map(d, _dfn(gfn), mfn, order, iommu_flags) : ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -1884,53 +1884,6 @@ static int intel_iommu_lookup_page(struc - return 0; - } - --int iommu_pte_flush(struct domain *d, uint64_t dfn, uint64_t *pte, -- int order, int present) --{ -- struct acpi_drhd_unit *drhd; -- struct vtd_iommu *iommu = NULL; -- struct domain_iommu *hd = dom_iommu(d); -- bool_t flush_dev_iotlb; -- int iommu_domid; -- int rc = 0; -- -- iommu_sync_cache(pte, sizeof(struct dma_pte)); -- -- for_each_drhd_unit ( drhd ) -- { -- iommu = drhd->iommu; -- if ( !test_bit(iommu->index, &hd->arch.iommu_bitmap) ) -- continue; -- -- flush_dev_iotlb = !!find_ats_dev_drhd(iommu); -- iommu_domid= domain_iommu_domid(d, iommu); -- if ( iommu_domid == -1 ) -- continue; -- -- rc = iommu_flush_iotlb_psi(iommu, iommu_domid, -- __dfn_to_daddr(dfn), -- order, !present, flush_dev_iotlb); -- if ( rc > 0 ) -- { -- iommu_flush_write_buffer(iommu); -- rc = 0; -- } -- } -- -- if ( unlikely(rc) ) -- { -- if ( !d->is_shutting_down && printk_ratelimit() ) -- printk(XENLOG_ERR VTDPREFIX -- " d%d: IOMMU pages flush failed: %d\n", -- d->domain_id, rc); -- -- if ( !is_hardware_domain(d) ) -- domain_crash(d); -- } -- -- return rc; --} -- - static int __init vtd_ept_page_compatible(struct vtd_iommu *iommu) - { - u64 ept_cap, vtd_cap = iommu->cap; ---- a/xen/include/asm-x86/iommu.h -+++ b/xen/include/asm-x86/iommu.h -@@ -97,10 +97,6 @@ static inline int iommu_adjust_irq_affin - : 0; - } - --/* While VT-d specific, this must get declared in a generic header. */ --int __must_check iommu_pte_flush(struct domain *d, u64 gfn, u64 *pte, -- int order, int present); -- - static inline bool iommu_supports_x2apic(void) - { - return iommu_init_ops && iommu_init_ops->supports_x2apic diff --git a/xsa327.patch b/xsa327.patch deleted file mode 100644 index 0541cfa..0000000 --- a/xsa327.patch +++ /dev/null @@ -1,63 +0,0 @@ -From 030300ebbb86c40c12db038714479d746167c767 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Tue, 26 May 2020 18:31:33 +0100 -Subject: [PATCH] xen: Check the alignment of the offset pased via - VCPUOP_register_vcpu_info - -Currently a guest is able to register any guest physical address to use -for the vcpu_info structure as long as the structure can fits in the -rest of the frame. - -This means a guest can provide an address that is not aligned to the -natural alignment of the structure. - -On Arm 32-bit, unaligned access are completely forbidden by the -hypervisor. This will result to a data abort which is fatal. - -On Arm 64-bit, unaligned access are only forbidden when used for atomic -access. As the structure contains fields (such as evtchn_pending_self) -that are updated using atomic operations, any unaligned access will be -fatal as well. - -While the misalignment is only fatal on Arm, a generic check is added -as an x86 guest shouldn't sensibly pass an unaligned address (this -would result to a split lock). - -This is XSA-327. - -Reported-by: Julien Grall -Signed-off-by: Julien Grall -Reviewed-by: Andrew Cooper -Reviewed-by: Stefano Stabellini ---- - xen/common/domain.c | 10 ++++++++++ - 1 file changed, 10 insertions(+) - -diff --git a/xen/common/domain.c b/xen/common/domain.c -index 7cc9526139a6..e9be05f1d05f 100644 ---- a/xen/common/domain.c -+++ b/xen/common/domain.c -@@ -1227,10 +1227,20 @@ int map_vcpu_info(struct vcpu *v, unsigned long gfn, unsigned offset) - void *mapping; - vcpu_info_t *new_info; - struct page_info *page; -+ unsigned int align; - - if ( offset > (PAGE_SIZE - sizeof(vcpu_info_t)) ) - return -EINVAL; - -+#ifdef CONFIG_COMPAT -+ if ( has_32bit_shinfo(d) ) -+ align = alignof(new_info->compat); -+ else -+#endif -+ align = alignof(*new_info); -+ if ( offset & (align - 1) ) -+ return -EINVAL; -+ - if ( !mfn_eq(v->vcpu_info_mfn, INVALID_MFN) ) - return -EINVAL; - --- -2.17.1 - diff --git a/xsa328-4.13-1.patch b/xsa328-4.13-1.patch deleted file mode 100644 index 56e48de..0000000 --- a/xsa328-4.13-1.patch +++ /dev/null @@ -1,118 +0,0 @@ -From: Jan Beulich -Subject: x86/EPT: ept_set_middle_entry() related adjustments - -ept_split_super_page() wants to further modify the newly allocated -table, so have ept_set_middle_entry() return the mapped pointer rather -than tearing it down and then getting re-established right again. - -Similarly ept_next_level() wants to hand back a mapped pointer of -the next level page, so re-use the one established by -ept_set_middle_entry() in case that path was taken. - -Pull the setting of suppress_ve ahead of insertion into the higher level -table, and don't have ept_split_super_page() set the field a 2nd time. - -This is part of XSA-328. - -Signed-off-by: Jan Beulich - ---- a/xen/arch/x86/mm/p2m-ept.c -+++ b/xen/arch/x86/mm/p2m-ept.c -@@ -187,8 +187,9 @@ static void ept_p2m_type_to_flags(struct - #define GUEST_TABLE_SUPER_PAGE 2 - #define GUEST_TABLE_POD_PAGE 3 - --/* Fill in middle levels of ept table */ --static int ept_set_middle_entry(struct p2m_domain *p2m, ept_entry_t *ept_entry) -+/* Fill in middle level of ept table; return pointer to mapped new table. */ -+static ept_entry_t *ept_set_middle_entry(struct p2m_domain *p2m, -+ ept_entry_t *ept_entry) - { - mfn_t mfn; - ept_entry_t *table; -@@ -196,7 +197,12 @@ static int ept_set_middle_entry(struct p - - mfn = p2m_alloc_ptp(p2m, 0); - if ( mfn_eq(mfn, INVALID_MFN) ) -- return 0; -+ return NULL; -+ -+ table = map_domain_page(mfn); -+ -+ for ( i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) -+ table[i].suppress_ve = 1; - - ept_entry->epte = 0; - ept_entry->mfn = mfn_x(mfn); -@@ -208,14 +214,7 @@ static int ept_set_middle_entry(struct p - - ept_entry->suppress_ve = 1; - -- table = map_domain_page(mfn); -- -- for ( i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) -- table[i].suppress_ve = 1; -- -- unmap_domain_page(table); -- -- return 1; -+ return table; - } - - /* free ept sub tree behind an entry */ -@@ -253,10 +252,10 @@ static bool_t ept_split_super_page(struc - - ASSERT(is_epte_superpage(ept_entry)); - -- if ( !ept_set_middle_entry(p2m, &new_ept) ) -+ table = ept_set_middle_entry(p2m, &new_ept); -+ if ( !table ) - return 0; - -- table = map_domain_page(_mfn(new_ept.mfn)); - trunk = 1UL << ((level - 1) * EPT_TABLE_ORDER); - - for ( i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) -@@ -267,7 +266,6 @@ static bool_t ept_split_super_page(struc - epte->sp = (level > 1); - epte->mfn += i * trunk; - epte->snp = is_iommu_enabled(p2m->domain) && iommu_snoop; -- epte->suppress_ve = 1; - - ept_p2m_type_to_flags(p2m, epte, epte->sa_p2mt, epte->access); - -@@ -306,8 +304,7 @@ static int ept_next_level(struct p2m_dom - ept_entry_t **table, unsigned long *gfn_remainder, - int next_level) - { -- unsigned long mfn; -- ept_entry_t *ept_entry, e; -+ ept_entry_t *ept_entry, *next = NULL, e; - u32 shift, index; - - shift = next_level * EPT_TABLE_ORDER; -@@ -332,19 +329,17 @@ static int ept_next_level(struct p2m_dom - if ( read_only ) - return GUEST_TABLE_MAP_FAILED; - -- if ( !ept_set_middle_entry(p2m, ept_entry) ) -+ next = ept_set_middle_entry(p2m, ept_entry); -+ if ( !next ) - return GUEST_TABLE_MAP_FAILED; -- else -- e = atomic_read_ept_entry(ept_entry); /* Refresh */ -+ /* e is now stale and hence may not be used anymore below. */ - } -- - /* The only time sp would be set here is if we had hit a superpage */ -- if ( is_epte_superpage(&e) ) -+ else if ( is_epte_superpage(&e) ) - return GUEST_TABLE_SUPER_PAGE; - -- mfn = e.mfn; - unmap_domain_page(*table); -- *table = map_domain_page(_mfn(mfn)); -+ *table = next ?: map_domain_page(_mfn(e.mfn)); - *gfn_remainder &= (1UL << shift) - 1; - return GUEST_TABLE_NORMAL_PAGE; - } diff --git a/xsa328-4.13-2.patch b/xsa328-4.13-2.patch deleted file mode 100644 index c4f437f..0000000 --- a/xsa328-4.13-2.patch +++ /dev/null @@ -1,48 +0,0 @@ -From: -Subject: x86/ept: atomically modify entries in ept_next_level - -ept_next_level was passing a live PTE pointer to ept_set_middle_entry, -which was then modified without taking into account that the PTE could -be part of a live EPT table. This wasn't a security issue because the -pages returned by p2m_alloc_ptp are zeroed, so adding such an entry -before actually initializing it didn't allow a guest to access -physical memory addresses it wasn't supposed to access. - -This is part of XSA-328. - -Reviewed-by: Jan Beulich - ---- a/xen/arch/x86/mm/p2m-ept.c -+++ b/xen/arch/x86/mm/p2m-ept.c -@@ -307,6 +307,8 @@ static int ept_next_level(struct p2m_dom - ept_entry_t *ept_entry, *next = NULL, e; - u32 shift, index; - -+ ASSERT(next_level); -+ - shift = next_level * EPT_TABLE_ORDER; - - index = *gfn_remainder >> shift; -@@ -323,16 +325,20 @@ static int ept_next_level(struct p2m_dom - - if ( !is_epte_present(&e) ) - { -+ int rc; -+ - if ( e.sa_p2mt == p2m_populate_on_demand ) - return GUEST_TABLE_POD_PAGE; - - if ( read_only ) - return GUEST_TABLE_MAP_FAILED; - -- next = ept_set_middle_entry(p2m, ept_entry); -+ next = ept_set_middle_entry(p2m, &e); - if ( !next ) - return GUEST_TABLE_MAP_FAILED; -- /* e is now stale and hence may not be used anymore below. */ -+ -+ rc = atomic_write_ept_entry(p2m, ept_entry, e, next_level); -+ ASSERT(rc == 0); - } - /* The only time sp would be set here is if we had hit a superpage */ - else if ( is_epte_superpage(&e) ) From 62a55dab2d98960bc9e8265ba1a2402e69ccf5ec Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 29 Jul 2020 14:35:36 +0000 Subject: [PATCH 044/194] - Rebuilt for https://fedoraproject.org/wiki/Fedora_33_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 01c3b2a..f50e780 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.14.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 @@ -909,6 +909,9 @@ fi %endif %changelog +* Wed Jul 29 2020 Fedora Release Engineering - 4.14.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + * Sun Jul 26 2020 Michael Young - 4.14.0-1 - update to 4.14.0 remove or adjust patches now included or superceded upstream From 3ba0708ee85fb283389c144d9e9b3a8609e0d5f1 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Mon, 24 Aug 2020 23:40:18 +0100 Subject: [PATCH 045/194] QEMU: usb: out-of-bounds r/w access issue [XSA-335, CVE-2020-14364] (#1871850) --- xen.spec | 10 +++++- xsa335-qemu.patch | 84 +++++++++++++++++++++++++++++++++++++++++++++++ xsa335-trad.patch | 45 +++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 xsa335-qemu.patch create mode 100644 xsa335-trad.patch diff --git a/xen.spec b/xen.spec index f50e780..5667009 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.14.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 @@ -111,6 +111,8 @@ Patch39: qemu.trad.CVE-2017-9330.patch Patch40: xen.drop.brctl.patch Patch42: xen.gcc9.fixes.patch Patch45: xen.gcc10.fixes.patch +Patch46: xsa335-qemu.patch +Patch47: xsa335-trad.patch %if %build_qemutrad @@ -312,6 +314,7 @@ manage Xen virtual machines. %patch40 -p1 %patch42 -p1 %patch45 -p1 +%patch47 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -328,6 +331,7 @@ popd # qemu-xen patches pushd tools/qemu-xen +%patch46 -p1 popd # stubdom sources @@ -909,6 +913,10 @@ fi %endif %changelog +* Mon Aug 24 2020 Michael Young - 4.14.0-3 +- QEMU: usb: out-of-bounds r/w access issue [XSA-335, CVE-2020-14364] + (#1871850) + * Wed Jul 29 2020 Fedora Release Engineering - 4.14.0-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild diff --git a/xsa335-qemu.patch b/xsa335-qemu.patch new file mode 100644 index 0000000..5617502 --- /dev/null +++ b/xsa335-qemu.patch @@ -0,0 +1,84 @@ +From c5bd2924c6d6a5bcbffb8b5e7798a88970131c07 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Mon, 17 Aug 2020 08:34:22 +0200 +Subject: [PATCH] usb: fix setup_len init (CVE-2020-14364) + +Store calculated setup_len in a local variable, verify it, and only +write it to the struct (USBDevice->setup_len) in case it passed the +sanity checks. + +This prevents other code (do_token_{in,out} functions specifically) +from working with invalid USBDevice->setup_len values and overrunning +the USBDevice->setup_buf[] buffer. + +Fixes: CVE-2020-14364 +Signed-off-by: Gerd Hoffmann +--- + hw/usb/core.c | 16 ++++++++++------ + 1 file changed, 10 insertions(+), 6 deletions(-) + +diff --git a/hw/usb/core.c b/hw/usb/core.c +index 5abd128b6bc5..5234dcc73fea 100644 +--- a/hw/usb/core.c ++++ b/hw/usb/core.c +@@ -129,6 +129,7 @@ void usb_wakeup(USBEndpoint *ep, unsigned int stream) + static void do_token_setup(USBDevice *s, USBPacket *p) + { + int request, value, index; ++ unsigned int setup_len; + + if (p->iov.size != 8) { + p->status = USB_RET_STALL; +@@ -138,14 +139,15 @@ static void do_token_setup(USBDevice *s, USBPacket *p) + usb_packet_copy(p, s->setup_buf, p->iov.size); + s->setup_index = 0; + p->actual_length = 0; +- s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; +- if (s->setup_len > sizeof(s->data_buf)) { ++ setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; ++ if (setup_len > sizeof(s->data_buf)) { + fprintf(stderr, + "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", +- s->setup_len, sizeof(s->data_buf)); ++ setup_len, sizeof(s->data_buf)); + p->status = USB_RET_STALL; + return; + } ++ s->setup_len = setup_len; + + request = (s->setup_buf[0] << 8) | s->setup_buf[1]; + value = (s->setup_buf[3] << 8) | s->setup_buf[2]; +@@ -259,26 +261,28 @@ static void do_token_out(USBDevice *s, USBPacket *p) + static void do_parameter(USBDevice *s, USBPacket *p) + { + int i, request, value, index; ++ unsigned int setup_len; + + for (i = 0; i < 8; i++) { + s->setup_buf[i] = p->parameter >> (i*8); + } + + s->setup_state = SETUP_STATE_PARAM; +- s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; + s->setup_index = 0; + + request = (s->setup_buf[0] << 8) | s->setup_buf[1]; + value = (s->setup_buf[3] << 8) | s->setup_buf[2]; + index = (s->setup_buf[5] << 8) | s->setup_buf[4]; + +- if (s->setup_len > sizeof(s->data_buf)) { ++ setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; ++ if (setup_len > sizeof(s->data_buf)) { + fprintf(stderr, + "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", +- s->setup_len, sizeof(s->data_buf)); ++ setup_len, sizeof(s->data_buf)); + p->status = USB_RET_STALL; + return; + } ++ s->setup_len = setup_len; + + if (p->pid == USB_TOKEN_OUT) { + usb_packet_copy(p, s->data_buf, s->setup_len); +-- +2.18.4 diff --git a/xsa335-trad.patch b/xsa335-trad.patch new file mode 100644 index 0000000..1310b84 --- /dev/null +++ b/xsa335-trad.patch @@ -0,0 +1,45 @@ +From a62cdd675bc6a8053f6797b6add29b2853b081e3 Mon Sep 17 00:00:00 2001 +From: Ian Jackson +Date: Wed, 19 Aug 2020 18:31:45 +0100 +Subject: [PATCH] SUPPORT.md: Desupport qemu trad except stub dm + +While investigating XSA-335 we discovered that many upstream security +fixes were missing. It is not practical to backport them. There is +no good reason to be running this very ancient version of qemu, except +that it is the only way to run a stub dm which is currently supported +by upstream. + +Signed-off-by: Ian Jackson +--- + SUPPORT.md | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/SUPPORT.md b/SUPPORT.md +index 1479055c45..b0939052e2 100644 +--- a/SUPPORT.md ++++ b/SUPPORT.md +@@ -758,6 +758,21 @@ See the section **Blkback** for image formats supported by QEMU. + + Status: Supported, not security supported + ++### qemu-xen-traditional ### ++ ++The Xen Project provides an old version of qemu with modifications ++which enable use as a device model stub domain. The old version is ++normally selected by default only in a stub dm configuration, but it ++can be requested explicitly in other configurations, for example in ++`xl` with `device_model_version="QEMU_XEN_TRADITIONAL"`. ++ ++ Status, Device Model Stub Domains: Supported, with caveats ++ Status, as host process device model: No security support, not recommended ++ ++qemu-xen-traditional is security supported only for those available ++devices which are supported for mainstream QEMU (see above), with ++trusted driver domains (see Device Model Stub Domains). ++ + ## Virtual Firmware + + ### x86/HVM iPXE +-- +2.20.1 + From 066167cb69ba3778d7dd5fdda2737738f4fbf482 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 3 Sep 2020 21:05:20 +0100 Subject: [PATCH 046/194] rebuild for OCaml 4.11.1 --- xen.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 5667009..82b6f3a 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.14.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 @@ -913,6 +913,9 @@ fi %endif %changelog +* Thu Sep 03 2020 Michael Young - 4.14.0-4 +- rebuild for OCaml 4.11.1 + * Mon Aug 24 2020 Michael Young - 4.14.0-3 - QEMU: usb: out-of-bounds r/w access issue [XSA-335, CVE-2020-14364] (#1871850) From 6594c32499443640d56b7cb95c00af428b96de7e Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 22 Sep 2020 17:23:20 +0100 Subject: [PATCH 047/194] 10 security updates x86 pv: Crash when handling guest access to MSR_MISC_ENABLE [XSA-333, CVE-2020-25602] Missing unlock in XENMEM_acquire_resource error path [XSA-334, CVE-2020-25598] race when migrating timers between x86 HVM vCPU-s [XSA-336, CVE-2020-25604] PCI passthrough code reading back hardware registers [XSA-337, CVE-2020-25595] once valid event channels may not turn invalid [XSA-338, CVE-2020-25597] x86 pv guest kernel DoS via SYSENTER [XSA-339, CVE-2020-25596] Missing memory barriers when accessing/allocating an event channel [XSA-340, CVE-2020-25603] out of bounds event channels available to 32-bit x86 domains [XSA-342, CVE-2020-25600] races with evtchn_reset() [XSA-343, CVE-2020-25599] lack of preemption in evtchn_reset() / evtchn_destroy() [XSA-344, CVE-2020-25601] --- xen.spec | 49 ++++++- xsa333.patch | 39 +++++ xsa334.patch | 51 +++++++ xsa336.patch | 283 +++++++++++++++++++++++++++++++++++ xsa337-1.patch | 87 +++++++++++ xsa337-2.patch | 184 +++++++++++++++++++++++ xsa338.patch | 42 ++++++ xsa339.patch | 76 ++++++++++ xsa340.patch | 65 ++++++++ xsa342.patch | 152 +++++++++++++++++++ xsa343-1.patch | 199 +++++++++++++++++++++++++ xsa343-2.patch | 295 +++++++++++++++++++++++++++++++++++++ xsa343-3.patch | 392 +++++++++++++++++++++++++++++++++++++++++++++++++ xsa344-1.patch | 137 +++++++++++++++++ xsa344-2.patch | 210 ++++++++++++++++++++++++++ 15 files changed, 2260 insertions(+), 1 deletion(-) create mode 100644 xsa333.patch create mode 100644 xsa334.patch create mode 100644 xsa336.patch create mode 100644 xsa337-1.patch create mode 100644 xsa337-2.patch create mode 100644 xsa338.patch create mode 100644 xsa339.patch create mode 100644 xsa340.patch create mode 100644 xsa342.patch create mode 100644 xsa343-1.patch create mode 100644 xsa343-2.patch create mode 100644 xsa343-3.patch create mode 100644 xsa344-1.patch create mode 100644 xsa344-2.patch diff --git a/xen.spec b/xen.spec index 82b6f3a..15c2a98 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.14.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 @@ -113,6 +113,20 @@ Patch42: xen.gcc9.fixes.patch Patch45: xen.gcc10.fixes.patch Patch46: xsa335-qemu.patch Patch47: xsa335-trad.patch +Patch48: xsa333.patch +Patch49: xsa334.patch +Patch50: xsa336.patch +Patch51: xsa337-1.patch +Patch52: xsa337-2.patch +Patch53: xsa338.patch +Patch54: xsa339.patch +Patch55: xsa340.patch +Patch56: xsa342.patch +Patch57: xsa343-1.patch +Patch58: xsa343-2.patch +Patch59: xsa343-3.patch +Patch60: xsa344-1.patch +Patch61: xsa344-2.patch %if %build_qemutrad @@ -315,6 +329,20 @@ manage Xen virtual machines. %patch42 -p1 %patch45 -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 @@ -913,6 +941,25 @@ fi %endif %changelog +* Tue Sep 22 2020 Michael Young - 4.14.0-5 +- x86 pv: Crash when handling guest access to MSR_MISC_ENABLE [XSA-333, + CVE-2020-25602] +- Missing unlock in XENMEM_acquire_resource error path [XSA-334, + CVE-2020-25598] +- race when migrating timers between x86 HVM vCPU-s [XSA-336, + CVE-2020-25604] +- PCI passthrough code reading back hardware registers [XSA-337, + CVE-2020-25595] +- once valid event channels may not turn invalid [XSA-338, CVE-2020-25597] +- x86 pv guest kernel DoS via SYSENTER [XSA-339, CVE-2020-25596] +- Missing memory barriers when accessing/allocating an event channel [XSA-340, + CVE-2020-25603] +- out of bounds event channels available to 32-bit x86 domains [XSA-342, + CVE-2020-25600] +- races with evtchn_reset() [XSA-343, CVE-2020-25599] +- lack of preemption in evtchn_reset() / evtchn_destroy() [XSA-344, + CVE-2020-25601] + * Thu Sep 03 2020 Michael Young - 4.14.0-4 - rebuild for OCaml 4.11.1 diff --git a/xsa333.patch b/xsa333.patch new file mode 100644 index 0000000..6b86c94 --- /dev/null +++ b/xsa333.patch @@ -0,0 +1,39 @@ +From: Andrew Cooper +Subject: x86/pv: Handle the Intel-specific MSR_MISC_ENABLE correctly + +This MSR doesn't exist on AMD hardware, and switching away from the safe +functions in the common MSR path was an erroneous change. + +Partially revert the change. + +This is XSA-333. + +Fixes: 4fdc932b3cc ("x86/Intel: drop another 32-bit leftover") +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +Reviewed-by: Wei Liu + +diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c +index efeb2a727e..6332c74b80 100644 +--- a/xen/arch/x86/pv/emul-priv-op.c ++++ b/xen/arch/x86/pv/emul-priv-op.c +@@ -924,7 +924,8 @@ static int read_msr(unsigned int reg, uint64_t *val, + return X86EMUL_OKAY; + + case MSR_IA32_MISC_ENABLE: +- rdmsrl(reg, *val); ++ if ( rdmsr_safe(reg, *val) ) ++ break; + *val = guest_misc_enable(*val); + return X86EMUL_OKAY; + +@@ -1059,7 +1060,8 @@ static int write_msr(unsigned int reg, uint64_t val, + break; + + case MSR_IA32_MISC_ENABLE: +- rdmsrl(reg, temp); ++ if ( rdmsr_safe(reg, temp) ) ++ break; + if ( val != guest_misc_enable(temp) ) + goto invalid; + return X86EMUL_OKAY; diff --git a/xsa334.patch b/xsa334.patch new file mode 100644 index 0000000..4260cdb --- /dev/null +++ b/xsa334.patch @@ -0,0 +1,51 @@ +From: Andrew Cooper +Subject: xen/memory: Don't skip the RCU unlock path in acquire_resource() + +In the case that an HVM Stubdomain makes an XENMEM_acquire_resource hypercall, +the FIXME path will bypass rcu_unlock_domain() on the way out of the function. + +Move the check to the start of the function. This does change the behaviour +of the get-size path for HVM Stubdomains, but that functionality is currently +broken and unused anyway, as well as being quite useless to entities which +can't actually map the resource anyway. + +This is XSA-334. + +Fixes: 83fa6552ce ("common: add a new mappable resource type: XENMEM_resource_grant_table") +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/common/memory.c b/xen/common/memory.c +index 1a3c9ffb30..29741d8904 100644 +--- a/xen/common/memory.c ++++ b/xen/common/memory.c +@@ -1058,6 +1058,14 @@ static int acquire_resource( + xen_pfn_t mfn_list[32]; + int rc; + ++ /* ++ * FIXME: Until foreign pages inserted into the P2M are properly ++ * reference counted, it is unsafe to allow mapping of ++ * resource pages unless the caller is the hardware domain. ++ */ ++ if ( paging_mode_translate(currd) && !is_hardware_domain(currd) ) ++ return -EACCES; ++ + if ( copy_from_guest(&xmar, arg, 1) ) + return -EFAULT; + +@@ -1114,14 +1122,6 @@ static int acquire_resource( + xen_pfn_t gfn_list[ARRAY_SIZE(mfn_list)]; + unsigned int i; + +- /* +- * FIXME: Until foreign pages inserted into the P2M are properly +- * reference counted, it is unsafe to allow mapping of +- * resource pages unless the caller is the hardware domain. +- */ +- if ( !is_hardware_domain(currd) ) +- return -EACCES; +- + if ( copy_from_guest(gfn_list, xmar.frame_list, xmar.nr_frames) ) + rc = -EFAULT; + diff --git a/xsa336.patch b/xsa336.patch new file mode 100644 index 0000000..b44c298 --- /dev/null +++ b/xsa336.patch @@ -0,0 +1,283 @@ +From: Roger Pau Monné +Subject: x86/vpt: fix race when migrating timers between vCPUs + +The current vPT code will migrate the emulated timers between vCPUs +(change the pt->vcpu field) while just holding the destination lock, +either from create_periodic_time or pt_adjust_global_vcpu_target if +the global target is adjusted. Changing the periodic_timer vCPU field +in this way creates a race where a third party could grab the lock in +the unlocked region of pt_adjust_global_vcpu_target (or before +create_periodic_time performs the vcpu change) and then release the +lock from a different vCPU, creating a locking imbalance. + +Introduce a per-domain rwlock in order to protect periodic_time +migration between vCPU lists. Taking the lock in read mode prevents +any timer from being migrated to a different vCPU, while taking it in +write mode allows performing migration of timers across vCPUs. The +per-vcpu locks are still used to protect all the other fields from the +periodic_timer struct. + +Note that such migration shouldn't happen frequently, and hence +there's no performance drop as a result of such locking. + +This is XSA-336. + +Reported-by: Igor Druzhinin +Tested-by: Igor Druzhinin +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +--- +Changes since v2: + - Re-order pt_adjust_vcpu to remove one if. + - Fix pt_lock to not call pt_vcpu_lock, as we might end up using a + stale value of pt->vcpu when taking the per-vcpu lock. + +Changes since v1: + - Use a per-domain rwlock to protect timer vCPU migration. + +--- a/xen/arch/x86/hvm/hvm.c ++++ b/xen/arch/x86/hvm/hvm.c +@@ -658,6 +658,8 @@ int hvm_domain_initialise(struct domain + /* need link to containing domain */ + d->arch.hvm.pl_time->domain = d; + ++ rwlock_init(&d->arch.hvm.pl_time->pt_migrate); ++ + /* Set the default IO Bitmap. */ + if ( is_hardware_domain(d) ) + { +--- a/xen/arch/x86/hvm/vpt.c ++++ b/xen/arch/x86/hvm/vpt.c +@@ -153,23 +153,32 @@ static int pt_irq_masked(struct periodic + return 1; + } + +-static void pt_lock(struct periodic_time *pt) ++static void pt_vcpu_lock(struct vcpu *v) + { +- struct vcpu *v; ++ read_lock(&v->domain->arch.hvm.pl_time->pt_migrate); ++ spin_lock(&v->arch.hvm.tm_lock); ++} + +- for ( ; ; ) +- { +- v = pt->vcpu; +- spin_lock(&v->arch.hvm.tm_lock); +- if ( likely(pt->vcpu == v) ) +- break; +- spin_unlock(&v->arch.hvm.tm_lock); +- } ++static void pt_vcpu_unlock(struct vcpu *v) ++{ ++ spin_unlock(&v->arch.hvm.tm_lock); ++ read_unlock(&v->domain->arch.hvm.pl_time->pt_migrate); ++} ++ ++static void pt_lock(struct periodic_time *pt) ++{ ++ /* ++ * We cannot use pt_vcpu_lock here, because we need to acquire the ++ * per-domain lock first and then (re-)fetch the value of pt->vcpu, or ++ * else we might be using a stale value of pt->vcpu. ++ */ ++ read_lock(&pt->vcpu->domain->arch.hvm.pl_time->pt_migrate); ++ spin_lock(&pt->vcpu->arch.hvm.tm_lock); + } + + static void pt_unlock(struct periodic_time *pt) + { +- spin_unlock(&pt->vcpu->arch.hvm.tm_lock); ++ pt_vcpu_unlock(pt->vcpu); + } + + static void pt_process_missed_ticks(struct periodic_time *pt) +@@ -219,7 +228,7 @@ void pt_save_timer(struct vcpu *v) + if ( v->pause_flags & VPF_blocked ) + return; + +- spin_lock(&v->arch.hvm.tm_lock); ++ pt_vcpu_lock(v); + + list_for_each_entry ( pt, head, list ) + if ( !pt->do_not_freeze ) +@@ -227,7 +236,7 @@ void pt_save_timer(struct vcpu *v) + + pt_freeze_time(v); + +- spin_unlock(&v->arch.hvm.tm_lock); ++ pt_vcpu_unlock(v); + } + + void pt_restore_timer(struct vcpu *v) +@@ -235,7 +244,7 @@ void pt_restore_timer(struct vcpu *v) + struct list_head *head = &v->arch.hvm.tm_list; + struct periodic_time *pt; + +- spin_lock(&v->arch.hvm.tm_lock); ++ pt_vcpu_lock(v); + + list_for_each_entry ( pt, head, list ) + { +@@ -248,7 +257,7 @@ void pt_restore_timer(struct vcpu *v) + + pt_thaw_time(v); + +- spin_unlock(&v->arch.hvm.tm_lock); ++ pt_vcpu_unlock(v); + } + + static void pt_timer_fn(void *data) +@@ -309,7 +318,7 @@ int pt_update_irq(struct vcpu *v) + int irq, pt_vector = -1; + bool level; + +- spin_lock(&v->arch.hvm.tm_lock); ++ pt_vcpu_lock(v); + + earliest_pt = NULL; + max_lag = -1ULL; +@@ -339,7 +348,7 @@ int pt_update_irq(struct vcpu *v) + + if ( earliest_pt == NULL ) + { +- spin_unlock(&v->arch.hvm.tm_lock); ++ pt_vcpu_unlock(v); + return -1; + } + +@@ -347,7 +356,7 @@ int pt_update_irq(struct vcpu *v) + irq = earliest_pt->irq; + level = earliest_pt->level; + +- spin_unlock(&v->arch.hvm.tm_lock); ++ pt_vcpu_unlock(v); + + switch ( earliest_pt->source ) + { +@@ -394,7 +403,7 @@ int pt_update_irq(struct vcpu *v) + time_cb *cb = NULL; + void *cb_priv; + +- spin_lock(&v->arch.hvm.tm_lock); ++ pt_vcpu_lock(v); + /* Make sure the timer is still on the list. */ + list_for_each_entry ( pt, &v->arch.hvm.tm_list, list ) + if ( pt == earliest_pt ) +@@ -404,7 +413,7 @@ int pt_update_irq(struct vcpu *v) + cb_priv = pt->priv; + break; + } +- spin_unlock(&v->arch.hvm.tm_lock); ++ pt_vcpu_unlock(v); + + if ( cb != NULL ) + cb(v, cb_priv); +@@ -441,12 +450,12 @@ void pt_intr_post(struct vcpu *v, struct + if ( intack.source == hvm_intsrc_vector ) + return; + +- spin_lock(&v->arch.hvm.tm_lock); ++ pt_vcpu_lock(v); + + pt = is_pt_irq(v, intack); + if ( pt == NULL ) + { +- spin_unlock(&v->arch.hvm.tm_lock); ++ pt_vcpu_unlock(v); + return; + } + +@@ -455,7 +464,7 @@ void pt_intr_post(struct vcpu *v, struct + cb = pt->cb; + cb_priv = pt->priv; + +- spin_unlock(&v->arch.hvm.tm_lock); ++ pt_vcpu_unlock(v); + + if ( cb != NULL ) + cb(v, cb_priv); +@@ -466,12 +475,12 @@ void pt_migrate(struct vcpu *v) + struct list_head *head = &v->arch.hvm.tm_list; + struct periodic_time *pt; + +- spin_lock(&v->arch.hvm.tm_lock); ++ pt_vcpu_lock(v); + + list_for_each_entry ( pt, head, list ) + migrate_timer(&pt->timer, v->processor); + +- spin_unlock(&v->arch.hvm.tm_lock); ++ pt_vcpu_unlock(v); + } + + void create_periodic_time( +@@ -490,7 +499,7 @@ void create_periodic_time( + + destroy_periodic_time(pt); + +- spin_lock(&v->arch.hvm.tm_lock); ++ write_lock(&v->domain->arch.hvm.pl_time->pt_migrate); + + pt->pending_intr_nr = 0; + pt->do_not_freeze = 0; +@@ -540,7 +549,7 @@ void create_periodic_time( + init_timer(&pt->timer, pt_timer_fn, pt, v->processor); + set_timer(&pt->timer, pt->scheduled); + +- spin_unlock(&v->arch.hvm.tm_lock); ++ write_unlock(&v->domain->arch.hvm.pl_time->pt_migrate); + } + + void destroy_periodic_time(struct periodic_time *pt) +@@ -565,30 +574,20 @@ void destroy_periodic_time(struct period + + static void pt_adjust_vcpu(struct periodic_time *pt, struct vcpu *v) + { +- int on_list; +- + ASSERT(pt->source == PTSRC_isa || pt->source == PTSRC_ioapic); + + if ( pt->vcpu == NULL ) + return; + +- pt_lock(pt); +- on_list = pt->on_list; +- if ( pt->on_list ) +- list_del(&pt->list); +- pt->on_list = 0; +- pt_unlock(pt); +- +- spin_lock(&v->arch.hvm.tm_lock); ++ write_lock(&pt->vcpu->domain->arch.hvm.pl_time->pt_migrate); + pt->vcpu = v; +- if ( on_list ) ++ if ( pt->on_list ) + { +- pt->on_list = 1; ++ list_del(&pt->list); + list_add(&pt->list, &v->arch.hvm.tm_list); +- + migrate_timer(&pt->timer, v->processor); + } +- spin_unlock(&v->arch.hvm.tm_lock); ++ write_unlock(&pt->vcpu->domain->arch.hvm.pl_time->pt_migrate); + } + + void pt_adjust_global_vcpu_target(struct vcpu *v) +--- a/xen/include/asm-x86/hvm/vpt.h ++++ b/xen/include/asm-x86/hvm/vpt.h +@@ -128,6 +128,13 @@ struct pl_time { /* platform time */ + struct RTCState vrtc; + struct HPETState vhpet; + struct PMTState vpmt; ++ /* ++ * rwlock to prevent periodic_time vCPU migration. Take the lock in read ++ * mode in order to prevent the vcpu field of periodic_time from changing. ++ * Lock must be taken in write mode when changes to the vcpu field are ++ * performed, as it allows exclusive access to all the timers of a domain. ++ */ ++ rwlock_t pt_migrate; + /* guest_time = Xen sys time + stime_offset */ + int64_t stime_offset; + /* Ensures monotonicity in appropriate timer modes. */ diff --git a/xsa337-1.patch b/xsa337-1.patch new file mode 100644 index 0000000..eae4185 --- /dev/null +++ b/xsa337-1.patch @@ -0,0 +1,87 @@ +From: Roger Pau Monné +Subject: x86/msi: get rid of read_msi_msg + +It's safer and faster to just use the cached last written +(untranslated) MSI message stored in msi_desc for the single user that +calls read_msi_msg. + +This also prevents relying on the data read from the device MSI +registers in order to figure out the index into the IOMMU interrupt +remapping table, which is not safe. + +This is part of XSA-337. + +Reported-by: Andrew Cooper +Requested-by: Andrew Cooper +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich + +--- a/xen/arch/x86/msi.c ++++ b/xen/arch/x86/msi.c +@@ -185,54 +185,6 @@ void msi_compose_msg(unsigned vector, co + MSI_DATA_VECTOR(vector); + } + +-static bool read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) +-{ +- switch ( entry->msi_attrib.type ) +- { +- case PCI_CAP_ID_MSI: +- { +- struct pci_dev *dev = entry->dev; +- int pos = entry->msi_attrib.pos; +- uint16_t data; +- +- msg->address_lo = pci_conf_read32(dev->sbdf, +- msi_lower_address_reg(pos)); +- if ( entry->msi_attrib.is_64 ) +- { +- msg->address_hi = pci_conf_read32(dev->sbdf, +- msi_upper_address_reg(pos)); +- data = pci_conf_read16(dev->sbdf, msi_data_reg(pos, 1)); +- } +- else +- { +- msg->address_hi = 0; +- data = pci_conf_read16(dev->sbdf, msi_data_reg(pos, 0)); +- } +- msg->data = data; +- break; +- } +- case PCI_CAP_ID_MSIX: +- { +- void __iomem *base = entry->mask_base; +- +- if ( unlikely(!msix_memory_decoded(entry->dev, +- entry->msi_attrib.pos)) ) +- return false; +- msg->address_lo = readl(base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); +- msg->address_hi = readl(base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); +- msg->data = readl(base + PCI_MSIX_ENTRY_DATA_OFFSET); +- break; +- } +- default: +- BUG(); +- } +- +- if ( iommu_intremap ) +- iommu_read_msi_from_ire(entry, msg); +- +- return true; +-} +- + static int write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) + { + entry->msg = *msg; +@@ -304,10 +256,7 @@ void set_msi_affinity(struct irq_desc *d + + ASSERT(spin_is_locked(&desc->lock)); + +- memset(&msg, 0, sizeof(msg)); +- if ( !read_msi_msg(msi_desc, &msg) ) +- return; +- ++ msg = msi_desc->msg; + msg.data &= ~MSI_DATA_VECTOR_MASK; + msg.data |= MSI_DATA_VECTOR(desc->arch.vector); + msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK; diff --git a/xsa337-2.patch b/xsa337-2.patch new file mode 100644 index 0000000..8089e68 --- /dev/null +++ b/xsa337-2.patch @@ -0,0 +1,184 @@ +From: Jan Beulich +Subject: x86/MSI-X: restrict reading of table/PBA bases from BARs + +When assigned to less trusted or un-trusted guests, devices may change +state behind our backs (they may e.g. get reset by means we may not know +about). Therefore we should avoid reading BARs from hardware once a +device is no longer owned by Dom0. Furthermore when we can't read a BAR, +or when we read zero, we shouldn't instead use the caller provided +address unless that caller can be trusted. + +Re-arrange the logic in msix_capability_init() such that only Dom0 (and +only if the device isn't DomU-owned yet) or calls through +PHYSDEVOP_prepare_msix will actually result in the reading of the +respective BAR register(s). Additionally do so only as long as in-use +table entries are known (note that invocation of PHYSDEVOP_prepare_msix +counts as a "pseudo" entry). In all other uses the value already +recorded will get used instead. + +Clear the recorded values in _pci_cleanup_msix() as well as on the one +affected error path. (Adjust this error path to also avoid blindly +disabling MSI-X when it was enabled on entry to the function.) + +While moving around variable declarations (in many cases to reduce their +scopes), also adjust some of their types. + +This is part of XSA-337. + +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné +--- +v2: Use "unsigned int" for moved bir, pbus, etc. Further restrict under + what conditions to read the BAR(s). + +--- a/xen/arch/x86/msi.c ++++ b/xen/arch/x86/msi.c +@@ -771,16 +771,14 @@ static int msix_capability_init(struct p + { + struct arch_msix *msix = dev->msix; + struct msi_desc *entry = NULL; +- int vf; + u16 control; + u64 table_paddr; + u32 table_offset; +- u8 bir, pbus, pslot, pfunc; + u16 seg = dev->seg; + u8 bus = dev->bus; + u8 slot = PCI_SLOT(dev->devfn); + u8 func = PCI_FUNC(dev->devfn); +- bool maskall = msix->host_maskall; ++ bool maskall = msix->host_maskall, zap_on_error = false; + unsigned int pos = pci_find_cap_offset(seg, bus, slot, func, + PCI_CAP_ID_MSIX); + +@@ -822,43 +820,45 @@ static int msix_capability_init(struct p + + /* Locate MSI-X table region */ + table_offset = pci_conf_read32(dev->sbdf, msix_table_offset_reg(pos)); +- bir = (u8)(table_offset & PCI_MSIX_BIRMASK); +- table_offset &= ~PCI_MSIX_BIRMASK; ++ if ( !msix->used_entries && ++ (!msi || ++ (is_hardware_domain(current->domain) && ++ (dev->domain == current->domain || dev->domain == dom_io))) ) ++ { ++ unsigned int bir = table_offset & PCI_MSIX_BIRMASK, pbus, pslot, pfunc; ++ int vf; ++ paddr_t pba_paddr; ++ unsigned int pba_offset; + +- if ( !dev->info.is_virtfn ) +- { +- pbus = bus; +- pslot = slot; +- pfunc = func; +- vf = -1; +- } +- else +- { +- pbus = dev->info.physfn.bus; +- pslot = PCI_SLOT(dev->info.physfn.devfn); +- pfunc = PCI_FUNC(dev->info.physfn.devfn); +- vf = PCI_BDF2(dev->bus, dev->devfn); +- } +- +- table_paddr = read_pci_mem_bar(seg, pbus, pslot, pfunc, bir, vf); +- WARN_ON(msi && msi->table_base != table_paddr); +- if ( !table_paddr ) +- { +- if ( !msi || !msi->table_base ) ++ if ( !dev->info.is_virtfn ) + { +- pci_conf_write16(dev->sbdf, msix_control_reg(pos), +- control & ~PCI_MSIX_FLAGS_ENABLE); +- xfree(entry); +- return -ENXIO; ++ pbus = bus; ++ pslot = slot; ++ pfunc = func; ++ vf = -1; ++ } ++ else ++ { ++ pbus = dev->info.physfn.bus; ++ pslot = PCI_SLOT(dev->info.physfn.devfn); ++ pfunc = PCI_FUNC(dev->info.physfn.devfn); ++ vf = PCI_BDF2(dev->bus, dev->devfn); + } +- table_paddr = msi->table_base; +- } +- table_paddr += table_offset; + +- if ( !msix->used_entries ) +- { +- u64 pba_paddr; +- u32 pba_offset; ++ table_paddr = read_pci_mem_bar(seg, pbus, pslot, pfunc, bir, vf); ++ WARN_ON(msi && msi->table_base != table_paddr); ++ if ( !table_paddr ) ++ { ++ if ( !msi || !msi->table_base ) ++ { ++ pci_conf_write16(dev->sbdf, msix_control_reg(pos), ++ control & ~PCI_MSIX_FLAGS_ENABLE); ++ xfree(entry); ++ return -ENXIO; ++ } ++ table_paddr = msi->table_base; ++ } ++ table_paddr += table_offset & ~PCI_MSIX_BIRMASK; + + msix->table.first = PFN_DOWN(table_paddr); + msix->table.last = PFN_DOWN(table_paddr + +@@ -877,7 +877,18 @@ static int msix_capability_init(struct p + BITS_TO_LONGS(msix->nr_entries) - 1); + WARN_ON(rangeset_overlaps_range(mmio_ro_ranges, msix->pba.first, + msix->pba.last)); ++ ++ zap_on_error = true; ++ } ++ else if ( !msix->table.first ) ++ { ++ pci_conf_write16(dev->sbdf, msix_control_reg(pos), control); ++ xfree(entry); ++ return -ENODATA; + } ++ else ++ table_paddr = (msix->table.first << PAGE_SHIFT) + ++ PAGE_OFFSET(table_offset & ~PCI_MSIX_BIRMASK); + + if ( entry ) + { +@@ -888,8 +899,15 @@ static int msix_capability_init(struct p + + if ( idx < 0 ) + { +- pci_conf_write16(dev->sbdf, msix_control_reg(pos), +- control & ~PCI_MSIX_FLAGS_ENABLE); ++ if ( zap_on_error ) ++ { ++ msix->table.first = 0; ++ msix->pba.first = 0; ++ ++ control &= ~PCI_MSIX_FLAGS_ENABLE; ++ } ++ ++ pci_conf_write16(dev->sbdf, msix_control_reg(pos), control); + xfree(entry); + return idx; + } +@@ -1072,9 +1090,14 @@ static void _pci_cleanup_msix(struct arc + if ( rangeset_remove_range(mmio_ro_ranges, msix->table.first, + msix->table.last) ) + WARN(); ++ msix->table.first = 0; ++ msix->table.last = 0; ++ + if ( rangeset_remove_range(mmio_ro_ranges, msix->pba.first, + msix->pba.last) ) + WARN(); ++ msix->pba.first = 0; ++ msix->pba.last = 0; + } + } + diff --git a/xsa338.patch b/xsa338.patch new file mode 100644 index 0000000..7765219 --- /dev/null +++ b/xsa338.patch @@ -0,0 +1,42 @@ +From: Jan Beulich +Subject: evtchn: relax port_is_valid() + +To avoid ports potentially becoming invalid behind the back of certain +other functions (due to ->max_evtchn shrinking) because of +- a guest invoking evtchn_reset() and from a 2nd vCPU opening new + channels in parallel (see also XSA-343), +- alloc_unbound_xen_event_channel() produced channels living above the + 2-level range (see also XSA-342), +drop the max_evtchns check from port_is_valid(). For a port for which +the function once returned "true", the returned value may not turn into +"false" later on. The function's result may only depend on bounds which +can only ever grow (which is the case for d->valid_evtchns). + +This also eliminates a false sense of safety, utilized by some of the +users (see again XSA-343): Without a suitable lock held, d->max_evtchns +may change at any time, and hence deducing that certain other operations +are safe when port_is_valid() returned true is not legitimate. The +opportunities to abuse this may get widened by the change here +(depending on guest and host configuration), but will be taken care of +by the other XSA. + +This is XSA-338. + +Fixes: 48974e6ce52e ("evtchn: use a per-domain variable for the max number of event channels") +Signed-off-by: Jan Beulich +Reviewed-by: Stefano Stabellini +Reviewed-by: Julien Grall +--- +v5: New, split from larger patch. + +--- a/xen/include/xen/event.h ++++ b/xen/include/xen/event.h +@@ -107,8 +107,6 @@ void notify_via_xen_event_channel(struct + + static inline bool_t port_is_valid(struct domain *d, unsigned int p) + { +- if ( p >= d->max_evtchns ) +- return 0; + return p < read_atomic(&d->valid_evtchns); + } + diff --git a/xsa339.patch b/xsa339.patch new file mode 100644 index 0000000..3311ae0 --- /dev/null +++ b/xsa339.patch @@ -0,0 +1,76 @@ +From: Andrew Cooper +Subject: x86/pv: Avoid double exception injection + +There is at least one path (SYSENTER with NT set, Xen converts to #GP) which +ends up injecting the #GP fault twice, first in compat_sysenter(), and then a +second time in compat_test_all_events(), due to the stale TBF_EXCEPTION left +in TRAPBOUNCE_flags. + +The guest kernel sees the second fault first, which is a kernel level #GP +pointing at the head of the #GP handler, and is therefore a userspace +trigger-able DoS. + +This particular bug has bitten us several times before, so rearrange +{compat_,}create_bounce_frame() to clobber TRAPBOUNCE on success, rather than +leaving this task to one area of code which isn't used uniformly. + +Other scenarios which might result in a double injection (e.g. two calls +directly to compat_create_bounce_frame) will now crash the guest, which is far +more obvious than letting the kernel run with corrupt state. + +This is XSA-339 + +Fixes: fdac9515607b ("x86: clear EFLAGS.NT in SYSENTER entry path") +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/x86_64/compat/entry.S b/xen/arch/x86/x86_64/compat/entry.S +index c3e62f8734..73619f57ca 100644 +--- a/xen/arch/x86/x86_64/compat/entry.S ++++ b/xen/arch/x86/x86_64/compat/entry.S +@@ -78,7 +78,6 @@ compat_process_softirqs: + sti + .Lcompat_bounce_exception: + call compat_create_bounce_frame +- movb $0, TRAPBOUNCE_flags(%rdx) + jmp compat_test_all_events + + ALIGN +@@ -352,7 +351,13 @@ __UNLIKELY_END(compat_bounce_null_selector) + movl %eax,UREGS_cs+8(%rsp) + movl TRAPBOUNCE_eip(%rdx),%eax + movl %eax,UREGS_rip+8(%rsp) ++ ++ /* Trapbounce complete. Clobber state to avoid an erroneous second injection. */ ++ xor %eax, %eax ++ mov %ax, TRAPBOUNCE_cs(%rdx) ++ mov %al, TRAPBOUNCE_flags(%rdx) + ret ++ + .section .fixup,"ax" + .Lfx13: + xorl %edi,%edi +diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S +index 1e880eb9f6..71a00e846b 100644 +--- a/xen/arch/x86/x86_64/entry.S ++++ b/xen/arch/x86/x86_64/entry.S +@@ -90,7 +90,6 @@ process_softirqs: + sti + .Lbounce_exception: + call create_bounce_frame +- movb $0, TRAPBOUNCE_flags(%rdx) + jmp test_all_events + + ALIGN +@@ -512,6 +511,11 @@ UNLIKELY_START(z, create_bounce_frame_bad_bounce_ip) + jmp asm_domain_crash_synchronous /* Does not return */ + __UNLIKELY_END(create_bounce_frame_bad_bounce_ip) + movq %rax,UREGS_rip+8(%rsp) ++ ++ /* Trapbounce complete. Clobber state to avoid an erroneous second injection. */ ++ xor %eax, %eax ++ mov %rax, TRAPBOUNCE_eip(%rdx) ++ mov %al, TRAPBOUNCE_flags(%rdx) + ret + + .pushsection .fixup, "ax", @progbits diff --git a/xsa340.patch b/xsa340.patch new file mode 100644 index 0000000..38d04da --- /dev/null +++ b/xsa340.patch @@ -0,0 +1,65 @@ +From: Julien Grall +Subject: xen/evtchn: Add missing barriers when accessing/allocating an event channel + +While the allocation of a bucket is always performed with the per-domain +lock, the bucket may be accessed without the lock taken (for instance, see +evtchn_send()). + +Instead such sites relies on port_is_valid() to return a non-zero value +when the port has a struct evtchn associated to it. The function will +mostly check whether the port is less than d->valid_evtchns as all the +buckets/event channels should be allocated up to that point. + +Unfortunately a compiler is free to re-order the assignment in +evtchn_allocate_port() so it would be possible to have d->valid_evtchns +updated before the new bucket has finish to allocate. + +Additionally on Arm, even if this was compiled "correctly", the +processor can still re-order the memory access. + +Add a write memory barrier in the allocation side and a read memory +barrier when the port is valid to prevent any re-ordering issue. + +This is XSA-340. + +Reported-by: Julien Grall +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini + +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -178,6 +178,13 @@ int evtchn_allocate_port(struct domain * + return -ENOMEM; + bucket_from_port(d, port) = chn; + ++ /* ++ * d->valid_evtchns is used to check whether the bucket can be ++ * accessed without the per-domain lock. Therefore, ++ * d->valid_evtchns should be seen *after* the new bucket has ++ * been setup. ++ */ ++ smp_wmb(); + write_atomic(&d->valid_evtchns, d->valid_evtchns + EVTCHNS_PER_BUCKET); + } + +--- a/xen/include/xen/event.h ++++ b/xen/include/xen/event.h +@@ -107,7 +107,17 @@ void notify_via_xen_event_channel(struct + + static inline bool_t port_is_valid(struct domain *d, unsigned int p) + { +- return p < read_atomic(&d->valid_evtchns); ++ if ( p >= read_atomic(&d->valid_evtchns) ) ++ return false; ++ ++ /* ++ * The caller will usually access the event channel afterwards and ++ * may be done without taking the per-domain lock. The barrier is ++ * going in pair the smp_wmb() barrier in evtchn_allocate_port(). ++ */ ++ smp_rmb(); ++ ++ return true; + } + + static inline struct evtchn *evtchn_from_port(struct domain *d, unsigned int p) diff --git a/xsa342.patch b/xsa342.patch new file mode 100644 index 0000000..c878c07 --- /dev/null +++ b/xsa342.patch @@ -0,0 +1,152 @@ +From: Jan Beulich +Subject: evtchn/x86: enforce correct upper limit for 32-bit guests + +The recording of d->max_evtchns in evtchn_2l_init(), in particular with +the limited set of callers of the function, is insufficient. Neither for +PV nor for HVM guests the bitness is known at domain_create() time, yet +the upper bound in 2-level mode depends upon guest bitness. Recording +too high a limit "allows" x86 32-bit domains to open not properly usable +event channels, management of which (inside Xen) would then result in +corruption of the shared info and vCPU info structures. + +Keep the upper limit dynamic for the 2-level case, introducing a helper +function to retrieve the effective limit. This helper is now supposed to +be private to the event channel code. The used in do_poll() and +domain_dump_evtchn_info() weren't consistent with port uses elsewhere +and hence get switched to port_is_valid(). + +Furthermore FIFO mode's setup_ports() gets adjusted to loop only up to +the prior ABI limit, rather than all the way up to the new one. + +Finally a word on the change to do_poll(): Accessing ->max_evtchns +without holding a suitable lock was never safe, as it as well as +->evtchn_port_ops may change behind do_poll()'s back. Using +port_is_valid() instead widens some the window for potential abuse, +until we've dealt with the race altogether (see XSA-343). + +This is XSA-342. + +Reported-by: Julien Grall +Fixes: 48974e6ce52e ("evtchn: use a per-domain variable for the max number of event channels") +Signed-off-by: Jan Beulich +Reviewed-by: Stefano Stabellini +Reviewed-by: Julien Grall +--- +v6: Drop redundant port_is_valid() check from domain_dump_evtchn_info(). + Add comment in evtchn_fifo_init_control(). +v5: Split off port_is_valid() change. Drop max_evtchns field. +v4: Also mention the alloc_unbound_xen_event_channel() case that gets + fixed here. +v2: Remove max_evtchns check from port_is_valid(). + +--- a/xen/common/event_2l.c ++++ b/xen/common/event_2l.c +@@ -103,7 +103,6 @@ static const struct evtchn_port_ops evtc + void evtchn_2l_init(struct domain *d) + { + d->evtchn_port_ops = &evtchn_port_ops_2l; +- d->max_evtchns = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); + } + + /* +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -151,7 +151,7 @@ static void free_evtchn_bucket(struct do + + int evtchn_allocate_port(struct domain *d, evtchn_port_t port) + { +- if ( port > d->max_evtchn_port || port >= d->max_evtchns ) ++ if ( port > d->max_evtchn_port || port >= max_evtchns(d) ) + return -ENOSPC; + + if ( port_is_valid(d, port) ) +@@ -1396,13 +1396,11 @@ static void domain_dump_evtchn_info(stru + + spin_lock(&d->event_lock); + +- for ( port = 1; port < d->max_evtchns; ++port ) ++ for ( port = 1; port_is_valid(d, port); ++port ) + { + const struct evtchn *chn; + char *ssid; + +- if ( !port_is_valid(d, port) ) +- continue; + chn = evtchn_from_port(d, port); + if ( chn->state == ECS_FREE ) + continue; +--- a/xen/common/event_fifo.c ++++ b/xen/common/event_fifo.c +@@ -478,7 +478,7 @@ static void cleanup_event_array(struct d + d->evtchn_fifo = NULL; + } + +-static void setup_ports(struct domain *d) ++static void setup_ports(struct domain *d, unsigned int prev_evtchns) + { + unsigned int port; + +@@ -488,7 +488,7 @@ static void setup_ports(struct domain *d + * - save its pending state. + * - set default priority. + */ +- for ( port = 1; port < d->max_evtchns; port++ ) ++ for ( port = 1; port < prev_evtchns; port++ ) + { + struct evtchn *evtchn; + +@@ -546,6 +546,8 @@ int evtchn_fifo_init_control(struct evtc + if ( !d->evtchn_fifo ) + { + struct vcpu *vcb; ++ /* Latch the value before it changes during setup_event_array(). */ ++ unsigned int prev_evtchns = max_evtchns(d); + + for_each_vcpu ( d, vcb ) { + rc = setup_control_block(vcb); +@@ -562,8 +564,7 @@ int evtchn_fifo_init_control(struct evtc + goto error; + + d->evtchn_port_ops = &evtchn_port_ops_fifo; +- d->max_evtchns = EVTCHN_FIFO_NR_CHANNELS; +- setup_ports(d); ++ setup_ports(d, prev_evtchns); + } + else + rc = map_control_block(v, gfn, offset); +--- a/xen/common/sched/core.c ++++ b/xen/common/sched/core.c +@@ -1428,7 +1428,7 @@ static long do_poll(struct sched_poll *s + goto out; + + rc = -EINVAL; +- if ( port >= d->max_evtchns ) ++ if ( !port_is_valid(d, port) ) + goto out; + + rc = 0; +--- a/xen/include/xen/event.h ++++ b/xen/include/xen/event.h +@@ -105,6 +105,12 @@ void notify_via_xen_event_channel(struct + #define bucket_from_port(d, p) \ + ((group_from_port(d, p))[((p) % EVTCHNS_PER_GROUP) / EVTCHNS_PER_BUCKET]) + ++static inline unsigned int max_evtchns(const struct domain *d) ++{ ++ return d->evtchn_fifo ? EVTCHN_FIFO_NR_CHANNELS ++ : BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); ++} ++ + static inline bool_t port_is_valid(struct domain *d, unsigned int p) + { + if ( p >= read_atomic(&d->valid_evtchns) ) +--- a/xen/include/xen/sched.h ++++ b/xen/include/xen/sched.h +@@ -359,7 +359,6 @@ struct domain + /* Event channel information. */ + struct evtchn *evtchn; /* first bucket only */ + struct evtchn **evtchn_group[NR_EVTCHN_GROUPS]; /* all other buckets */ +- unsigned int max_evtchns; /* number supported by ABI */ + unsigned int max_evtchn_port; /* max permitted port number */ + unsigned int valid_evtchns; /* number of allocated event channels */ + spinlock_t event_lock; diff --git a/xsa343-1.patch b/xsa343-1.patch new file mode 100644 index 0000000..0abbc03 --- /dev/null +++ b/xsa343-1.patch @@ -0,0 +1,199 @@ +From: Jan Beulich +Subject: evtchn: evtchn_reset() shouldn't succeed with still-open ports + +While the function closes all ports, it does so without holding any +lock, and hence racing requests may be issued causing new ports to get +opened. This would have been problematic in particular if such a newly +opened port had a port number above the new implementation limit (i.e. +when switching from FIFO to 2-level) after the reset, as prior to +"evtchn: relax port_is_valid()" this could have led to e.g. +evtchn_close()'s "BUG_ON(!port_is_valid(d2, port2))" to trigger. + +Introduce a counter of active ports and check that it's (still) no +larger then the number of Xen internally used ones after obtaining the +necessary lock in evtchn_reset(). + +As to the access model of the new {active,xen}_evtchns fields - while +all writes get done using write_atomic(), reads ought to use +read_atomic() only when outside of a suitably locked region. + +Note that as of now evtchn_bind_virq() and evtchn_bind_ipi() don't have +a need to call check_free_port(). + +This is part of XSA-343. + +Signed-off-by: Jan Beulich +Reviewed-by: Stefano Stabellini +Reviewed-by: Julien Grall +--- +v7: Drop optimization from evtchn_reset(). +v6: Fix loop exit condition in evtchn_reset(). Use {read,write}_atomic() + also for xen_evtchns. +v5: Move increment in alloc_unbound_xen_event_channel() out of the inner + locked region. +v4: Account for Xen internal ports. +v3: Document intended access next to new struct field. +v2: Add comment to check_free_port(). Drop commented out calls. + +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -188,6 +188,8 @@ int evtchn_allocate_port(struct domain * + write_atomic(&d->valid_evtchns, d->valid_evtchns + EVTCHNS_PER_BUCKET); + } + ++ write_atomic(&d->active_evtchns, d->active_evtchns + 1); ++ + return 0; + } + +@@ -211,11 +213,26 @@ static int get_free_port(struct domain * + return -ENOSPC; + } + ++/* ++ * Check whether a port is still marked free, and if so update the domain ++ * counter accordingly. To be used on function exit paths. ++ */ ++static void check_free_port(struct domain *d, evtchn_port_t port) ++{ ++ if ( port_is_valid(d, port) && ++ evtchn_from_port(d, port)->state == ECS_FREE ) ++ write_atomic(&d->active_evtchns, d->active_evtchns - 1); ++} ++ + void evtchn_free(struct domain *d, struct evtchn *chn) + { + /* Clear pending event to avoid unexpected behavior on re-bind. */ + evtchn_port_clear_pending(d, chn); + ++ if ( consumer_is_xen(chn) ) ++ write_atomic(&d->xen_evtchns, d->xen_evtchns - 1); ++ write_atomic(&d->active_evtchns, d->active_evtchns - 1); ++ + /* Reset binding to vcpu0 when the channel is freed. */ + chn->state = ECS_FREE; + chn->notify_vcpu_id = 0; +@@ -258,6 +275,7 @@ static long evtchn_alloc_unbound(evtchn_ + alloc->port = port; + + out: ++ check_free_port(d, port); + spin_unlock(&d->event_lock); + rcu_unlock_domain(d); + +@@ -351,6 +369,7 @@ static long evtchn_bind_interdomain(evtc + bind->local_port = lport; + + out: ++ check_free_port(ld, lport); + spin_unlock(&ld->event_lock); + if ( ld != rd ) + spin_unlock(&rd->event_lock); +@@ -488,7 +507,7 @@ static long evtchn_bind_pirq(evtchn_bind + struct domain *d = current->domain; + struct vcpu *v = d->vcpu[0]; + struct pirq *info; +- int port, pirq = bind->pirq; ++ int port = 0, pirq = bind->pirq; + long rc; + + if ( (pirq < 0) || (pirq >= d->nr_pirqs) ) +@@ -536,6 +555,7 @@ static long evtchn_bind_pirq(evtchn_bind + arch_evtchn_bind_pirq(d, pirq); + + out: ++ check_free_port(d, port); + spin_unlock(&d->event_lock); + + return rc; +@@ -1011,10 +1031,10 @@ int evtchn_unmask(unsigned int port) + return 0; + } + +- + int evtchn_reset(struct domain *d) + { + unsigned int i; ++ int rc = 0; + + if ( d != current->domain && !d->controller_pause_count ) + return -EINVAL; +@@ -1024,7 +1044,9 @@ int evtchn_reset(struct domain *d) + + spin_lock(&d->event_lock); + +- if ( d->evtchn_fifo ) ++ if ( d->active_evtchns > d->xen_evtchns ) ++ rc = -EAGAIN; ++ else if ( d->evtchn_fifo ) + { + /* Switching back to 2-level ABI. */ + evtchn_fifo_destroy(d); +@@ -1033,7 +1055,7 @@ int evtchn_reset(struct domain *d) + + spin_unlock(&d->event_lock); + +- return 0; ++ return rc; + } + + static long evtchn_set_priority(const struct evtchn_set_priority *set_priority) +@@ -1219,10 +1241,9 @@ int alloc_unbound_xen_event_channel( + + spin_lock(&ld->event_lock); + +- rc = get_free_port(ld); ++ port = rc = get_free_port(ld); + if ( rc < 0 ) + goto out; +- port = rc; + chn = evtchn_from_port(ld, port); + + rc = xsm_evtchn_unbound(XSM_TARGET, ld, chn, remote_domid); +@@ -1238,7 +1259,10 @@ int alloc_unbound_xen_event_channel( + + spin_unlock(&chn->lock); + ++ write_atomic(&ld->xen_evtchns, ld->xen_evtchns + 1); ++ + out: ++ check_free_port(ld, port); + spin_unlock(&ld->event_lock); + + return rc < 0 ? rc : port; +@@ -1314,6 +1338,7 @@ int evtchn_init(struct domain *d, unsign + return -EINVAL; + } + evtchn_from_port(d, 0)->state = ECS_RESERVED; ++ write_atomic(&d->active_evtchns, 0); + + #if MAX_VIRT_CPUS > BITS_PER_LONG + d->poll_mask = xzalloc_array(unsigned long, BITS_TO_LONGS(d->max_vcpus)); +@@ -1340,6 +1365,8 @@ void evtchn_destroy(struct domain *d) + for ( i = 0; port_is_valid(d, i); i++ ) + evtchn_close(d, i, 0); + ++ ASSERT(!d->active_evtchns); ++ + clear_global_virq_handlers(d); + + evtchn_fifo_destroy(d); +--- a/xen/include/xen/sched.h ++++ b/xen/include/xen/sched.h +@@ -361,6 +361,16 @@ struct domain + struct evtchn **evtchn_group[NR_EVTCHN_GROUPS]; /* all other buckets */ + unsigned int max_evtchn_port; /* max permitted port number */ + unsigned int valid_evtchns; /* number of allocated event channels */ ++ /* ++ * Number of in-use event channels. Writers should use write_atomic(). ++ * Readers need to use read_atomic() only when not holding event_lock. ++ */ ++ unsigned int active_evtchns; ++ /* ++ * Number of event channels used internally by Xen (not subject to ++ * EVTCHNOP_reset). Read/write access like for active_evtchns. ++ */ ++ unsigned int xen_evtchns; + spinlock_t event_lock; + const struct evtchn_port_ops *evtchn_port_ops; + struct evtchn_fifo_domain *evtchn_fifo; diff --git a/xsa343-2.patch b/xsa343-2.patch new file mode 100644 index 0000000..b8eb499 --- /dev/null +++ b/xsa343-2.patch @@ -0,0 +1,295 @@ +From: Jan Beulich +Subject: evtchn: convert per-channel lock to be IRQ-safe + +... in order for send_guest_{global,vcpu}_virq() to be able to make use +of it. + +This is part of XSA-343. + +Signed-off-by: Jan Beulich +Acked-by: Julien Grall +--- +v6: New. +--- +TBD: This is the "dumb" conversion variant. In a couple of cases the + slightly simpler spin_{,un}lock_irq() could apparently be used. + +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -248,6 +248,7 @@ static long evtchn_alloc_unbound(evtchn_ + int port; + domid_t dom = alloc->dom; + long rc; ++ unsigned long flags; + + d = rcu_lock_domain_by_any_id(dom); + if ( d == NULL ) +@@ -263,14 +264,14 @@ static long evtchn_alloc_unbound(evtchn_ + if ( rc ) + goto out; + +- spin_lock(&chn->lock); ++ spin_lock_irqsave(&chn->lock, flags); + + chn->state = ECS_UNBOUND; + if ( (chn->u.unbound.remote_domid = alloc->remote_dom) == DOMID_SELF ) + chn->u.unbound.remote_domid = current->domain->domain_id; + evtchn_port_init(d, chn); + +- spin_unlock(&chn->lock); ++ spin_unlock_irqrestore(&chn->lock, flags); + + alloc->port = port; + +@@ -283,26 +284,32 @@ static long evtchn_alloc_unbound(evtchn_ + } + + +-static void double_evtchn_lock(struct evtchn *lchn, struct evtchn *rchn) ++static unsigned long double_evtchn_lock(struct evtchn *lchn, ++ struct evtchn *rchn) + { +- if ( lchn < rchn ) ++ unsigned long flags; ++ ++ if ( lchn <= rchn ) + { +- spin_lock(&lchn->lock); +- spin_lock(&rchn->lock); ++ spin_lock_irqsave(&lchn->lock, flags); ++ if ( lchn != rchn ) ++ spin_lock(&rchn->lock); + } + else + { +- if ( lchn != rchn ) +- spin_lock(&rchn->lock); ++ spin_lock_irqsave(&rchn->lock, flags); + spin_lock(&lchn->lock); + } ++ ++ return flags; + } + +-static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn) ++static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn, ++ unsigned long flags) + { +- spin_unlock(&lchn->lock); + if ( lchn != rchn ) +- spin_unlock(&rchn->lock); ++ spin_unlock(&lchn->lock); ++ spin_unlock_irqrestore(&rchn->lock, flags); + } + + static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) +@@ -312,6 +319,7 @@ static long evtchn_bind_interdomain(evtc + int lport, rport = bind->remote_port; + domid_t rdom = bind->remote_dom; + long rc; ++ unsigned long flags; + + if ( rdom == DOMID_SELF ) + rdom = current->domain->domain_id; +@@ -347,7 +355,7 @@ static long evtchn_bind_interdomain(evtc + if ( rc ) + goto out; + +- double_evtchn_lock(lchn, rchn); ++ flags = double_evtchn_lock(lchn, rchn); + + lchn->u.interdomain.remote_dom = rd; + lchn->u.interdomain.remote_port = rport; +@@ -364,7 +372,7 @@ static long evtchn_bind_interdomain(evtc + */ + evtchn_port_set_pending(ld, lchn->notify_vcpu_id, lchn); + +- double_evtchn_unlock(lchn, rchn); ++ double_evtchn_unlock(lchn, rchn, flags); + + bind->local_port = lport; + +@@ -387,6 +395,7 @@ int evtchn_bind_virq(evtchn_bind_virq_t + struct domain *d = current->domain; + int virq = bind->virq, vcpu = bind->vcpu; + int rc = 0; ++ unsigned long flags; + + if ( (virq < 0) || (virq >= ARRAY_SIZE(v->virq_to_evtchn)) ) + return -EINVAL; +@@ -424,14 +433,14 @@ int evtchn_bind_virq(evtchn_bind_virq_t + + chn = evtchn_from_port(d, port); + +- spin_lock(&chn->lock); ++ spin_lock_irqsave(&chn->lock, flags); + + chn->state = ECS_VIRQ; + chn->notify_vcpu_id = vcpu; + chn->u.virq = virq; + evtchn_port_init(d, chn); + +- spin_unlock(&chn->lock); ++ spin_unlock_irqrestore(&chn->lock, flags); + + v->virq_to_evtchn[virq] = bind->port = port; + +@@ -448,6 +457,7 @@ static long evtchn_bind_ipi(evtchn_bind_ + struct domain *d = current->domain; + int port, vcpu = bind->vcpu; + long rc = 0; ++ unsigned long flags; + + if ( domain_vcpu(d, vcpu) == NULL ) + return -ENOENT; +@@ -459,13 +469,13 @@ static long evtchn_bind_ipi(evtchn_bind_ + + chn = evtchn_from_port(d, port); + +- spin_lock(&chn->lock); ++ spin_lock_irqsave(&chn->lock, flags); + + chn->state = ECS_IPI; + chn->notify_vcpu_id = vcpu; + evtchn_port_init(d, chn); + +- spin_unlock(&chn->lock); ++ spin_unlock_irqrestore(&chn->lock, flags); + + bind->port = port; + +@@ -509,6 +519,7 @@ static long evtchn_bind_pirq(evtchn_bind + struct pirq *info; + int port = 0, pirq = bind->pirq; + long rc; ++ unsigned long flags; + + if ( (pirq < 0) || (pirq >= d->nr_pirqs) ) + return -EINVAL; +@@ -541,14 +552,14 @@ static long evtchn_bind_pirq(evtchn_bind + goto out; + } + +- spin_lock(&chn->lock); ++ spin_lock_irqsave(&chn->lock, flags); + + chn->state = ECS_PIRQ; + chn->u.pirq.irq = pirq; + link_pirq_port(port, chn, v); + evtchn_port_init(d, chn); + +- spin_unlock(&chn->lock); ++ spin_unlock_irqrestore(&chn->lock, flags); + + bind->port = port; + +@@ -569,6 +580,7 @@ int evtchn_close(struct domain *d1, int + struct evtchn *chn1, *chn2; + int port2; + long rc = 0; ++ unsigned long flags; + + again: + spin_lock(&d1->event_lock); +@@ -668,14 +680,14 @@ int evtchn_close(struct domain *d1, int + BUG_ON(chn2->state != ECS_INTERDOMAIN); + BUG_ON(chn2->u.interdomain.remote_dom != d1); + +- double_evtchn_lock(chn1, chn2); ++ flags = double_evtchn_lock(chn1, chn2); + + evtchn_free(d1, chn1); + + chn2->state = ECS_UNBOUND; + chn2->u.unbound.remote_domid = d1->domain_id; + +- double_evtchn_unlock(chn1, chn2); ++ double_evtchn_unlock(chn1, chn2, flags); + + goto out; + +@@ -683,9 +695,9 @@ int evtchn_close(struct domain *d1, int + BUG(); + } + +- spin_lock(&chn1->lock); ++ spin_lock_irqsave(&chn1->lock, flags); + evtchn_free(d1, chn1); +- spin_unlock(&chn1->lock); ++ spin_unlock_irqrestore(&chn1->lock, flags); + + out: + if ( d2 != NULL ) +@@ -705,13 +717,14 @@ int evtchn_send(struct domain *ld, unsig + struct evtchn *lchn, *rchn; + struct domain *rd; + int rport, ret = 0; ++ unsigned long flags; + + if ( !port_is_valid(ld, lport) ) + return -EINVAL; + + lchn = evtchn_from_port(ld, lport); + +- spin_lock(&lchn->lock); ++ spin_lock_irqsave(&lchn->lock, flags); + + /* Guest cannot send via a Xen-attached event channel. */ + if ( unlikely(consumer_is_xen(lchn)) ) +@@ -746,7 +759,7 @@ int evtchn_send(struct domain *ld, unsig + } + + out: +- spin_unlock(&lchn->lock); ++ spin_unlock_irqrestore(&lchn->lock, flags); + + return ret; + } +@@ -1238,6 +1251,7 @@ int alloc_unbound_xen_event_channel( + { + struct evtchn *chn; + int port, rc; ++ unsigned long flags; + + spin_lock(&ld->event_lock); + +@@ -1250,14 +1264,14 @@ int alloc_unbound_xen_event_channel( + if ( rc ) + goto out; + +- spin_lock(&chn->lock); ++ spin_lock_irqsave(&chn->lock, flags); + + chn->state = ECS_UNBOUND; + chn->xen_consumer = get_xen_consumer(notification_fn); + chn->notify_vcpu_id = lvcpu; + chn->u.unbound.remote_domid = remote_domid; + +- spin_unlock(&chn->lock); ++ spin_unlock_irqrestore(&chn->lock, flags); + + write_atomic(&ld->xen_evtchns, ld->xen_evtchns + 1); + +@@ -1280,11 +1294,12 @@ void notify_via_xen_event_channel(struct + { + struct evtchn *lchn, *rchn; + struct domain *rd; ++ unsigned long flags; + + ASSERT(port_is_valid(ld, lport)); + lchn = evtchn_from_port(ld, lport); + +- spin_lock(&lchn->lock); ++ spin_lock_irqsave(&lchn->lock, flags); + + if ( likely(lchn->state == ECS_INTERDOMAIN) ) + { +@@ -1294,7 +1309,7 @@ void notify_via_xen_event_channel(struct + evtchn_port_set_pending(rd, rchn->notify_vcpu_id, rchn); + } + +- spin_unlock(&lchn->lock); ++ spin_unlock_irqrestore(&lchn->lock, flags); + } + + void evtchn_check_pollers(struct domain *d, unsigned int port) diff --git a/xsa343-3.patch b/xsa343-3.patch new file mode 100644 index 0000000..e513e30 --- /dev/null +++ b/xsa343-3.patch @@ -0,0 +1,392 @@ +From: Jan Beulich +Subject: evtchn: address races with evtchn_reset() + +Neither d->evtchn_port_ops nor max_evtchns(d) may be used in an entirely +lock-less manner, as both may change by a racing evtchn_reset(). In the +common case, at least one of the domain's event lock or the per-channel +lock needs to be held. In the specific case of the inter-domain sending +by evtchn_send() and notify_via_xen_event_channel() holding the other +side's per-channel lock is sufficient, as the channel can't change state +without both per-channel locks held. Without such a channel changing +state, evtchn_reset() can't complete successfully. + +Lock-free accesses continue to be permitted for the shim (calling some +otherwise internal event channel functions), as this happens while the +domain is in effectively single-threaded mode. Special care also needs +taking for the shim's marking of in-use ports as ECS_RESERVED (allowing +use of such ports in the shim case is okay because switching into and +hence also out of FIFO mode is impossihble there). + +As a side effect, certain operations on Xen bound event channels which +were mistakenly permitted so far (e.g. unmask or poll) will be refused +now. + +This is part of XSA-343. + +Reported-by: Julien Grall +Signed-off-by: Jan Beulich +Acked-by: Julien Grall +--- +v9: Add arch_evtchn_is_special() to fix PV shim. +v8: Add BUILD_BUG_ON() in evtchn_usable(). +v7: Add locking related comment ahead of struct evtchn_port_ops. +v6: New. +--- +TBD: I've been considering to move some of the wrappers from xen/event.h + into event_channel.c (or even drop them altogether), when they + require external locking (e.g. evtchn_port_init() or + evtchn_port_set_priority()). Does anyone have a strong opinion + either way? + +--- a/xen/arch/x86/irq.c ++++ b/xen/arch/x86/irq.c +@@ -2488,14 +2488,24 @@ static void dump_irqs(unsigned char key) + + for ( i = 0; i < action->nr_guests; ) + { ++ struct evtchn *evtchn; ++ unsigned int pending = 2, masked = 2; ++ + d = action->guest[i++]; + pirq = domain_irq_to_pirq(d, irq); + info = pirq_info(d, pirq); ++ evtchn = evtchn_from_port(d, info->evtchn); ++ local_irq_disable(); ++ if ( spin_trylock(&evtchn->lock) ) ++ { ++ pending = evtchn_is_pending(d, evtchn); ++ masked = evtchn_is_masked(d, evtchn); ++ spin_unlock(&evtchn->lock); ++ } ++ local_irq_enable(); + printk("d%d:%3d(%c%c%c)%c", +- d->domain_id, pirq, +- evtchn_port_is_pending(d, info->evtchn) ? 'P' : '-', +- evtchn_port_is_masked(d, info->evtchn) ? 'M' : '-', +- info->masked ? 'M' : '-', ++ d->domain_id, pirq, "-P?"[pending], ++ "-M?"[masked], info->masked ? 'M' : '-', + i < action->nr_guests ? ',' : '\n'); + } + } +--- a/xen/arch/x86/pv/shim.c ++++ b/xen/arch/x86/pv/shim.c +@@ -660,8 +660,11 @@ void pv_shim_inject_evtchn(unsigned int + if ( port_is_valid(guest, port) ) + { + struct evtchn *chn = evtchn_from_port(guest, port); ++ unsigned long flags; + ++ spin_lock_irqsave(&chn->lock, flags); + evtchn_port_set_pending(guest, chn->notify_vcpu_id, chn); ++ spin_unlock_irqrestore(&chn->lock, flags); + } + } + +--- a/xen/common/event_2l.c ++++ b/xen/common/event_2l.c +@@ -63,8 +63,10 @@ static void evtchn_2l_unmask(struct doma + } + } + +-static bool evtchn_2l_is_pending(const struct domain *d, evtchn_port_t port) ++static bool evtchn_2l_is_pending(const struct domain *d, ++ const struct evtchn *evtchn) + { ++ evtchn_port_t port = evtchn->port; + unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); + + ASSERT(port < max_ports); +@@ -72,8 +74,10 @@ static bool evtchn_2l_is_pending(const s + guest_test_bit(d, port, &shared_info(d, evtchn_pending))); + } + +-static bool evtchn_2l_is_masked(const struct domain *d, evtchn_port_t port) ++static bool evtchn_2l_is_masked(const struct domain *d, ++ const struct evtchn *evtchn) + { ++ evtchn_port_t port = evtchn->port; + unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); + + ASSERT(port < max_ports); +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -156,8 +156,9 @@ int evtchn_allocate_port(struct domain * + + if ( port_is_valid(d, port) ) + { +- if ( evtchn_from_port(d, port)->state != ECS_FREE || +- evtchn_port_is_busy(d, port) ) ++ const struct evtchn *chn = evtchn_from_port(d, port); ++ ++ if ( chn->state != ECS_FREE || evtchn_is_busy(d, chn) ) + return -EBUSY; + } + else +@@ -774,6 +775,7 @@ void send_guest_vcpu_virq(struct vcpu *v + unsigned long flags; + int port; + struct domain *d; ++ struct evtchn *chn; + + ASSERT(!virq_is_global(virq)); + +@@ -784,7 +786,10 @@ void send_guest_vcpu_virq(struct vcpu *v + goto out; + + d = v->domain; +- evtchn_port_set_pending(d, v->vcpu_id, evtchn_from_port(d, port)); ++ chn = evtchn_from_port(d, port); ++ spin_lock(&chn->lock); ++ evtchn_port_set_pending(d, v->vcpu_id, chn); ++ spin_unlock(&chn->lock); + + out: + spin_unlock_irqrestore(&v->virq_lock, flags); +@@ -813,7 +818,9 @@ void send_guest_global_virq(struct domai + goto out; + + chn = evtchn_from_port(d, port); ++ spin_lock(&chn->lock); + evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); ++ spin_unlock(&chn->lock); + + out: + spin_unlock_irqrestore(&v->virq_lock, flags); +@@ -823,6 +830,7 @@ void send_guest_pirq(struct domain *d, c + { + int port; + struct evtchn *chn; ++ unsigned long flags; + + /* + * PV guests: It should not be possible to race with __evtchn_close(). The +@@ -837,7 +845,9 @@ void send_guest_pirq(struct domain *d, c + } + + chn = evtchn_from_port(d, port); ++ spin_lock_irqsave(&chn->lock, flags); + evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); ++ spin_unlock_irqrestore(&chn->lock, flags); + } + + static struct domain *global_virq_handlers[NR_VIRQS] __read_mostly; +@@ -1034,12 +1044,15 @@ int evtchn_unmask(unsigned int port) + { + struct domain *d = current->domain; + struct evtchn *evtchn; ++ unsigned long flags; + + if ( unlikely(!port_is_valid(d, port)) ) + return -EINVAL; + + evtchn = evtchn_from_port(d, port); ++ spin_lock_irqsave(&evtchn->lock, flags); + evtchn_port_unmask(d, evtchn); ++ spin_unlock_irqrestore(&evtchn->lock, flags); + + return 0; + } +@@ -1449,8 +1462,8 @@ static void domain_dump_evtchn_info(stru + + printk(" %4u [%d/%d/", + port, +- evtchn_port_is_pending(d, port), +- evtchn_port_is_masked(d, port)); ++ evtchn_is_pending(d, chn), ++ evtchn_is_masked(d, chn)); + evtchn_port_print_state(d, chn); + printk("]: s=%d n=%d x=%d", + chn->state, chn->notify_vcpu_id, chn->xen_consumer); +--- a/xen/common/event_fifo.c ++++ b/xen/common/event_fifo.c +@@ -296,23 +296,26 @@ static void evtchn_fifo_unmask(struct do + evtchn_fifo_set_pending(v, evtchn); + } + +-static bool evtchn_fifo_is_pending(const struct domain *d, evtchn_port_t port) ++static bool evtchn_fifo_is_pending(const struct domain *d, ++ const struct evtchn *evtchn) + { +- const event_word_t *word = evtchn_fifo_word_from_port(d, port); ++ const event_word_t *word = evtchn_fifo_word_from_port(d, evtchn->port); + + return word && guest_test_bit(d, EVTCHN_FIFO_PENDING, word); + } + +-static bool_t evtchn_fifo_is_masked(const struct domain *d, evtchn_port_t port) ++static bool_t evtchn_fifo_is_masked(const struct domain *d, ++ const struct evtchn *evtchn) + { +- const event_word_t *word = evtchn_fifo_word_from_port(d, port); ++ const event_word_t *word = evtchn_fifo_word_from_port(d, evtchn->port); + + return !word || guest_test_bit(d, EVTCHN_FIFO_MASKED, word); + } + +-static bool_t evtchn_fifo_is_busy(const struct domain *d, evtchn_port_t port) ++static bool_t evtchn_fifo_is_busy(const struct domain *d, ++ const struct evtchn *evtchn) + { +- const event_word_t *word = evtchn_fifo_word_from_port(d, port); ++ const event_word_t *word = evtchn_fifo_word_from_port(d, evtchn->port); + + return word && guest_test_bit(d, EVTCHN_FIFO_LINKED, word); + } +--- a/xen/include/asm-x86/event.h ++++ b/xen/include/asm-x86/event.h +@@ -47,4 +47,10 @@ static inline bool arch_virq_is_global(u + return true; + } + ++#ifdef CONFIG_PV_SHIM ++# include ++# define arch_evtchn_is_special(chn) \ ++ (pv_shim && (chn)->port && (chn)->state == ECS_RESERVED) ++#endif ++ + #endif +--- a/xen/include/xen/event.h ++++ b/xen/include/xen/event.h +@@ -133,6 +133,24 @@ static inline struct evtchn *evtchn_from + return bucket_from_port(d, p) + (p % EVTCHNS_PER_BUCKET); + } + ++/* ++ * "usable" as in "by a guest", i.e. Xen consumed channels are assumed to be ++ * taken care of separately where used for Xen's internal purposes. ++ */ ++static bool evtchn_usable(const struct evtchn *evtchn) ++{ ++ if ( evtchn->xen_consumer ) ++ return false; ++ ++#ifdef arch_evtchn_is_special ++ if ( arch_evtchn_is_special(evtchn) ) ++ return true; ++#endif ++ ++ BUILD_BUG_ON(ECS_FREE > ECS_RESERVED); ++ return evtchn->state > ECS_RESERVED; ++} ++ + /* Wait on a Xen-attached event channel. */ + #define wait_on_xen_event_channel(port, condition) \ + do { \ +@@ -165,19 +183,24 @@ int evtchn_reset(struct domain *d); + + /* + * Low-level event channel port ops. ++ * ++ * All hooks have to be called with a lock held which prevents the channel ++ * from changing state. This may be the domain event lock, the per-channel ++ * lock, or in the case of sending interdomain events also the other side's ++ * per-channel lock. Exceptions apply in certain cases for the PV shim. + */ + struct evtchn_port_ops { + void (*init)(struct domain *d, struct evtchn *evtchn); + void (*set_pending)(struct vcpu *v, struct evtchn *evtchn); + void (*clear_pending)(struct domain *d, struct evtchn *evtchn); + void (*unmask)(struct domain *d, struct evtchn *evtchn); +- bool (*is_pending)(const struct domain *d, evtchn_port_t port); +- bool (*is_masked)(const struct domain *d, evtchn_port_t port); ++ bool (*is_pending)(const struct domain *d, const struct evtchn *evtchn); ++ bool (*is_masked)(const struct domain *d, const struct evtchn *evtchn); + /* + * Is the port unavailable because it's still being cleaned up + * after being closed? + */ +- bool (*is_busy)(const struct domain *d, evtchn_port_t port); ++ bool (*is_busy)(const struct domain *d, const struct evtchn *evtchn); + int (*set_priority)(struct domain *d, struct evtchn *evtchn, + unsigned int priority); + void (*print_state)(struct domain *d, const struct evtchn *evtchn); +@@ -193,38 +216,67 @@ static inline void evtchn_port_set_pendi + unsigned int vcpu_id, + struct evtchn *evtchn) + { +- d->evtchn_port_ops->set_pending(d->vcpu[vcpu_id], evtchn); ++ if ( evtchn_usable(evtchn) ) ++ d->evtchn_port_ops->set_pending(d->vcpu[vcpu_id], evtchn); + } + + static inline void evtchn_port_clear_pending(struct domain *d, + struct evtchn *evtchn) + { +- d->evtchn_port_ops->clear_pending(d, evtchn); ++ if ( evtchn_usable(evtchn) ) ++ d->evtchn_port_ops->clear_pending(d, evtchn); + } + + static inline void evtchn_port_unmask(struct domain *d, + struct evtchn *evtchn) + { +- d->evtchn_port_ops->unmask(d, evtchn); ++ if ( evtchn_usable(evtchn) ) ++ d->evtchn_port_ops->unmask(d, evtchn); + } + +-static inline bool evtchn_port_is_pending(const struct domain *d, +- evtchn_port_t port) ++static inline bool evtchn_is_pending(const struct domain *d, ++ const struct evtchn *evtchn) + { +- return d->evtchn_port_ops->is_pending(d, port); ++ return evtchn_usable(evtchn) && d->evtchn_port_ops->is_pending(d, evtchn); + } + +-static inline bool evtchn_port_is_masked(const struct domain *d, +- evtchn_port_t port) ++static inline bool evtchn_port_is_pending(struct domain *d, evtchn_port_t port) + { +- return d->evtchn_port_ops->is_masked(d, port); ++ struct evtchn *evtchn = evtchn_from_port(d, port); ++ bool rc; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&evtchn->lock, flags); ++ rc = evtchn_is_pending(d, evtchn); ++ spin_unlock_irqrestore(&evtchn->lock, flags); ++ ++ return rc; ++} ++ ++static inline bool evtchn_is_masked(const struct domain *d, ++ const struct evtchn *evtchn) ++{ ++ return !evtchn_usable(evtchn) || d->evtchn_port_ops->is_masked(d, evtchn); ++} ++ ++static inline bool evtchn_port_is_masked(struct domain *d, evtchn_port_t port) ++{ ++ struct evtchn *evtchn = evtchn_from_port(d, port); ++ bool rc; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&evtchn->lock, flags); ++ rc = evtchn_is_masked(d, evtchn); ++ spin_unlock_irqrestore(&evtchn->lock, flags); ++ ++ return rc; + } + +-static inline bool evtchn_port_is_busy(const struct domain *d, +- evtchn_port_t port) ++static inline bool evtchn_is_busy(const struct domain *d, ++ const struct evtchn *evtchn) + { + return d->evtchn_port_ops->is_busy && +- d->evtchn_port_ops->is_busy(d, port); ++ d->evtchn_port_ops->is_busy(d, evtchn); + } + + static inline int evtchn_port_set_priority(struct domain *d, +@@ -233,6 +285,8 @@ static inline int evtchn_port_set_priori + { + if ( !d->evtchn_port_ops->set_priority ) + return -ENOSYS; ++ if ( !evtchn_usable(evtchn) ) ++ return -EACCES; + return d->evtchn_port_ops->set_priority(d, evtchn, priority); + } + diff --git a/xsa344-1.patch b/xsa344-1.patch new file mode 100644 index 0000000..3834075 --- /dev/null +++ b/xsa344-1.patch @@ -0,0 +1,137 @@ +From: Jan Beulich +Subject: evtchn: arrange for preemption in evtchn_destroy() + +Especially closing of fully established interdomain channels can take +quite some time, due to the locking involved. Therefore we shouldn't +assume we can clean up still active ports all in one go. Besides adding +the necessary preemption check, also avoid pointlessly starting from +(or now really ending at) 0; 1 is the lowest numbered port which may +need closing. + +Since we're now reducing ->valid_evtchns, free_xen_event_channel(), +and (at least to be on the safe side) notify_via_xen_event_channel() +need to cope with attempts to close / unbind from / send through already +closed (and no longer valid, as per port_is_valid()) ports. + +This is part of XSA-344. + +Signed-off-by: Jan Beulich +Acked-by: Julien Grall +Reviewed-by: Stefano Stabellini +--- +v8: Drop ->active->evtchns part of main loop condition. Drop BUG_ON() + conversion in evtchn_close(). +v7: Comment the barriers added in v6. +v6: Add barriers ahead of new ->is_dying checks. +v5: Also adjust BUG_ON() in evtchn_close(). +v4: New. + +--- a/xen/common/domain.c ++++ b/xen/common/domain.c +@@ -715,12 +715,14 @@ int domain_kill(struct domain *d) + return domain_kill(d); + d->is_dying = DOMDYING_dying; + argo_destroy(d); +- evtchn_destroy(d); + gnttab_release_mappings(d); + vnuma_destroy(d->vnuma); + domain_set_outstanding_pages(d, 0); + /* fallthrough */ + case DOMDYING_dying: ++ rc = evtchn_destroy(d); ++ if ( rc ) ++ break; + rc = domain_relinquish_resources(d); + if ( rc != 0 ) + break; +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -1297,7 +1297,16 @@ int alloc_unbound_xen_event_channel( + + void free_xen_event_channel(struct domain *d, int port) + { +- BUG_ON(!port_is_valid(d, port)); ++ if ( !port_is_valid(d, port) ) ++ { ++ /* ++ * Make sure ->is_dying is read /after/ ->valid_evtchns, pairing ++ * with the spin_barrier() and BUG_ON() in evtchn_destroy(). ++ */ ++ smp_rmb(); ++ BUG_ON(!d->is_dying); ++ return; ++ } + + evtchn_close(d, port, 0); + } +@@ -1309,7 +1318,17 @@ void notify_via_xen_event_channel(struct + struct domain *rd; + unsigned long flags; + +- ASSERT(port_is_valid(ld, lport)); ++ if ( !port_is_valid(ld, lport) ) ++ { ++ /* ++ * Make sure ->is_dying is read /after/ ->valid_evtchns, pairing ++ * with the spin_barrier() and BUG_ON() in evtchn_destroy(). ++ */ ++ smp_rmb(); ++ ASSERT(ld->is_dying); ++ return; ++ } ++ + lchn = evtchn_from_port(ld, lport); + + spin_lock_irqsave(&lchn->lock, flags); +@@ -1380,8 +1399,7 @@ int evtchn_init(struct domain *d, unsign + return 0; + } + +- +-void evtchn_destroy(struct domain *d) ++int evtchn_destroy(struct domain *d) + { + unsigned int i; + +@@ -1390,14 +1408,29 @@ void evtchn_destroy(struct domain *d) + spin_barrier(&d->event_lock); + + /* Close all existing event channels. */ +- for ( i = 0; port_is_valid(d, i); i++ ) ++ for ( i = d->valid_evtchns; --i; ) ++ { + evtchn_close(d, i, 0); + ++ /* ++ * Avoid preempting when called from domain_create()'s error path, ++ * and don't check too often (choice of frequency is arbitrary). ++ */ ++ if ( i && !(i & 0x3f) && d->is_dying != DOMDYING_dead && ++ hypercall_preempt_check() ) ++ { ++ write_atomic(&d->valid_evtchns, i); ++ return -ERESTART; ++ } ++ } ++ + ASSERT(!d->active_evtchns); + + clear_global_virq_handlers(d); + + evtchn_fifo_destroy(d); ++ ++ return 0; + } + + +--- a/xen/include/xen/sched.h ++++ b/xen/include/xen/sched.h +@@ -138,7 +138,7 @@ struct evtchn + } __attribute__((aligned(64))); + + int evtchn_init(struct domain *d, unsigned int max_port); +-void evtchn_destroy(struct domain *d); /* from domain_kill */ ++int evtchn_destroy(struct domain *d); /* from domain_kill */ + void evtchn_destroy_final(struct domain *d); /* from complete_domain_destroy */ + + struct waitqueue_vcpu; diff --git a/xsa344-2.patch b/xsa344-2.patch new file mode 100644 index 0000000..c03344b --- /dev/null +++ b/xsa344-2.patch @@ -0,0 +1,210 @@ +From: Jan Beulich +Subject: evtchn: arrange for preemption in evtchn_reset() + +Like for evtchn_destroy() looping over all possible event channels to +close them can take a significant amount of time. Unlike done there, we +can't alter domain properties (i.e. d->valid_evtchns) here. Borrow, in a +lightweight form, the paging domctl continuation concept, redirecting +the continuations to different sub-ops. Just like there this is to be +able to allow for predictable overall results of the involved sub-ops: +Racing requests should either complete or be refused. + +Note that a domain can't interfere with an already started (by a remote +domain) reset, due to being paused. It can prevent a remote reset from +happening by leaving a reset unfinished, but that's only going to affect +itself. + +This is part of XSA-344. + +Signed-off-by: Jan Beulich +Acked-by: Julien Grall +Reviewed-by: Stefano Stabellini +--- +v9: Undo v7 changes. +v7: Move extension of loop condition in evtchn_reset() here, to match + the earlier patch'es change to evtchn_destroy(). +v6: Also protect the last write of d->next_evtchn. Re-base over changes + to earlier patches. +v4: New. + +--- a/xen/common/domain.c ++++ b/xen/common/domain.c +@@ -1145,7 +1145,7 @@ void domain_unpause_except_self(struct d + domain_unpause(d); + } + +-int domain_soft_reset(struct domain *d) ++int domain_soft_reset(struct domain *d, bool resuming) + { + struct vcpu *v; + int rc; +@@ -1159,7 +1159,7 @@ int domain_soft_reset(struct domain *d) + } + spin_unlock(&d->shutdown_lock); + +- rc = evtchn_reset(d); ++ rc = evtchn_reset(d, resuming); + if ( rc ) + return rc; + +--- a/xen/common/domctl.c ++++ b/xen/common/domctl.c +@@ -477,12 +477,22 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + } + + case XEN_DOMCTL_soft_reset: ++ case XEN_DOMCTL_soft_reset_cont: + if ( d == current->domain ) /* no domain_pause() */ + { + ret = -EINVAL; + break; + } +- ret = domain_soft_reset(d); ++ ret = domain_soft_reset(d, op->cmd == XEN_DOMCTL_soft_reset_cont); ++ if ( ret == -ERESTART ) ++ { ++ op->cmd = XEN_DOMCTL_soft_reset_cont; ++ if ( !__copy_field_to_guest(u_domctl, op, cmd) ) ++ ret = hypercall_create_continuation(__HYPERVISOR_domctl, ++ "h", u_domctl); ++ else ++ ret = -EFAULT; ++ } + break; + + case XEN_DOMCTL_destroydomain: +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -1057,7 +1057,7 @@ int evtchn_unmask(unsigned int port) + return 0; + } + +-int evtchn_reset(struct domain *d) ++int evtchn_reset(struct domain *d, bool resuming) + { + unsigned int i; + int rc = 0; +@@ -1065,11 +1065,40 @@ int evtchn_reset(struct domain *d) + if ( d != current->domain && !d->controller_pause_count ) + return -EINVAL; + +- for ( i = 0; port_is_valid(d, i); i++ ) ++ spin_lock(&d->event_lock); ++ ++ /* ++ * If we are resuming, then start where we stopped. Otherwise, check ++ * that a reset operation is not already in progress, and if none is, ++ * record that this is now the case. ++ */ ++ i = resuming ? d->next_evtchn : !d->next_evtchn; ++ if ( i > d->next_evtchn ) ++ d->next_evtchn = i; ++ ++ spin_unlock(&d->event_lock); ++ ++ if ( !i ) ++ return -EBUSY; ++ ++ for ( ; port_is_valid(d, i); i++ ) ++ { + evtchn_close(d, i, 1); + ++ /* NB: Choice of frequency is arbitrary. */ ++ if ( !(i & 0x3f) && hypercall_preempt_check() ) ++ { ++ spin_lock(&d->event_lock); ++ d->next_evtchn = i; ++ spin_unlock(&d->event_lock); ++ return -ERESTART; ++ } ++ } ++ + spin_lock(&d->event_lock); + ++ d->next_evtchn = 0; ++ + if ( d->active_evtchns > d->xen_evtchns ) + rc = -EAGAIN; + else if ( d->evtchn_fifo ) +@@ -1204,7 +1233,8 @@ long do_event_channel_op(int cmd, XEN_GU + break; + } + +- case EVTCHNOP_reset: { ++ case EVTCHNOP_reset: ++ case EVTCHNOP_reset_cont: { + struct evtchn_reset reset; + struct domain *d; + +@@ -1217,9 +1247,13 @@ long do_event_channel_op(int cmd, XEN_GU + + rc = xsm_evtchn_reset(XSM_TARGET, current->domain, d); + if ( !rc ) +- rc = evtchn_reset(d); ++ rc = evtchn_reset(d, cmd == EVTCHNOP_reset_cont); + + rcu_unlock_domain(d); ++ ++ if ( rc == -ERESTART ) ++ rc = hypercall_create_continuation(__HYPERVISOR_event_channel_op, ++ "ih", EVTCHNOP_reset_cont, arg); + break; + } + +--- a/xen/include/public/domctl.h ++++ b/xen/include/public/domctl.h +@@ -1159,7 +1159,10 @@ struct xen_domctl { + #define XEN_DOMCTL_iomem_permission 20 + #define XEN_DOMCTL_ioport_permission 21 + #define XEN_DOMCTL_hypercall_init 22 +-#define XEN_DOMCTL_arch_setup 23 /* Obsolete IA64 only */ ++#ifdef __XEN__ ++/* #define XEN_DOMCTL_arch_setup 23 Obsolete IA64 only */ ++#define XEN_DOMCTL_soft_reset_cont 23 ++#endif + #define XEN_DOMCTL_settimeoffset 24 + #define XEN_DOMCTL_getvcpuaffinity 25 + #define XEN_DOMCTL_real_mode_area 26 /* Obsolete PPC only */ +--- a/xen/include/public/event_channel.h ++++ b/xen/include/public/event_channel.h +@@ -74,6 +74,9 @@ + #define EVTCHNOP_init_control 11 + #define EVTCHNOP_expand_array 12 + #define EVTCHNOP_set_priority 13 ++#ifdef __XEN__ ++#define EVTCHNOP_reset_cont 14 ++#endif + /* ` } */ + + typedef uint32_t evtchn_port_t; +--- a/xen/include/xen/event.h ++++ b/xen/include/xen/event.h +@@ -171,7 +171,7 @@ void evtchn_check_pollers(struct domain + void evtchn_2l_init(struct domain *d); + + /* Close all event channels and reset to 2-level ABI. */ +-int evtchn_reset(struct domain *d); ++int evtchn_reset(struct domain *d, bool resuming); + + /* + * Low-level event channel port ops. +--- a/xen/include/xen/sched.h ++++ b/xen/include/xen/sched.h +@@ -371,6 +371,8 @@ struct domain + * EVTCHNOP_reset). Read/write access like for active_evtchns. + */ + unsigned int xen_evtchns; ++ /* Port to resume from in evtchn_reset(), when in a continuation. */ ++ unsigned int next_evtchn; + spinlock_t event_lock; + const struct evtchn_port_ops *evtchn_port_ops; + struct evtchn_fifo_domain *evtchn_fifo; +@@ -663,7 +665,7 @@ int domain_kill(struct domain *d); + int domain_shutdown(struct domain *d, u8 reason); + void domain_resume(struct domain *d); + +-int domain_soft_reset(struct domain *d); ++int domain_soft_reset(struct domain *d, bool resuming); + + int vcpu_start_shutdown_deferral(struct vcpu *v); + void vcpu_end_shutdown_deferral(struct vcpu *v); From 205c567efbf20c1f2443cd1cbd9e1bd8e85de035 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 22 Sep 2020 19:27:40 +0100 Subject: [PATCH 048/194] add CVE references --- xen.spec | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/xen.spec b/xen.spec index 15c2a98..360471b 100644 --- a/xen.spec +++ b/xen.spec @@ -949,16 +949,17 @@ fi - race when migrating timers between x86 HVM vCPU-s [XSA-336, CVE-2020-25604] - PCI passthrough code reading back hardware registers [XSA-337, - CVE-2020-25595] + CVE-2020-25595] (#1881587) - once valid event channels may not turn invalid [XSA-338, CVE-2020-25597] + (#1881588) - x86 pv guest kernel DoS via SYSENTER [XSA-339, CVE-2020-25596] - Missing memory barriers when accessing/allocating an event channel [XSA-340, - CVE-2020-25603] + CVE-2020-25603] (#1881583) - out of bounds event channels available to 32-bit x86 domains [XSA-342, - CVE-2020-25600] -- races with evtchn_reset() [XSA-343, CVE-2020-25599] + CVE-2020-25600] (#1881582) +- races with evtchn_reset() [XSA-343, CVE-2020-25599] (#1881581) - lack of preemption in evtchn_reset() / evtchn_destroy() [XSA-344, - CVE-2020-25601] + CVE-2020-25601] (#1881586) * Thu Sep 03 2020 Michael Young - 4.14.0-4 - rebuild for OCaml 4.11.1 From 4cbc5a57f1b9ccf1da9536171bb12e056b405e43 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 22 Sep 2020 19:54:18 +0100 Subject: [PATCH 049/194] add more CVE references --- xen.spec | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/xen.spec b/xen.spec index 360471b..fa2d2c9 100644 --- a/xen.spec +++ b/xen.spec @@ -943,16 +943,17 @@ fi %changelog * Tue Sep 22 2020 Michael Young - 4.14.0-5 - x86 pv: Crash when handling guest access to MSR_MISC_ENABLE [XSA-333, - CVE-2020-25602] + CVE-2020-25602] (#1881619) - Missing unlock in XENMEM_acquire_resource error path [XSA-334, - CVE-2020-25598] + CVE-2020-25598] (#1881616) - race when migrating timers between x86 HVM vCPU-s [XSA-336, - CVE-2020-25604] + CVE-2020-25604] (#1881618) - PCI passthrough code reading back hardware registers [XSA-337, CVE-2020-25595] (#1881587) - once valid event channels may not turn invalid [XSA-338, CVE-2020-25597] (#1881588) - x86 pv guest kernel DoS via SYSENTER [XSA-339, CVE-2020-25596] + (#1881617) - Missing memory barriers when accessing/allocating an event channel [XSA-340, CVE-2020-25603] (#1881583) - out of bounds event channels available to 32-bit x86 domains [XSA-342, From 054f9045addd0dc82c1c1c5c9fb6e65b9f41fff0 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 20 Oct 2020 18:52:27 +0100 Subject: [PATCH 050/194] 3 security updates x86: Race condition in Xen mapping code [XSA-345] undue deferral of IOMMU TLB flushes [XSA-346] unsafe AMD IOMMU page table updates [XSA-347] --- xen.spec | 22 +- ...map_pages_to_xen-to-have-only-a-sing.patch | 94 +++++++ ...modify_xen_mappings-to-have-one-exit.patch | 68 +++++ ...ome-races-in-hypervisor-mapping-upda.patch | 249 ++++++++++++++++++ xsa346-1.patch | 50 ++++ xsa346-2.patch | 204 ++++++++++++++ xsa347-4.14-1.patch | 149 +++++++++++ xsa347-4.14-2.patch | 72 +++++ xsa347-4.14-3.patch | 59 +++++ 9 files changed, 966 insertions(+), 1 deletion(-) create mode 100644 xsa345-4.14-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch create mode 100644 xsa345-4.14-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch create mode 100644 xsa345-4.14-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch create mode 100644 xsa346-1.patch create mode 100644 xsa346-2.patch create mode 100644 xsa347-4.14-1.patch create mode 100644 xsa347-4.14-2.patch create mode 100644 xsa347-4.14-3.patch diff --git a/xen.spec b/xen.spec index fa2d2c9..fb8aad0 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.14.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,14 @@ Patch58: xsa343-2.patch Patch59: xsa343-3.patch Patch60: xsa344-1.patch Patch61: xsa344-2.patch +Patch62: xsa345-4.14-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch +Patch63: xsa345-4.14-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch +Patch64: xsa345-4.14-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch +Patch65: xsa346-1.patch +Patch66: xsa346-2.patch +Patch67: xsa347-4.14-1.patch +Patch68: xsa347-4.14-2.patch +Patch69: xsa347-4.14-3.patch %if %build_qemutrad @@ -343,6 +351,14 @@ 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 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -941,6 +957,10 @@ fi %endif %changelog +* Tue Oct 20 2020 Michael Young - 4.14.0-6 +- x86: Race condition in Xen mapping code [XSA-345] +- undue deferral of IOMMU TLB flushes [XSA-346] +- unsafe AMD IOMMU page table updates [XSA-347] * Tue Sep 22 2020 Michael Young - 4.14.0-5 - x86 pv: Crash when handling guest access to MSR_MISC_ENABLE [XSA-333, CVE-2020-25602] (#1881619) diff --git a/xsa345-4.14-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch b/xsa345-4.14-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch new file mode 100644 index 0000000..847c34a --- /dev/null +++ b/xsa345-4.14-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch @@ -0,0 +1,94 @@ +From e9c5a9ee5e2e888f8bb05cf0a353ed635300abe3 Mon Sep 17 00:00:00 2001 +From: Wei Liu +Date: Sat, 11 Jan 2020 21:57:41 +0000 +Subject: [PATCH 1/3] x86/mm: Refactor map_pages_to_xen to have only a single + exit path + +We will soon need to perform clean-ups before returning. + +No functional change. + +This is part of XSA-345. + +Reported-by: Hongyan Xia +Signed-off-by: Wei Liu +Signed-off-by: Hongyan Xia +Signed-off-by: George Dunlap +Acked-by: Jan Beulich +--- + xen/arch/x86/mm.c | 17 +++++++++++------ + 1 file changed, 11 insertions(+), 6 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 82bc676553..03f6e6aa62 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -5088,6 +5088,7 @@ int map_pages_to_xen( + l2_pgentry_t *pl2e, ol2e; + l1_pgentry_t *pl1e, ol1e; + unsigned int i; ++ int rc = -ENOMEM; + + #define flush_flags(oldf) do { \ + unsigned int o_ = (oldf); \ +@@ -5108,7 +5109,8 @@ int map_pages_to_xen( + l3_pgentry_t ol3e, *pl3e = virt_to_xen_l3e(virt); + + if ( !pl3e ) +- return -ENOMEM; ++ goto out; ++ + ol3e = *pl3e; + + if ( cpu_has_page1gb && +@@ -5198,7 +5200,7 @@ int map_pages_to_xen( + + l2t = alloc_xen_pagetable(); + if ( l2t == NULL ) +- return -ENOMEM; ++ goto out; + + for ( i = 0; i < L2_PAGETABLE_ENTRIES; i++ ) + l2e_write(l2t + i, +@@ -5227,7 +5229,7 @@ int map_pages_to_xen( + + pl2e = virt_to_xen_l2e(virt); + if ( !pl2e ) +- return -ENOMEM; ++ goto out; + + if ( ((((virt >> PAGE_SHIFT) | mfn_x(mfn)) & + ((1u << PAGETABLE_ORDER) - 1)) == 0) && +@@ -5271,7 +5273,7 @@ int map_pages_to_xen( + { + pl1e = virt_to_xen_l1e(virt); + if ( pl1e == NULL ) +- return -ENOMEM; ++ goto out; + } + else if ( l2e_get_flags(*pl2e) & _PAGE_PSE ) + { +@@ -5299,7 +5301,7 @@ int map_pages_to_xen( + + l1t = alloc_xen_pagetable(); + if ( l1t == NULL ) +- return -ENOMEM; ++ goto out; + + for ( i = 0; i < L1_PAGETABLE_ENTRIES; i++ ) + l1e_write(&l1t[i], +@@ -5445,7 +5447,10 @@ int map_pages_to_xen( + + #undef flush_flags + +- return 0; ++ rc = 0; ++ ++ out: ++ return rc; + } + + int populate_pt_range(unsigned long virt, unsigned long nr_mfns) +-- +2.25.1 + diff --git a/xsa345-4.14-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch b/xsa345-4.14-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch new file mode 100644 index 0000000..d31af1c --- /dev/null +++ b/xsa345-4.14-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch @@ -0,0 +1,68 @@ +From 8645adb7ac679e5ddc5c39e0c5c918e4a2ba5391 Mon Sep 17 00:00:00 2001 +From: Wei Liu +Date: Sat, 11 Jan 2020 21:57:42 +0000 +Subject: [PATCH 2/3] x86/mm: Refactor modify_xen_mappings to have one exit + path + +We will soon need to perform clean-ups before returning. + +No functional change. + +This is part of XSA-345. + +Reported-by: Hongyan Xia +Signed-off-by: Wei Liu +Signed-off-by: Hongyan Xia +Signed-off-by: George Dunlap +Acked-by: Jan Beulich +--- + xen/arch/x86/mm.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 03f6e6aa62..2468347a45 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -5477,6 +5477,7 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + l1_pgentry_t *pl1e; + unsigned int i; + unsigned long v = s; ++ int rc = -ENOMEM; + + /* Set of valid PTE bits which may be altered. */ + #define FLAGS_MASK (_PAGE_NX|_PAGE_DIRTY|_PAGE_ACCESSED|_PAGE_RW|_PAGE_PRESENT) +@@ -5520,7 +5521,8 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + /* PAGE1GB: shatter the superpage and fall through. */ + l2t = alloc_xen_pagetable(); + if ( !l2t ) +- return -ENOMEM; ++ goto out; ++ + for ( i = 0; i < L2_PAGETABLE_ENTRIES; i++ ) + l2e_write(l2t + i, + l2e_from_pfn(l3e_get_pfn(*pl3e) + +@@ -5577,7 +5579,8 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + /* PSE: shatter the superpage and try again. */ + l1t = alloc_xen_pagetable(); + if ( !l1t ) +- return -ENOMEM; ++ goto out; ++ + for ( i = 0; i < L1_PAGETABLE_ENTRIES; i++ ) + l1e_write(&l1t[i], + l1e_from_pfn(l2e_get_pfn(*pl2e) + i, +@@ -5710,7 +5713,10 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + flush_area(NULL, FLUSH_TLB_GLOBAL); + + #undef FLAGS_MASK +- return 0; ++ rc = 0; ++ ++ out: ++ return rc; + } + + #undef flush_area +-- +2.25.1 + diff --git a/xsa345-4.14-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch b/xsa345-4.14-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch new file mode 100644 index 0000000..2e4dd6f --- /dev/null +++ b/xsa345-4.14-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch @@ -0,0 +1,249 @@ +From 6b020418d0554d9ec6eb201f50776a72db67739b Mon Sep 17 00:00:00 2001 +From: Hongyan Xia +Date: Sat, 11 Jan 2020 21:57:43 +0000 +Subject: [PATCH 3/3] x86/mm: Prevent some races in hypervisor mapping updates + +map_pages_to_xen will attempt to coalesce mappings into 2MiB and 1GiB +superpages if possible, to maximize TLB efficiency. This means both +replacing superpage entries with smaller entries, and replacing +smaller entries with superpages. + +Unfortunately, while some potential races are handled correctly, +others are not. These include: + +1. When one processor modifies a sub-superpage mapping while another +processor replaces the entire range with a superpage. + +Take the following example: + +Suppose L3[N] points to L2. And suppose we have two processors, A and +B. + +* A walks the pagetables, get a pointer to L2. +* B replaces L3[N] with a 1GiB mapping. +* B Frees L2 +* A writes L2[M] # + +This is race exacerbated by the fact that virt_to_xen_l[21]e doesn't +handle higher-level superpages properly: If you call virt_xen_to_l2e +on a virtual address within an L3 superpage, you'll either hit a BUG() +(most likely), or get a pointer into the middle of a data page; same +with virt_xen_to_l1 on a virtual address within either an L3 or L2 +superpage. + +So take the following example: + +* A reads pl3e and discovers it to point to an L2. +* B replaces L3[N] with a 1GiB mapping +* A calls virt_to_xen_l2e() and hits the BUG_ON() # + +2. When two processors simultaneously try to replace a sub-superpage +mapping with a superpage mapping. + +Take the following example: + +Suppose L3[N] points to L2. And suppose we have two processors, A and B, +both trying to replace L3[N] with a superpage. + +* A walks the pagetables, get a pointer to pl3e, and takes a copy ol3e pointing to L2. +* B walks the pagetables, gets a pointre to pl3e, and takes a copy ol3e pointing to L2. +* A writes the new value into L3[N] +* B writes the new value into L3[N] +* A recursively frees all the L1's under L2, then frees L2 +* B recursively double-frees all the L1's under L2, then double-frees L2 # + +Fix this by grabbing a lock for the entirety of the mapping update +operation. + +Rather than grabbing map_pgdir_lock for the entire operation, however, +repurpose the PGT_locked bit from L3's page->type_info as a lock. +This means that rather than locking the entire address space, we +"only" lock a single 512GiB chunk of hypervisor address space at a +time. + +There was a proposal for a lock-and-reverify approach, where we walk +the pagetables to the point where we decide what to do; then grab the +map_pgdir_lock, re-verify the information we collected without the +lock, and finally make the change (starting over again if anything had +changed). Without being able to guarantee that the L2 table wasn't +freed, however, that means every read would need to be considered +potentially unsafe. Thinking carefully about that is probably +something that wants to be done on public, not under time pressure. + +This is part of XSA-345. + +Reported-by: Hongyan Xia +Signed-off-by: Hongyan Xia +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +--- + xen/arch/x86/mm.c | 92 +++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 89 insertions(+), 3 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 2468347a45..9c55b2b9e3 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -2088,6 +2088,50 @@ void page_unlock(struct page_info *page) + current_locked_page_set(NULL); + } + ++/* ++ * L3 table locks: ++ * ++ * Used for serialization in map_pages_to_xen() and modify_xen_mappings(). ++ * ++ * For Xen PT pages, the page->u.inuse.type_info is unused and it is safe to ++ * reuse the PGT_locked flag. This lock is taken only when we move down to L3 ++ * tables and below, since L4 (and above, for 5-level paging) is still globally ++ * protected by map_pgdir_lock. ++ * ++ * PV MMU update hypercalls call map_pages_to_xen while holding a page's page_lock(). ++ * This has two implications: ++ * - We cannot reuse reuse current_locked_page_* for debugging ++ * - To avoid the chance of deadlock, even for different pages, we ++ * must never grab page_lock() after grabbing l3t_lock(). This ++ * includes any page_lock()-based locks, such as ++ * mem_sharing_page_lock(). ++ * ++ * Also note that we grab the map_pgdir_lock while holding the ++ * l3t_lock(), so to avoid deadlock we must avoid grabbing them in ++ * reverse order. ++ */ ++static void l3t_lock(struct page_info *page) ++{ ++ unsigned long x, nx; ++ ++ do { ++ while ( (x = page->u.inuse.type_info) & PGT_locked ) ++ cpu_relax(); ++ nx = x | PGT_locked; ++ } while ( cmpxchg(&page->u.inuse.type_info, x, nx) != x ); ++} ++ ++static void l3t_unlock(struct page_info *page) ++{ ++ unsigned long x, nx, y = page->u.inuse.type_info; ++ ++ do { ++ x = y; ++ BUG_ON(!(x & PGT_locked)); ++ nx = x & ~PGT_locked; ++ } while ( (y = cmpxchg(&page->u.inuse.type_info, x, nx)) != x ); ++} ++ + #ifdef CONFIG_PV + /* + * PTE flags that a guest may change without re-validating the PTE. +@@ -5078,6 +5122,23 @@ l1_pgentry_t *virt_to_xen_l1e(unsigned long v) + flush_area_local((const void *)v, f) : \ + flush_area_all((const void *)v, f)) + ++#define L3T_INIT(page) (page) = ZERO_BLOCK_PTR ++ ++#define L3T_LOCK(page) \ ++ do { \ ++ if ( locking ) \ ++ l3t_lock(page); \ ++ } while ( false ) ++ ++#define L3T_UNLOCK(page) \ ++ do { \ ++ if ( locking && (page) != ZERO_BLOCK_PTR ) \ ++ { \ ++ l3t_unlock(page); \ ++ (page) = ZERO_BLOCK_PTR; \ ++ } \ ++ } while ( false ) ++ + int map_pages_to_xen( + unsigned long virt, + mfn_t mfn, +@@ -5089,6 +5150,7 @@ int map_pages_to_xen( + l1_pgentry_t *pl1e, ol1e; + unsigned int i; + int rc = -ENOMEM; ++ struct page_info *current_l3page; + + #define flush_flags(oldf) do { \ + unsigned int o_ = (oldf); \ +@@ -5104,13 +5166,20 @@ int map_pages_to_xen( + } \ + } while (0) + ++ L3T_INIT(current_l3page); ++ + while ( nr_mfns != 0 ) + { +- l3_pgentry_t ol3e, *pl3e = virt_to_xen_l3e(virt); ++ l3_pgentry_t *pl3e, ol3e; + ++ L3T_UNLOCK(current_l3page); ++ ++ pl3e = virt_to_xen_l3e(virt); + if ( !pl3e ) + goto out; + ++ current_l3page = virt_to_page(pl3e); ++ L3T_LOCK(current_l3page); + ol3e = *pl3e; + + if ( cpu_has_page1gb && +@@ -5450,6 +5519,7 @@ int map_pages_to_xen( + rc = 0; + + out: ++ L3T_UNLOCK(current_l3page); + return rc; + } + +@@ -5478,6 +5548,7 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + unsigned int i; + unsigned long v = s; + int rc = -ENOMEM; ++ struct page_info *current_l3page; + + /* Set of valid PTE bits which may be altered. */ + #define FLAGS_MASK (_PAGE_NX|_PAGE_DIRTY|_PAGE_ACCESSED|_PAGE_RW|_PAGE_PRESENT) +@@ -5486,11 +5557,22 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + ASSERT(IS_ALIGNED(s, PAGE_SIZE)); + ASSERT(IS_ALIGNED(e, PAGE_SIZE)); + ++ L3T_INIT(current_l3page); ++ + while ( v < e ) + { +- l3_pgentry_t *pl3e = virt_to_xen_l3e(v); ++ l3_pgentry_t *pl3e; ++ ++ L3T_UNLOCK(current_l3page); + +- if ( !pl3e || !(l3e_get_flags(*pl3e) & _PAGE_PRESENT) ) ++ pl3e = virt_to_xen_l3e(v); ++ if ( !pl3e ) ++ goto out; ++ ++ current_l3page = virt_to_page(pl3e); ++ L3T_LOCK(current_l3page); ++ ++ if ( !(l3e_get_flags(*pl3e) & _PAGE_PRESENT) ) + { + /* Confirm the caller isn't trying to create new mappings. */ + ASSERT(!(nf & _PAGE_PRESENT)); +@@ -5716,9 +5798,13 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + rc = 0; + + out: ++ L3T_UNLOCK(current_l3page); + return rc; + } + ++#undef L3T_LOCK ++#undef L3T_UNLOCK ++ + #undef flush_area + + int destroy_xen_mappings(unsigned long s, unsigned long e) +-- +2.25.1 + diff --git a/xsa346-1.patch b/xsa346-1.patch new file mode 100644 index 0000000..3ffeb65 --- /dev/null +++ b/xsa346-1.patch @@ -0,0 +1,50 @@ +From: Jan Beulich +Subject: IOMMU: suppress "iommu_dont_flush_iotlb" when about to free a page + +Deferring flushes to a single, wide range one - as is done when +handling XENMAPSPACE_gmfn_range - is okay only as long as +pages don't get freed ahead of the eventual flush. While the only +function setting the flag (xenmem_add_to_physmap()) suggests by its name +that it's only mapping new entries, in reality the way +xenmem_add_to_physmap_one() works means an unmap would happen not only +for the page being moved (but not freed) but, if the destination GFN is +populated, also for the page being displaced from that GFN. Collapsing +the two flushes for this GFN into just one (end even more so deferring +it to a batched invocation) is not correct. + +This is part of XSA-346. + +Fixes: cf95b2a9fd5a ("iommu: Introduce per cpu flag (iommu_dont_flush_iotlb) to avoid unnecessary iotlb... ") +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant +Acked-by: Julien Grall + +--- a/xen/common/memory.c ++++ b/xen/common/memory.c +@@ -293,6 +293,7 @@ int guest_remove_page(struct domain *d, + p2m_type_t p2mt; + #endif + mfn_t mfn; ++ bool *dont_flush_p, dont_flush; + int rc; + + #ifdef CONFIG_X86 +@@ -379,8 +380,18 @@ int guest_remove_page(struct domain *d, + return -ENXIO; + } + ++ /* ++ * Since we're likely to free the page below, we need to suspend ++ * xenmem_add_to_physmap()'s suppressing of IOMMU TLB flushes. ++ */ ++ dont_flush_p = &this_cpu(iommu_dont_flush_iotlb); ++ dont_flush = *dont_flush_p; ++ *dont_flush_p = false; ++ + rc = guest_physmap_remove_page(d, _gfn(gmfn), mfn, 0); + ++ *dont_flush_p = dont_flush; ++ + /* + * With the lack of an IOMMU on some platforms, domains with DMA-capable + * device must retrieve the same pfn when the hypercall populate_physmap diff --git a/xsa346-2.patch b/xsa346-2.patch new file mode 100644 index 0000000..630496a --- /dev/null +++ b/xsa346-2.patch @@ -0,0 +1,204 @@ +From: Jan Beulich +Subject: IOMMU: hold page ref until after deferred TLB flush + +When moving around a page via XENMAPSPACE_gmfn_range, deferring the TLB +flush for the "from" GFN range requires that the page remains allocated +to the guest until the TLB flush has actually occurred. Otherwise a +parallel hypercall to remove the page would only flush the TLB for the +GFN it has been moved to, but not the one is was mapped at originally. + +This is part of XSA-346. + +Fixes: cf95b2a9fd5a ("iommu: Introduce per cpu flag (iommu_dont_flush_iotlb) to avoid unnecessary iotlb... ") +Reported-by: Julien Grall +Signed-off-by: Jan Beulich +Acked-by: Julien Grall + +--- a/xen/arch/arm/mm.c ++++ b/xen/arch/arm/mm.c +@@ -1407,7 +1407,7 @@ void share_xen_page_with_guest(struct pa + int xenmem_add_to_physmap_one( + struct domain *d, + unsigned int space, +- union xen_add_to_physmap_batch_extra extra, ++ union add_to_physmap_extra extra, + unsigned long idx, + gfn_t gfn) + { +@@ -1480,10 +1480,6 @@ int xenmem_add_to_physmap_one( + break; + } + case XENMAPSPACE_dev_mmio: +- /* extra should be 0. Reserved for future use. */ +- if ( extra.res0 ) +- return -EOPNOTSUPP; +- + rc = map_dev_mmio_region(d, gfn, 1, _mfn(idx)); + return rc; + +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -4497,7 +4497,7 @@ static int handle_iomem_range(unsigned l + int xenmem_add_to_physmap_one( + struct domain *d, + unsigned int space, +- union xen_add_to_physmap_batch_extra extra, ++ union add_to_physmap_extra extra, + unsigned long idx, + gfn_t gpfn) + { +@@ -4581,9 +4581,20 @@ int xenmem_add_to_physmap_one( + rc = guest_physmap_add_page(d, gpfn, mfn, PAGE_ORDER_4K); + + put_both: +- /* In the XENMAPSPACE_gmfn case, we took a ref of the gfn at the top. */ ++ /* ++ * 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 ) ++ { + put_gfn(d, gfn); ++ if ( !rc && extra.ppage ) ++ { ++ *extra.ppage = page; ++ page = NULL; ++ } ++ } + + if ( page ) + put_page(page); +--- a/xen/common/memory.c ++++ b/xen/common/memory.c +@@ -815,13 +815,12 @@ int xenmem_add_to_physmap(struct domain + { + unsigned int done = 0; + long rc = 0; +- union xen_add_to_physmap_batch_extra extra; ++ union add_to_physmap_extra extra = {}; ++ struct page_info *pages[16]; + + ASSERT(paging_mode_translate(d)); + +- if ( xatp->space != XENMAPSPACE_gmfn_foreign ) +- extra.res0 = 0; +- else ++ if ( xatp->space == XENMAPSPACE_gmfn_foreign ) + extra.foreign_domid = DOMID_INVALID; + + if ( xatp->space != XENMAPSPACE_gmfn_range ) +@@ -836,7 +835,10 @@ int xenmem_add_to_physmap(struct domain + xatp->size -= start; + + if ( is_iommu_enabled(d) ) ++ { + this_cpu(iommu_dont_flush_iotlb) = 1; ++ extra.ppage = &pages[0]; ++ } + + while ( xatp->size > done ) + { +@@ -848,8 +850,12 @@ int xenmem_add_to_physmap(struct domain + xatp->idx++; + xatp->gpfn++; + ++ if ( extra.ppage ) ++ ++extra.ppage; ++ + /* Check for continuation if it's not the last iteration. */ +- if ( xatp->size > ++done && hypercall_preempt_check() ) ++ if ( (++done > ARRAY_SIZE(pages) && extra.ppage) || ++ (xatp->size > done && hypercall_preempt_check()) ) + { + rc = start + done; + break; +@@ -859,6 +865,7 @@ int xenmem_add_to_physmap(struct domain + if ( is_iommu_enabled(d) ) + { + int ret; ++ unsigned int i; + + this_cpu(iommu_dont_flush_iotlb) = 0; + +@@ -867,6 +874,15 @@ int xenmem_add_to_physmap(struct domain + if ( unlikely(ret) && rc >= 0 ) + rc = ret; + ++ /* ++ * Now that the IOMMU TLB flush was done for the original GFN, drop ++ * the page references. The 2nd flush below is fine to make later, as ++ * whoever removes the page again from its new GFN will have to do ++ * another flush anyway. ++ */ ++ for ( i = 0; i < done; ++i ) ++ put_page(pages[i]); ++ + ret = iommu_iotlb_flush(d, _dfn(xatp->gpfn - done), done, + IOMMU_FLUSHF_added | IOMMU_FLUSHF_modified); + if ( unlikely(ret) && rc >= 0 ) +@@ -880,6 +896,8 @@ static int xenmem_add_to_physmap_batch(s + struct xen_add_to_physmap_batch *xatpb, + unsigned int extent) + { ++ union add_to_physmap_extra extra = {}; ++ + if ( unlikely(xatpb->size < extent) ) + return -EILSEQ; + +@@ -891,6 +909,19 @@ static int xenmem_add_to_physmap_batch(s + !guest_handle_subrange_okay(xatpb->errs, extent, xatpb->size - 1) ) + return -EFAULT; + ++ switch ( xatpb->space ) ++ { ++ case XENMAPSPACE_dev_mmio: ++ /* res0 is reserved for future use. */ ++ if ( xatpb->u.res0 ) ++ return -EOPNOTSUPP; ++ break; ++ ++ case XENMAPSPACE_gmfn_foreign: ++ extra.foreign_domid = xatpb->u.foreign_domid; ++ break; ++ } ++ + while ( xatpb->size > extent ) + { + xen_ulong_t idx; +@@ -903,8 +934,7 @@ static int xenmem_add_to_physmap_batch(s + extent, 1)) ) + return -EFAULT; + +- rc = xenmem_add_to_physmap_one(d, xatpb->space, +- xatpb->u, ++ rc = xenmem_add_to_physmap_one(d, xatpb->space, extra, + idx, _gfn(gpfn)); + + if ( unlikely(__copy_to_guest_offset(xatpb->errs, extent, &rc, 1)) ) +--- a/xen/include/xen/mm.h ++++ b/xen/include/xen/mm.h +@@ -592,8 +592,22 @@ void scrub_one_page(struct page_info *); + page_list_del(pg, page_to_list(d, pg)) + #endif + ++union add_to_physmap_extra { ++ /* ++ * XENMAPSPACE_gmfn: When deferring TLB flushes, a page reference needs ++ * to be kept until after the flush, so the page can't get removed from ++ * the domain (and re-used for another purpose) beforehand. By passing ++ * non-NULL, the caller of xenmem_add_to_physmap_one() indicates it wants ++ * to have ownership of such a reference transferred in the success case. ++ */ ++ struct page_info **ppage; ++ ++ /* XENMAPSPACE_gmfn_foreign */ ++ domid_t foreign_domid; ++}; ++ + int xenmem_add_to_physmap_one(struct domain *d, unsigned int space, +- union xen_add_to_physmap_batch_extra extra, ++ union add_to_physmap_extra extra, + unsigned long idx, gfn_t gfn); + + int xenmem_add_to_physmap(struct domain *d, struct xen_add_to_physmap *xatp, diff --git a/xsa347-4.14-1.patch b/xsa347-4.14-1.patch new file mode 100644 index 0000000..c110ee6 --- /dev/null +++ b/xsa347-4.14-1.patch @@ -0,0 +1,149 @@ +From: Jan Beulich +Subject: AMD/IOMMU: convert amd_iommu_pte from struct to union + +This is to add a "raw" counterpart to the bitfield equivalent. Take the +opportunity and + - convert fields to bool / unsigned int, + - drop the naming of the reserved field, + - shorten the names of the ignored ones. + +This is part of XSA-347. + +Signed-off-by: Jan Beulich +Reviewed-by: Andrew Cooper +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/amd/iommu-defs.h ++++ b/xen/drivers/passthrough/amd/iommu-defs.h +@@ -451,20 +451,23 @@ union amd_iommu_x2apic_control { + #define IOMMU_PAGE_TABLE_U32_PER_ENTRY (IOMMU_PAGE_TABLE_ENTRY_SIZE / 4) + #define IOMMU_PAGE_TABLE_ALIGNMENT 4096 + +-struct amd_iommu_pte { +- uint64_t pr:1; +- uint64_t ignored0:4; +- uint64_t a:1; +- uint64_t d:1; +- uint64_t ignored1:2; +- uint64_t next_level:3; +- uint64_t mfn:40; +- uint64_t reserved:7; +- uint64_t u:1; +- uint64_t fc:1; +- uint64_t ir:1; +- uint64_t iw:1; +- uint64_t ignored2:1; ++union amd_iommu_pte { ++ uint64_t raw; ++ struct { ++ bool pr:1; ++ unsigned int ign0:4; ++ bool a:1; ++ bool d:1; ++ unsigned int ign1:2; ++ unsigned int next_level:3; ++ uint64_t mfn:40; ++ unsigned int :7; ++ bool u:1; ++ bool fc:1; ++ bool ir:1; ++ bool iw:1; ++ unsigned int ign2:1; ++ }; + }; + + /* Paging modes */ +--- a/xen/drivers/passthrough/amd/iommu_map.c ++++ b/xen/drivers/passthrough/amd/iommu_map.c +@@ -34,7 +34,7 @@ static unsigned int pfn_to_pde_idx(unsig + static unsigned int clear_iommu_pte_present(unsigned long l1_mfn, + unsigned long dfn) + { +- struct amd_iommu_pte *table, *pte; ++ union amd_iommu_pte *table, *pte; + unsigned int flush_flags; + + table = map_domain_page(_mfn(l1_mfn)); +@@ -48,7 +48,7 @@ static unsigned int clear_iommu_pte_pres + return flush_flags; + } + +-static unsigned int set_iommu_pde_present(struct amd_iommu_pte *pte, ++static unsigned int set_iommu_pde_present(union amd_iommu_pte *pte, + unsigned long next_mfn, + unsigned int next_level, bool iw, + bool ir) +@@ -83,7 +83,7 @@ static unsigned int set_iommu_pte_presen + int pde_level, + bool iw, bool ir) + { +- struct amd_iommu_pte *table, *pde; ++ union amd_iommu_pte *table, *pde; + unsigned int flush_flags; + + table = map_domain_page(_mfn(pt_mfn)); +@@ -174,7 +174,7 @@ void iommu_dte_set_guest_cr3(struct amd_ + static int iommu_pde_from_dfn(struct domain *d, unsigned long dfn, + unsigned long pt_mfn[], bool map) + { +- struct amd_iommu_pte *pde, *next_table_vaddr; ++ union amd_iommu_pte *pde, *next_table_vaddr; + unsigned long next_table_mfn; + unsigned int level; + struct page_info *table; +@@ -448,7 +448,7 @@ int __init amd_iommu_quarantine_init(str + unsigned long end_gfn = + 1ul << (DEFAULT_DOMAIN_ADDRESS_WIDTH - PAGE_SHIFT); + unsigned int level = amd_iommu_get_paging_mode(end_gfn); +- struct amd_iommu_pte *table; ++ union amd_iommu_pte *table; + + if ( hd->arch.root_table ) + { +@@ -479,7 +479,7 @@ int __init amd_iommu_quarantine_init(str + + for ( i = 0; i < PTE_PER_TABLE_SIZE; i++ ) + { +- struct amd_iommu_pte *pde = &table[i]; ++ union amd_iommu_pte *pde = &table[i]; + + /* + * PDEs are essentially a subset of PTEs, so this function +--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c ++++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c +@@ -387,7 +387,7 @@ static void deallocate_next_page_table(s + + static void deallocate_page_table(struct page_info *pg) + { +- struct amd_iommu_pte *table_vaddr; ++ union amd_iommu_pte *table_vaddr; + unsigned int index, level = PFN_ORDER(pg); + + PFN_ORDER(pg) = 0; +@@ -402,7 +402,7 @@ static void deallocate_page_table(struct + + for ( index = 0; index < PTE_PER_TABLE_SIZE; index++ ) + { +- struct amd_iommu_pte *pde = &table_vaddr[index]; ++ union amd_iommu_pte *pde = &table_vaddr[index]; + + if ( pde->mfn && pde->next_level && pde->pr ) + { +@@ -554,7 +554,7 @@ static void amd_dump_p2m_table_level(str + paddr_t gpa, int indent) + { + paddr_t address; +- struct amd_iommu_pte *table_vaddr; ++ const union amd_iommu_pte *table_vaddr; + int index; + + if ( level < 1 ) +@@ -570,7 +570,7 @@ static void amd_dump_p2m_table_level(str + + for ( index = 0; index < PTE_PER_TABLE_SIZE; index++ ) + { +- struct amd_iommu_pte *pde = &table_vaddr[index]; ++ const union amd_iommu_pte *pde = &table_vaddr[index]; + + if ( !(index % 2) ) + process_pending_softirqs(); diff --git a/xsa347-4.14-2.patch b/xsa347-4.14-2.patch new file mode 100644 index 0000000..a8f4776 --- /dev/null +++ b/xsa347-4.14-2.patch @@ -0,0 +1,72 @@ +From: Jan Beulich +Subject: AMD/IOMMU: update live PTEs atomically + +Updating a live PTE bitfield by bitfield risks the compiler re-ordering +the individual updates as well as splitting individual updates into +multiple memory writes. Construct the new entry fully in a local +variable, do the check to determine the flushing needs on the thus +established new entry, and then write the new entry by a single insn. + +Similarly using memset() to clear a PTE is unsafe, as the order of +writes the function does is, at least in principle, undefined. + +This is part of XSA-347. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/amd/iommu_map.c ++++ b/xen/drivers/passthrough/amd/iommu_map.c +@@ -41,7 +41,7 @@ static unsigned int clear_iommu_pte_pres + pte = &table[pfn_to_pde_idx(dfn, 1)]; + + flush_flags = pte->pr ? IOMMU_FLUSHF_modified : 0; +- memset(pte, 0, sizeof(*pte)); ++ write_atomic(&pte->raw, 0); + + unmap_domain_page(table); + +@@ -53,26 +53,30 @@ static unsigned int set_iommu_pde_presen + unsigned int next_level, bool iw, + bool ir) + { ++ union amd_iommu_pte new = {}, old; + unsigned int flush_flags = IOMMU_FLUSHF_added; + +- if ( pte->pr && +- (pte->mfn != next_mfn || +- pte->iw != iw || +- pte->ir != ir || +- pte->next_level != next_level) ) +- flush_flags |= IOMMU_FLUSHF_modified; +- + /* + * FC bit should be enabled in PTE, this helps to solve potential + * issues with ATS devices + */ +- pte->fc = !next_level; ++ new.fc = !next_level; ++ ++ new.mfn = next_mfn; ++ new.iw = iw; ++ new.ir = ir; ++ new.next_level = next_level; ++ new.pr = true; ++ ++ old.raw = read_atomic(&pte->raw); ++ old.ign0 = 0; ++ old.ign1 = 0; ++ old.ign2 = 0; ++ ++ if ( old.pr && old.raw != new.raw ) ++ flush_flags |= IOMMU_FLUSHF_modified; + +- pte->mfn = next_mfn; +- pte->iw = iw; +- pte->ir = ir; +- pte->next_level = next_level; +- pte->pr = 1; ++ write_atomic(&pte->raw, new.raw); + + return flush_flags; + } diff --git a/xsa347-4.14-3.patch b/xsa347-4.14-3.patch new file mode 100644 index 0000000..1fb8c60 --- /dev/null +++ b/xsa347-4.14-3.patch @@ -0,0 +1,59 @@ +From: Jan Beulich +Subject: AMD/IOMMU: ensure suitable ordering of DTE modifications + +DMA and interrupt translation should be enabled only after other +applicable DTE fields have been written. Similarly when disabling +translation or when moving a device between domains, translation should +first be disabled, before other entry fields get modified. Note however +that the "moving" aspect doesn't apply to the interrupt remapping side, +as domain specifics are maintained in the IRTEs here, not the DTE. We +also never disable interrupt remapping once it got enabled for a device +(the respective argument passed is always the immutable iommu_intremap). + +This is part of XSA-347. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/amd/iommu_map.c ++++ b/xen/drivers/passthrough/amd/iommu_map.c +@@ -103,11 +103,18 @@ void amd_iommu_set_root_page_table(struc + uint64_t root_ptr, uint16_t domain_id, + uint8_t paging_mode, bool valid) + { ++ if ( valid || dte->v ) ++ { ++ dte->tv = false; ++ dte->v = true; ++ smp_wmb(); ++ } + dte->domain_id = domain_id; + dte->pt_root = paddr_to_pfn(root_ptr); + dte->iw = true; + dte->ir = true; + dte->paging_mode = paging_mode; ++ smp_wmb(); + dte->tv = true; + dte->v = valid; + } +@@ -130,6 +137,7 @@ void amd_iommu_set_intremap_table( + } + + dte->ig = false; /* unmapped interrupts result in i/o page faults */ ++ smp_wmb(); + dte->iv = valid; + } + +--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c ++++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c +@@ -117,7 +117,10 @@ static void amd_iommu_setup_domain_devic + /* Undo what amd_iommu_disable_domain_device() may have done. */ + ivrs_dev = &get_ivrs_mappings(iommu->seg)[req_id]; + if ( dte->it_root ) ++ { + dte->int_ctl = IOMMU_DEV_TABLE_INT_CONTROL_TRANSLATED; ++ smp_wmb(); ++ } + dte->iv = iommu_intremap; + dte->ex = ivrs_dev->dte_allow_exclusion; + dte->sys_mgt = MASK_EXTR(ivrs_dev->device_flags, ACPI_IVHD_SYSTEM_MGMT); From a104bbcf02a19aac1ff709614f8304996326bcd3 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 28 Oct 2020 20:46:44 +0000 Subject: [PATCH 051/194] x86 PV guest INVLPG-like flushes may leave stale TLB entries [XSA-286, CVE-2020-27674] (#1891092) simplify grub scripts (patches from Thierry Vignaud ) some fixes for gcc 11 --- xen.gcc11.fixes.patch | 45 +++++ xen.spec | 104 ++++++----- ...and-L3-parts-of-the-walk-out-of-do_p.patch | 73 ++++++++ ...-mm-check-page-types-in-do_page_walk.patch | 170 +++++++++++++++++ ...ng-linear-page-tables-in-map_guest_l.patch | 92 ++++++++++ ...ng-linear-page-tables-in-guest_get_e.patch | 172 ++++++++++++++++++ ...ng-top-level-linear-page-tables-in-u.patch | 101 ++++++++++ ...use-of-linear-page-tables-to-shadow-.patch | 106 +++++++++++ 8 files changed, 821 insertions(+), 42 deletions(-) create mode 100644 xen.gcc11.fixes.patch create mode 100644 xsa286-4.14-0001-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch create mode 100644 xsa286-4.14-0002-x86-mm-check-page-types-in-do_page_walk.patch create mode 100644 xsa286-4.14-0003-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch create mode 100644 xsa286-4.14-0004-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch create mode 100644 xsa286-4.14-0005-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch create mode 100644 xsa286-4.14-0006-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch diff --git a/xen.gcc11.fixes.patch b/xen.gcc11.fixes.patch new file mode 100644 index 0000000..e3c4058 --- /dev/null +++ b/xen.gcc11.fixes.patch @@ -0,0 +1,45 @@ +--- 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 @@ + + #define vmac_update vhash_update + +-void vhash_update(unsigned char m[], ++void vhash_update(uint8_t *m, + 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.spec b/xen.spec index fb8aad0..531de5f 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.14.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 @@ -135,6 +135,13 @@ Patch66: xsa346-2.patch Patch67: xsa347-4.14-1.patch Patch68: xsa347-4.14-2.patch Patch69: xsa347-4.14-3.patch +Patch70: xen.gcc11.fixes.patch +Patch71: xsa286-4.14-0001-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch +Patch72: xsa286-4.14-0002-x86-mm-check-page-types-in-do_page_walk.patch +Patch73: xsa286-4.14-0003-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch +Patch74: xsa286-4.14-0004-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch +Patch75: xsa286-4.14-0005-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch +Patch76: xsa286-4.14-0006-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch %if %build_qemutrad @@ -359,6 +366,13 @@ manage Xen virtual machines. %patch67 -p1 %patch68 -p1 %patch69 -p1 +%patch70 -p1 +%patch71 -p1 +%patch72 -p1 +%patch73 -p1 +%patch74 -p1 +%patch75 -p1 +%patch76 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -629,55 +643,50 @@ fi %if %build_hyp %post hypervisor +do_it() { + DIR=$1 + TARGET=$2 + if [ -d $DIR ]; then + 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 + fi +} if [ $1 == 1 -a -f /sbin/grub2-mkconfig ]; then - if [ -f /boot/grub2/grub.cfg ]; then - /sbin/grub2-mkconfig -o /boot/grub2/grub.cfg - sed -i -e '/insmod module2/d' /boot/grub2/grub.cfg - fi - if [ -f /boot/efi/EFI/fedora/grub.cfg ]; then - /sbin/grub2-mkconfig -o /boot/efi/EFI/fedora/grub.cfg - sed -i -e '/insmod module2/d' /boot/efi/EFI/fedora/grub.cfg - fi + for f in /boot/grub2/grub.cfg /boot/efi/EFI/fedora/grub.cfg; do + if [ -f $f ]; then + /sbin/grub2-mkconfig -o $f + sed -i -e '/insmod module2/d' $f + fi + done fi if [ -f /sbin/grub2-mkconfig ]; then if [ -f /boot/grub2/grub.cfg ]; then - if [ -d /usr/lib/grub/i386-pc ]; then - if [ ! -d /boot/grub2/i386-pc ]; then - mkdir /boot/grub2/i386-pc - fi - if [ -f /usr/lib/grub/i386-pc/relocator.mod -a ! -f /boot/grub2/i386-pc/relocator.mod ]; then - cp -p /usr/lib/grub/i386-pc/relocator.mod /boot/grub2/i386-pc/relocator.mod - fi - if [ -f /usr/lib/grub/i386-pc/multiboot2.mod -a ! -f /boot/grub2/i386-pc/multiboot2.mod ]; then - cp -p /usr/lib/grub/i386-pc/multiboot2.mod /boot/grub2/i386-pc/multiboot2.mod - fi - fi + DIR=/usr/lib/grub/i386-pc + TARGET=/boot/grub2/i386-pc + do_it $DIR $TARGET fi if [ -f /boot/efi/EFI/fedora/grub.cfg ]; then - if [ -d /usr/lib/grub/x86_64-efi ]; then - if [ ! -d /boot/efi/EFI/fedora/x86_64-efi ]; then - mkdir /boot/efi/EFI/fedora/x86_64-efi - fi - if [ -f /usr/lib/grub/x86_64-efi/relocator.mod -a ! -f /boot/efi/EFI/fedora/x86_64-efi/relocator.mod ]; then - cp -p /usr/lib/grub/x86_64-efi/relocator.mod /boot/efi/EFI/fedora/x86_64-efi/relocator.mod - fi - if [ -f /usr/lib/grub/x86_64-efi/multiboot2.mod -a ! -f /boot/efi/EFI/fedora/x86_64-efi/multiboot2.mod ]; then - cp -p /usr/lib/grub/x86_64-efi/multiboot2.mod /boot/efi/EFI/fedora/x86_64-efi/multiboot2.mod - fi - fi + 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 - if [ -f /boot/grub2/grub.cfg ]; then - /sbin/grub2-mkconfig -o /boot/grub2/grub.cfg - sed -i -e '/insmod module2/d' /boot/grub2/grub.cfg - fi - if [ -f /boot/efi/EFI/fedora/grub.cfg ]; then - /sbin/grub2-mkconfig -o /boot/efi/EFI/fedora/grub.cfg - sed -i -e '/insmod module2/d' /boot/efi/EFI/fedora/grub.cfg - fi + for f in /boot/grub2/grub.cfg /boot/efi/EFI/fedora/grub.cfg; do + if [ -f $f ]; then + /sbin/grub2-mkconfig -o $f + sed -i -e '/insmod module2/d' $f + fi + done fi %endif @@ -957,10 +966,21 @@ fi %endif %changelog +* Wed Oct 28 2020 Michael Young - 4.14.0-7 +- x86 PV guest INVLPG-like flushes may leave stale TLB entries + [XSA-286, CVE-2020-27674] (#1891092) +- simplify grub scripts (patches from Thierry Vignaud ) +- some fixes for gcc 11 + +* Thu Oct 22 2020 Michael Young - 4.14.0-6.1 + * Tue Oct 20 2020 Michael Young - 4.14.0-6 -- x86: Race condition in Xen mapping code [XSA-345] -- undue deferral of IOMMU TLB flushes [XSA-346] -- unsafe AMD IOMMU page table updates [XSA-347] +- x86: Race condition in Xen mapping code [XSA-345, CVE-2020-27672] + (#1891097) +- undue deferral of IOMMU TLB flushes [XSA-346, CVE-2020-27671] + (#1891093) +- unsafe AMD IOMMU page table updates [XSA-347, CVE-2020-27670] + (#1891088) * Tue Sep 22 2020 Michael Young - 4.14.0-5 - x86 pv: Crash when handling guest access to MSR_MISC_ENABLE [XSA-333, CVE-2020-25602] (#1881619) diff --git a/xsa286-4.14-0001-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch b/xsa286-4.14-0001-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch new file mode 100644 index 0000000..6459e1f --- /dev/null +++ b/xsa286-4.14-0001-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch @@ -0,0 +1,73 @@ +From: Jan Beulich +Subject: x86/mm: split L4 and L3 parts of the walk out of do_page_walk() + +The L3 one at least is going to be re-used by a subsequent patch, and +splitting the L4 one then as well seems only natural. + +This is part of XSA-286. + +Signed-off-by: Jan Beulich +Reviewed-by: George Dunlap +Reviewed-by: Andrew Cooper + +diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c +index 48fd60a876..c25eb01e41 100644 +--- a/xen/arch/x86/x86_64/mm.c ++++ b/xen/arch/x86/x86_64/mm.c +@@ -44,26 +44,47 @@ unsigned int __read_mostly m2p_compat_vstart = __HYPERVISOR_COMPAT_VIRT_START; + + l2_pgentry_t *compat_idle_pg_table_l2; + +-void *do_page_walk(struct vcpu *v, unsigned long addr) ++static l4_pgentry_t page_walk_get_l4e(pagetable_t root, unsigned long addr) + { +- unsigned long mfn = pagetable_get_pfn(v->arch.guest_table); +- l4_pgentry_t l4e, *l4t; +- l3_pgentry_t l3e, *l3t; +- l2_pgentry_t l2e, *l2t; +- l1_pgentry_t l1e, *l1t; ++ unsigned long mfn = pagetable_get_pfn(root); ++ l4_pgentry_t *l4t, l4e; + +- if ( !is_pv_vcpu(v) || !is_canonical_address(addr) ) +- return NULL; ++ if ( !is_canonical_address(addr) ) ++ return l4e_empty(); + + l4t = map_domain_page(_mfn(mfn)); + l4e = l4t[l4_table_offset(addr)]; + unmap_domain_page(l4t); ++ ++ return l4e; ++} ++ ++static l3_pgentry_t page_walk_get_l3e(pagetable_t root, unsigned long addr) ++{ ++ l4_pgentry_t l4e = page_walk_get_l4e(root, addr); ++ l3_pgentry_t *l3t, l3e; ++ + if ( !(l4e_get_flags(l4e) & _PAGE_PRESENT) ) +- return NULL; ++ return l3e_empty(); + + l3t = map_l3t_from_l4e(l4e); + l3e = l3t[l3_table_offset(addr)]; + unmap_domain_page(l3t); ++ ++ return l3e; ++} ++ ++void *do_page_walk(struct vcpu *v, unsigned long addr) ++{ ++ l3_pgentry_t l3e; ++ l2_pgentry_t l2e, *l2t; ++ l1_pgentry_t l1e, *l1t; ++ unsigned long mfn; ++ ++ if ( !is_pv_vcpu(v) ) ++ return NULL; ++ ++ l3e = page_walk_get_l3e(v->arch.guest_table, addr); + mfn = l3e_get_pfn(l3e); + if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || !mfn_valid(_mfn(mfn)) ) + return NULL; diff --git a/xsa286-4.14-0002-x86-mm-check-page-types-in-do_page_walk.patch b/xsa286-4.14-0002-x86-mm-check-page-types-in-do_page_walk.patch new file mode 100644 index 0000000..3a05a74 --- /dev/null +++ b/xsa286-4.14-0002-x86-mm-check-page-types-in-do_page_walk.patch @@ -0,0 +1,170 @@ +From: Jan Beulich +Subject: x86/mm: check page types in do_page_walk() + +For page table entries read to be guaranteed valid, transiently locking +the pages and validating their types is necessary. Note that guest use +of linear page tables is intentionally not taken into account here, as +ordinary data (guest stacks) can't possibly live inside page tables. + +This is part of XSA-286. + +Signed-off-by: Jan Beulich +Reviewed-by: George Dunlap +Reviewed-by: Andrew Cooper + +diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c +index c25eb01e41..6305cf6033 100644 +--- a/xen/arch/x86/x86_64/mm.c ++++ b/xen/arch/x86/x86_64/mm.c +@@ -46,15 +46,29 @@ l2_pgentry_t *compat_idle_pg_table_l2; + + static l4_pgentry_t page_walk_get_l4e(pagetable_t root, unsigned long addr) + { +- unsigned long mfn = pagetable_get_pfn(root); +- l4_pgentry_t *l4t, l4e; ++ mfn_t mfn = pagetable_get_mfn(root); ++ /* current's root page table can't disappear under our feet. */ ++ bool need_lock = !mfn_eq(mfn, pagetable_get_mfn(current->arch.guest_table)); ++ struct page_info *pg; ++ l4_pgentry_t l4e = l4e_empty(); + + if ( !is_canonical_address(addr) ) + return l4e_empty(); + +- l4t = map_domain_page(_mfn(mfn)); +- l4e = l4t[l4_table_offset(addr)]; +- unmap_domain_page(l4t); ++ pg = mfn_to_page(mfn); ++ if ( need_lock && !page_lock(pg) ) ++ return l4e_empty(); ++ ++ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l4_page_table ) ++ { ++ l4_pgentry_t *l4t = map_domain_page(mfn); ++ ++ l4e = l4t[l4_table_offset(addr)]; ++ unmap_domain_page(l4t); ++ } ++ ++ if ( need_lock ) ++ page_unlock(pg); + + return l4e; + } +@@ -62,14 +76,26 @@ static l4_pgentry_t page_walk_get_l4e(pagetable_t root, unsigned long addr) + static l3_pgentry_t page_walk_get_l3e(pagetable_t root, unsigned long addr) + { + l4_pgentry_t l4e = page_walk_get_l4e(root, addr); +- l3_pgentry_t *l3t, l3e; ++ mfn_t mfn = l4e_get_mfn(l4e); ++ struct page_info *pg; ++ l3_pgentry_t l3e = l3e_empty(); + + if ( !(l4e_get_flags(l4e) & _PAGE_PRESENT) ) + return l3e_empty(); + +- l3t = map_l3t_from_l4e(l4e); +- l3e = l3t[l3_table_offset(addr)]; +- unmap_domain_page(l3t); ++ pg = mfn_to_page(mfn); ++ if ( !page_lock(pg) ) ++ return l3e_empty(); ++ ++ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l3_page_table ) ++ { ++ l3_pgentry_t *l3t = map_domain_page(mfn); ++ ++ l3e = l3t[l3_table_offset(addr)]; ++ unmap_domain_page(l3t); ++ } ++ ++ page_unlock(pg); + + return l3e; + } +@@ -77,44 +103,67 @@ static l3_pgentry_t page_walk_get_l3e(pagetable_t root, unsigned long addr) + void *do_page_walk(struct vcpu *v, unsigned long addr) + { + l3_pgentry_t l3e; +- l2_pgentry_t l2e, *l2t; +- l1_pgentry_t l1e, *l1t; +- unsigned long mfn; ++ l2_pgentry_t l2e = l2e_empty(); ++ l1_pgentry_t l1e = l1e_empty(); ++ mfn_t mfn; ++ struct page_info *pg; + + if ( !is_pv_vcpu(v) ) + return NULL; + + l3e = page_walk_get_l3e(v->arch.guest_table, addr); +- mfn = l3e_get_pfn(l3e); +- if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || !mfn_valid(_mfn(mfn)) ) ++ mfn = l3e_get_mfn(l3e); ++ if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || !mfn_valid(mfn) ) + return NULL; + if ( (l3e_get_flags(l3e) & _PAGE_PSE) ) + { +- mfn += PFN_DOWN(addr & ((1UL << L3_PAGETABLE_SHIFT) - 1)); ++ mfn = mfn_add(mfn, PFN_DOWN(addr & ((1UL << L3_PAGETABLE_SHIFT) - 1))); + goto ret; + } + +- l2t = map_domain_page(_mfn(mfn)); +- l2e = l2t[l2_table_offset(addr)]; +- unmap_domain_page(l2t); +- mfn = l2e_get_pfn(l2e); +- if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) || !mfn_valid(_mfn(mfn)) ) ++ pg = mfn_to_page(mfn); ++ if ( !page_lock(pg) ) ++ return NULL; ++ ++ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l2_page_table ) ++ { ++ const l2_pgentry_t *l2t = map_domain_page(mfn); ++ ++ l2e = l2t[l2_table_offset(addr)]; ++ unmap_domain_page(l2t); ++ } ++ ++ page_unlock(pg); ++ ++ mfn = l2e_get_mfn(l2e); ++ if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) || !mfn_valid(mfn) ) + return NULL; + if ( (l2e_get_flags(l2e) & _PAGE_PSE) ) + { +- mfn += PFN_DOWN(addr & ((1UL << L2_PAGETABLE_SHIFT) - 1)); ++ mfn = mfn_add(mfn, PFN_DOWN(addr & ((1UL << L2_PAGETABLE_SHIFT) - 1))); + goto ret; + } + +- l1t = map_domain_page(_mfn(mfn)); +- l1e = l1t[l1_table_offset(addr)]; +- unmap_domain_page(l1t); +- mfn = l1e_get_pfn(l1e); +- if ( !(l1e_get_flags(l1e) & _PAGE_PRESENT) || !mfn_valid(_mfn(mfn)) ) ++ pg = mfn_to_page(mfn); ++ if ( !page_lock(pg) ) ++ return NULL; ++ ++ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l1_page_table ) ++ { ++ const l1_pgentry_t *l1t = map_domain_page(mfn); ++ ++ l1e = l1t[l1_table_offset(addr)]; ++ unmap_domain_page(l1t); ++ } ++ ++ page_unlock(pg); ++ ++ mfn = l1e_get_mfn(l1e); ++ if ( !(l1e_get_flags(l1e) & _PAGE_PRESENT) || !mfn_valid(mfn) ) + return NULL; + + ret: +- return map_domain_page(_mfn(mfn)) + (addr & ~PAGE_MASK); ++ return map_domain_page(mfn) + (addr & ~PAGE_MASK); + } + + /* diff --git a/xsa286-4.14-0003-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch b/xsa286-4.14-0003-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch new file mode 100644 index 0000000..5a5c251 --- /dev/null +++ b/xsa286-4.14-0003-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch @@ -0,0 +1,92 @@ +From: Jan Beulich +Subject: x86/mm: avoid using linear page tables in map_guest_l1e() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Replace the linear L2 table access by an actual page walk. + +This is part of XSA-286. + +Reported-by: Jann Horn +Signed-off-by: Jan Beulich +Signed-off-by: Roger Pau Monné +Reviewed-by: George Dunlap +Reviewed-by: Andrew Cooper + +diff --git a/xen/arch/x86/pv/mm.c b/xen/arch/x86/pv/mm.c +index 5d4cd00941..7be098f5ef 100644 +--- a/xen/arch/x86/pv/mm.c ++++ b/xen/arch/x86/pv/mm.c +@@ -40,11 +40,14 @@ l1_pgentry_t *map_guest_l1e(unsigned long linear, mfn_t *gl1mfn) + if ( unlikely(!__addr_ok(linear)) ) + return NULL; + +- /* Find this l1e and its enclosing l1mfn in the linear map. */ +- if ( __copy_from_user(&l2e, +- &__linear_l2_table[l2_linear_offset(linear)], +- sizeof(l2_pgentry_t)) ) ++ if ( unlikely(!(current->arch.flags & TF_kernel_mode)) ) ++ { ++ ASSERT_UNREACHABLE(); + return NULL; ++ } ++ ++ /* Find this l1e and its enclosing l1mfn. */ ++ l2e = page_walk_get_l2e(current->arch.guest_table, linear); + + /* Check flags that it will be safe to read the l1e. */ + if ( (l2e_get_flags(l2e) & (_PAGE_PRESENT | _PAGE_PSE)) != _PAGE_PRESENT ) +diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c +index 6305cf6033..71a8bfc024 100644 +--- a/xen/arch/x86/x86_64/mm.c ++++ b/xen/arch/x86/x86_64/mm.c +@@ -100,6 +100,34 @@ static l3_pgentry_t page_walk_get_l3e(pagetable_t root, unsigned long addr) + return l3e; + } + ++l2_pgentry_t page_walk_get_l2e(pagetable_t root, unsigned long addr) ++{ ++ l3_pgentry_t l3e = page_walk_get_l3e(root, addr); ++ mfn_t mfn = l3e_get_mfn(l3e); ++ struct page_info *pg; ++ l2_pgentry_t l2e = l2e_empty(); ++ ++ if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || ++ (l3e_get_flags(l3e) & _PAGE_PSE) ) ++ return l2e_empty(); ++ ++ pg = mfn_to_page(mfn); ++ if ( !page_lock(pg) ) ++ return l2e_empty(); ++ ++ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l2_page_table ) ++ { ++ l2_pgentry_t *l2t = map_domain_page(mfn); ++ ++ l2e = l2t[l2_table_offset(addr)]; ++ unmap_domain_page(l2t); ++ } ++ ++ page_unlock(pg); ++ ++ return l2e; ++} ++ + void *do_page_walk(struct vcpu *v, unsigned long addr) + { + l3_pgentry_t l3e; +diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h +index 7e74996053..12ea812381 100644 +--- a/xen/include/asm-x86/mm.h ++++ b/xen/include/asm-x86/mm.h +@@ -579,7 +579,9 @@ void audit_domains(void); + void make_cr3(struct vcpu *v, mfn_t mfn); + void update_cr3(struct vcpu *v); + int vcpu_destroy_pagetables(struct vcpu *); ++ + void *do_page_walk(struct vcpu *v, unsigned long addr); ++l2_pgentry_t page_walk_get_l2e(pagetable_t root, unsigned long addr); + + /* Allocator functions for Xen pagetables. */ + void *alloc_xen_pagetable(void); diff --git a/xsa286-4.14-0004-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch b/xsa286-4.14-0004-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch new file mode 100644 index 0000000..9783f79 --- /dev/null +++ b/xsa286-4.14-0004-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch @@ -0,0 +1,172 @@ +From: Jan Beulich +Subject: x86/mm: avoid using linear page tables in guest_get_eff_kern_l1e() + +First of all drop guest_get_eff_l1e() entirely - there's no actual user +of it: pv_ro_page_fault() has a guest_kernel_mode() conditional around +its only call site. + +Then replace the linear L1 table access by an actual page walk. + +This is part of XSA-286. + +Reported-by: Jann Horn +Signed-off-by: Jan Beulich +Reviewed-by: George Dunlap +Reviewed-by: Andrew Cooper + +diff --git a/xen/arch/x86/pv/mm.c b/xen/arch/x86/pv/mm.c +index 7be098f5ef..5e4081aecd 100644 +--- a/xen/arch/x86/pv/mm.c ++++ b/xen/arch/x86/pv/mm.c +@@ -59,27 +59,6 @@ l1_pgentry_t *map_guest_l1e(unsigned long linear, mfn_t *gl1mfn) + } + + /* +- * Read the guest's l1e that maps this address, from the kernel-mode +- * page tables. +- */ +-static l1_pgentry_t guest_get_eff_kern_l1e(unsigned long linear) +-{ +- struct vcpu *curr = current; +- const bool user_mode = !(curr->arch.flags & TF_kernel_mode); +- l1_pgentry_t l1e; +- +- if ( user_mode ) +- toggle_guest_pt(curr); +- +- l1e = guest_get_eff_l1e(linear); +- +- if ( user_mode ) +- toggle_guest_pt(curr); +- +- return l1e; +-} +- +-/* + * Map a guest's LDT page (covering the byte at @offset from start of the LDT) + * into Xen's virtual range. Returns true if the mapping changed, false + * otherwise. +diff --git a/xen/arch/x86/pv/mm.h b/xen/arch/x86/pv/mm.h +index a1bd473b29..43d33a1fd1 100644 +--- a/xen/arch/x86/pv/mm.h ++++ b/xen/arch/x86/pv/mm.h +@@ -5,19 +5,19 @@ l1_pgentry_t *map_guest_l1e(unsigned long linear, mfn_t *gl1mfn); + + int new_guest_cr3(mfn_t mfn); + +-/* Read a PV guest's l1e that maps this linear address. */ +-static inline l1_pgentry_t guest_get_eff_l1e(unsigned long linear) ++/* ++ * Read the guest's l1e that maps this address, from the kernel-mode ++ * page tables. ++ */ ++static inline l1_pgentry_t guest_get_eff_kern_l1e(unsigned long linear) + { +- l1_pgentry_t l1e; ++ l1_pgentry_t l1e = l1e_empty(); + + ASSERT(!paging_mode_translate(current->domain)); + ASSERT(!paging_mode_external(current->domain)); + +- if ( unlikely(!__addr_ok(linear)) || +- __copy_from_user(&l1e, +- &__linear_l1_table[l1_linear_offset(linear)], +- sizeof(l1_pgentry_t)) ) +- l1e = l1e_empty(); ++ if ( likely(__addr_ok(linear)) ) ++ l1e = page_walk_get_l1e(current->arch.guest_table, linear); + + return l1e; + } +diff --git a/xen/arch/x86/pv/ro-page-fault.c b/xen/arch/x86/pv/ro-page-fault.c +index 0eedb70002..ce31dd401d 100644 +--- a/xen/arch/x86/pv/ro-page-fault.c ++++ b/xen/arch/x86/pv/ro-page-fault.c +@@ -349,7 +349,7 @@ int pv_ro_page_fault(unsigned long addr, struct cpu_user_regs *regs) + bool mmio_ro; + + /* Attempt to read the PTE that maps the VA being accessed. */ +- pte = guest_get_eff_l1e(addr); ++ pte = guest_get_eff_kern_l1e(addr); + + /* We are only looking for read-only mappings */ + if ( ((l1e_get_flags(pte) & (_PAGE_PRESENT | _PAGE_RW)) != _PAGE_PRESENT) ) +diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c +index 71a8bfc024..9e87a55174 100644 +--- a/xen/arch/x86/x86_64/mm.c ++++ b/xen/arch/x86/x86_64/mm.c +@@ -128,6 +128,62 @@ l2_pgentry_t page_walk_get_l2e(pagetable_t root, unsigned long addr) + return l2e; + } + ++/* ++ * For now no "set_accessed" parameter, as all callers want it set to true. ++ * For now also no "set_dirty" parameter, as all callers deal with r/o ++ * mappings, and we don't want to set the dirty bit there (conflicts with ++ * CET-SS). However, as there are CPUs which may set the dirty bit on r/o ++ * PTEs, the logic below tolerates the bit becoming set "behind our backs". ++ */ ++l1_pgentry_t page_walk_get_l1e(pagetable_t root, unsigned long addr) ++{ ++ l2_pgentry_t l2e = page_walk_get_l2e(root, addr); ++ mfn_t mfn = l2e_get_mfn(l2e); ++ struct page_info *pg; ++ l1_pgentry_t l1e = l1e_empty(); ++ ++ if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) || ++ (l2e_get_flags(l2e) & _PAGE_PSE) ) ++ return l1e_empty(); ++ ++ pg = mfn_to_page(mfn); ++ if ( !page_lock(pg) ) ++ return l1e_empty(); ++ ++ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l1_page_table ) ++ { ++ l1_pgentry_t *l1t = map_domain_page(mfn); ++ ++ l1e = l1t[l1_table_offset(addr)]; ++ ++ if ( (l1e_get_flags(l1e) & (_PAGE_ACCESSED | _PAGE_PRESENT)) == ++ _PAGE_PRESENT ) ++ { ++ l1_pgentry_t ol1e = l1e; ++ ++ l1e_add_flags(l1e, _PAGE_ACCESSED); ++ /* ++ * Best effort only; with the lock held the page shouldn't ++ * change anyway, except for the dirty bit to perhaps become set. ++ */ ++ while ( cmpxchg(&l1e_get_intpte(l1t[l1_table_offset(addr)]), ++ l1e_get_intpte(ol1e), l1e_get_intpte(l1e)) != ++ l1e_get_intpte(ol1e) && ++ !(l1e_get_flags(l1e) & _PAGE_DIRTY) ) ++ { ++ l1e_add_flags(ol1e, _PAGE_DIRTY); ++ l1e_add_flags(l1e, _PAGE_DIRTY); ++ } ++ } ++ ++ unmap_domain_page(l1t); ++ } ++ ++ page_unlock(pg); ++ ++ return l1e; ++} ++ + void *do_page_walk(struct vcpu *v, unsigned long addr) + { + l3_pgentry_t l3e; +diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h +index 12ea812381..da1a6f5712 100644 +--- a/xen/include/asm-x86/mm.h ++++ b/xen/include/asm-x86/mm.h +@@ -582,6 +582,7 @@ int vcpu_destroy_pagetables(struct vcpu *); + + void *do_page_walk(struct vcpu *v, unsigned long addr); + l2_pgentry_t page_walk_get_l2e(pagetable_t root, unsigned long addr); ++l1_pgentry_t page_walk_get_l1e(pagetable_t root, unsigned long addr); + + /* Allocator functions for Xen pagetables. */ + void *alloc_xen_pagetable(void); diff --git a/xsa286-4.14-0005-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch b/xsa286-4.14-0005-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch new file mode 100644 index 0000000..ca38773 --- /dev/null +++ b/xsa286-4.14-0005-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch @@ -0,0 +1,101 @@ +From: Jan Beulich +Subject: x86/mm: avoid using top level linear page tables in + {,un}map_domain_page() + +Move the page table recursion two levels down. This entails avoiding +to free the recursive mapping prematurely in free_perdomain_mappings(). + +This is part of XSA-286. + +Reported-by: Jann Horn +Signed-off-by: Jan Beulich +Reviewed-by: George Dunlap +Reviewed-by: Andrew Cooper + +diff --git a/xen/arch/x86/domain_page.c b/xen/arch/x86/domain_page.c +index b03728e18e..ed6a2bf081 100644 +--- a/xen/arch/x86/domain_page.c ++++ b/xen/arch/x86/domain_page.c +@@ -65,7 +65,8 @@ void __init mapcache_override_current(struct vcpu *v) + #define mapcache_l2_entry(e) ((e) >> PAGETABLE_ORDER) + #define MAPCACHE_L2_ENTRIES (mapcache_l2_entry(MAPCACHE_ENTRIES - 1) + 1) + #define MAPCACHE_L1ENT(idx) \ +- __linear_l1_table[l1_linear_offset(MAPCACHE_VIRT_START + pfn_to_paddr(idx))] ++ ((l1_pgentry_t *)(MAPCACHE_VIRT_START | \ ++ ((L2_PAGETABLE_ENTRIES - 1) << L2_PAGETABLE_SHIFT)))[idx] + + void *map_domain_page(mfn_t mfn) + { +@@ -235,6 +236,7 @@ int mapcache_domain_init(struct domain *d) + { + struct mapcache_domain *dcache = &d->arch.pv.mapcache; + unsigned int bitmap_pages; ++ int rc; + + ASSERT(is_pv_domain(d)); + +@@ -243,8 +245,10 @@ int mapcache_domain_init(struct domain *d) + return 0; + #endif + ++ BUILD_BUG_ON(MAPCACHE_VIRT_START & ((1 << L3_PAGETABLE_SHIFT) - 1)); + BUILD_BUG_ON(MAPCACHE_VIRT_END + PAGE_SIZE * (3 + +- 2 * PFN_UP(BITS_TO_LONGS(MAPCACHE_ENTRIES) * sizeof(long))) > ++ 2 * PFN_UP(BITS_TO_LONGS(MAPCACHE_ENTRIES) * sizeof(long))) + ++ (1U << L2_PAGETABLE_SHIFT) > + MAPCACHE_VIRT_START + (PERDOMAIN_SLOT_MBYTES << 20)); + bitmap_pages = PFN_UP(BITS_TO_LONGS(MAPCACHE_ENTRIES) * sizeof(long)); + dcache->inuse = (void *)MAPCACHE_VIRT_END + PAGE_SIZE; +@@ -253,9 +257,25 @@ int mapcache_domain_init(struct domain *d) + + spin_lock_init(&dcache->lock); + +- return create_perdomain_mapping(d, (unsigned long)dcache->inuse, +- 2 * bitmap_pages + 1, +- NIL(l1_pgentry_t *), NULL); ++ rc = create_perdomain_mapping(d, (unsigned long)dcache->inuse, ++ 2 * bitmap_pages + 1, ++ NIL(l1_pgentry_t *), NULL); ++ if ( !rc ) ++ { ++ /* ++ * Install mapping of our L2 table into its own last slot, for easy ++ * access to the L1 entries via MAPCACHE_L1ENT(). ++ */ ++ l3_pgentry_t *l3t = __map_domain_page(d->arch.perdomain_l3_pg); ++ l3_pgentry_t l3e = l3t[l3_table_offset(MAPCACHE_VIRT_END)]; ++ l2_pgentry_t *l2t = map_l2t_from_l3e(l3e); ++ ++ l2e_get_intpte(l2t[L2_PAGETABLE_ENTRIES - 1]) = l3e_get_intpte(l3e); ++ unmap_domain_page(l2t); ++ unmap_domain_page(l3t); ++ } ++ ++ return rc; + } + + int mapcache_vcpu_init(struct vcpu *v) +@@ -346,7 +366,7 @@ mfn_t domain_page_map_to_mfn(const void *ptr) + else + { + ASSERT(va >= MAPCACHE_VIRT_START && va < MAPCACHE_VIRT_END); +- pl1e = &__linear_l1_table[l1_linear_offset(va)]; ++ pl1e = &MAPCACHE_L1ENT(PFN_DOWN(va - MAPCACHE_VIRT_START)); + } + + return l1e_get_mfn(*pl1e); +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 82bc676553..582ea09725 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -5953,6 +5953,10 @@ void free_perdomain_mappings(struct domain *d) + { + struct page_info *l1pg = l2e_get_page(l2tab[j]); + ++ /* mapcache_domain_init() installs a recursive entry. */ ++ if ( l1pg == l2pg ) ++ continue; ++ + if ( l2e_get_flags(l2tab[j]) & _PAGE_AVAIL0 ) + { + l1_pgentry_t *l1tab = __map_domain_page(l1pg); diff --git a/xsa286-4.14-0006-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch b/xsa286-4.14-0006-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch new file mode 100644 index 0000000..69ad37c --- /dev/null +++ b/xsa286-4.14-0006-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch @@ -0,0 +1,106 @@ +From: Jan Beulich +Subject: x86/mm: restrict use of linear page tables to shadow mode code + +Other code does not require them to be set up anymore, so restrict when +to populate the respective L4 slot and reduce visibility of the +accessors. + +While with the removal of all uses the vulnerability is actually fixed, +removing the creation of the linear mapping adds an extra layer of +protection. Similarly reducing visibility of the accessors mostly +eliminates the risk of undue re-introduction of uses of the linear +mappings. + +This is (not strictly) part of XSA-286. + +Signed-off-by: Jan Beulich +Reviewed-by: George Dunlap +Reviewed-by: Andrew Cooper + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 582ea09725..57333bb120 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -1682,9 +1682,10 @@ void init_xen_l4_slots(l4_pgentry_t *l4t, mfn_t l4mfn, + l4t[l4_table_offset(PCI_MCFG_VIRT_START)] = + idle_pg_table[l4_table_offset(PCI_MCFG_VIRT_START)]; + +- /* Slot 258: Self linear mappings. */ ++ /* Slot 258: Self linear mappings (shadow pt only). */ + ASSERT(!mfn_eq(l4mfn, INVALID_MFN)); + l4t[l4_table_offset(LINEAR_PT_VIRT_START)] = ++ !shadow_mode_external(d) ? l4e_empty() : + l4e_from_mfn(l4mfn, __PAGE_HYPERVISOR_RW); + + /* Slot 259: Shadow linear mappings (if applicable) .*/ +diff --git a/xen/arch/x86/mm/shadow/private.h b/xen/arch/x86/mm/shadow/private.h +index 3fd3f0617a..bb2f50cb6e 100644 +--- a/xen/arch/x86/mm/shadow/private.h ++++ b/xen/arch/x86/mm/shadow/private.h +@@ -139,6 +139,15 @@ enum { + # define GUEST_PTE_SIZE 4 + #endif + ++/* Where to find each level of the linear mapping */ ++#define __linear_l1_table ((l1_pgentry_t *)(LINEAR_PT_VIRT_START)) ++#define __linear_l2_table \ ++ ((l2_pgentry_t *)(__linear_l1_table + l1_linear_offset(LINEAR_PT_VIRT_START))) ++#define __linear_l3_table \ ++ ((l3_pgentry_t *)(__linear_l2_table + l2_linear_offset(LINEAR_PT_VIRT_START))) ++#define __linear_l4_table \ ++ ((l4_pgentry_t *)(__linear_l3_table + l3_linear_offset(LINEAR_PT_VIRT_START))) ++ + /****************************************************************************** + * Auditing routines + */ +diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c +index 9e87a55174..ce03f83f52 100644 +--- a/xen/arch/x86/x86_64/mm.c ++++ b/xen/arch/x86/x86_64/mm.c +@@ -808,9 +808,6 @@ void __init paging_init(void) + + machine_to_phys_mapping_valid = 1; + +- /* Set up linear page table mapping. */ +- l4e_write(&idle_pg_table[l4_table_offset(LINEAR_PT_VIRT_START)], +- l4e_from_paddr(__pa(idle_pg_table), __PAGE_HYPERVISOR_RW)); + return; + + nomem: +diff --git a/xen/include/asm-x86/config.h b/xen/include/asm-x86/config.h +index 665e9cc31d..17b8ea0cfd 100644 +--- a/xen/include/asm-x86/config.h ++++ b/xen/include/asm-x86/config.h +@@ -197,7 +197,7 @@ extern unsigned char boot_edid_info[128]; + */ + #define PCI_MCFG_VIRT_START (PML4_ADDR(257)) + #define PCI_MCFG_VIRT_END (PCI_MCFG_VIRT_START + PML4_ENTRY_BYTES) +-/* Slot 258: linear page table (guest table). */ ++/* Slot 258: linear page table (monitor table, HVM only). */ + #define LINEAR_PT_VIRT_START (PML4_ADDR(258)) + #define LINEAR_PT_VIRT_END (LINEAR_PT_VIRT_START + PML4_ENTRY_BYTES) + /* Slot 259: linear page table (shadow table). */ +diff --git a/xen/include/asm-x86/page.h b/xen/include/asm-x86/page.h +index f632affaef..fd2574267c 100644 +--- a/xen/include/asm-x86/page.h ++++ b/xen/include/asm-x86/page.h +@@ -294,19 +294,6 @@ void copy_page_sse2(void *, const void *); + #define vmap_to_mfn(va) _mfn(l1e_get_pfn(*virt_to_xen_l1e((unsigned long)(va)))) + #define vmap_to_page(va) mfn_to_page(vmap_to_mfn(va)) + +-#endif /* !defined(__ASSEMBLY__) */ +- +-/* Where to find each level of the linear mapping */ +-#define __linear_l1_table ((l1_pgentry_t *)(LINEAR_PT_VIRT_START)) +-#define __linear_l2_table \ +- ((l2_pgentry_t *)(__linear_l1_table + l1_linear_offset(LINEAR_PT_VIRT_START))) +-#define __linear_l3_table \ +- ((l3_pgentry_t *)(__linear_l2_table + l2_linear_offset(LINEAR_PT_VIRT_START))) +-#define __linear_l4_table \ +- ((l4_pgentry_t *)(__linear_l3_table + l3_linear_offset(LINEAR_PT_VIRT_START))) +- +- +-#ifndef __ASSEMBLY__ + extern root_pgentry_t idle_pg_table[ROOT_PAGETABLE_ENTRIES]; + extern l2_pgentry_t *compat_idle_pg_table_l2; + extern unsigned int m2p_compat_vstart; From 6554d4f5574f16dc0cfe096b20790d73d0c775b1 Mon Sep 17 00:00:00 2001 From: Jeff Law Date: Fri, 30 Oct 2020 11:56:27 -0600 Subject: [PATCH 052/194] - Work around gcc-11 stringop-overflow diagnostics as well --- xen.gcc11.fixes.patch | 13 +++++++++++++ xen.spec | 5 ++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/xen.gcc11.fixes.patch b/xen.gcc11.fixes.patch index e3c4058..f6b67ac 100644 --- a/xen.gcc11.fixes.patch +++ b/xen.gcc11.fixes.patch @@ -43,3 +43,16 @@ { 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 ++++ b/xen/arch/x86/tboot.c +@@ -91,7 +91,7 @@ static void __init tboot_copy_memory(unsigned char *va, uint32_t size, + + void __init tboot_probe(void) + { +- tboot_shared_t *tboot_shared; ++ tboot_shared_t * volatile tboot_shared; + + /* Look for valid page-aligned address for shared page. */ + if ( !opt_tboot_pa || (opt_tboot_pa & ~PAGE_MASK) ) diff --git a/xen.spec b/xen.spec index 531de5f..34c318c 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.14.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 @@ -966,6 +966,9 @@ fi %endif %changelog +* Fri Oct 30 2020 Jeff Law - 4.14.0-6 +- Work around gcc-11 stringop-overflow diagnostics as well + * Wed Oct 28 2020 Michael Young - 4.14.0-7 - x86 PV guest INVLPG-like flushes may leave stale TLB entries [XSA-286, CVE-2020-27674] (#1891092) From f458ebe460a5c29df70fb985d7e791f362c38ae0 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 3 Nov 2020 20:48:36 +0000 Subject: [PATCH 053/194] revised patch for XSA-286 (mitigating performance impact) --- xen.spec | 22 +-- ...and-L3-parts-of-the-walk-out-of-do_p.patch | 73 -------- ...H_TLB_GLOBAL-in-do_mmu_update-for-XP.patch | 58 ++++++ ...-mm-check-page-types-in-do_page_walk.patch | 170 ----------------- ...-in-response-to-paging-structure-cha.patch | 166 +++++++++++++++++ ...ng-linear-page-tables-in-map_guest_l.patch | 92 ---------- ...ng-linear-page-tables-in-guest_get_e.patch | 172 ------------------ ...ng-top-level-linear-page-tables-in-u.patch | 101 ---------- ...use-of-linear-page-tables-to-shadow-.patch | 106 ----------- 9 files changed, 232 insertions(+), 728 deletions(-) delete mode 100644 xsa286-4.14-0001-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch create mode 100644 xsa286-4.14-0001-x86-pv-Drop-FLUSH_TLB_GLOBAL-in-do_mmu_update-for-XP.patch delete mode 100644 xsa286-4.14-0002-x86-mm-check-page-types-in-do_page_walk.patch create mode 100644 xsa286-4.14-0002-x86-pv-Flush-TLB-in-response-to-paging-structure-cha.patch delete mode 100644 xsa286-4.14-0003-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch delete mode 100644 xsa286-4.14-0004-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch delete mode 100644 xsa286-4.14-0005-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch delete mode 100644 xsa286-4.14-0006-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch diff --git a/xen.spec b/xen.spec index 34c318c..a1b2011 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.14.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 @@ -136,12 +136,8 @@ Patch67: xsa347-4.14-1.patch Patch68: xsa347-4.14-2.patch Patch69: xsa347-4.14-3.patch Patch70: xen.gcc11.fixes.patch -Patch71: xsa286-4.14-0001-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch -Patch72: xsa286-4.14-0002-x86-mm-check-page-types-in-do_page_walk.patch -Patch73: xsa286-4.14-0003-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch -Patch74: xsa286-4.14-0004-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch -Patch75: xsa286-4.14-0005-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch -Patch76: xsa286-4.14-0006-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch +Patch71: xsa286-4.14-0001-x86-pv-Drop-FLUSH_TLB_GLOBAL-in-do_mmu_update-for-XP.patch +Patch72: xsa286-4.14-0002-x86-pv-Flush-TLB-in-response-to-paging-structure-cha.patch %if %build_qemutrad @@ -369,10 +365,6 @@ manage Xen virtual machines. %patch70 -p1 %patch71 -p1 %patch72 -p1 -%patch73 -p1 -%patch74 -p1 -%patch75 -p1 -%patch76 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -966,7 +958,10 @@ fi %endif %changelog -* Fri Oct 30 2020 Jeff Law - 4.14.0-6 +* Tue Nov 03 2020 Michael Young - 4.14.0-9 +- revised patch for XSA-286 (mitigating performance impact) + +* Fri Oct 30 2020 Jeff Law - 4.14.0-8 - Work around gcc-11 stringop-overflow diagnostics as well * Wed Oct 28 2020 Michael Young - 4.14.0-7 @@ -975,8 +970,6 @@ fi - simplify grub scripts (patches from Thierry Vignaud ) - some fixes for gcc 11 -* Thu Oct 22 2020 Michael Young - 4.14.0-6.1 - * Tue Oct 20 2020 Michael Young - 4.14.0-6 - x86: Race condition in Xen mapping code [XSA-345, CVE-2020-27672] (#1891097) @@ -984,6 +977,7 @@ fi (#1891093) - unsafe AMD IOMMU page table updates [XSA-347, CVE-2020-27670] (#1891088) + * Tue Sep 22 2020 Michael Young - 4.14.0-5 - x86 pv: Crash when handling guest access to MSR_MISC_ENABLE [XSA-333, CVE-2020-25602] (#1881619) diff --git a/xsa286-4.14-0001-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch b/xsa286-4.14-0001-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch deleted file mode 100644 index 6459e1f..0000000 --- a/xsa286-4.14-0001-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch +++ /dev/null @@ -1,73 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: split L4 and L3 parts of the walk out of do_page_walk() - -The L3 one at least is going to be re-used by a subsequent patch, and -splitting the L4 one then as well seems only natural. - -This is part of XSA-286. - -Signed-off-by: Jan Beulich -Reviewed-by: George Dunlap -Reviewed-by: Andrew Cooper - -diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c -index 48fd60a876..c25eb01e41 100644 ---- a/xen/arch/x86/x86_64/mm.c -+++ b/xen/arch/x86/x86_64/mm.c -@@ -44,26 +44,47 @@ unsigned int __read_mostly m2p_compat_vstart = __HYPERVISOR_COMPAT_VIRT_START; - - l2_pgentry_t *compat_idle_pg_table_l2; - --void *do_page_walk(struct vcpu *v, unsigned long addr) -+static l4_pgentry_t page_walk_get_l4e(pagetable_t root, unsigned long addr) - { -- unsigned long mfn = pagetable_get_pfn(v->arch.guest_table); -- l4_pgentry_t l4e, *l4t; -- l3_pgentry_t l3e, *l3t; -- l2_pgentry_t l2e, *l2t; -- l1_pgentry_t l1e, *l1t; -+ unsigned long mfn = pagetable_get_pfn(root); -+ l4_pgentry_t *l4t, l4e; - -- if ( !is_pv_vcpu(v) || !is_canonical_address(addr) ) -- return NULL; -+ if ( !is_canonical_address(addr) ) -+ return l4e_empty(); - - l4t = map_domain_page(_mfn(mfn)); - l4e = l4t[l4_table_offset(addr)]; - unmap_domain_page(l4t); -+ -+ return l4e; -+} -+ -+static l3_pgentry_t page_walk_get_l3e(pagetable_t root, unsigned long addr) -+{ -+ l4_pgentry_t l4e = page_walk_get_l4e(root, addr); -+ l3_pgentry_t *l3t, l3e; -+ - if ( !(l4e_get_flags(l4e) & _PAGE_PRESENT) ) -- return NULL; -+ return l3e_empty(); - - l3t = map_l3t_from_l4e(l4e); - l3e = l3t[l3_table_offset(addr)]; - unmap_domain_page(l3t); -+ -+ return l3e; -+} -+ -+void *do_page_walk(struct vcpu *v, unsigned long addr) -+{ -+ l3_pgentry_t l3e; -+ l2_pgentry_t l2e, *l2t; -+ l1_pgentry_t l1e, *l1t; -+ unsigned long mfn; -+ -+ if ( !is_pv_vcpu(v) ) -+ return NULL; -+ -+ l3e = page_walk_get_l3e(v->arch.guest_table, addr); - mfn = l3e_get_pfn(l3e); - if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || !mfn_valid(_mfn(mfn)) ) - return NULL; diff --git a/xsa286-4.14-0001-x86-pv-Drop-FLUSH_TLB_GLOBAL-in-do_mmu_update-for-XP.patch b/xsa286-4.14-0001-x86-pv-Drop-FLUSH_TLB_GLOBAL-in-do_mmu_update-for-XP.patch new file mode 100644 index 0000000..5e9109d --- /dev/null +++ b/xsa286-4.14-0001-x86-pv-Drop-FLUSH_TLB_GLOBAL-in-do_mmu_update-for-XP.patch @@ -0,0 +1,58 @@ +From 941f69a428cd989144300519e548e346c681a1b3 Mon Sep 17 00:00:00 2001 +From: Andrew Cooper +Date: Thu, 22 Oct 2020 11:28:58 +0100 +Subject: [PATCH 1/2] x86/pv: Drop FLUSH_TLB_GLOBAL in do_mmu_update() for XPTI + +c/s 9d1d31ad9498 "x86: slightly reduce Meltdown band-aid overhead" removed the +use of Global TLB flushes on the Xen entry path, but added a FLUSH_TLB_GLOBAL +to the L4 path in do_mmu_update(). + +However, this was unnecessary. + +It is the guests responsibility to perform appropriate TLB flushing if the L4 +modification altered an established mapping in a flush-relevant way. In this +case, an MMUEXT_OP hypercall will follow. The case which Xen needs to cover +is when new mappings are created, and the resync on the exit-to-guest path +covers this correctly. + +There is a corner case with multiple vCPUs in hypercalls at the same time, +which 9d1d31ad9498 changed, and this patch changes back to its original XPTI +behaviour. + +Architecturally, established TLB entries can continue to be used until the +broadcast flush has completed. Therefore, even with concurrent hypercalls, +the guest cannot depend on older mappings not being used until an MMUEXT_OP +hypercall completes. Xen's implementation of guest-initiated flushes will +take correct effect on top of an in-progress hypercall, picking up new mapping +setting before the other vCPU's MMUEXT_OP completes. + +Note: The correctness of this change is not impacted by whether XPTI uses +global mappings or not. Correctness there depends on the behaviour of Xen on +the entry/exit paths when switching two/from the XPTI "shadow" pagetables. + +This is (not really) XSA-286 (but necessary to simplify the logic). + +Fixes: 9d1d31ad9498 ("x86: slightly reduce Meltdown band-aid overhead") +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 055e1c3a3d95b1e753148369fbc4ba48782dd602) +--- + xen/arch/x86/mm.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 3cb6fabdae..1caa2df0a5 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -4193,7 +4193,7 @@ long do_mmu_update( + + cpumask_andnot(mask, pt_owner->dirty_cpumask, cpumask_of(cpu)); + if ( !cpumask_empty(mask) ) +- flush_mask(mask, FLUSH_TLB_GLOBAL | FLUSH_ROOT_PGTBL); ++ flush_mask(mask, FLUSH_ROOT_PGTBL); + } + + perfc_add(num_page_updates, i); +-- +2.20.1 + diff --git a/xsa286-4.14-0002-x86-mm-check-page-types-in-do_page_walk.patch b/xsa286-4.14-0002-x86-mm-check-page-types-in-do_page_walk.patch deleted file mode 100644 index 3a05a74..0000000 --- a/xsa286-4.14-0002-x86-mm-check-page-types-in-do_page_walk.patch +++ /dev/null @@ -1,170 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: check page types in do_page_walk() - -For page table entries read to be guaranteed valid, transiently locking -the pages and validating their types is necessary. Note that guest use -of linear page tables is intentionally not taken into account here, as -ordinary data (guest stacks) can't possibly live inside page tables. - -This is part of XSA-286. - -Signed-off-by: Jan Beulich -Reviewed-by: George Dunlap -Reviewed-by: Andrew Cooper - -diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c -index c25eb01e41..6305cf6033 100644 ---- a/xen/arch/x86/x86_64/mm.c -+++ b/xen/arch/x86/x86_64/mm.c -@@ -46,15 +46,29 @@ l2_pgentry_t *compat_idle_pg_table_l2; - - static l4_pgentry_t page_walk_get_l4e(pagetable_t root, unsigned long addr) - { -- unsigned long mfn = pagetable_get_pfn(root); -- l4_pgentry_t *l4t, l4e; -+ mfn_t mfn = pagetable_get_mfn(root); -+ /* current's root page table can't disappear under our feet. */ -+ bool need_lock = !mfn_eq(mfn, pagetable_get_mfn(current->arch.guest_table)); -+ struct page_info *pg; -+ l4_pgentry_t l4e = l4e_empty(); - - if ( !is_canonical_address(addr) ) - return l4e_empty(); - -- l4t = map_domain_page(_mfn(mfn)); -- l4e = l4t[l4_table_offset(addr)]; -- unmap_domain_page(l4t); -+ pg = mfn_to_page(mfn); -+ if ( need_lock && !page_lock(pg) ) -+ return l4e_empty(); -+ -+ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l4_page_table ) -+ { -+ l4_pgentry_t *l4t = map_domain_page(mfn); -+ -+ l4e = l4t[l4_table_offset(addr)]; -+ unmap_domain_page(l4t); -+ } -+ -+ if ( need_lock ) -+ page_unlock(pg); - - return l4e; - } -@@ -62,14 +76,26 @@ static l4_pgentry_t page_walk_get_l4e(pagetable_t root, unsigned long addr) - static l3_pgentry_t page_walk_get_l3e(pagetable_t root, unsigned long addr) - { - l4_pgentry_t l4e = page_walk_get_l4e(root, addr); -- l3_pgentry_t *l3t, l3e; -+ mfn_t mfn = l4e_get_mfn(l4e); -+ struct page_info *pg; -+ l3_pgentry_t l3e = l3e_empty(); - - if ( !(l4e_get_flags(l4e) & _PAGE_PRESENT) ) - return l3e_empty(); - -- l3t = map_l3t_from_l4e(l4e); -- l3e = l3t[l3_table_offset(addr)]; -- unmap_domain_page(l3t); -+ pg = mfn_to_page(mfn); -+ if ( !page_lock(pg) ) -+ return l3e_empty(); -+ -+ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l3_page_table ) -+ { -+ l3_pgentry_t *l3t = map_domain_page(mfn); -+ -+ l3e = l3t[l3_table_offset(addr)]; -+ unmap_domain_page(l3t); -+ } -+ -+ page_unlock(pg); - - return l3e; - } -@@ -77,44 +103,67 @@ static l3_pgentry_t page_walk_get_l3e(pagetable_t root, unsigned long addr) - void *do_page_walk(struct vcpu *v, unsigned long addr) - { - l3_pgentry_t l3e; -- l2_pgentry_t l2e, *l2t; -- l1_pgentry_t l1e, *l1t; -- unsigned long mfn; -+ l2_pgentry_t l2e = l2e_empty(); -+ l1_pgentry_t l1e = l1e_empty(); -+ mfn_t mfn; -+ struct page_info *pg; - - if ( !is_pv_vcpu(v) ) - return NULL; - - l3e = page_walk_get_l3e(v->arch.guest_table, addr); -- mfn = l3e_get_pfn(l3e); -- if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || !mfn_valid(_mfn(mfn)) ) -+ mfn = l3e_get_mfn(l3e); -+ if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || !mfn_valid(mfn) ) - return NULL; - if ( (l3e_get_flags(l3e) & _PAGE_PSE) ) - { -- mfn += PFN_DOWN(addr & ((1UL << L3_PAGETABLE_SHIFT) - 1)); -+ mfn = mfn_add(mfn, PFN_DOWN(addr & ((1UL << L3_PAGETABLE_SHIFT) - 1))); - goto ret; - } - -- l2t = map_domain_page(_mfn(mfn)); -- l2e = l2t[l2_table_offset(addr)]; -- unmap_domain_page(l2t); -- mfn = l2e_get_pfn(l2e); -- if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) || !mfn_valid(_mfn(mfn)) ) -+ pg = mfn_to_page(mfn); -+ if ( !page_lock(pg) ) -+ return NULL; -+ -+ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l2_page_table ) -+ { -+ const l2_pgentry_t *l2t = map_domain_page(mfn); -+ -+ l2e = l2t[l2_table_offset(addr)]; -+ unmap_domain_page(l2t); -+ } -+ -+ page_unlock(pg); -+ -+ mfn = l2e_get_mfn(l2e); -+ if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) || !mfn_valid(mfn) ) - return NULL; - if ( (l2e_get_flags(l2e) & _PAGE_PSE) ) - { -- mfn += PFN_DOWN(addr & ((1UL << L2_PAGETABLE_SHIFT) - 1)); -+ mfn = mfn_add(mfn, PFN_DOWN(addr & ((1UL << L2_PAGETABLE_SHIFT) - 1))); - goto ret; - } - -- l1t = map_domain_page(_mfn(mfn)); -- l1e = l1t[l1_table_offset(addr)]; -- unmap_domain_page(l1t); -- mfn = l1e_get_pfn(l1e); -- if ( !(l1e_get_flags(l1e) & _PAGE_PRESENT) || !mfn_valid(_mfn(mfn)) ) -+ pg = mfn_to_page(mfn); -+ if ( !page_lock(pg) ) -+ return NULL; -+ -+ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l1_page_table ) -+ { -+ const l1_pgentry_t *l1t = map_domain_page(mfn); -+ -+ l1e = l1t[l1_table_offset(addr)]; -+ unmap_domain_page(l1t); -+ } -+ -+ page_unlock(pg); -+ -+ mfn = l1e_get_mfn(l1e); -+ if ( !(l1e_get_flags(l1e) & _PAGE_PRESENT) || !mfn_valid(mfn) ) - return NULL; - - ret: -- return map_domain_page(_mfn(mfn)) + (addr & ~PAGE_MASK); -+ return map_domain_page(mfn) + (addr & ~PAGE_MASK); - } - - /* diff --git a/xsa286-4.14-0002-x86-pv-Flush-TLB-in-response-to-paging-structure-cha.patch b/xsa286-4.14-0002-x86-pv-Flush-TLB-in-response-to-paging-structure-cha.patch new file mode 100644 index 0000000..22af1ca --- /dev/null +++ b/xsa286-4.14-0002-x86-pv-Flush-TLB-in-response-to-paging-structure-cha.patch @@ -0,0 +1,166 @@ +From 10bb63c203f42d931fa1fa7dbbae7ce1765cecf2 Mon Sep 17 00:00:00 2001 +From: Andrew Cooper +Date: Mon, 19 Oct 2020 15:51:22 +0100 +Subject: [PATCH 2/2] x86/pv: Flush TLB in response to paging structure changes + +With MMU_UPDATE, a PV guest can make changes to higher level pagetables. This +is safe from Xen's point of view (as the update only affects guest mappings), +and the guest is required to flush (if necessary) after making updates. + +However, Xen's use of linear pagetables (UPDATE_VA_MAPPING, GNTTABOP_map, +writeable pagetables, etc.) is an implementation detail outside of the +API/ABI. + +Changes in the paging structure require invalidations in the linear pagetable +range for subsequent accesses into the linear pagetables to access non-stale +mappings. Xen must provide suitable flushing to prevent intermixed guest +actions from accidentally accessing/modifying the wrong pagetable. + +For all L2 and higher modifications, flush the TLB. PV guests cannot create +L2 or higher entries with the Global bit set, so no mappings established in +the linear range can be global. (This could in principle be an order 39 flush +starting at LINEAR_PT_VIRT_START, but no such mechanism exists in practice.) + +Express the necessary flushes as a set of booleans which accumulate across the +operation. Comment the flushing logic extensively. + +This is XSA-286. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 16a20963b3209788f2c0d3a3eebb7d92f03f5883) +--- + xen/arch/x86/mm.c | 69 ++++++++++++++++++++++++++++++++++++++++------- + 1 file changed, 59 insertions(+), 10 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 1caa2df0a5..61cf6a7b9b 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -3896,7 +3896,8 @@ long do_mmu_update( + struct vcpu *curr = current, *v = curr; + struct domain *d = v->domain, *pt_owner = d, *pg_owner; + mfn_t map_mfn = INVALID_MFN, mfn; +- bool sync_guest = false; ++ bool flush_linear_pt = false, flush_root_pt_local = false, ++ flush_root_pt_others = false; + uint32_t xsm_needed = 0; + uint32_t xsm_checked = 0; + int rc = put_old_guest_table(curr); +@@ -4046,6 +4047,8 @@ long do_mmu_update( + break; + rc = mod_l2_entry(va, l2e_from_intpte(req.val), mfn, + cmd == MMU_PT_UPDATE_PRESERVE_AD, v); ++ if ( !rc ) ++ flush_linear_pt = true; + break; + + case PGT_l3_page_table: +@@ -4053,6 +4056,8 @@ long do_mmu_update( + break; + rc = mod_l3_entry(va, l3e_from_intpte(req.val), mfn, + cmd == MMU_PT_UPDATE_PRESERVE_AD, v); ++ if ( !rc ) ++ flush_linear_pt = true; + break; + + case PGT_l4_page_table: +@@ -4060,6 +4065,8 @@ long do_mmu_update( + break; + rc = mod_l4_entry(va, l4e_from_intpte(req.val), mfn, + cmd == MMU_PT_UPDATE_PRESERVE_AD, v); ++ if ( !rc ) ++ flush_linear_pt = true; + if ( !rc && pt_owner->arch.pv.xpti ) + { + bool local_in_use = false; +@@ -4068,7 +4075,7 @@ long do_mmu_update( + mfn) ) + { + local_in_use = true; +- get_cpu_info()->root_pgt_changed = true; ++ flush_root_pt_local = true; + } + + /* +@@ -4080,7 +4087,7 @@ long do_mmu_update( + (1 + !!(page->u.inuse.type_info & PGT_pinned) + + mfn_eq(pagetable_get_mfn(curr->arch.guest_table_user), + mfn) + local_in_use) ) +- sync_guest = true; ++ flush_root_pt_others = true; + } + break; + +@@ -4182,19 +4189,61 @@ long do_mmu_update( + if ( va ) + unmap_domain_page(va); + +- if ( sync_guest ) ++ /* ++ * Perform required TLB maintenance. ++ * ++ * This logic currently depend on flush_linear_pt being a superset of the ++ * flush_root_pt_* conditions. ++ * ++ * pt_owner may not be current->domain. This may occur during ++ * construction of 32bit PV guests, or debugging of PV guests. The ++ * behaviour cannot be correct with domain unpaused. We therefore expect ++ * pt_owner->dirty_cpumask to be empty, but it is a waste of effort to ++ * explicitly check for, and exclude, this corner case. ++ * ++ * flush_linear_pt requires a FLUSH_TLB to all dirty CPUs. The flush must ++ * be performed now to maintain correct behaviour across a multicall. ++ * i.e. we cannot relax FLUSH_TLB to FLUSH_ROOT_PGTBL, given that the ++ * former is a side effect of the latter, because the resync (which is in ++ * the return-to-guest path) happens too late. ++ * ++ * flush_root_pt_* requires FLUSH_ROOT_PGTBL on either the local CPU ++ * (implies pt_owner == current->domain and current->processor set in ++ * pt_owner->dirty_cpumask), and/or all *other* dirty CPUs as there are ++ * references we can't account for locally. ++ */ ++ if ( flush_linear_pt /* || flush_root_pt_local || flush_root_pt_others */ ) + { ++ unsigned int cpu = smp_processor_id(); ++ cpumask_t *mask = pt_owner->dirty_cpumask; ++ + /* +- * Force other vCPU-s of the affected guest to pick up L4 entry +- * changes (if any). ++ * Always handle local flushing separately (if applicable), to ++ * separate the flush invocations appropriately for scope of the two ++ * flush_root_pt_* variables. + */ +- unsigned int cpu = smp_processor_id(); +- cpumask_t *mask = per_cpu(scratch_cpumask, cpu); ++ if ( likely(cpumask_test_cpu(cpu, mask)) ) ++ { ++ mask = per_cpu(scratch_cpumask, cpu); + +- cpumask_andnot(mask, pt_owner->dirty_cpumask, cpumask_of(cpu)); ++ cpumask_copy(mask, pt_owner->dirty_cpumask); ++ __cpumask_clear_cpu(cpu, mask); ++ ++ flush_local(FLUSH_TLB | ++ (flush_root_pt_local ? FLUSH_ROOT_PGTBL : 0)); ++ } ++ else ++ /* Sanity check. flush_root_pt_local implies local cpu is dirty. */ ++ ASSERT(!flush_root_pt_local); ++ ++ /* Flush the remote dirty CPUs. Does not include the local CPU. */ + if ( !cpumask_empty(mask) ) +- flush_mask(mask, FLUSH_ROOT_PGTBL); ++ flush_mask(mask, FLUSH_TLB | ++ (flush_root_pt_others ? FLUSH_ROOT_PGTBL : 0)); + } ++ else ++ /* Sanity check. flush_root_pt_* implies flush_linear_pt. */ ++ ASSERT(!flush_root_pt_local && !flush_root_pt_others); + + perfc_add(num_page_updates, i); + +-- +2.20.1 + diff --git a/xsa286-4.14-0003-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch b/xsa286-4.14-0003-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch deleted file mode 100644 index 5a5c251..0000000 --- a/xsa286-4.14-0003-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch +++ /dev/null @@ -1,92 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: avoid using linear page tables in map_guest_l1e() -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Replace the linear L2 table access by an actual page walk. - -This is part of XSA-286. - -Reported-by: Jann Horn -Signed-off-by: Jan Beulich -Signed-off-by: Roger Pau Monné -Reviewed-by: George Dunlap -Reviewed-by: Andrew Cooper - -diff --git a/xen/arch/x86/pv/mm.c b/xen/arch/x86/pv/mm.c -index 5d4cd00941..7be098f5ef 100644 ---- a/xen/arch/x86/pv/mm.c -+++ b/xen/arch/x86/pv/mm.c -@@ -40,11 +40,14 @@ l1_pgentry_t *map_guest_l1e(unsigned long linear, mfn_t *gl1mfn) - if ( unlikely(!__addr_ok(linear)) ) - return NULL; - -- /* Find this l1e and its enclosing l1mfn in the linear map. */ -- if ( __copy_from_user(&l2e, -- &__linear_l2_table[l2_linear_offset(linear)], -- sizeof(l2_pgentry_t)) ) -+ if ( unlikely(!(current->arch.flags & TF_kernel_mode)) ) -+ { -+ ASSERT_UNREACHABLE(); - return NULL; -+ } -+ -+ /* Find this l1e and its enclosing l1mfn. */ -+ l2e = page_walk_get_l2e(current->arch.guest_table, linear); - - /* Check flags that it will be safe to read the l1e. */ - if ( (l2e_get_flags(l2e) & (_PAGE_PRESENT | _PAGE_PSE)) != _PAGE_PRESENT ) -diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c -index 6305cf6033..71a8bfc024 100644 ---- a/xen/arch/x86/x86_64/mm.c -+++ b/xen/arch/x86/x86_64/mm.c -@@ -100,6 +100,34 @@ static l3_pgentry_t page_walk_get_l3e(pagetable_t root, unsigned long addr) - return l3e; - } - -+l2_pgentry_t page_walk_get_l2e(pagetable_t root, unsigned long addr) -+{ -+ l3_pgentry_t l3e = page_walk_get_l3e(root, addr); -+ mfn_t mfn = l3e_get_mfn(l3e); -+ struct page_info *pg; -+ l2_pgentry_t l2e = l2e_empty(); -+ -+ if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || -+ (l3e_get_flags(l3e) & _PAGE_PSE) ) -+ return l2e_empty(); -+ -+ pg = mfn_to_page(mfn); -+ if ( !page_lock(pg) ) -+ return l2e_empty(); -+ -+ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l2_page_table ) -+ { -+ l2_pgentry_t *l2t = map_domain_page(mfn); -+ -+ l2e = l2t[l2_table_offset(addr)]; -+ unmap_domain_page(l2t); -+ } -+ -+ page_unlock(pg); -+ -+ return l2e; -+} -+ - void *do_page_walk(struct vcpu *v, unsigned long addr) - { - l3_pgentry_t l3e; -diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h -index 7e74996053..12ea812381 100644 ---- a/xen/include/asm-x86/mm.h -+++ b/xen/include/asm-x86/mm.h -@@ -579,7 +579,9 @@ void audit_domains(void); - void make_cr3(struct vcpu *v, mfn_t mfn); - void update_cr3(struct vcpu *v); - int vcpu_destroy_pagetables(struct vcpu *); -+ - void *do_page_walk(struct vcpu *v, unsigned long addr); -+l2_pgentry_t page_walk_get_l2e(pagetable_t root, unsigned long addr); - - /* Allocator functions for Xen pagetables. */ - void *alloc_xen_pagetable(void); diff --git a/xsa286-4.14-0004-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch b/xsa286-4.14-0004-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch deleted file mode 100644 index 9783f79..0000000 --- a/xsa286-4.14-0004-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch +++ /dev/null @@ -1,172 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: avoid using linear page tables in guest_get_eff_kern_l1e() - -First of all drop guest_get_eff_l1e() entirely - there's no actual user -of it: pv_ro_page_fault() has a guest_kernel_mode() conditional around -its only call site. - -Then replace the linear L1 table access by an actual page walk. - -This is part of XSA-286. - -Reported-by: Jann Horn -Signed-off-by: Jan Beulich -Reviewed-by: George Dunlap -Reviewed-by: Andrew Cooper - -diff --git a/xen/arch/x86/pv/mm.c b/xen/arch/x86/pv/mm.c -index 7be098f5ef..5e4081aecd 100644 ---- a/xen/arch/x86/pv/mm.c -+++ b/xen/arch/x86/pv/mm.c -@@ -59,27 +59,6 @@ l1_pgentry_t *map_guest_l1e(unsigned long linear, mfn_t *gl1mfn) - } - - /* -- * Read the guest's l1e that maps this address, from the kernel-mode -- * page tables. -- */ --static l1_pgentry_t guest_get_eff_kern_l1e(unsigned long linear) --{ -- struct vcpu *curr = current; -- const bool user_mode = !(curr->arch.flags & TF_kernel_mode); -- l1_pgentry_t l1e; -- -- if ( user_mode ) -- toggle_guest_pt(curr); -- -- l1e = guest_get_eff_l1e(linear); -- -- if ( user_mode ) -- toggle_guest_pt(curr); -- -- return l1e; --} -- --/* - * Map a guest's LDT page (covering the byte at @offset from start of the LDT) - * into Xen's virtual range. Returns true if the mapping changed, false - * otherwise. -diff --git a/xen/arch/x86/pv/mm.h b/xen/arch/x86/pv/mm.h -index a1bd473b29..43d33a1fd1 100644 ---- a/xen/arch/x86/pv/mm.h -+++ b/xen/arch/x86/pv/mm.h -@@ -5,19 +5,19 @@ l1_pgentry_t *map_guest_l1e(unsigned long linear, mfn_t *gl1mfn); - - int new_guest_cr3(mfn_t mfn); - --/* Read a PV guest's l1e that maps this linear address. */ --static inline l1_pgentry_t guest_get_eff_l1e(unsigned long linear) -+/* -+ * Read the guest's l1e that maps this address, from the kernel-mode -+ * page tables. -+ */ -+static inline l1_pgentry_t guest_get_eff_kern_l1e(unsigned long linear) - { -- l1_pgentry_t l1e; -+ l1_pgentry_t l1e = l1e_empty(); - - ASSERT(!paging_mode_translate(current->domain)); - ASSERT(!paging_mode_external(current->domain)); - -- if ( unlikely(!__addr_ok(linear)) || -- __copy_from_user(&l1e, -- &__linear_l1_table[l1_linear_offset(linear)], -- sizeof(l1_pgentry_t)) ) -- l1e = l1e_empty(); -+ if ( likely(__addr_ok(linear)) ) -+ l1e = page_walk_get_l1e(current->arch.guest_table, linear); - - return l1e; - } -diff --git a/xen/arch/x86/pv/ro-page-fault.c b/xen/arch/x86/pv/ro-page-fault.c -index 0eedb70002..ce31dd401d 100644 ---- a/xen/arch/x86/pv/ro-page-fault.c -+++ b/xen/arch/x86/pv/ro-page-fault.c -@@ -349,7 +349,7 @@ int pv_ro_page_fault(unsigned long addr, struct cpu_user_regs *regs) - bool mmio_ro; - - /* Attempt to read the PTE that maps the VA being accessed. */ -- pte = guest_get_eff_l1e(addr); -+ pte = guest_get_eff_kern_l1e(addr); - - /* We are only looking for read-only mappings */ - if ( ((l1e_get_flags(pte) & (_PAGE_PRESENT | _PAGE_RW)) != _PAGE_PRESENT) ) -diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c -index 71a8bfc024..9e87a55174 100644 ---- a/xen/arch/x86/x86_64/mm.c -+++ b/xen/arch/x86/x86_64/mm.c -@@ -128,6 +128,62 @@ l2_pgentry_t page_walk_get_l2e(pagetable_t root, unsigned long addr) - return l2e; - } - -+/* -+ * For now no "set_accessed" parameter, as all callers want it set to true. -+ * For now also no "set_dirty" parameter, as all callers deal with r/o -+ * mappings, and we don't want to set the dirty bit there (conflicts with -+ * CET-SS). However, as there are CPUs which may set the dirty bit on r/o -+ * PTEs, the logic below tolerates the bit becoming set "behind our backs". -+ */ -+l1_pgentry_t page_walk_get_l1e(pagetable_t root, unsigned long addr) -+{ -+ l2_pgentry_t l2e = page_walk_get_l2e(root, addr); -+ mfn_t mfn = l2e_get_mfn(l2e); -+ struct page_info *pg; -+ l1_pgentry_t l1e = l1e_empty(); -+ -+ if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) || -+ (l2e_get_flags(l2e) & _PAGE_PSE) ) -+ return l1e_empty(); -+ -+ pg = mfn_to_page(mfn); -+ if ( !page_lock(pg) ) -+ return l1e_empty(); -+ -+ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l1_page_table ) -+ { -+ l1_pgentry_t *l1t = map_domain_page(mfn); -+ -+ l1e = l1t[l1_table_offset(addr)]; -+ -+ if ( (l1e_get_flags(l1e) & (_PAGE_ACCESSED | _PAGE_PRESENT)) == -+ _PAGE_PRESENT ) -+ { -+ l1_pgentry_t ol1e = l1e; -+ -+ l1e_add_flags(l1e, _PAGE_ACCESSED); -+ /* -+ * Best effort only; with the lock held the page shouldn't -+ * change anyway, except for the dirty bit to perhaps become set. -+ */ -+ while ( cmpxchg(&l1e_get_intpte(l1t[l1_table_offset(addr)]), -+ l1e_get_intpte(ol1e), l1e_get_intpte(l1e)) != -+ l1e_get_intpte(ol1e) && -+ !(l1e_get_flags(l1e) & _PAGE_DIRTY) ) -+ { -+ l1e_add_flags(ol1e, _PAGE_DIRTY); -+ l1e_add_flags(l1e, _PAGE_DIRTY); -+ } -+ } -+ -+ unmap_domain_page(l1t); -+ } -+ -+ page_unlock(pg); -+ -+ return l1e; -+} -+ - void *do_page_walk(struct vcpu *v, unsigned long addr) - { - l3_pgentry_t l3e; -diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h -index 12ea812381..da1a6f5712 100644 ---- a/xen/include/asm-x86/mm.h -+++ b/xen/include/asm-x86/mm.h -@@ -582,6 +582,7 @@ int vcpu_destroy_pagetables(struct vcpu *); - - void *do_page_walk(struct vcpu *v, unsigned long addr); - l2_pgentry_t page_walk_get_l2e(pagetable_t root, unsigned long addr); -+l1_pgentry_t page_walk_get_l1e(pagetable_t root, unsigned long addr); - - /* Allocator functions for Xen pagetables. */ - void *alloc_xen_pagetable(void); diff --git a/xsa286-4.14-0005-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch b/xsa286-4.14-0005-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch deleted file mode 100644 index ca38773..0000000 --- a/xsa286-4.14-0005-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch +++ /dev/null @@ -1,101 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: avoid using top level linear page tables in - {,un}map_domain_page() - -Move the page table recursion two levels down. This entails avoiding -to free the recursive mapping prematurely in free_perdomain_mappings(). - -This is part of XSA-286. - -Reported-by: Jann Horn -Signed-off-by: Jan Beulich -Reviewed-by: George Dunlap -Reviewed-by: Andrew Cooper - -diff --git a/xen/arch/x86/domain_page.c b/xen/arch/x86/domain_page.c -index b03728e18e..ed6a2bf081 100644 ---- a/xen/arch/x86/domain_page.c -+++ b/xen/arch/x86/domain_page.c -@@ -65,7 +65,8 @@ void __init mapcache_override_current(struct vcpu *v) - #define mapcache_l2_entry(e) ((e) >> PAGETABLE_ORDER) - #define MAPCACHE_L2_ENTRIES (mapcache_l2_entry(MAPCACHE_ENTRIES - 1) + 1) - #define MAPCACHE_L1ENT(idx) \ -- __linear_l1_table[l1_linear_offset(MAPCACHE_VIRT_START + pfn_to_paddr(idx))] -+ ((l1_pgentry_t *)(MAPCACHE_VIRT_START | \ -+ ((L2_PAGETABLE_ENTRIES - 1) << L2_PAGETABLE_SHIFT)))[idx] - - void *map_domain_page(mfn_t mfn) - { -@@ -235,6 +236,7 @@ int mapcache_domain_init(struct domain *d) - { - struct mapcache_domain *dcache = &d->arch.pv.mapcache; - unsigned int bitmap_pages; -+ int rc; - - ASSERT(is_pv_domain(d)); - -@@ -243,8 +245,10 @@ int mapcache_domain_init(struct domain *d) - return 0; - #endif - -+ BUILD_BUG_ON(MAPCACHE_VIRT_START & ((1 << L3_PAGETABLE_SHIFT) - 1)); - BUILD_BUG_ON(MAPCACHE_VIRT_END + PAGE_SIZE * (3 + -- 2 * PFN_UP(BITS_TO_LONGS(MAPCACHE_ENTRIES) * sizeof(long))) > -+ 2 * PFN_UP(BITS_TO_LONGS(MAPCACHE_ENTRIES) * sizeof(long))) + -+ (1U << L2_PAGETABLE_SHIFT) > - MAPCACHE_VIRT_START + (PERDOMAIN_SLOT_MBYTES << 20)); - bitmap_pages = PFN_UP(BITS_TO_LONGS(MAPCACHE_ENTRIES) * sizeof(long)); - dcache->inuse = (void *)MAPCACHE_VIRT_END + PAGE_SIZE; -@@ -253,9 +257,25 @@ int mapcache_domain_init(struct domain *d) - - spin_lock_init(&dcache->lock); - -- return create_perdomain_mapping(d, (unsigned long)dcache->inuse, -- 2 * bitmap_pages + 1, -- NIL(l1_pgentry_t *), NULL); -+ rc = create_perdomain_mapping(d, (unsigned long)dcache->inuse, -+ 2 * bitmap_pages + 1, -+ NIL(l1_pgentry_t *), NULL); -+ if ( !rc ) -+ { -+ /* -+ * Install mapping of our L2 table into its own last slot, for easy -+ * access to the L1 entries via MAPCACHE_L1ENT(). -+ */ -+ l3_pgentry_t *l3t = __map_domain_page(d->arch.perdomain_l3_pg); -+ l3_pgentry_t l3e = l3t[l3_table_offset(MAPCACHE_VIRT_END)]; -+ l2_pgentry_t *l2t = map_l2t_from_l3e(l3e); -+ -+ l2e_get_intpte(l2t[L2_PAGETABLE_ENTRIES - 1]) = l3e_get_intpte(l3e); -+ unmap_domain_page(l2t); -+ unmap_domain_page(l3t); -+ } -+ -+ return rc; - } - - int mapcache_vcpu_init(struct vcpu *v) -@@ -346,7 +366,7 @@ mfn_t domain_page_map_to_mfn(const void *ptr) - else - { - ASSERT(va >= MAPCACHE_VIRT_START && va < MAPCACHE_VIRT_END); -- pl1e = &__linear_l1_table[l1_linear_offset(va)]; -+ pl1e = &MAPCACHE_L1ENT(PFN_DOWN(va - MAPCACHE_VIRT_START)); - } - - return l1e_get_mfn(*pl1e); -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 82bc676553..582ea09725 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -5953,6 +5953,10 @@ void free_perdomain_mappings(struct domain *d) - { - struct page_info *l1pg = l2e_get_page(l2tab[j]); - -+ /* mapcache_domain_init() installs a recursive entry. */ -+ if ( l1pg == l2pg ) -+ continue; -+ - if ( l2e_get_flags(l2tab[j]) & _PAGE_AVAIL0 ) - { - l1_pgentry_t *l1tab = __map_domain_page(l1pg); diff --git a/xsa286-4.14-0006-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch b/xsa286-4.14-0006-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch deleted file mode 100644 index 69ad37c..0000000 --- a/xsa286-4.14-0006-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch +++ /dev/null @@ -1,106 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: restrict use of linear page tables to shadow mode code - -Other code does not require them to be set up anymore, so restrict when -to populate the respective L4 slot and reduce visibility of the -accessors. - -While with the removal of all uses the vulnerability is actually fixed, -removing the creation of the linear mapping adds an extra layer of -protection. Similarly reducing visibility of the accessors mostly -eliminates the risk of undue re-introduction of uses of the linear -mappings. - -This is (not strictly) part of XSA-286. - -Signed-off-by: Jan Beulich -Reviewed-by: George Dunlap -Reviewed-by: Andrew Cooper - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 582ea09725..57333bb120 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1682,9 +1682,10 @@ void init_xen_l4_slots(l4_pgentry_t *l4t, mfn_t l4mfn, - l4t[l4_table_offset(PCI_MCFG_VIRT_START)] = - idle_pg_table[l4_table_offset(PCI_MCFG_VIRT_START)]; - -- /* Slot 258: Self linear mappings. */ -+ /* Slot 258: Self linear mappings (shadow pt only). */ - ASSERT(!mfn_eq(l4mfn, INVALID_MFN)); - l4t[l4_table_offset(LINEAR_PT_VIRT_START)] = -+ !shadow_mode_external(d) ? l4e_empty() : - l4e_from_mfn(l4mfn, __PAGE_HYPERVISOR_RW); - - /* Slot 259: Shadow linear mappings (if applicable) .*/ -diff --git a/xen/arch/x86/mm/shadow/private.h b/xen/arch/x86/mm/shadow/private.h -index 3fd3f0617a..bb2f50cb6e 100644 ---- a/xen/arch/x86/mm/shadow/private.h -+++ b/xen/arch/x86/mm/shadow/private.h -@@ -139,6 +139,15 @@ enum { - # define GUEST_PTE_SIZE 4 - #endif - -+/* Where to find each level of the linear mapping */ -+#define __linear_l1_table ((l1_pgentry_t *)(LINEAR_PT_VIRT_START)) -+#define __linear_l2_table \ -+ ((l2_pgentry_t *)(__linear_l1_table + l1_linear_offset(LINEAR_PT_VIRT_START))) -+#define __linear_l3_table \ -+ ((l3_pgentry_t *)(__linear_l2_table + l2_linear_offset(LINEAR_PT_VIRT_START))) -+#define __linear_l4_table \ -+ ((l4_pgentry_t *)(__linear_l3_table + l3_linear_offset(LINEAR_PT_VIRT_START))) -+ - /****************************************************************************** - * Auditing routines - */ -diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c -index 9e87a55174..ce03f83f52 100644 ---- a/xen/arch/x86/x86_64/mm.c -+++ b/xen/arch/x86/x86_64/mm.c -@@ -808,9 +808,6 @@ void __init paging_init(void) - - machine_to_phys_mapping_valid = 1; - -- /* Set up linear page table mapping. */ -- l4e_write(&idle_pg_table[l4_table_offset(LINEAR_PT_VIRT_START)], -- l4e_from_paddr(__pa(idle_pg_table), __PAGE_HYPERVISOR_RW)); - return; - - nomem: -diff --git a/xen/include/asm-x86/config.h b/xen/include/asm-x86/config.h -index 665e9cc31d..17b8ea0cfd 100644 ---- a/xen/include/asm-x86/config.h -+++ b/xen/include/asm-x86/config.h -@@ -197,7 +197,7 @@ extern unsigned char boot_edid_info[128]; - */ - #define PCI_MCFG_VIRT_START (PML4_ADDR(257)) - #define PCI_MCFG_VIRT_END (PCI_MCFG_VIRT_START + PML4_ENTRY_BYTES) --/* Slot 258: linear page table (guest table). */ -+/* Slot 258: linear page table (monitor table, HVM only). */ - #define LINEAR_PT_VIRT_START (PML4_ADDR(258)) - #define LINEAR_PT_VIRT_END (LINEAR_PT_VIRT_START + PML4_ENTRY_BYTES) - /* Slot 259: linear page table (shadow table). */ -diff --git a/xen/include/asm-x86/page.h b/xen/include/asm-x86/page.h -index f632affaef..fd2574267c 100644 ---- a/xen/include/asm-x86/page.h -+++ b/xen/include/asm-x86/page.h -@@ -294,19 +294,6 @@ void copy_page_sse2(void *, const void *); - #define vmap_to_mfn(va) _mfn(l1e_get_pfn(*virt_to_xen_l1e((unsigned long)(va)))) - #define vmap_to_page(va) mfn_to_page(vmap_to_mfn(va)) - --#endif /* !defined(__ASSEMBLY__) */ -- --/* Where to find each level of the linear mapping */ --#define __linear_l1_table ((l1_pgentry_t *)(LINEAR_PT_VIRT_START)) --#define __linear_l2_table \ -- ((l2_pgentry_t *)(__linear_l1_table + l1_linear_offset(LINEAR_PT_VIRT_START))) --#define __linear_l3_table \ -- ((l3_pgentry_t *)(__linear_l2_table + l2_linear_offset(LINEAR_PT_VIRT_START))) --#define __linear_l4_table \ -- ((l4_pgentry_t *)(__linear_l3_table + l3_linear_offset(LINEAR_PT_VIRT_START))) -- -- --#ifndef __ASSEMBLY__ - extern root_pgentry_t idle_pg_table[ROOT_PAGETABLE_ENTRIES]; - extern l2_pgentry_t *compat_idle_pg_table_l2; - extern unsigned int m2p_compat_vstart; From 30f2ec4b6ca91786459f8ad9257140b2a6dfa11f Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 10 Nov 2020 20:21:03 +0000 Subject: [PATCH 054/194] Information leak via power sidechannel [XSA-351] add make as build requires --- xen.spec | 14 +++- xsa351-arm.patch | 58 +++++++++++++++ xsa351-x86-4.14-1.patch | 155 ++++++++++++++++++++++++++++++++++++++++ xsa351-x86-4.14-2.patch | 126 ++++++++++++++++++++++++++++++++ 4 files changed, 351 insertions(+), 2 deletions(-) create mode 100644 xsa351-arm.patch create mode 100644 xsa351-x86-4.14-1.patch create mode 100644 xsa351-x86-4.14-2.patch diff --git a/xen.spec b/xen.spec index a1b2011..1f9d6fc 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.14.0 -Release: 9%{?dist} +Release: 10%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -138,6 +138,9 @@ Patch69: xsa347-4.14-3.patch Patch70: xen.gcc11.fixes.patch Patch71: xsa286-4.14-0001-x86-pv-Drop-FLUSH_TLB_GLOBAL-in-do_mmu_update-for-XP.patch Patch72: xsa286-4.14-0002-x86-pv-Flush-TLB-in-response-to-paging-structure-cha.patch +Patch73: xsa351-arm.patch +Patch74: xsa351-x86-4.14-1.patch +Patch75: xsa351-x86-4.14-2.patch %if %build_qemutrad @@ -181,7 +184,7 @@ BuildRequires: checkpolicy m4 # cross compiler for building 64-bit hypervisor on ix86 BuildRequires: gcc-x86_64-linux-gnu %endif -BuildRequires: gcc +BuildRequires: gcc make Requires: iproute Requires: python3-lxml Requires: xen-runtime = %{version}-%{release} @@ -365,6 +368,9 @@ manage Xen virtual machines. %patch70 -p1 %patch71 -p1 %patch72 -p1 +%patch73 -p1 +%patch74 -p1 +%patch75 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -958,6 +964,10 @@ fi %endif %changelog +* Tue Nov 10 2020 Michael Young - 4.14.0-10 +- Information leak via power sidechannel [XSA-351] +- add make as build requires + * Tue Nov 03 2020 Michael Young - 4.14.0-9 - revised patch for XSA-286 (mitigating performance impact) diff --git a/xsa351-arm.patch b/xsa351-arm.patch new file mode 100644 index 0000000..d0d1941 --- /dev/null +++ b/xsa351-arm.patch @@ -0,0 +1,58 @@ +From: Julien Grall +Subject: xen/arm: Always trap AMU system registers + +The Activity Monitors Unit (AMU) has been introduced by ARMv8.4. It is +considered to be unsafe to be expose to guests as they might expose +information about code executed by other guests or the host. + +Arm provided a way to trap all the AMU system registers by setting +CPTR_EL2.TAM to 1. + +Unfortunately, on older revision of the specification, the bit 30 (now +CPTR_EL1.TAM) was RES0. Because of that, Xen is setting it to 0 and +therefore the system registers would be exposed to the guest when it is +run on processors with AMU. + +As the bit is mark as UNKNOWN at boot in Armv8.4, the only safe solution +for us is to always set CPTR_EL1.TAM to 1. + +Guest trying to access the AMU system registers will now receive an +undefined instruction. Unfortunately, this means that even well-behaved +guest may fail to boot because we don't sanitize the ID registers. + +This is a known issues with other Armv8.0+ features (e.g. SVE, Pointer +Auth). This will taken care separately. + +This is part of XSA-351 (or XSA-93 re-born). + +Signed-off-by: Julien Grall +Reviewed-by: Andre Przywara +Reviewed-by: Stefano Stabellini +Reviewed-by: Bertrand Marquis + +diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c +index a36f145e67..22bd1bd4c6 100644 +--- a/xen/arch/arm/traps.c ++++ b/xen/arch/arm/traps.c +@@ -151,7 +151,8 @@ void init_traps(void) + * On ARM64 the TCPx bits which we set here (0..9,12,13) are all + * RES1, i.e. they would trap whether we did this write or not. + */ +- WRITE_SYSREG((HCPTR_CP_MASK & ~(HCPTR_CP(10) | HCPTR_CP(11))) | HCPTR_TTA, ++ WRITE_SYSREG((HCPTR_CP_MASK & ~(HCPTR_CP(10) | HCPTR_CP(11))) | ++ HCPTR_TTA | HCPTR_TAM, + CPTR_EL2); + + /* +diff --git a/xen/include/asm-arm/processor.h b/xen/include/asm-arm/processor.h +index 3ca67f8157..d3d12a9d19 100644 +--- a/xen/include/asm-arm/processor.h ++++ b/xen/include/asm-arm/processor.h +@@ -351,6 +351,7 @@ + #define VTCR_RES1 (_AC(1,UL)<<31) + + /* HCPTR Hyp. Coprocessor Trap Register */ ++#define HCPTR_TAM ((_AC(1,U)<<30)) + #define HCPTR_TTA ((_AC(1,U)<<20)) /* Trap trace registers */ + #define HCPTR_CP(x) ((_AC(1,U)<<(x))) /* Trap Coprocessor x */ + #define HCPTR_CP_MASK ((_AC(1,U)<<14)-1) diff --git a/xsa351-x86-4.14-1.patch b/xsa351-x86-4.14-1.patch new file mode 100644 index 0000000..1be14fb --- /dev/null +++ b/xsa351-x86-4.14-1.patch @@ -0,0 +1,155 @@ +From: =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= +Subject: x86/msr: fix handling of MSR_IA32_PERF_{STATUS/CTL} +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Currently a PV hardware domain can also be given control over the CPU +frequency, and such guest is allowed to write to MSR_IA32_PERF_CTL. +However since commit 322ec7c89f6 the default behavior has been changed +to reject accesses to not explicitly handled MSRs, preventing PV +guests that manage CPU frequency from reading +MSR_IA32_PERF_{STATUS/CTL}. + +Additionally some HVM guests (Windows at least) will attempt to read +MSR_IA32_PERF_CTL and will panic if given back a #GP fault: + + vmx.c:3035:d8v0 RDMSR 0x00000199 unimplemented + d8v0 VIRIDIAN CRASH: 3b c0000096 fffff806871c1651 ffffda0253683720 0 + +Move the handling of MSR_IA32_PERF_{STATUS/CTL} to the common MSR +handling shared between HVM and PV guests, and add an explicit case +for reads to MSR_IA32_PERF_{STATUS/CTL}. + +Restore previous behavior and allow PV guests with the required +permissions to read the contents of the mentioned MSRs. Non privileged +guests will get 0 when trying to read those registers, as writes to +MSR_IA32_PERF_CTL by such guest will already be silently dropped. + +Fixes: 322ec7c89f6 ('x86/pv: disallow access to unknown MSRs') +Fixes: 84e848fd7a1 ('x86/hvm: disallow access to unknown MSRs') +Signed-off-by: Roger Pau Monné +Signed-off-by: Andrew Cooper +Reviewed-by: Roger Pau Monné +Reviewed-by: Jan Beulich +(cherry picked from commit 3059178798a23ba870ff86ff54d442a07e6651fc) + +diff --git a/xen/arch/x86/msr.c b/xen/arch/x86/msr.c +index d72ab0fa1f..3db26faf08 100644 +--- a/xen/arch/x86/msr.c ++++ b/xen/arch/x86/msr.c +@@ -245,6 +245,25 @@ int guest_rdmsr(struct vcpu *v, uint32_t msr, uint64_t *val) + *val = msrs->misc_features_enables.raw; + break; + ++ /* ++ * These MSRs are not enumerated in CPUID. They have been around ++ * since the Pentium 4, and implemented by other vendors. ++ * ++ * Some versions of Windows try reading these before setting up a #GP ++ * handler, and Linux has several unguarded reads as well. Provide ++ * RAZ semantics, in general, but permit a cpufreq controller dom0 to ++ * have full access. ++ */ ++ case MSR_IA32_PERF_STATUS: ++ case MSR_IA32_PERF_CTL: ++ if ( !(cp->x86_vendor & (X86_VENDOR_INTEL | X86_VENDOR_CENTAUR)) ) ++ goto gp_fault; ++ ++ *val = 0; ++ if ( likely(!is_cpufreq_controller(d)) || rdmsr_safe(msr, *val) == 0 ) ++ break; ++ goto gp_fault; ++ + case MSR_X2APIC_FIRST ... MSR_X2APIC_LAST: + if ( !is_hvm_domain(d) || v != curr ) + goto gp_fault; +@@ -343,6 +362,7 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) + case MSR_INTEL_CORE_THREAD_COUNT: + case MSR_INTEL_PLATFORM_INFO: + case MSR_ARCH_CAPABILITIES: ++ case MSR_IA32_PERF_STATUS: + /* Read-only */ + case MSR_TEST_CTRL: + case MSR_TSX_FORCE_ABORT: +@@ -454,6 +474,21 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) + break; + } + ++ /* ++ * This MSR is not enumerated in CPUID. It has been around since the ++ * Pentium 4, and implemented by other vendors. ++ * ++ * To match the RAZ semantics, implement as write-discard, except for ++ * a cpufreq controller dom0 which has full access. ++ */ ++ case MSR_IA32_PERF_CTL: ++ if ( !(cp->x86_vendor & (X86_VENDOR_INTEL | X86_VENDOR_CENTAUR)) ) ++ goto gp_fault; ++ ++ if ( likely(!is_cpufreq_controller(d)) || wrmsr_safe(msr, val) == 0 ) ++ break; ++ goto gp_fault; ++ + case MSR_X2APIC_FIRST ... MSR_X2APIC_LAST: + if ( !is_hvm_domain(d) || v != curr ) + goto gp_fault; +diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c +index 85a9fd4767..5c7b9117ae 100644 +--- a/xen/arch/x86/pv/emul-priv-op.c ++++ b/xen/arch/x86/pv/emul-priv-op.c +@@ -820,12 +820,6 @@ static inline uint64_t guest_misc_enable(uint64_t val) + return val; + } + +-static inline bool is_cpufreq_controller(const struct domain *d) +-{ +- return ((cpufreq_controller == FREQCTL_dom0_kernel) && +- is_hardware_domain(d)); +-} +- + static int read_msr(unsigned int reg, uint64_t *val, + struct x86_emulate_ctxt *ctxt) + { +@@ -1070,14 +1064,6 @@ static int write_msr(unsigned int reg, uint64_t val, + return X86EMUL_OKAY; + break; + +- case MSR_IA32_PERF_CTL: +- if ( boot_cpu_data.x86_vendor != X86_VENDOR_INTEL ) +- break; +- if ( likely(!is_cpufreq_controller(currd)) || +- wrmsr_safe(reg, val) == 0 ) +- return X86EMUL_OKAY; +- break; +- + case MSR_IA32_THERM_CONTROL: + case MSR_IA32_ENERGY_PERF_BIAS: + if ( boot_cpu_data.x86_vendor != X86_VENDOR_INTEL ) +diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h +index a0d87ef9d0..97ba8e0795 100644 +--- a/xen/include/xen/sched.h ++++ b/xen/include/xen/sched.h +@@ -1071,6 +1071,22 @@ extern enum cpufreq_controller { + FREQCTL_none, FREQCTL_dom0_kernel, FREQCTL_xen + } cpufreq_controller; + ++static always_inline bool is_cpufreq_controller(const struct domain *d) ++{ ++ /* ++ * A PV dom0 can be nominated as the cpufreq controller, instead of using ++ * Xen's cpufreq driver, at which point dom0 gets direct access to certain ++ * MSRs. ++ * ++ * This interface only works when dom0 is identity pinned and has the same ++ * number of vCPUs as pCPUs on the system. ++ * ++ * It would be far better to paravirtualise the interface. ++ */ ++ return (is_pv_domain(d) && is_hardware_domain(d) && ++ cpufreq_controller == FREQCTL_dom0_kernel); ++} ++ + int cpupool_move_domain(struct domain *d, struct cpupool *c); + int cpupool_do_sysctl(struct xen_sysctl_cpupool_op *op); + int cpupool_get_id(const struct domain *d); diff --git a/xsa351-x86-4.14-2.patch b/xsa351-x86-4.14-2.patch new file mode 100644 index 0000000..2c0f4e7 --- /dev/null +++ b/xsa351-x86-4.14-2.patch @@ -0,0 +1,126 @@ +From: Andrew Cooper +Subject: x86/msr: Disallow guest access to the RAPL MSRs + +Researchers have demonstrated using the RAPL interface to perform a +differential power analysis attack to recover AES keys used by other cores in +the system. + +Furthermore, even privileged guests cannot use this interface correctly, due +to MSR scope and vcpu scheduling issues. The interface would want to be +paravirtualised to be used sensibly. + +Disallow access to the RAPL MSRs completely, as well as other MSRs which +potentially access fine grain power information. + +This is part of XSA-351. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/msr.c b/xen/arch/x86/msr.c +index 3db26faf08..aa107823ac 100644 +--- a/xen/arch/x86/msr.c ++++ b/xen/arch/x86/msr.c +@@ -185,6 +185,13 @@ int guest_rdmsr(struct vcpu *v, uint32_t msr, uint64_t *val) + case MSR_TSX_CTRL: + case MSR_MCU_OPT_CTRL: + case MSR_RTIT_OUTPUT_BASE ... MSR_RTIT_ADDR_B(7): ++ case MSR_RAPL_POWER_UNIT: ++ case MSR_PKG_POWER_LIMIT ... MSR_PKG_POWER_INFO: ++ case MSR_DRAM_POWER_LIMIT ... MSR_DRAM_POWER_INFO: ++ case MSR_PP0_POWER_LIMIT ... MSR_PP0_POLICY: ++ case MSR_PP1_POWER_LIMIT ... MSR_PP1_POLICY: ++ case MSR_PLATFORM_ENERGY_COUNTER: ++ case MSR_PLATFORM_POWER_LIMIT: + case MSR_U_CET: + case MSR_S_CET: + case MSR_PL0_SSP ... MSR_INTERRUPT_SSP_TABLE: +@@ -192,6 +199,8 @@ int guest_rdmsr(struct vcpu *v, uint32_t msr, uint64_t *val) + case MSR_AMD64_LWP_CBADDR: + case MSR_PPIN_CTL: + case MSR_PPIN: ++ case MSR_F15H_CU_POWER ... MSR_F15H_CU_MAX_POWER: ++ case MSR_AMD_RAPL_POWER_UNIT ... MSR_AMD_PKG_ENERGY_STATUS: + case MSR_AMD_PPIN_CTL: + case MSR_AMD_PPIN: + /* Not offered to guests. */ +@@ -369,6 +378,13 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) + case MSR_TSX_CTRL: + case MSR_MCU_OPT_CTRL: + case MSR_RTIT_OUTPUT_BASE ... MSR_RTIT_ADDR_B(7): ++ case MSR_RAPL_POWER_UNIT: ++ case MSR_PKG_POWER_LIMIT ... MSR_PKG_POWER_INFO: ++ case MSR_DRAM_POWER_LIMIT ... MSR_DRAM_POWER_INFO: ++ case MSR_PP0_POWER_LIMIT ... MSR_PP0_POLICY: ++ case MSR_PP1_POWER_LIMIT ... MSR_PP1_POLICY: ++ case MSR_PLATFORM_ENERGY_COUNTER: ++ case MSR_PLATFORM_POWER_LIMIT: + case MSR_U_CET: + case MSR_S_CET: + case MSR_PL0_SSP ... MSR_INTERRUPT_SSP_TABLE: +@@ -376,6 +392,8 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) + case MSR_AMD64_LWP_CBADDR: + case MSR_PPIN_CTL: + case MSR_PPIN: ++ case MSR_F15H_CU_POWER ... MSR_F15H_CU_MAX_POWER: ++ case MSR_AMD_RAPL_POWER_UNIT ... MSR_AMD_PKG_ENERGY_STATUS: + case MSR_AMD_PPIN_CTL: + case MSR_AMD_PPIN: + /* Not offered to guests. */ +diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h +index 0fe98af923..5e64ecff91 100644 +--- a/xen/include/asm-x86/msr-index.h ++++ b/xen/include/asm-x86/msr-index.h +@@ -77,6 +77,38 @@ + #define MSR_RTIT_ADDR_A(n) (0x00000580 + (n) * 2) + #define MSR_RTIT_ADDR_B(n) (0x00000581 + (n) * 2) + ++/* ++ * Intel Runtime Average Power Limiting (RAPL) interface. Power plane base ++ * addresses (MSR_*_POWER_LIMIT) are model specific, but have so-far been ++ * consistent since their introduction in SandyBridge. ++ * ++ * Offsets of functionality from the power plane base is architectural, but ++ * not all power planes support all functionality. ++ */ ++#define MSR_RAPL_POWER_UNIT 0x00000606 ++ ++#define MSR_PKG_POWER_LIMIT 0x00000610 ++#define MSR_PKG_ENERGY_STATUS 0x00000611 ++#define MSR_PKG_PERF_STATUS 0x00000613 ++#define MSR_PKG_POWER_INFO 0x00000614 ++ ++#define MSR_DRAM_POWER_LIMIT 0x00000618 ++#define MSR_DRAM_ENERGY_STATUS 0x00000619 ++#define MSR_DRAM_PERF_STATUS 0x0000061b ++#define MSR_DRAM_POWER_INFO 0x0000061c ++ ++#define MSR_PP0_POWER_LIMIT 0x00000638 ++#define MSR_PP0_ENERGY_STATUS 0x00000639 ++#define MSR_PP0_POLICY 0x0000063a ++ ++#define MSR_PP1_POWER_LIMIT 0x00000640 ++#define MSR_PP1_ENERGY_STATUS 0x00000641 ++#define MSR_PP1_POLICY 0x00000642 ++ ++/* Intel Platform-wide power interface. */ ++#define MSR_PLATFORM_ENERGY_COUNTER 0x0000064d ++#define MSR_PLATFORM_POWER_LIMIT 0x0000065c ++ + #define MSR_U_CET 0x000006a0 + #define MSR_S_CET 0x000006a2 + #define CET_SHSTK_EN (_AC(1, ULL) << 0) +@@ -92,6 +124,13 @@ + #define PASID_PASID_MASK 0x000fffff + #define PASID_VALID (_AC(1, ULL) << 31) + ++#define MSR_F15H_CU_POWER 0xc001007a ++#define MSR_F15H_CU_MAX_POWER 0xc001007b ++ ++#define MSR_AMD_RAPL_POWER_UNIT 0xc0010299 ++#define MSR_AMD_CORE_ENERGY_STATUS 0xc001029a ++#define MSR_AMD_PKG_ENERGY_STATUS 0xc001029b ++ + /* + * Legacy MSR constants in need of cleanup. No new MSRs below this comment. + */ From 4d1f7dbe43f8ca68cec7ea74d8ab1cb117a2fe27 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 12 Nov 2020 20:24:10 +0000 Subject: [PATCH 055/194] add CVE and bug reference --- xen.spec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 1f9d6fc..b0f7d27 100644 --- a/xen.spec +++ b/xen.spec @@ -965,7 +965,8 @@ fi %changelog * Tue Nov 10 2020 Michael Young - 4.14.0-10 -- Information leak via power sidechannel [XSA-351] +- Information leak via power sidechannel [XSA-351, CVE-2020-28368] + (#1897146) - add make as build requires * Tue Nov 03 2020 Michael Young - 4.14.0-9 From 32c79cab5f9d2be048d67d0fbd80eec3898b373b Mon Sep 17 00:00:00 2001 From: Michael Young Date: Mon, 23 Nov 2020 21:43:26 +0000 Subject: [PATCH 056/194] support zstd compressed kernels (dom0 only) based on linux kernel code --- xen.spec | 7 +- zstd-dom0.patch | 9214 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 9220 insertions(+), 1 deletion(-) create mode 100644 zstd-dom0.patch diff --git a/xen.spec b/xen.spec index b0f7d27..92cfe82 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.14.0 -Release: 10%{?dist} +Release: 11%{?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,7 @@ Patch72: xsa286-4.14-0002-x86-pv-Flush-TLB-in-response-to-paging-structure-cha.p Patch73: xsa351-arm.patch Patch74: xsa351-x86-4.14-1.patch Patch75: xsa351-x86-4.14-2.patch +Patch76: zstd-dom0.patch %if %build_qemutrad @@ -371,6 +372,7 @@ manage Xen virtual machines. %patch73 -p1 %patch74 -p1 %patch75 -p1 +%patch76 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -964,6 +966,9 @@ fi %endif %changelog +* Mon Nov 23 2020 Michael Young - 4.14.0-11 +- support zstd compressed kernels (dom0 only) based on linux kernel code + * Tue Nov 10 2020 Michael Young - 4.14.0-10 - Information leak via power sidechannel [XSA-351, CVE-2020-28368] (#1897146) diff --git a/zstd-dom0.patch b/zstd-dom0.patch new file mode 100644 index 0000000..57b7f76 --- /dev/null +++ b/zstd-dom0.patch @@ -0,0 +1,9214 @@ +diff --git a/xen/common/Makefile b/xen/common/Makefile +index d109f279a4..5ba09f04ac 100644 +--- a/xen/common/Makefile ++++ b/xen/common/Makefile +@@ -59,7 +59,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..0da27b0ab6 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, "\050\265\057\375", 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..a2c382fddc +--- /dev/null ++++ b/xen/common/unzstd.c +@@ -0,0 +1,332 @@ ++/* ++ * 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 ++ * ++ * 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. ++ */ ++ ++/* ++ * Preboot environments #include "path/to/decompress_unzstd.c". ++ * All of the source files we depend on must be #included. ++ * zstd's only source dependeny is xxhash, which has no source ++ * dependencies. ++ * ++ * When UNZSTD_PREBOOT is defined we declare __decompress(), which is ++ * used for kernel decompression, instead of unzstd(). ++ * ++ * Define __DISABLE_EXPORTS in preboot environments to prevent symbols ++ * from xxhash and zstd from being exported by the EXPORT_SYMBOL macro. ++ */ ++ ++#include "decompress.h" ++#include "xxhash.c" ++#include "zstd/entropy_common.c" ++#include "zstd/fse_decompress.c" ++#include "zstd/huf_decompress.c" ++#include "zstd/zstd_common.c" ++#include "zstd/decompress.c" ++ ++#include ++ ++/* 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, unsigned int 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, long out_len, ++ 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; ++ ++ if (out_len == 0) ++ out_len = INT_MAX; /* no limit */ ++ ++ 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 (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 (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; ++} ++ ++STATIC int INIT unzstd(unsigned char *buf, unsigned int len, ++ int (*fill)(void*, unsigned int), ++ int (*flush)(void*, unsigned int), ++ unsigned char *out_buf, ++ unsigned int *pos, ++ void (*error)(const char *x)) ++{ ++ return __unzstd(buf, len, fill, flush, out_buf, 0, pos, error); ++} +diff --git a/xen/common/xxhash.c b/xen/common/xxhash.c +new file mode 100644 +index 0000000000..3ab3e01859 +--- /dev/null ++++ b/xen/common/xxhash.c +@@ -0,0 +1,484 @@ ++/* ++ * 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 "zstd/private.h" ++ ++/*-************************************* ++ * Macros ++ **************************************/ ++#define xxh_rotl32(x, r) ((x << r) | (x >> (32 - r))) ++#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 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; ++ ++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 INIT xxh32_copy_state(struct xxh32_state *dst, const struct xxh32_state *src) ++{ ++ memcpy(dst, src, sizeof(*dst)); ++} ++ ++void INIT xxh64_copy_state(struct xxh64_state *dst, const struct xxh64_state *src) ++{ ++ memcpy(dst, src, sizeof(*dst)); ++} ++ ++/*-*************************** ++ * Simple Hash Functions ++ ****************************/ ++static uint32_t INIT 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 INIT 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; ++} ++ ++static uint64_t INIT 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 INIT 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 INIT 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 INIT 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)); ++} ++ ++void INIT 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 INIT 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 INIT 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; ++} ++ ++int INIT 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 INIT 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; ++} +diff --git a/xen/common/zstd/bitstream.h b/xen/common/zstd/bitstream.h +new file mode 100644 +index 0000000000..3a49784d5c +--- /dev/null ++++ b/xen/common/zstd/bitstream.h +@@ -0,0 +1,379 @@ ++/* ++ * 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); ++ /* fall through */ ++ case 6: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[5]) << (sizeof(bitD->bitContainer) * 8 - 24); ++ /* fall through */ ++ case 5: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[4]) << (sizeof(bitD->bitContainer) * 8 - 32); ++ /* fall through */ ++ case 4: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[3]) << 24; ++ /* fall through */ ++ case 3: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[2]) << 16; ++ /* fall through */ ++ case 2: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[1]) << 8; ++ 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..8e627d881a +--- /dev/null ++++ b/xen/common/zstd/decompress.c +@@ -0,0 +1,2489 @@ ++/** ++ * 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 void INIT 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); ++ /* fall through */ ++ 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 INIT 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 INIT 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 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 INIT *ZSTD_DDictDictContent(const ZSTD_DDict *ddict); ++static size_t INIT 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 */ ++ /* fall through */ ++ ++ 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 */ ++ /* fall through */ ++ ++ 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; ++ } ++ /* fall through */ ++ ++ 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 */ ++ } ++ /* fall through */ ++ ++ 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 */ ++ } ++ } ++ /* fall through */ ++ ++ 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..ecbfe51dfb +--- /dev/null ++++ b/xen/common/zstd/error_private.h +@@ -0,0 +1,53 @@ ++/** ++ * 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 */ ++#include /* enum list */ ++ ++/* **************************************** ++* 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 ERR_isError(size_t code) { return (code > ERROR(maxCode)); } ++ ++ERR_STATIC ERR_enum ERR_getErrorCode(size_t code) ++{ ++ if (!ERR_isError(code)) ++ return (ERR_enum)0; ++ return (ERR_enum)(0 - code); ++} ++ ++#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..041a5a1f0a +--- /dev/null ++++ b/xen/common/zstd/fse_decompress.c +@@ -0,0 +1,323 @@ ++/* ++ * 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 /* 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..f79603a12f +--- /dev/null ++++ b/xen/common/zstd/huf_decompress.c +@@ -0,0 +1,958 @@ ++/* ++ * 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 /* 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..d2fa444687 +--- /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 "private.h" ++ ++/*-**************************************** ++* 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/private.h b/xen/common/zstd/private.h +new file mode 100644 +index 0000000000..fac4d3c095 +--- /dev/null ++++ b/xen/common/zstd/private.h +@@ -0,0 +1,105 @@ ++#ifndef ZSTD_PRIVATE_H ++#define ZSTD_PRIVATE_H ++ ++#include ++#include ++#include ++ ++typedef ssize_t __attribute__((__mode__(__pointer__))) ptrdiff_t; ++ ++/* from kernel include/linux/unaligned/access_ok.h */ ++ ++static always_inline u16 get_unaligned_le16(const void *p) ++{ ++ return le16_to_cpup((__le16 *)p); ++} ++ ++static always_inline u32 get_unaligned_le32(const void *p) ++{ ++ return le32_to_cpup((__le32 *)p); ++} ++ ++static always_inline u64 get_unaligned_le64(const void *p) ++{ ++ return le64_to_cpup((__le64 *)p); ++} ++ ++static always_inline u32 get_unaligned_be32(const void *p) ++{ ++ return be32_to_cpup((__be32 *)p); ++} ++ ++static always_inline u64 get_unaligned_be64(const void *p) ++{ ++ return be64_to_cpup((__be64 *)p); ++} ++ ++static always_inline void put_unaligned_le16(u16 val, void *p) ++{ ++ *((__le16 *)p) = cpu_to_le16(val); ++} ++ ++static always_inline void put_unaligned_le32(u32 val, void *p) ++{ ++ *((__le32 *)p) = cpu_to_le32(val); ++} ++ ++static always_inline void put_unaligned_le64(u64 val, void *p) ++{ ++ *((__le64 *)p) = cpu_to_le64(val); ++} ++ ++static always_inline void put_unaligned_be32(u32 val, void *p) ++{ ++ *((__be32 *)p) = cpu_to_be32(val); ++} ++ ++static always_inline void put_unaligned_be64(u64 val, void *p) ++{ ++ *((__be64 *)p) = cpu_to_be64(val); ++} ++ ++ ++/* from kernel include/asm-generic/unaligned.h with linux/unaligned/generic.h ++ assuming little endian */ ++ ++extern void __bad_unaligned_access_size(void); ++ ++#define get_unaligned(ptr) ((__force typeof(*(ptr)))({ \ ++ __builtin_choose_expr(sizeof(*(ptr)) == 1, *(ptr), \ ++ __builtin_choose_expr(sizeof(*(ptr)) == 2, get_unaligned_le16((ptr)), \ ++ __builtin_choose_expr(sizeof(*(ptr)) == 4, get_unaligned_le32((ptr)), \ ++ __builtin_choose_expr(sizeof(*(ptr)) == 8, get_unaligned_le64((ptr)), \ ++ __bad_unaligned_access_size())))); \ ++ })) ++ ++#define put_unaligned(val, ptr) ({ \ ++ void *__gu_p = (ptr); \ ++ switch (sizeof(*(ptr))) { \ ++ case 1: \ ++ *(u8 *)__gu_p = (__force u8)(val); \ ++ break; \ ++ case 2: \ ++ put_unaligned_le16((__force u16)(val), __gu_p); \ ++ break; \ ++ case 4: \ ++ put_unaligned_le32((__force u32)(val), __gu_p); \ ++ break; \ ++ case 8: \ ++ put_unaligned_le64((__force u64)(val), __gu_p); \ ++ break; \ ++ default: \ ++ __bad_unaligned_access_size(); \ ++ break; \ ++ } \ ++ (void)0; }) ++ ++ ++/* from kernel linux/kernel.h and uapi/linux/kernel.h */ ++ ++#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1) ++#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask)) ++#define ALIGN(x, a) __ALIGN_KERNEL((x), (a)) ++#define PTR_ALIGN(p, a) ((typeof(p))ALIGN((unsigned long)(p), (a))) ++ ++#endif /* ZSTD_PRIVATE_H */ +diff --git a/xen/common/zstd/zstd_common.c b/xen/common/zstd/zstd_common.c +new file mode 100644 +index 0000000000..1b13903538 +--- /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..1b13840c44 +--- /dev/null ++++ b/xen/common/zstd/zstd_internal.h +@@ -0,0 +1,265 @@ ++/** ++ * 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 ++ ++/*-************************************* ++* Dependencies ++***************************************/ ++#include "error_private.h" ++#include "mem.h" ++#include ++#include ++ ++/*-************************************* ++* shared macros ++***************************************/ ++#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_OPT_NUM (1 << 12) ++#define ZSTD_DICT_MAGIC 0xEC30A437 /* v0.7+ */ ++ ++#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}; ++ ++#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/common/zstd/zstd_opt.h b/xen/common/zstd/zstd_opt.h +new file mode 100644 +index 0000000000..55e1b4cba8 +--- /dev/null ++++ b/xen/common/zstd/zstd_opt.h +@@ -0,0 +1,1014 @@ ++/** ++ * Copyright (c) 2016-present, Przemyslaw Skibinski, 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 file is intended to be included within zstd_compress.c */ ++ ++#ifndef ZSTD_OPT_H_91842398743 ++#define ZSTD_OPT_H_91842398743 ++ ++#define ZSTD_LITFREQ_ADD 2 ++#define ZSTD_FREQ_DIV 4 ++#define ZSTD_MAX_PRICE (1 << 30) ++ ++/*-************************************* ++* Price functions for optimal parser ++***************************************/ ++FORCE_INLINE void ZSTD_setLog2Prices(seqStore_t *ssPtr) ++{ ++ ssPtr->log2matchLengthSum = ZSTD_highbit32(ssPtr->matchLengthSum + 1); ++ ssPtr->log2litLengthSum = ZSTD_highbit32(ssPtr->litLengthSum + 1); ++ ssPtr->log2litSum = ZSTD_highbit32(ssPtr->litSum + 1); ++ ssPtr->log2offCodeSum = ZSTD_highbit32(ssPtr->offCodeSum + 1); ++ ssPtr->factor = 1 + ((ssPtr->litSum >> 5) / ssPtr->litLengthSum) + ((ssPtr->litSum << 1) / (ssPtr->litSum + ssPtr->matchSum)); ++} ++ ++ZSTD_STATIC void ZSTD_rescaleFreqs(seqStore_t *ssPtr, const BYTE *src, size_t srcSize) ++{ ++ unsigned u; ++ ++ ssPtr->cachedLiterals = NULL; ++ ssPtr->cachedPrice = ssPtr->cachedLitLength = 0; ++ ssPtr->staticPrices = 0; ++ ++ if (ssPtr->litLengthSum == 0) { ++ if (srcSize <= 1024) ++ ssPtr->staticPrices = 1; ++ ++ for (u = 0; u <= MaxLit; u++) ++ ssPtr->litFreq[u] = 0; ++ for (u = 0; u < srcSize; u++) ++ ssPtr->litFreq[src[u]]++; ++ ++ ssPtr->litSum = 0; ++ ssPtr->litLengthSum = MaxLL + 1; ++ ssPtr->matchLengthSum = MaxML + 1; ++ ssPtr->offCodeSum = (MaxOff + 1); ++ ssPtr->matchSum = (ZSTD_LITFREQ_ADD << Litbits); ++ ++ for (u = 0; u <= MaxLit; u++) { ++ ssPtr->litFreq[u] = 1 + (ssPtr->litFreq[u] >> ZSTD_FREQ_DIV); ++ ssPtr->litSum += ssPtr->litFreq[u]; ++ } ++ for (u = 0; u <= MaxLL; u++) ++ ssPtr->litLengthFreq[u] = 1; ++ for (u = 0; u <= MaxML; u++) ++ ssPtr->matchLengthFreq[u] = 1; ++ for (u = 0; u <= MaxOff; u++) ++ ssPtr->offCodeFreq[u] = 1; ++ } else { ++ ssPtr->matchLengthSum = 0; ++ ssPtr->litLengthSum = 0; ++ ssPtr->offCodeSum = 0; ++ ssPtr->matchSum = 0; ++ ssPtr->litSum = 0; ++ ++ for (u = 0; u <= MaxLit; u++) { ++ ssPtr->litFreq[u] = 1 + (ssPtr->litFreq[u] >> (ZSTD_FREQ_DIV + 1)); ++ ssPtr->litSum += ssPtr->litFreq[u]; ++ } ++ for (u = 0; u <= MaxLL; u++) { ++ ssPtr->litLengthFreq[u] = 1 + (ssPtr->litLengthFreq[u] >> (ZSTD_FREQ_DIV + 1)); ++ ssPtr->litLengthSum += ssPtr->litLengthFreq[u]; ++ } ++ for (u = 0; u <= MaxML; u++) { ++ ssPtr->matchLengthFreq[u] = 1 + (ssPtr->matchLengthFreq[u] >> ZSTD_FREQ_DIV); ++ ssPtr->matchLengthSum += ssPtr->matchLengthFreq[u]; ++ ssPtr->matchSum += ssPtr->matchLengthFreq[u] * (u + 3); ++ } ++ ssPtr->matchSum *= ZSTD_LITFREQ_ADD; ++ for (u = 0; u <= MaxOff; u++) { ++ ssPtr->offCodeFreq[u] = 1 + (ssPtr->offCodeFreq[u] >> ZSTD_FREQ_DIV); ++ ssPtr->offCodeSum += ssPtr->offCodeFreq[u]; ++ } ++ } ++ ++ ZSTD_setLog2Prices(ssPtr); ++} ++ ++FORCE_INLINE U32 ZSTD_getLiteralPrice(seqStore_t *ssPtr, U32 litLength, const BYTE *literals) ++{ ++ U32 price, u; ++ ++ if (ssPtr->staticPrices) ++ return ZSTD_highbit32((U32)litLength + 1) + (litLength * 6); ++ ++ if (litLength == 0) ++ return ssPtr->log2litLengthSum - ZSTD_highbit32(ssPtr->litLengthFreq[0] + 1); ++ ++ /* literals */ ++ if (ssPtr->cachedLiterals == literals) { ++ U32 const additional = litLength - ssPtr->cachedLitLength; ++ const BYTE *literals2 = ssPtr->cachedLiterals + ssPtr->cachedLitLength; ++ price = ssPtr->cachedPrice + additional * ssPtr->log2litSum; ++ for (u = 0; u < additional; u++) ++ price -= ZSTD_highbit32(ssPtr->litFreq[literals2[u]] + 1); ++ ssPtr->cachedPrice = price; ++ ssPtr->cachedLitLength = litLength; ++ } else { ++ price = litLength * ssPtr->log2litSum; ++ for (u = 0; u < litLength; u++) ++ price -= ZSTD_highbit32(ssPtr->litFreq[literals[u]] + 1); ++ ++ if (litLength >= 12) { ++ ssPtr->cachedLiterals = literals; ++ ssPtr->cachedPrice = price; ++ ssPtr->cachedLitLength = litLength; ++ } ++ } ++ ++ /* literal Length */ ++ { ++ const BYTE LL_deltaCode = 19; ++ const BYTE llCode = (litLength > 63) ? (BYTE)ZSTD_highbit32(litLength) + LL_deltaCode : LL_Code[litLength]; ++ price += LL_bits[llCode] + ssPtr->log2litLengthSum - ZSTD_highbit32(ssPtr->litLengthFreq[llCode] + 1); ++ } ++ ++ return price; ++} ++ ++FORCE_INLINE U32 ZSTD_getPrice(seqStore_t *seqStorePtr, U32 litLength, const BYTE *literals, U32 offset, U32 matchLength, const int ultra) ++{ ++ /* offset */ ++ U32 price; ++ BYTE const offCode = (BYTE)ZSTD_highbit32(offset + 1); ++ ++ if (seqStorePtr->staticPrices) ++ return ZSTD_getLiteralPrice(seqStorePtr, litLength, literals) + ZSTD_highbit32((U32)matchLength + 1) + 16 + offCode; ++ ++ price = offCode + seqStorePtr->log2offCodeSum - ZSTD_highbit32(seqStorePtr->offCodeFreq[offCode] + 1); ++ if (!ultra && offCode >= 20) ++ price += (offCode - 19) * 2; ++ ++ /* match Length */ ++ { ++ const BYTE ML_deltaCode = 36; ++ const BYTE mlCode = (matchLength > 127) ? (BYTE)ZSTD_highbit32(matchLength) + ML_deltaCode : ML_Code[matchLength]; ++ price += ML_bits[mlCode] + seqStorePtr->log2matchLengthSum - ZSTD_highbit32(seqStorePtr->matchLengthFreq[mlCode] + 1); ++ } ++ ++ return price + ZSTD_getLiteralPrice(seqStorePtr, litLength, literals) + seqStorePtr->factor; ++} ++ ++ZSTD_STATIC void ZSTD_updatePrice(seqStore_t *seqStorePtr, U32 litLength, const BYTE *literals, U32 offset, U32 matchLength) ++{ ++ U32 u; ++ ++ /* literals */ ++ seqStorePtr->litSum += litLength * ZSTD_LITFREQ_ADD; ++ for (u = 0; u < litLength; u++) ++ seqStorePtr->litFreq[literals[u]] += ZSTD_LITFREQ_ADD; ++ ++ /* literal Length */ ++ { ++ const BYTE LL_deltaCode = 19; ++ const BYTE llCode = (litLength > 63) ? (BYTE)ZSTD_highbit32(litLength) + LL_deltaCode : LL_Code[litLength]; ++ seqStorePtr->litLengthFreq[llCode]++; ++ seqStorePtr->litLengthSum++; ++ } ++ ++ /* match offset */ ++ { ++ BYTE const offCode = (BYTE)ZSTD_highbit32(offset + 1); ++ seqStorePtr->offCodeSum++; ++ seqStorePtr->offCodeFreq[offCode]++; ++ } ++ ++ /* match Length */ ++ { ++ const BYTE ML_deltaCode = 36; ++ const BYTE mlCode = (matchLength > 127) ? (BYTE)ZSTD_highbit32(matchLength) + ML_deltaCode : ML_Code[matchLength]; ++ seqStorePtr->matchLengthFreq[mlCode]++; ++ seqStorePtr->matchLengthSum++; ++ } ++ ++ ZSTD_setLog2Prices(seqStorePtr); ++} ++ ++#define SET_PRICE(pos, mlen_, offset_, litlen_, price_) \ ++ { \ ++ while (last_pos < pos) { \ ++ opt[last_pos + 1].price = ZSTD_MAX_PRICE; \ ++ last_pos++; \ ++ } \ ++ opt[pos].mlen = mlen_; \ ++ opt[pos].off = offset_; \ ++ opt[pos].litlen = litlen_; \ ++ opt[pos].price = price_; \ ++ } ++ ++/* Update hashTable3 up to ip (excluded) ++ Assumption : always within prefix (i.e. not within extDict) */ ++FORCE_INLINE ++U32 ZSTD_insertAndFindFirstIndexHash3(ZSTD_CCtx *zc, const BYTE *ip) ++{ ++ U32 *const hashTable3 = zc->hashTable3; ++ U32 const hashLog3 = zc->hashLog3; ++ const BYTE *const base = zc->base; ++ U32 idx = zc->nextToUpdate3; ++ const U32 target = zc->nextToUpdate3 = (U32)(ip - base); ++ const size_t hash3 = ZSTD_hash3Ptr(ip, hashLog3); ++ ++ while (idx < target) { ++ hashTable3[ZSTD_hash3Ptr(base + idx, hashLog3)] = idx; ++ idx++; ++ } ++ ++ return hashTable3[hash3]; ++} ++ ++/*-************************************* ++* Binary Tree search ++***************************************/ ++static U32 ZSTD_insertBtAndGetAllMatches(ZSTD_CCtx *zc, const BYTE *const ip, const BYTE *const iLimit, U32 nbCompares, const U32 mls, U32 extDict, ++ ZSTD_match_t *matches, const U32 minMatchLen) ++{ ++ const BYTE *const base = zc->base; ++ const U32 curr = (U32)(ip - base); ++ const U32 hashLog = zc->params.cParams.hashLog; ++ const size_t h = ZSTD_hashPtr(ip, hashLog, mls); ++ U32 *const hashTable = zc->hashTable; ++ U32 matchIndex = hashTable[h]; ++ U32 *const bt = zc->chainTable; ++ const U32 btLog = zc->params.cParams.chainLog - 1; ++ const U32 btMask = (1U << btLog) - 1; ++ size_t commonLengthSmaller = 0, commonLengthLarger = 0; ++ const BYTE *const dictBase = zc->dictBase; ++ const U32 dictLimit = zc->dictLimit; ++ const BYTE *const dictEnd = dictBase + dictLimit; ++ const BYTE *const prefixStart = base + dictLimit; ++ const U32 btLow = btMask >= curr ? 0 : curr - btMask; ++ const U32 windowLow = zc->lowLimit; ++ U32 *smallerPtr = bt + 2 * (curr & btMask); ++ U32 *largerPtr = bt + 2 * (curr & btMask) + 1; ++ U32 matchEndIdx = curr + 8; ++ U32 dummy32; /* to be nullified at the end */ ++ U32 mnum = 0; ++ ++ const U32 minMatch = (mls == 3) ? 3 : 4; ++ size_t bestLength = minMatchLen - 1; ++ ++ if (minMatch == 3) { /* HC3 match finder */ ++ U32 const matchIndex3 = ZSTD_insertAndFindFirstIndexHash3(zc, ip); ++ if (matchIndex3 > windowLow && (curr - matchIndex3 < (1 << 18))) { ++ const BYTE *match; ++ size_t currMl = 0; ++ if ((!extDict) || matchIndex3 >= dictLimit) { ++ match = base + matchIndex3; ++ if (match[bestLength] == ip[bestLength]) ++ currMl = ZSTD_count(ip, match, iLimit); ++ } else { ++ match = dictBase + matchIndex3; ++ if (ZSTD_readMINMATCH(match, MINMATCH) == ++ ZSTD_readMINMATCH(ip, MINMATCH)) /* assumption : matchIndex3 <= dictLimit-4 (by table construction) */ ++ currMl = ZSTD_count_2segments(ip + MINMATCH, match + MINMATCH, iLimit, dictEnd, prefixStart) + MINMATCH; ++ } ++ ++ /* save best solution */ ++ if (currMl > bestLength) { ++ bestLength = currMl; ++ matches[mnum].off = ZSTD_REP_MOVE_OPT + curr - matchIndex3; ++ matches[mnum].len = (U32)currMl; ++ mnum++; ++ if (currMl > ZSTD_OPT_NUM) ++ goto update; ++ if (ip + currMl == iLimit) ++ goto update; /* best possible, and avoid read overflow*/ ++ } ++ } ++ } ++ ++ hashTable[h] = curr; /* Update Hash Table */ ++ ++ while (nbCompares-- && (matchIndex > windowLow)) { ++ U32 *nextPtr = bt + 2 * (matchIndex & btMask); ++ size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ ++ const BYTE *match; ++ ++ if ((!extDict) || (matchIndex + matchLength >= dictLimit)) { ++ match = base + matchIndex; ++ if (match[matchLength] == ip[matchLength]) { ++ matchLength += ZSTD_count(ip + matchLength + 1, match + matchLength + 1, iLimit) + 1; ++ } ++ } else { ++ match = dictBase + matchIndex; ++ matchLength += ZSTD_count_2segments(ip + matchLength, match + matchLength, iLimit, dictEnd, prefixStart); ++ if (matchIndex + matchLength >= dictLimit) ++ match = base + matchIndex; /* to prepare for next usage of match[matchLength] */ ++ } ++ ++ if (matchLength > bestLength) { ++ if (matchLength > matchEndIdx - matchIndex) ++ matchEndIdx = matchIndex + (U32)matchLength; ++ bestLength = matchLength; ++ matches[mnum].off = ZSTD_REP_MOVE_OPT + curr - matchIndex; ++ matches[mnum].len = (U32)matchLength; ++ mnum++; ++ if (matchLength > ZSTD_OPT_NUM) ++ break; ++ if (ip + matchLength == iLimit) /* equal : no way to know if inf or sup */ ++ break; /* drop, to guarantee consistency (miss a little bit of compression) */ ++ } ++ ++ if (match[matchLength] < ip[matchLength]) { ++ /* match is smaller than curr */ ++ *smallerPtr = matchIndex; /* update smaller idx */ ++ commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */ ++ if (matchIndex <= btLow) { ++ smallerPtr = &dummy32; ++ break; ++ } /* beyond tree size, stop the search */ ++ smallerPtr = nextPtr + 1; /* new "smaller" => larger of match */ ++ matchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to curr) */ ++ } else { ++ /* match is larger than curr */ ++ *largerPtr = matchIndex; ++ commonLengthLarger = matchLength; ++ if (matchIndex <= btLow) { ++ largerPtr = &dummy32; ++ break; ++ } /* beyond tree size, stop the search */ ++ largerPtr = nextPtr; ++ matchIndex = nextPtr[0]; ++ } ++ } ++ ++ *smallerPtr = *largerPtr = 0; ++ ++update: ++ zc->nextToUpdate = (matchEndIdx > curr + 8) ? matchEndIdx - 8 : curr + 1; ++ return mnum; ++} ++ ++/** Tree updater, providing best match */ ++static U32 ZSTD_BtGetAllMatches(ZSTD_CCtx *zc, const BYTE *const ip, const BYTE *const iLimit, const U32 maxNbAttempts, const U32 mls, ZSTD_match_t *matches, ++ const U32 minMatchLen) ++{ ++ if (ip < zc->base + zc->nextToUpdate) ++ return 0; /* skipped area */ ++ ZSTD_updateTree(zc, ip, iLimit, maxNbAttempts, mls); ++ return ZSTD_insertBtAndGetAllMatches(zc, ip, iLimit, maxNbAttempts, mls, 0, matches, minMatchLen); ++} ++ ++static U32 ZSTD_BtGetAllMatches_selectMLS(ZSTD_CCtx *zc, /* Index table will be updated */ ++ const BYTE *ip, const BYTE *const iHighLimit, const U32 maxNbAttempts, const U32 matchLengthSearch, ++ ZSTD_match_t *matches, const U32 minMatchLen) ++{ ++ switch (matchLengthSearch) { ++ case 3: return ZSTD_BtGetAllMatches(zc, ip, iHighLimit, maxNbAttempts, 3, matches, minMatchLen); ++ default: ++ case 4: return ZSTD_BtGetAllMatches(zc, ip, iHighLimit, maxNbAttempts, 4, matches, minMatchLen); ++ case 5: return ZSTD_BtGetAllMatches(zc, ip, iHighLimit, maxNbAttempts, 5, matches, minMatchLen); ++ case 7: ++ case 6: return ZSTD_BtGetAllMatches(zc, ip, iHighLimit, maxNbAttempts, 6, matches, minMatchLen); ++ } ++} ++ ++/** Tree updater, providing best match */ ++static U32 ZSTD_BtGetAllMatches_extDict(ZSTD_CCtx *zc, const BYTE *const ip, const BYTE *const iLimit, const U32 maxNbAttempts, const U32 mls, ++ ZSTD_match_t *matches, const U32 minMatchLen) ++{ ++ if (ip < zc->base + zc->nextToUpdate) ++ return 0; /* skipped area */ ++ ZSTD_updateTree_extDict(zc, ip, iLimit, maxNbAttempts, mls); ++ return ZSTD_insertBtAndGetAllMatches(zc, ip, iLimit, maxNbAttempts, mls, 1, matches, minMatchLen); ++} ++ ++static U32 ZSTD_BtGetAllMatches_selectMLS_extDict(ZSTD_CCtx *zc, /* Index table will be updated */ ++ const BYTE *ip, const BYTE *const iHighLimit, const U32 maxNbAttempts, const U32 matchLengthSearch, ++ ZSTD_match_t *matches, const U32 minMatchLen) ++{ ++ switch (matchLengthSearch) { ++ case 3: return ZSTD_BtGetAllMatches_extDict(zc, ip, iHighLimit, maxNbAttempts, 3, matches, minMatchLen); ++ default: ++ case 4: return ZSTD_BtGetAllMatches_extDict(zc, ip, iHighLimit, maxNbAttempts, 4, matches, minMatchLen); ++ case 5: return ZSTD_BtGetAllMatches_extDict(zc, ip, iHighLimit, maxNbAttempts, 5, matches, minMatchLen); ++ case 7: ++ case 6: return ZSTD_BtGetAllMatches_extDict(zc, ip, iHighLimit, maxNbAttempts, 6, matches, minMatchLen); ++ } ++} ++ ++/*-******************************* ++* Optimal parser ++*********************************/ ++FORCE_INLINE ++void ZSTD_compressBlock_opt_generic(ZSTD_CCtx *ctx, const void *src, size_t srcSize, const int ultra) ++{ ++ seqStore_t *seqStorePtr = &(ctx->seqStore); ++ const BYTE *const istart = (const BYTE *)src; ++ const BYTE *ip = istart; ++ const BYTE *anchor = istart; ++ const BYTE *const iend = istart + srcSize; ++ const BYTE *const ilimit = iend - 8; ++ const BYTE *const base = ctx->base; ++ const BYTE *const prefixStart = base + ctx->dictLimit; ++ ++ const U32 maxSearches = 1U << ctx->params.cParams.searchLog; ++ const U32 sufficient_len = ctx->params.cParams.targetLength; ++ const U32 mls = ctx->params.cParams.searchLength; ++ const U32 minMatch = (ctx->params.cParams.searchLength == 3) ? 3 : 4; ++ ++ ZSTD_optimal_t *opt = seqStorePtr->priceTable; ++ ZSTD_match_t *matches = seqStorePtr->matchTable; ++ const BYTE *inr; ++ U32 offset, rep[ZSTD_REP_NUM]; ++ ++ /* init */ ++ ctx->nextToUpdate3 = ctx->nextToUpdate; ++ ZSTD_rescaleFreqs(seqStorePtr, (const BYTE *)src, srcSize); ++ ip += (ip == prefixStart); ++ { ++ U32 i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ rep[i] = ctx->rep[i]; ++ } ++ ++ /* Match Loop */ ++ while (ip < ilimit) { ++ U32 cur, match_num, last_pos, litlen, price; ++ U32 u, mlen, best_mlen, best_off, litLength; ++ memset(opt, 0, sizeof(ZSTD_optimal_t)); ++ last_pos = 0; ++ litlen = (U32)(ip - anchor); ++ ++ /* check repCode */ ++ { ++ U32 i, last_i = ZSTD_REP_CHECK + (ip == anchor); ++ for (i = (ip == anchor); i < last_i; i++) { ++ const S32 repCur = (i == ZSTD_REP_MOVE_OPT) ? (rep[0] - 1) : rep[i]; ++ if ((repCur > 0) && (repCur < (S32)(ip - prefixStart)) && ++ (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(ip - repCur, minMatch))) { ++ mlen = (U32)ZSTD_count(ip + minMatch, ip + minMatch - repCur, iend) + minMatch; ++ if (mlen > sufficient_len || mlen >= ZSTD_OPT_NUM) { ++ best_mlen = mlen; ++ best_off = i; ++ cur = 0; ++ last_pos = 1; ++ goto _storeSequence; ++ } ++ best_off = i - (ip == anchor); ++ do { ++ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra); ++ if (mlen > last_pos || price < opt[mlen].price) ++ SET_PRICE(mlen, mlen, i, litlen, price); /* note : macro modifies last_pos */ ++ mlen--; ++ } while (mlen >= minMatch); ++ } ++ } ++ } ++ ++ match_num = ZSTD_BtGetAllMatches_selectMLS(ctx, ip, iend, maxSearches, mls, matches, minMatch); ++ ++ if (!last_pos && !match_num) { ++ ip++; ++ continue; ++ } ++ ++ if (match_num && (matches[match_num - 1].len > sufficient_len || matches[match_num - 1].len >= ZSTD_OPT_NUM)) { ++ best_mlen = matches[match_num - 1].len; ++ best_off = matches[match_num - 1].off; ++ cur = 0; ++ last_pos = 1; ++ goto _storeSequence; ++ } ++ ++ /* set prices using matches at position = 0 */ ++ best_mlen = (last_pos) ? last_pos : minMatch; ++ for (u = 0; u < match_num; u++) { ++ mlen = (u > 0) ? matches[u - 1].len + 1 : best_mlen; ++ best_mlen = matches[u].len; ++ while (mlen <= best_mlen) { ++ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, matches[u].off - 1, mlen - MINMATCH, ultra); ++ if (mlen > last_pos || price < opt[mlen].price) ++ SET_PRICE(mlen, mlen, matches[u].off, litlen, price); /* note : macro modifies last_pos */ ++ mlen++; ++ } ++ } ++ ++ if (last_pos < minMatch) { ++ ip++; ++ continue; ++ } ++ ++ /* initialize opt[0] */ ++ { ++ U32 i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ opt[0].rep[i] = rep[i]; ++ } ++ opt[0].mlen = 1; ++ opt[0].litlen = litlen; ++ ++ /* check further positions */ ++ for (cur = 1; cur <= last_pos; cur++) { ++ inr = ip + cur; ++ ++ if (opt[cur - 1].mlen == 1) { ++ litlen = opt[cur - 1].litlen + 1; ++ if (cur > litlen) { ++ price = opt[cur - litlen].price + ZSTD_getLiteralPrice(seqStorePtr, litlen, inr - litlen); ++ } else ++ price = ZSTD_getLiteralPrice(seqStorePtr, litlen, anchor); ++ } else { ++ litlen = 1; ++ price = opt[cur - 1].price + ZSTD_getLiteralPrice(seqStorePtr, litlen, inr - 1); ++ } ++ ++ if (cur > last_pos || price <= opt[cur].price) ++ SET_PRICE(cur, 1, 0, litlen, price); ++ ++ if (cur == last_pos) ++ break; ++ ++ if (inr > ilimit) /* last match must start at a minimum distance of 8 from oend */ ++ continue; ++ ++ mlen = opt[cur].mlen; ++ if (opt[cur].off > ZSTD_REP_MOVE_OPT) { ++ opt[cur].rep[2] = opt[cur - mlen].rep[1]; ++ opt[cur].rep[1] = opt[cur - mlen].rep[0]; ++ opt[cur].rep[0] = opt[cur].off - ZSTD_REP_MOVE_OPT; ++ } else { ++ opt[cur].rep[2] = (opt[cur].off > 1) ? opt[cur - mlen].rep[1] : opt[cur - mlen].rep[2]; ++ opt[cur].rep[1] = (opt[cur].off > 0) ? opt[cur - mlen].rep[0] : opt[cur - mlen].rep[1]; ++ opt[cur].rep[0] = ++ ((opt[cur].off == ZSTD_REP_MOVE_OPT) && (mlen != 1)) ? (opt[cur - mlen].rep[0] - 1) : (opt[cur - mlen].rep[opt[cur].off]); ++ } ++ ++ best_mlen = minMatch; ++ { ++ U32 i, last_i = ZSTD_REP_CHECK + (mlen != 1); ++ for (i = (opt[cur].mlen != 1); i < last_i; i++) { /* check rep */ ++ const S32 repCur = (i == ZSTD_REP_MOVE_OPT) ? (opt[cur].rep[0] - 1) : opt[cur].rep[i]; ++ if ((repCur > 0) && (repCur < (S32)(inr - prefixStart)) && ++ (ZSTD_readMINMATCH(inr, minMatch) == ZSTD_readMINMATCH(inr - repCur, minMatch))) { ++ mlen = (U32)ZSTD_count(inr + minMatch, inr + minMatch - repCur, iend) + minMatch; ++ ++ if (mlen > sufficient_len || cur + mlen >= ZSTD_OPT_NUM) { ++ best_mlen = mlen; ++ best_off = i; ++ last_pos = cur + 1; ++ goto _storeSequence; ++ } ++ ++ best_off = i - (opt[cur].mlen != 1); ++ if (mlen > best_mlen) ++ best_mlen = mlen; ++ ++ do { ++ if (opt[cur].mlen == 1) { ++ litlen = opt[cur].litlen; ++ if (cur > litlen) { ++ price = opt[cur - litlen].price + ZSTD_getPrice(seqStorePtr, litlen, inr - litlen, ++ best_off, mlen - MINMATCH, ultra); ++ } else ++ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra); ++ } else { ++ litlen = 0; ++ price = opt[cur].price + ZSTD_getPrice(seqStorePtr, 0, NULL, best_off, mlen - MINMATCH, ultra); ++ } ++ ++ if (cur + mlen > last_pos || price <= opt[cur + mlen].price) ++ SET_PRICE(cur + mlen, mlen, i, litlen, price); ++ mlen--; ++ } while (mlen >= minMatch); ++ } ++ } ++ } ++ ++ match_num = ZSTD_BtGetAllMatches_selectMLS(ctx, inr, iend, maxSearches, mls, matches, best_mlen); ++ ++ if (match_num > 0 && (matches[match_num - 1].len > sufficient_len || cur + matches[match_num - 1].len >= ZSTD_OPT_NUM)) { ++ best_mlen = matches[match_num - 1].len; ++ best_off = matches[match_num - 1].off; ++ last_pos = cur + 1; ++ goto _storeSequence; ++ } ++ ++ /* set prices using matches at position = cur */ ++ for (u = 0; u < match_num; u++) { ++ mlen = (u > 0) ? matches[u - 1].len + 1 : best_mlen; ++ best_mlen = matches[u].len; ++ ++ while (mlen <= best_mlen) { ++ if (opt[cur].mlen == 1) { ++ litlen = opt[cur].litlen; ++ if (cur > litlen) ++ price = opt[cur - litlen].price + ZSTD_getPrice(seqStorePtr, litlen, ip + cur - litlen, ++ matches[u].off - 1, mlen - MINMATCH, ultra); ++ else ++ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, matches[u].off - 1, mlen - MINMATCH, ultra); ++ } else { ++ litlen = 0; ++ price = opt[cur].price + ZSTD_getPrice(seqStorePtr, 0, NULL, matches[u].off - 1, mlen - MINMATCH, ultra); ++ } ++ ++ if (cur + mlen > last_pos || (price < opt[cur + mlen].price)) ++ SET_PRICE(cur + mlen, mlen, matches[u].off, litlen, price); ++ ++ mlen++; ++ } ++ } ++ } ++ ++ best_mlen = opt[last_pos].mlen; ++ best_off = opt[last_pos].off; ++ cur = last_pos - best_mlen; ++ ++ /* store sequence */ ++_storeSequence: /* cur, last_pos, best_mlen, best_off have to be set */ ++ opt[0].mlen = 1; ++ ++ while (1) { ++ mlen = opt[cur].mlen; ++ offset = opt[cur].off; ++ opt[cur].mlen = best_mlen; ++ opt[cur].off = best_off; ++ best_mlen = mlen; ++ best_off = offset; ++ if (mlen > cur) ++ break; ++ cur -= mlen; ++ } ++ ++ for (u = 0; u <= last_pos;) { ++ u += opt[u].mlen; ++ } ++ ++ for (cur = 0; cur < last_pos;) { ++ mlen = opt[cur].mlen; ++ if (mlen == 1) { ++ ip++; ++ cur++; ++ continue; ++ } ++ offset = opt[cur].off; ++ cur += mlen; ++ litLength = (U32)(ip - anchor); ++ ++ if (offset > ZSTD_REP_MOVE_OPT) { ++ rep[2] = rep[1]; ++ rep[1] = rep[0]; ++ rep[0] = offset - ZSTD_REP_MOVE_OPT; ++ offset--; ++ } else { ++ if (offset != 0) { ++ best_off = (offset == ZSTD_REP_MOVE_OPT) ? (rep[0] - 1) : (rep[offset]); ++ if (offset != 1) ++ rep[2] = rep[1]; ++ rep[1] = rep[0]; ++ rep[0] = best_off; ++ } ++ if (litLength == 0) ++ offset--; ++ } ++ ++ ZSTD_updatePrice(seqStorePtr, litLength, anchor, offset, mlen - MINMATCH); ++ ZSTD_storeSeq(seqStorePtr, litLength, anchor, offset, mlen - MINMATCH); ++ anchor = ip = ip + mlen; ++ } ++ } /* for (cur=0; cur < last_pos; ) */ ++ ++ /* Save reps for next block */ ++ { ++ int i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ ctx->repToConfirm[i] = rep[i]; ++ } ++ ++ /* Last Literals */ ++ { ++ size_t const lastLLSize = iend - anchor; ++ memcpy(seqStorePtr->lit, anchor, lastLLSize); ++ seqStorePtr->lit += lastLLSize; ++ } ++} ++ ++FORCE_INLINE ++void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx *ctx, const void *src, size_t srcSize, const int ultra) ++{ ++ seqStore_t *seqStorePtr = &(ctx->seqStore); ++ const BYTE *const istart = (const BYTE *)src; ++ const BYTE *ip = istart; ++ const BYTE *anchor = istart; ++ const BYTE *const iend = istart + srcSize; ++ const BYTE *const ilimit = iend - 8; ++ const BYTE *const base = ctx->base; ++ const U32 lowestIndex = ctx->lowLimit; ++ const U32 dictLimit = ctx->dictLimit; ++ const BYTE *const prefixStart = base + dictLimit; ++ const BYTE *const dictBase = ctx->dictBase; ++ const BYTE *const dictEnd = dictBase + dictLimit; ++ ++ const U32 maxSearches = 1U << ctx->params.cParams.searchLog; ++ const U32 sufficient_len = ctx->params.cParams.targetLength; ++ const U32 mls = ctx->params.cParams.searchLength; ++ const U32 minMatch = (ctx->params.cParams.searchLength == 3) ? 3 : 4; ++ ++ ZSTD_optimal_t *opt = seqStorePtr->priceTable; ++ ZSTD_match_t *matches = seqStorePtr->matchTable; ++ const BYTE *inr; ++ ++ /* init */ ++ U32 offset, rep[ZSTD_REP_NUM]; ++ { ++ U32 i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ rep[i] = ctx->rep[i]; ++ } ++ ++ ctx->nextToUpdate3 = ctx->nextToUpdate; ++ ZSTD_rescaleFreqs(seqStorePtr, (const BYTE *)src, srcSize); ++ ip += (ip == prefixStart); ++ ++ /* Match Loop */ ++ while (ip < ilimit) { ++ U32 cur, match_num, last_pos, litlen, price; ++ U32 u, mlen, best_mlen, best_off, litLength; ++ U32 curr = (U32)(ip - base); ++ memset(opt, 0, sizeof(ZSTD_optimal_t)); ++ last_pos = 0; ++ opt[0].litlen = (U32)(ip - anchor); ++ ++ /* check repCode */ ++ { ++ U32 i, last_i = ZSTD_REP_CHECK + (ip == anchor); ++ for (i = (ip == anchor); i < last_i; i++) { ++ const S32 repCur = (i == ZSTD_REP_MOVE_OPT) ? (rep[0] - 1) : rep[i]; ++ const U32 repIndex = (U32)(curr - repCur); ++ const BYTE *const repBase = repIndex < dictLimit ? dictBase : base; ++ const BYTE *const repMatch = repBase + repIndex; ++ if ((repCur > 0 && repCur <= (S32)curr) && ++ (((U32)((dictLimit - 1) - repIndex) >= 3) & (repIndex > lowestIndex)) /* intentional overflow */ ++ && (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch))) { ++ /* repcode detected we should take it */ ++ const BYTE *const repEnd = repIndex < dictLimit ? dictEnd : iend; ++ mlen = (U32)ZSTD_count_2segments(ip + minMatch, repMatch + minMatch, iend, repEnd, prefixStart) + minMatch; ++ ++ if (mlen > sufficient_len || mlen >= ZSTD_OPT_NUM) { ++ best_mlen = mlen; ++ best_off = i; ++ cur = 0; ++ last_pos = 1; ++ goto _storeSequence; ++ } ++ ++ best_off = i - (ip == anchor); ++ litlen = opt[0].litlen; ++ do { ++ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra); ++ if (mlen > last_pos || price < opt[mlen].price) ++ SET_PRICE(mlen, mlen, i, litlen, price); /* note : macro modifies last_pos */ ++ mlen--; ++ } while (mlen >= minMatch); ++ } ++ } ++ } ++ ++ match_num = ZSTD_BtGetAllMatches_selectMLS_extDict(ctx, ip, iend, maxSearches, mls, matches, minMatch); /* first search (depth 0) */ ++ ++ if (!last_pos && !match_num) { ++ ip++; ++ continue; ++ } ++ ++ { ++ U32 i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ opt[0].rep[i] = rep[i]; ++ } ++ opt[0].mlen = 1; ++ ++ if (match_num && (matches[match_num - 1].len > sufficient_len || matches[match_num - 1].len >= ZSTD_OPT_NUM)) { ++ best_mlen = matches[match_num - 1].len; ++ best_off = matches[match_num - 1].off; ++ cur = 0; ++ last_pos = 1; ++ goto _storeSequence; ++ } ++ ++ best_mlen = (last_pos) ? last_pos : minMatch; ++ ++ /* set prices using matches at position = 0 */ ++ for (u = 0; u < match_num; u++) { ++ mlen = (u > 0) ? matches[u - 1].len + 1 : best_mlen; ++ best_mlen = matches[u].len; ++ litlen = opt[0].litlen; ++ while (mlen <= best_mlen) { ++ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, matches[u].off - 1, mlen - MINMATCH, ultra); ++ if (mlen > last_pos || price < opt[mlen].price) ++ SET_PRICE(mlen, mlen, matches[u].off, litlen, price); ++ mlen++; ++ } ++ } ++ ++ if (last_pos < minMatch) { ++ ip++; ++ continue; ++ } ++ ++ /* check further positions */ ++ for (cur = 1; cur <= last_pos; cur++) { ++ inr = ip + cur; ++ ++ if (opt[cur - 1].mlen == 1) { ++ litlen = opt[cur - 1].litlen + 1; ++ if (cur > litlen) { ++ price = opt[cur - litlen].price + ZSTD_getLiteralPrice(seqStorePtr, litlen, inr - litlen); ++ } else ++ price = ZSTD_getLiteralPrice(seqStorePtr, litlen, anchor); ++ } else { ++ litlen = 1; ++ price = opt[cur - 1].price + ZSTD_getLiteralPrice(seqStorePtr, litlen, inr - 1); ++ } ++ ++ if (cur > last_pos || price <= opt[cur].price) ++ SET_PRICE(cur, 1, 0, litlen, price); ++ ++ if (cur == last_pos) ++ break; ++ ++ if (inr > ilimit) /* last match must start at a minimum distance of 8 from oend */ ++ continue; ++ ++ mlen = opt[cur].mlen; ++ if (opt[cur].off > ZSTD_REP_MOVE_OPT) { ++ opt[cur].rep[2] = opt[cur - mlen].rep[1]; ++ opt[cur].rep[1] = opt[cur - mlen].rep[0]; ++ opt[cur].rep[0] = opt[cur].off - ZSTD_REP_MOVE_OPT; ++ } else { ++ opt[cur].rep[2] = (opt[cur].off > 1) ? opt[cur - mlen].rep[1] : opt[cur - mlen].rep[2]; ++ opt[cur].rep[1] = (opt[cur].off > 0) ? opt[cur - mlen].rep[0] : opt[cur - mlen].rep[1]; ++ opt[cur].rep[0] = ++ ((opt[cur].off == ZSTD_REP_MOVE_OPT) && (mlen != 1)) ? (opt[cur - mlen].rep[0] - 1) : (opt[cur - mlen].rep[opt[cur].off]); ++ } ++ ++ best_mlen = minMatch; ++ { ++ U32 i, last_i = ZSTD_REP_CHECK + (mlen != 1); ++ for (i = (mlen != 1); i < last_i; i++) { ++ const S32 repCur = (i == ZSTD_REP_MOVE_OPT) ? (opt[cur].rep[0] - 1) : opt[cur].rep[i]; ++ const U32 repIndex = (U32)(curr + cur - repCur); ++ const BYTE *const repBase = repIndex < dictLimit ? dictBase : base; ++ const BYTE *const repMatch = repBase + repIndex; ++ if ((repCur > 0 && repCur <= (S32)(curr + cur)) && ++ (((U32)((dictLimit - 1) - repIndex) >= 3) & (repIndex > lowestIndex)) /* intentional overflow */ ++ && (ZSTD_readMINMATCH(inr, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch))) { ++ /* repcode detected */ ++ const BYTE *const repEnd = repIndex < dictLimit ? dictEnd : iend; ++ mlen = (U32)ZSTD_count_2segments(inr + minMatch, repMatch + minMatch, iend, repEnd, prefixStart) + minMatch; ++ ++ if (mlen > sufficient_len || cur + mlen >= ZSTD_OPT_NUM) { ++ best_mlen = mlen; ++ best_off = i; ++ last_pos = cur + 1; ++ goto _storeSequence; ++ } ++ ++ best_off = i - (opt[cur].mlen != 1); ++ if (mlen > best_mlen) ++ best_mlen = mlen; ++ ++ do { ++ if (opt[cur].mlen == 1) { ++ litlen = opt[cur].litlen; ++ if (cur > litlen) { ++ price = opt[cur - litlen].price + ZSTD_getPrice(seqStorePtr, litlen, inr - litlen, ++ best_off, mlen - MINMATCH, ultra); ++ } else ++ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra); ++ } else { ++ litlen = 0; ++ price = opt[cur].price + ZSTD_getPrice(seqStorePtr, 0, NULL, best_off, mlen - MINMATCH, ultra); ++ } ++ ++ if (cur + mlen > last_pos || price <= opt[cur + mlen].price) ++ SET_PRICE(cur + mlen, mlen, i, litlen, price); ++ mlen--; ++ } while (mlen >= minMatch); ++ } ++ } ++ } ++ ++ match_num = ZSTD_BtGetAllMatches_selectMLS_extDict(ctx, inr, iend, maxSearches, mls, matches, minMatch); ++ ++ if (match_num > 0 && (matches[match_num - 1].len > sufficient_len || cur + matches[match_num - 1].len >= ZSTD_OPT_NUM)) { ++ best_mlen = matches[match_num - 1].len; ++ best_off = matches[match_num - 1].off; ++ last_pos = cur + 1; ++ goto _storeSequence; ++ } ++ ++ /* set prices using matches at position = cur */ ++ for (u = 0; u < match_num; u++) { ++ mlen = (u > 0) ? matches[u - 1].len + 1 : best_mlen; ++ best_mlen = matches[u].len; ++ ++ while (mlen <= best_mlen) { ++ if (opt[cur].mlen == 1) { ++ litlen = opt[cur].litlen; ++ if (cur > litlen) ++ price = opt[cur - litlen].price + ZSTD_getPrice(seqStorePtr, litlen, ip + cur - litlen, ++ matches[u].off - 1, mlen - MINMATCH, ultra); ++ else ++ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, matches[u].off - 1, mlen - MINMATCH, ultra); ++ } else { ++ litlen = 0; ++ price = opt[cur].price + ZSTD_getPrice(seqStorePtr, 0, NULL, matches[u].off - 1, mlen - MINMATCH, ultra); ++ } ++ ++ if (cur + mlen > last_pos || (price < opt[cur + mlen].price)) ++ SET_PRICE(cur + mlen, mlen, matches[u].off, litlen, price); ++ ++ mlen++; ++ } ++ } ++ } /* for (cur = 1; cur <= last_pos; cur++) */ ++ ++ best_mlen = opt[last_pos].mlen; ++ best_off = opt[last_pos].off; ++ cur = last_pos - best_mlen; ++ ++ /* store sequence */ ++_storeSequence: /* cur, last_pos, best_mlen, best_off have to be set */ ++ opt[0].mlen = 1; ++ ++ while (1) { ++ mlen = opt[cur].mlen; ++ offset = opt[cur].off; ++ opt[cur].mlen = best_mlen; ++ opt[cur].off = best_off; ++ best_mlen = mlen; ++ best_off = offset; ++ if (mlen > cur) ++ break; ++ cur -= mlen; ++ } ++ ++ for (u = 0; u <= last_pos;) { ++ u += opt[u].mlen; ++ } ++ ++ for (cur = 0; cur < last_pos;) { ++ mlen = opt[cur].mlen; ++ if (mlen == 1) { ++ ip++; ++ cur++; ++ continue; ++ } ++ offset = opt[cur].off; ++ cur += mlen; ++ litLength = (U32)(ip - anchor); ++ ++ if (offset > ZSTD_REP_MOVE_OPT) { ++ rep[2] = rep[1]; ++ rep[1] = rep[0]; ++ rep[0] = offset - ZSTD_REP_MOVE_OPT; ++ offset--; ++ } else { ++ if (offset != 0) { ++ best_off = (offset == ZSTD_REP_MOVE_OPT) ? (rep[0] - 1) : (rep[offset]); ++ if (offset != 1) ++ rep[2] = rep[1]; ++ rep[1] = rep[0]; ++ rep[0] = best_off; ++ } ++ ++ if (litLength == 0) ++ offset--; ++ } ++ ++ ZSTD_updatePrice(seqStorePtr, litLength, anchor, offset, mlen - MINMATCH); ++ ZSTD_storeSeq(seqStorePtr, litLength, anchor, offset, mlen - MINMATCH); ++ anchor = ip = ip + mlen; ++ } ++ } /* for (cur=0; cur < last_pos; ) */ ++ ++ /* Save reps for next block */ ++ { ++ int i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ ctx->repToConfirm[i] = rep[i]; ++ } ++ ++ /* Last Literals */ ++ { ++ size_t lastLLSize = iend - anchor; ++ memcpy(seqStorePtr->lit, anchor, lastLLSize); ++ seqStorePtr->lit += lastLLSize; ++ } ++} ++ ++#endif /* ZSTD_OPT_H_91842398743 */ +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); + +diff --git a/xen/include/xen/xxhash.h b/xen/include/xen/xxhash.h +new file mode 100644 +index 0000000000..13ddc616d1 +--- /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 XXHASH_H ++#define XXHASH_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 /* XXHASH_H */ +diff --git a/xen/include/xen/zstd.h b/xen/include/xen/zstd.h +new file mode 100644 +index 0000000000..eb33582a18 +--- /dev/null ++++ b/xen/include/xen/zstd.h +@@ -0,0 +1,1157 @@ ++/* ++ * 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_H ++#define ZSTD_H ++ ++/* ====== Dependency ======*/ ++#include /* size_t */ ++ ++ ++/*-***************************************************************************** ++ * Introduction ++ * ++ * zstd, short for Zstandard, is a fast lossless compression algorithm, ++ * targeting real-time compression scenarios at zlib-level and better ++ * compression ratios. The zstd compression library provides in-memory ++ * compression and decompression functions. The library supports compression ++ * levels from 1 up to ZSTD_maxCLevel() which is 22. Levels >= 20, labeled ++ * ultra, should be used with caution, as they require more memory. ++ * Compression can be done in: ++ * - a single step, reusing a context (described as Explicit memory management) ++ * - unbounded multiple steps (described as Streaming compression) ++ * The compression ratio achievable on small data can be highly improved using ++ * compression with a dictionary in: ++ * - a single step (described as Simple dictionary API) ++ * - a single step, reusing a dictionary (described as Fast dictionary API) ++ ******************************************************************************/ ++ ++/*====== Helper functions ======*/ ++ ++/** ++ * 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; ++ ++/** ++ * ZSTD_maxCLevel() - maximum compression level available ++ * ++ * Return: Maximum compression level available. ++ */ ++int ZSTD_maxCLevel(void); ++/** ++ * ZSTD_compressBound() - maximum compressed size in worst case scenario ++ * @srcSize: The size of the data to compress. ++ * ++ * Return: The maximum compressed size in the worst case scenario. ++ */ ++size_t ZSTD_compressBound(size_t srcSize); ++/** ++ * 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 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 ZSTD_getErrorCode( ++ size_t functionResult) ++{ ++ if (!ZSTD_isError(functionResult)) ++ return (ZSTD_ErrorCode)0; ++ return (ZSTD_ErrorCode)(0 - functionResult); ++} ++ ++/** ++ * enum ZSTD_strategy - zstd compression search strategy ++ * ++ * From faster to stronger. ++ */ ++typedef enum { ++ ZSTD_fast, ++ ZSTD_dfast, ++ ZSTD_greedy, ++ ZSTD_lazy, ++ ZSTD_lazy2, ++ ZSTD_btlazy2, ++ ZSTD_btopt, ++ ZSTD_btopt2 ++} ZSTD_strategy; ++ ++/** ++ * struct ZSTD_compressionParameters - zstd compression parameters ++ * @windowLog: Log of the largest match distance. Larger means more ++ * compression, and more memory needed during decompression. ++ * @chainLog: Fully searched segment. Larger means more compression, slower, ++ * and more memory (useless for fast). ++ * @hashLog: Dispatch table. Larger means more compression, ++ * slower, and more memory. ++ * @searchLog: Number of searches. Larger means more compression and slower. ++ * @searchLength: Match length searched. Larger means faster decompression, ++ * sometimes less compression. ++ * @targetLength: Acceptable match size for optimal parser (only). Larger means ++ * more compression, and slower. ++ * @strategy: The zstd compression strategy. ++ */ ++typedef struct { ++ unsigned int windowLog; ++ unsigned int chainLog; ++ unsigned int hashLog; ++ unsigned int searchLog; ++ unsigned int searchLength; ++ unsigned int targetLength; ++ ZSTD_strategy strategy; ++} ZSTD_compressionParameters; ++ ++/** ++ * struct ZSTD_frameParameters - zstd frame parameters ++ * @contentSizeFlag: Controls whether content size will be present in the frame ++ * header (when known). ++ * @checksumFlag: Controls whether a 32-bit checksum is generated at the end ++ * of the frame for error detection. ++ * @noDictIDFlag: Controls whether dictID will be saved into the frame header ++ * when using dictionary compression. ++ * ++ * The default value is all fields set to 0. ++ */ ++typedef struct { ++ unsigned int contentSizeFlag; ++ unsigned int checksumFlag; ++ unsigned int noDictIDFlag; ++} ZSTD_frameParameters; ++ ++/** ++ * struct ZSTD_parameters - zstd parameters ++ * @cParams: The compression parameters. ++ * @fParams: The frame parameters. ++ */ ++typedef struct { ++ ZSTD_compressionParameters cParams; ++ ZSTD_frameParameters fParams; ++} ZSTD_parameters; ++ ++/** ++ * ZSTD_getCParams() - returns ZSTD_compressionParameters for selected level ++ * @compressionLevel: The compression level from 1 to ZSTD_maxCLevel(). ++ * @estimatedSrcSize: The estimated source size to compress or 0 if unknown. ++ * @dictSize: The dictionary size or 0 if a dictionary isn't being used. ++ * ++ * Return: The selected ZSTD_compressionParameters. ++ */ ++ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, ++ unsigned long long estimatedSrcSize, size_t dictSize); ++ ++/** ++ * ZSTD_getParams() - returns ZSTD_parameters for selected level ++ * @compressionLevel: The compression level from 1 to ZSTD_maxCLevel(). ++ * @estimatedSrcSize: The estimated source size to compress or 0 if unknown. ++ * @dictSize: The dictionary size or 0 if a dictionary isn't being used. ++ * ++ * The same as ZSTD_getCParams() except also selects the default frame ++ * parameters (all zero). ++ * ++ * Return: The selected ZSTD_parameters. ++ */ ++ZSTD_parameters ZSTD_getParams(int compressionLevel, ++ unsigned long long estimatedSrcSize, size_t dictSize); ++ ++/*-************************************* ++ * Explicit memory management ++ **************************************/ ++ ++/** ++ * ZSTD_CCtxWorkspaceBound() - amount of memory needed to initialize a ZSTD_CCtx ++ * @cParams: The compression parameters to be used for compression. ++ * ++ * If multiple compression parameters might be used, the caller must call ++ * ZSTD_CCtxWorkspaceBound() for each set of parameters and use the maximum ++ * size. ++ * ++ * Return: A lower bound on the size of the workspace that is passed to ++ * ZSTD_initCCtx(). ++ */ ++size_t ZSTD_CCtxWorkspaceBound(ZSTD_compressionParameters cParams); ++ ++/** ++ * struct ZSTD_CCtx - the zstd compression context ++ * ++ * When compressing many times it is recommended to allocate a context just once ++ * and reuse it for each successive compression operation. ++ */ ++typedef struct ZSTD_CCtx_s ZSTD_CCtx; ++/** ++ * ZSTD_initCCtx() - initialize a zstd compression context ++ * @workspace: The workspace to emplace the context into. It must outlive ++ * the returned context. ++ * @workspaceSize: The size of workspace. Use ZSTD_CCtxWorkspaceBound() to ++ * determine how large the workspace must be. ++ * ++ * Return: A compression context emplaced into workspace. ++ */ ++ZSTD_CCtx *ZSTD_initCCtx(void *workspace, size_t workspaceSize); ++ ++/** ++ * ZSTD_compressCCtx() - compress src into dst ++ * @ctx: The context. Must have been initialized with a workspace at ++ * least as large as ZSTD_CCtxWorkspaceBound(params.cParams). ++ * @dst: The buffer to compress src into. ++ * @dstCapacity: The size of the destination buffer. May be any size, but ++ * ZSTD_compressBound(srcSize) is guaranteed to be large enough. ++ * @src: The data to compress. ++ * @srcSize: The size of the data to compress. ++ * @params: The parameters to use for compression. See ZSTD_getParams(). ++ * ++ * Return: The compressed size or an error, which can be checked using ++ * ZSTD_isError(). ++ */ ++size_t ZSTD_compressCCtx(ZSTD_CCtx *ctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize, ZSTD_parameters params); ++ ++/** ++ * ZSTD_DCtxWorkspaceBound() - amount of memory needed to initialize a ZSTD_DCtx ++ * ++ * Return: A lower bound on the size of the workspace that is passed to ++ * ZSTD_initDCtx(). ++ */ ++size_t ZSTD_DCtxWorkspaceBound(void); ++ ++/** ++ * struct ZSTD_DCtx - the zstd decompression context ++ * ++ * When decompressing many times it is recommended to allocate a context just ++ * once and reuse it for each successive decompression operation. ++ */ ++typedef struct ZSTD_DCtx_s ZSTD_DCtx; ++/** ++ * ZSTD_initDCtx() - initialize a zstd decompression context ++ * @workspace: The workspace to emplace the context into. It must outlive ++ * the returned context. ++ * @workspaceSize: The size of workspace. Use ZSTD_DCtxWorkspaceBound() to ++ * determine how large the workspace must be. ++ * ++ * Return: A decompression context emplaced into workspace. ++ */ ++ZSTD_DCtx *ZSTD_initDCtx(void *workspace, size_t workspaceSize); ++ ++/** ++ * ZSTD_decompressDCtx() - decompress zstd compressed src into dst ++ * @ctx: The decompression context. ++ * @dst: The buffer to decompress src into. ++ * @dstCapacity: The size of the destination buffer. Must be at least as large ++ * as the decompressed size. If the caller cannot upper bound the ++ * decompressed size, then it's better to use the streaming API. ++ * @src: The zstd compressed data to decompress. Multiple concatenated ++ * frames and skippable frames are allowed. ++ * @srcSize: The exact size of the data to decompress. ++ * ++ * Return: The decompressed size or an error, which can be checked using ++ * ZSTD_isError(). ++ */ ++size_t ZSTD_decompressDCtx(ZSTD_DCtx *ctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize); ++ ++/*-************************ ++ * Simple dictionary API ++ **************************/ ++ ++/** ++ * ZSTD_compress_usingDict() - compress src into dst using a dictionary ++ * @ctx: The context. Must have been initialized with a workspace at ++ * least as large as ZSTD_CCtxWorkspaceBound(params.cParams). ++ * @dst: The buffer to compress src into. ++ * @dstCapacity: The size of the destination buffer. May be any size, but ++ * ZSTD_compressBound(srcSize) is guaranteed to be large enough. ++ * @src: The data to compress. ++ * @srcSize: The size of the data to compress. ++ * @dict: The dictionary to use for compression. ++ * @dictSize: The size of the dictionary. ++ * @params: The parameters to use for compression. See ZSTD_getParams(). ++ * ++ * Compression using a predefined dictionary. The same dictionary must be used ++ * during decompression. ++ * ++ * Return: The compressed size or an error, which can be checked using ++ * ZSTD_isError(). ++ */ ++size_t ZSTD_compress_usingDict(ZSTD_CCtx *ctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize, const void *dict, size_t dictSize, ++ ZSTD_parameters params); ++ ++/** ++ * ZSTD_decompress_usingDict() - decompress src into dst using a dictionary ++ * @ctx: The decompression context. ++ * @dst: The buffer to decompress src into. ++ * @dstCapacity: The size of the destination buffer. Must be at least as large ++ * as the decompressed size. If the caller cannot upper bound the ++ * decompressed size, then it's better to use the streaming API. ++ * @src: The zstd compressed data to decompress. Multiple concatenated ++ * frames and skippable frames are allowed. ++ * @srcSize: The exact size of the data to decompress. ++ * @dict: The dictionary to use for decompression. The same dictionary ++ * must've been used to compress the data. ++ * @dictSize: The size of the dictionary. ++ * ++ * Return: The decompressed size or an error, which can be checked using ++ * ZSTD_isError(). ++ */ ++size_t ZSTD_decompress_usingDict(ZSTD_DCtx *ctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize, const void *dict, size_t dictSize); ++ ++/*-************************** ++ * Fast dictionary API ++ ***************************/ ++ ++/** ++ * ZSTD_CDictWorkspaceBound() - memory needed to initialize a ZSTD_CDict ++ * @cParams: The compression parameters to be used for compression. ++ * ++ * Return: A lower bound on the size of the workspace that is passed to ++ * ZSTD_initCDict(). ++ */ ++size_t ZSTD_CDictWorkspaceBound(ZSTD_compressionParameters cParams); ++ ++/** ++ * struct ZSTD_CDict - a digested dictionary to be used for compression ++ */ ++typedef struct ZSTD_CDict_s ZSTD_CDict; ++ ++/** ++ * ZSTD_initCDict() - initialize a digested dictionary for compression ++ * @dictBuffer: The dictionary to digest. The buffer is referenced by the ++ * ZSTD_CDict so it must outlive the returned ZSTD_CDict. ++ * @dictSize: The size of the dictionary. ++ * @params: The parameters to use for compression. See ZSTD_getParams(). ++ * @workspace: The workspace. It must outlive the returned ZSTD_CDict. ++ * @workspaceSize: The workspace size. Must be at least ++ * ZSTD_CDictWorkspaceBound(params.cParams). ++ * ++ * When compressing multiple messages / blocks with the same dictionary it is ++ * recommended to load it just once. The ZSTD_CDict merely references the ++ * dictBuffer, so it must outlive the returned ZSTD_CDict. ++ * ++ * Return: The digested dictionary emplaced into workspace. ++ */ ++ZSTD_CDict *ZSTD_initCDict(const void *dictBuffer, size_t dictSize, ++ ZSTD_parameters params, void *workspace, size_t workspaceSize); ++ ++/** ++ * ZSTD_compress_usingCDict() - compress src into dst using a ZSTD_CDict ++ * @ctx: The context. Must have been initialized with a workspace at ++ * least as large as ZSTD_CCtxWorkspaceBound(cParams) where ++ * cParams are the compression parameters used to initialize the ++ * cdict. ++ * @dst: The buffer to compress src into. ++ * @dstCapacity: The size of the destination buffer. May be any size, but ++ * ZSTD_compressBound(srcSize) is guaranteed to be large enough. ++ * @src: The data to compress. ++ * @srcSize: The size of the data to compress. ++ * @cdict: The digested dictionary to use for compression. ++ * @params: The parameters to use for compression. See ZSTD_getParams(). ++ * ++ * Compression using a digested dictionary. The same dictionary must be used ++ * during decompression. ++ * ++ * Return: The compressed size or an error, which can be checked using ++ * ZSTD_isError(). ++ */ ++size_t ZSTD_compress_usingCDict(ZSTD_CCtx *cctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize, const ZSTD_CDict *cdict); ++ ++ ++/** ++ * ZSTD_DDictWorkspaceBound() - memory needed to initialize a ZSTD_DDict ++ * ++ * Return: A lower bound on the size of the workspace that is passed to ++ * ZSTD_initDDict(). ++ */ ++size_t ZSTD_DDictWorkspaceBound(void); ++ ++/** ++ * struct ZSTD_DDict - a digested dictionary to be used for decompression ++ */ ++typedef struct ZSTD_DDict_s ZSTD_DDict; ++ ++/** ++ * ZSTD_initDDict() - initialize a digested dictionary for decompression ++ * @dictBuffer: The dictionary to digest. The buffer is referenced by the ++ * ZSTD_DDict so it must outlive the returned ZSTD_DDict. ++ * @dictSize: The size of the dictionary. ++ * @workspace: The workspace. It must outlive the returned ZSTD_DDict. ++ * @workspaceSize: The workspace size. Must be at least ++ * ZSTD_DDictWorkspaceBound(). ++ * ++ * When decompressing multiple messages / blocks with the same dictionary it is ++ * recommended to load it just once. The ZSTD_DDict merely references the ++ * dictBuffer, so it must outlive the returned ZSTD_DDict. ++ * ++ * Return: The digested dictionary emplaced into workspace. ++ */ ++ZSTD_DDict *ZSTD_initDDict(const void *dictBuffer, size_t dictSize, ++ void *workspace, size_t workspaceSize); ++ ++/** ++ * ZSTD_decompress_usingDDict() - decompress src into dst using a ZSTD_DDict ++ * @ctx: The decompression context. ++ * @dst: The buffer to decompress src into. ++ * @dstCapacity: The size of the destination buffer. Must be at least as large ++ * as the decompressed size. If the caller cannot upper bound the ++ * decompressed size, then it's better to use the streaming API. ++ * @src: The zstd compressed data to decompress. Multiple concatenated ++ * frames and skippable frames are allowed. ++ * @srcSize: The exact size of the data to decompress. ++ * @ddict: The digested dictionary to use for decompression. The same ++ * dictionary must've been used to compress the data. ++ * ++ * Return: The decompressed size or an error, which can be checked using ++ * ZSTD_isError(). ++ */ ++size_t ZSTD_decompress_usingDDict(ZSTD_DCtx *dctx, void *dst, ++ size_t dstCapacity, const void *src, size_t srcSize, ++ const ZSTD_DDict *ddict); ++ ++ ++/*-************************** ++ * Streaming ++ ***************************/ ++ ++/** ++ * 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; ++ ++ ++ ++/*-***************************************************************************** ++ * Streaming compression - HowTo ++ * ++ * A ZSTD_CStream object is required to track streaming operation. ++ * Use ZSTD_initCStream() to initialize a ZSTD_CStream object. ++ * ZSTD_CStream objects can be reused multiple times on consecutive compression ++ * operations. It is recommended to re-use ZSTD_CStream in situations where many ++ * streaming operations will be achieved consecutively. Use one separate ++ * ZSTD_CStream per thread for parallel execution. ++ * ++ * Use ZSTD_compressStream() repetitively to consume input stream. ++ * The function will automatically update both `pos` fields. ++ * Note that it may not consume the entire input, in which case `pos < size`, ++ * and it's up to the caller to present again remaining data. ++ * It returns a hint for the preferred number of bytes to use as an input for ++ * the next function call. ++ * ++ * At any moment, it's possible to flush whatever data remains within internal ++ * buffer, using ZSTD_flushStream(). `output->pos` will be updated. There might ++ * still be some content left within the internal buffer if `output->size` is ++ * too small. It returns the number of bytes left in the internal buffer and ++ * must be called until it returns 0. ++ * ++ * ZSTD_endStream() instructs to finish a frame. It will perform a flush and ++ * write frame epilogue. The epilogue is required for decoders to consider a ++ * frame completed. Similar to ZSTD_flushStream(), it may not be able to flush ++ * the full content if `output->size` is too small. In which case, call again ++ * ZSTD_endStream() to complete the flush. It returns the number of bytes left ++ * in the internal buffer and must be called until it returns 0. ++ ******************************************************************************/ ++ ++/** ++ * ZSTD_CStreamWorkspaceBound() - memory needed to initialize a ZSTD_CStream ++ * @cParams: The compression parameters to be used for compression. ++ * ++ * Return: A lower bound on the size of the workspace that is passed to ++ * ZSTD_initCStream() and ZSTD_initCStream_usingCDict(). ++ */ ++size_t ZSTD_CStreamWorkspaceBound(ZSTD_compressionParameters cParams); ++ ++/** ++ * struct ZSTD_CStream - the zstd streaming compression context ++ */ ++typedef struct ZSTD_CStream_s ZSTD_CStream; ++ ++/*===== ZSTD_CStream management functions =====*/ ++/** ++ * ZSTD_initCStream() - initialize a zstd streaming compression context ++ * @params: The zstd compression parameters. ++ * @pledgedSrcSize: If params.fParams.contentSizeFlag == 1 then the caller must ++ * pass the source size (zero means empty source). Otherwise, ++ * the caller may optionally pass the source size, or zero if ++ * unknown. ++ * @workspace: The workspace to emplace the context into. It must outlive ++ * the returned context. ++ * @workspaceSize: The size of workspace. ++ * Use ZSTD_CStreamWorkspaceBound(params.cParams) to determine ++ * how large the workspace must be. ++ * ++ * Return: The zstd streaming compression context. ++ */ ++ZSTD_CStream *ZSTD_initCStream(ZSTD_parameters params, ++ unsigned long long pledgedSrcSize, void *workspace, ++ size_t workspaceSize); ++ ++/** ++ * ZSTD_initCStream_usingCDict() - initialize a streaming compression context ++ * @cdict: The digested dictionary to use for compression. ++ * @pledgedSrcSize: Optionally the source size, or zero if unknown. ++ * @workspace: The workspace to emplace the context into. It must outlive ++ * the returned context. ++ * @workspaceSize: The size of workspace. Call ZSTD_CStreamWorkspaceBound() ++ * with the cParams used to initialize the cdict to determine ++ * how large the workspace must be. ++ * ++ * Return: The zstd streaming compression context. ++ */ ++ZSTD_CStream *ZSTD_initCStream_usingCDict(const ZSTD_CDict *cdict, ++ unsigned long long pledgedSrcSize, void *workspace, ++ size_t workspaceSize); ++ ++/*===== Streaming compression functions =====*/ ++/** ++ * ZSTD_resetCStream() - reset the context using parameters from creation ++ * @zcs: The zstd streaming compression context to reset. ++ * @pledgedSrcSize: Optionally the source size, or zero if unknown. ++ * ++ * Resets the context using the parameters from creation. Skips dictionary ++ * loading, since it can be reused. If `pledgedSrcSize` is non-zero the frame ++ * content size is always written into the frame header. ++ * ++ * Return: Zero or an error, which can be checked using ZSTD_isError(). ++ */ ++size_t ZSTD_resetCStream(ZSTD_CStream *zcs, unsigned long long pledgedSrcSize); ++/** ++ * ZSTD_compressStream() - streaming compress some of input into output ++ * @zcs: The zstd streaming compression context. ++ * @output: Destination buffer. `output->pos` is updated to indicate how much ++ * compressed data was written. ++ * @input: Source buffer. `input->pos` is updated to indicate how much data was ++ * read. Note that it may not consume the entire input, in which case ++ * `input->pos < input->size`, and it's up to the caller to present ++ * remaining data again. ++ * ++ * The `input` and `output` buffers may be any size. Guaranteed to make some ++ * forward progress if `input` and `output` are not empty. ++ * ++ * Return: A hint for the number of bytes to use as the input for the next ++ * function call or an error, which can be checked using ++ * ZSTD_isError(). ++ */ ++size_t ZSTD_compressStream(ZSTD_CStream *zcs, ZSTD_outBuffer *output, ++ ZSTD_inBuffer *input); ++/** ++ * ZSTD_flushStream() - flush internal buffers into output ++ * @zcs: The zstd streaming compression context. ++ * @output: Destination buffer. `output->pos` is updated to indicate how much ++ * compressed data was written. ++ * ++ * ZSTD_flushStream() must be called until it returns 0, meaning all the data ++ * has been flushed. Since ZSTD_flushStream() causes a block to be ended, ++ * calling it too often will degrade the compression ratio. ++ * ++ * Return: The number of bytes still present within internal buffers or an ++ * error, which can be checked using ZSTD_isError(). ++ */ ++size_t ZSTD_flushStream(ZSTD_CStream *zcs, ZSTD_outBuffer *output); ++/** ++ * ZSTD_endStream() - flush internal buffers into output and end the frame ++ * @zcs: The zstd streaming compression context. ++ * @output: Destination buffer. `output->pos` is updated to indicate how much ++ * compressed data was written. ++ * ++ * ZSTD_endStream() must be called until it returns 0, meaning all the data has ++ * been flushed and the frame epilogue has been written. ++ * ++ * Return: The number of bytes still present within internal buffers or an ++ * error, which can be checked using ZSTD_isError(). ++ */ ++size_t ZSTD_endStream(ZSTD_CStream *zcs, ZSTD_outBuffer *output); ++ ++/** ++ * ZSTD_CStreamInSize() - recommended size for the input buffer ++ * ++ * Return: The recommended size for the input buffer. ++ */ ++size_t ZSTD_CStreamInSize(void); ++/** ++ * ZSTD_CStreamOutSize() - recommended size for the output buffer ++ * ++ * When the output buffer is at least this large, it is guaranteed to be large ++ * enough to flush at least one complete compressed block. ++ * ++ * Return: The recommended size for the output buffer. ++ */ ++size_t ZSTD_CStreamOutSize(void); ++ ++ ++ ++/*-***************************************************************************** ++ * Streaming decompression - HowTo ++ * ++ * A ZSTD_DStream object is required to track streaming operations. ++ * Use ZSTD_initDStream() to initialize a ZSTD_DStream object. ++ * ZSTD_DStream objects can be re-used multiple times. ++ * ++ * Use ZSTD_decompressStream() repetitively to consume your input. ++ * The function will update both `pos` fields. ++ * If `input->pos < input->size`, some input has not been consumed. ++ * It's up to the caller to present again remaining data. ++ * If `output->pos < output->size`, decoder has flushed everything it could. ++ * Returns 0 iff a frame is completely decoded and fully flushed. ++ * Otherwise it returns a suggested next input size that will never load more ++ * than the current frame. ++ ******************************************************************************/ ++ ++/** ++ * ZSTD_DStreamWorkspaceBound() - memory needed to initialize a ZSTD_DStream ++ * @maxWindowSize: The maximum window size allowed for compressed frames. ++ * ++ * Return: A lower bound on the size of the workspace that is passed to ++ * ZSTD_initDStream() and ZSTD_initDStream_usingDDict(). ++ */ ++size_t ZSTD_DStreamWorkspaceBound(size_t maxWindowSize); ++ ++/** ++ * struct ZSTD_DStream - the zstd streaming decompression context ++ */ ++typedef struct ZSTD_DStream_s ZSTD_DStream; ++/*===== ZSTD_DStream management functions =====*/ ++/** ++ * ZSTD_initDStream() - initialize a zstd streaming decompression context ++ * @maxWindowSize: The maximum window size allowed for compressed frames. ++ * @workspace: The workspace to emplace the context into. It must outlive ++ * the returned context. ++ * @workspaceSize: The size of workspace. ++ * Use ZSTD_DStreamWorkspaceBound(maxWindowSize) to determine ++ * how large the workspace must be. ++ * ++ * Return: The zstd streaming decompression context. ++ */ ++ZSTD_DStream *ZSTD_initDStream(size_t maxWindowSize, void *workspace, ++ size_t workspaceSize); ++/** ++ * ZSTD_initDStream_usingDDict() - initialize streaming decompression context ++ * @maxWindowSize: The maximum window size allowed for compressed frames. ++ * @ddict: The digested dictionary to use for decompression. ++ * @workspace: The workspace to emplace the context into. It must outlive ++ * the returned context. ++ * @workspaceSize: The size of workspace. ++ * Use ZSTD_DStreamWorkspaceBound(maxWindowSize) to determine ++ * how large the workspace must be. ++ * ++ * Return: The zstd streaming decompression context. ++ */ ++ZSTD_DStream *ZSTD_initDStream_usingDDict(size_t maxWindowSize, ++ const ZSTD_DDict *ddict, void *workspace, size_t workspaceSize); ++ ++/*===== Streaming decompression functions =====*/ ++/** ++ * ZSTD_resetDStream() - reset the context using parameters from creation ++ * @zds: The zstd streaming decompression context to reset. ++ * ++ * Resets the context using the parameters from creation. Skips dictionary ++ * loading, since it can be reused. ++ * ++ * Return: Zero or an error, which can be checked using ZSTD_isError(). ++ */ ++size_t ZSTD_resetDStream(ZSTD_DStream *zds); ++/** ++ * ZSTD_decompressStream() - streaming decompress some of input into output ++ * @zds: The zstd streaming decompression context. ++ * @output: Destination buffer. `output.pos` is updated to indicate how much ++ * decompressed data was written. ++ * @input: Source buffer. `input.pos` is updated to indicate how much data was ++ * read. Note that it may not consume the entire input, in which case ++ * `input.pos < input.size`, and it's up to the caller to present ++ * remaining data again. ++ * ++ * The `input` and `output` buffers may be any size. Guaranteed to make some ++ * forward progress if `input` and `output` are not empty. ++ * ZSTD_decompressStream() will not consume the last byte of the frame until ++ * the entire frame is flushed. ++ * ++ * Return: Returns 0 iff a frame is completely decoded and fully flushed. ++ * Otherwise returns a hint for the number of bytes to use as the input ++ * for the next function call or an error, which can be checked using ++ * ZSTD_isError(). The size hint will never load more than the frame. ++ */ ++size_t ZSTD_decompressStream(ZSTD_DStream *zds, ZSTD_outBuffer *output, ++ ZSTD_inBuffer *input); ++ ++/** ++ * ZSTD_DStreamInSize() - recommended size for the input buffer ++ * ++ * Return: The recommended size for the input buffer. ++ */ ++size_t ZSTD_DStreamInSize(void); ++/** ++ * ZSTD_DStreamOutSize() - recommended size for the output buffer ++ * ++ * When the output buffer is at least this large, it is guaranteed to be large ++ * enough to flush at least one complete decompressed block. ++ * ++ * Return: The recommended size for the output buffer. ++ */ ++size_t ZSTD_DStreamOutSize(void); ++ ++ ++/* --- Constants ---*/ ++#define ZSTD_MAGICNUMBER 0xFD2FB528 /* >= v0.8.0 */ ++#define ZSTD_MAGIC_SKIPPABLE_START 0x184D2A50U ++ ++#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 ++ ++/* 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; ++ ++ ++/*-************************************* ++ * Compressed size functions ++ **************************************/ ++ ++/** ++ * ZSTD_findFrameCompressedSize() - returns the size of a compressed frame ++ * @src: Source buffer. It should point to the start of a zstd encoded frame ++ * or a skippable frame. ++ * @srcSize: The size of the source buffer. It must be at least as large as the ++ * size of the frame. ++ * ++ * Return: The compressed size of the frame pointed to by `src` or an error, ++ * which can be check with ZSTD_isError(). ++ * Suitable to pass to ZSTD_decompress() or similar functions. ++ */ ++size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize); ++ ++/*-************************************* ++ * Decompressed size functions ++ **************************************/ ++/** ++ * ZSTD_getFrameContentSize() - returns the content size in a zstd frame header ++ * @src: It should point to the start of a zstd encoded frame. ++ * @srcSize: The size of the source buffer. It must be at least as large as the ++ * frame header. `ZSTD_frameHeaderSize_max` is always large enough. ++ * ++ * Return: The frame content size stored in the frame header if known. ++ * `ZSTD_CONTENTSIZE_UNKNOWN` if the content size isn't stored in the ++ * frame header. `ZSTD_CONTENTSIZE_ERROR` on invalid input. ++ */ ++unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize); ++ ++/** ++ * ZSTD_findDecompressedSize() - returns decompressed size of a series of frames ++ * @src: It should point to the start of a series of zstd encoded and/or ++ * skippable frames. ++ * @srcSize: The exact size of the series of frames. ++ * ++ * If any zstd encoded frame in the series doesn't have the frame content size ++ * set, `ZSTD_CONTENTSIZE_UNKNOWN` is returned. But frame content size is always ++ * set when using ZSTD_compress(). The decompressed size can be very large. ++ * If the source is untrusted, the decompressed size could be wrong or ++ * intentionally modified. Always ensure the result fits within the ++ * application's authorized limits. ZSTD_findDecompressedSize() handles multiple ++ * frames, and so it must traverse the input to read each frame header. This is ++ * efficient as most of the data is skipped, however it does mean that all frame ++ * data must be present and valid. ++ * ++ * Return: Decompressed size of all the data contained in the frames if known. ++ * `ZSTD_CONTENTSIZE_UNKNOWN` if the decompressed size is unknown. ++ * `ZSTD_CONTENTSIZE_ERROR` if an error occurred. ++ */ ++unsigned long long ZSTD_findDecompressedSize(const void *src, size_t srcSize); ++ ++/*-************************************* ++ * Advanced compression functions ++ **************************************/ ++/** ++ * ZSTD_checkCParams() - ensure parameter values remain within authorized range ++ * @cParams: The zstd compression parameters. ++ * ++ * Return: Zero or an error, which can be checked using ZSTD_isError(). ++ */ ++size_t ZSTD_checkCParams(ZSTD_compressionParameters cParams); ++ ++/** ++ * ZSTD_adjustCParams() - optimize parameters for a given srcSize and dictSize ++ * @srcSize: Optionally the estimated source size, or zero if unknown. ++ * @dictSize: Optionally the estimated dictionary size, or zero if unknown. ++ * ++ * Return: The optimized parameters. ++ */ ++ZSTD_compressionParameters ZSTD_adjustCParams( ++ ZSTD_compressionParameters cParams, unsigned long long srcSize, ++ size_t dictSize); ++ ++/*--- Advanced decompression functions ---*/ ++ ++/** ++ * ZSTD_isFrame() - returns true iff the buffer starts with a valid frame ++ * @buffer: The source buffer to check. ++ * @size: The size of the source buffer, must be at least 4 bytes. ++ * ++ * Return: True iff the buffer starts with a zstd or skippable frame identifier. ++ */ ++unsigned int ZSTD_isFrame(const void *buffer, size_t size); ++ ++/** ++ * ZSTD_getDictID_fromDict() - returns the dictionary id stored in a dictionary ++ * @dict: The dictionary buffer. ++ * @dictSize: The size of the dictionary buffer. ++ * ++ * Return: The dictionary id stored within the dictionary or 0 if the ++ * dictionary is not a zstd dictionary. If it returns 0 the ++ * dictionary can still be loaded as a content-only dictionary. ++ */ ++unsigned int ZSTD_getDictID_fromDict(const void *dict, size_t dictSize); ++ ++/** ++ * ZSTD_getDictID_fromDDict() - returns the dictionary id stored in a ZSTD_DDict ++ * @ddict: The ddict to find the id of. ++ * ++ * Return: The dictionary id stored within `ddict` or 0 if the dictionary is not ++ * a zstd dictionary. If it returns 0 `ddict` will be loaded as a ++ * content-only dictionary. ++ */ ++unsigned int ZSTD_getDictID_fromDDict(const ZSTD_DDict *ddict); ++ ++/** ++ * ZSTD_getDictID_fromFrame() - returns the dictionary id stored in a zstd frame ++ * @src: Source buffer. It must be a zstd encoded frame. ++ * @srcSize: The size of the source buffer. It must be at least as large as the ++ * frame header. `ZSTD_frameHeaderSize_max` is always large enough. ++ * ++ * Return: The dictionary id required to decompress the frame stored within ++ * `src` or 0 if the dictionary id could not be decoded. It can return ++ * 0 if the frame does not require a dictionary, the dictionary id ++ * wasn't stored in the frame, `src` is not a zstd frame, or `srcSize` ++ * is too small. ++ */ ++unsigned int ZSTD_getDictID_fromFrame(const void *src, size_t srcSize); ++ ++/** ++ * 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; ++ ++/** ++ * ZSTD_getFrameParams() - extracts parameters from a zstd or skippable frame ++ * @fparamsPtr: On success the frame parameters are written here. ++ * @src: The source buffer. It must point to a zstd or skippable frame. ++ * @srcSize: The size of the source buffer. `ZSTD_frameHeaderSize_max` is ++ * always large enough to succeed. ++ * ++ * Return: 0 on success. If more data is required it returns how many bytes ++ * must be provided to make forward progress. Otherwise it returns ++ * an error, which can be checked using ZSTD_isError(). ++ */ ++size_t ZSTD_getFrameParams(ZSTD_frameParams *fparamsPtr, const void *src, ++ size_t srcSize); ++ ++/*-***************************************************************************** ++ * Buffer-less and synchronous inner streaming functions ++ * ++ * This is an advanced API, giving full control over buffer management, for ++ * users which need direct control over memory. ++ * But it's also a complex one, with many restrictions (documented below). ++ * Prefer using normal streaming API for an easier experience ++ ******************************************************************************/ ++ ++/*-***************************************************************************** ++ * Buffer-less streaming compression (synchronous mode) ++ * ++ * A ZSTD_CCtx object is required to track streaming operations. ++ * Use ZSTD_initCCtx() to initialize a context. ++ * ZSTD_CCtx object can be re-used multiple times within successive compression ++ * operations. ++ * ++ * Start by initializing a context. ++ * Use ZSTD_compressBegin(), or ZSTD_compressBegin_usingDict() for dictionary ++ * compression, ++ * or ZSTD_compressBegin_advanced(), for finer parameter control. ++ * It's also possible to duplicate a reference context which has already been ++ * initialized, using ZSTD_copyCCtx() ++ * ++ * Then, consume your input using ZSTD_compressContinue(). ++ * There are some important considerations to keep in mind when using this ++ * advanced function : ++ * - ZSTD_compressContinue() has no internal buffer. It uses externally provided ++ * buffer only. ++ * - Interface is synchronous : input is consumed entirely and produce 1+ ++ * (or more) compressed blocks. ++ * - Caller must ensure there is enough space in `dst` to store compressed data ++ * under worst case scenario. Worst case evaluation is provided by ++ * ZSTD_compressBound(). ++ * ZSTD_compressContinue() doesn't guarantee recover after a failed ++ * compression. ++ * - ZSTD_compressContinue() presumes prior input ***is still accessible and ++ * unmodified*** (up to maximum distance size, see WindowLog). ++ * It remembers all previous contiguous blocks, plus one separated memory ++ * segment (which can itself consists of multiple contiguous blocks) ++ * - ZSTD_compressContinue() detects that prior input has been overwritten when ++ * `src` buffer overlaps. In which case, it will "discard" the relevant memory ++ * section from its history. ++ * ++ * Finish a frame with ZSTD_compressEnd(), which will write the last block(s) ++ * and optional checksum. It's possible to use srcSize==0, in which case, it ++ * will write a final empty block to end the frame. Without last block mark, ++ * frames will be considered unfinished (corrupted) by decoders. ++ * ++ * `ZSTD_CCtx` object can be re-used (ZSTD_compressBegin()) to compress some new ++ * frame. ++ ******************************************************************************/ ++ ++/*===== Buffer-less streaming compression functions =====*/ ++size_t ZSTD_compressBegin(ZSTD_CCtx *cctx, int compressionLevel); ++size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx *cctx, const void *dict, ++ size_t dictSize, int compressionLevel); ++size_t ZSTD_compressBegin_advanced(ZSTD_CCtx *cctx, const void *dict, ++ size_t dictSize, ZSTD_parameters params, ++ unsigned long long pledgedSrcSize); ++size_t ZSTD_copyCCtx(ZSTD_CCtx *cctx, const ZSTD_CCtx *preparedCCtx, ++ unsigned long long pledgedSrcSize); ++size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx *cctx, const ZSTD_CDict *cdict, ++ unsigned long long pledgedSrcSize); ++size_t ZSTD_compressContinue(ZSTD_CCtx *cctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize); ++size_t ZSTD_compressEnd(ZSTD_CCtx *cctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize); ++ ++ ++ ++/*-***************************************************************************** ++ * Buffer-less streaming decompression (synchronous mode) ++ * ++ * A ZSTD_DCtx object is required to track streaming operations. ++ * Use ZSTD_initDCtx() to initialize a context. ++ * A ZSTD_DCtx object can be re-used multiple times. ++ * ++ * First typical operation is to retrieve frame parameters, using ++ * ZSTD_getFrameParams(). It fills a ZSTD_frameParams structure which provide ++ * important information to correctly decode the frame, such as the minimum ++ * rolling buffer size to allocate to decompress data (`windowSize`), and the ++ * dictionary ID used. ++ * Note: content size is optional, it may not be present. 0 means unknown. ++ * Note that these values could be wrong, either because of data malformation, ++ * or because an attacker is spoofing deliberate false information. As a ++ * consequence, check that values remain within valid application range, ++ * especially `windowSize`, before allocation. Each application can set its own ++ * limit, depending on local restrictions. For extended interoperability, it is ++ * recommended to support at least 8 MB. ++ * Frame parameters are extracted from the beginning of the compressed frame. ++ * Data fragment must be large enough to ensure successful decoding, typically ++ * `ZSTD_frameHeaderSize_max` bytes. ++ * Result: 0: successful decoding, the `ZSTD_frameParams` structure is filled. ++ * >0: `srcSize` is too small, provide at least this many bytes. ++ * errorCode, which can be tested using ZSTD_isError(). ++ * ++ * Start decompression, with ZSTD_decompressBegin() or ++ * ZSTD_decompressBegin_usingDict(). Alternatively, you can copy a prepared ++ * context, using ZSTD_copyDCtx(). ++ * ++ * Then use ZSTD_nextSrcSizeToDecompress() and ZSTD_decompressContinue() ++ * alternatively. ++ * ZSTD_nextSrcSizeToDecompress() tells how many bytes to provide as 'srcSize' ++ * to ZSTD_decompressContinue(). ++ * ZSTD_decompressContinue() requires this _exact_ amount of bytes, or it will ++ * fail. ++ * ++ * The result of ZSTD_decompressContinue() is the number of bytes regenerated ++ * within 'dst' (necessarily <= dstCapacity). It can be zero, which is not an ++ * error; it just means ZSTD_decompressContinue() has decoded some metadata ++ * item. It can also be an error code, which can be tested with ZSTD_isError(). ++ * ++ * ZSTD_decompressContinue() needs previous data blocks during decompression, up ++ * to `windowSize`. They should preferably be located contiguously, prior to ++ * current block. Alternatively, a round buffer of sufficient size is also ++ * possible. Sufficient size is determined by frame parameters. ++ * ZSTD_decompressContinue() is very sensitive to contiguity, if 2 blocks don't ++ * follow each other, make sure that either the compressor breaks contiguity at ++ * the same place, or that previous contiguous segment is large enough to ++ * properly handle maximum back-reference. ++ * ++ * A frame is fully decoded when ZSTD_nextSrcSizeToDecompress() returns zero. ++ * Context can then be reset to start a new decompression. ++ * ++ * Note: it's possible to know if next input to present is a header or a block, ++ * using ZSTD_nextInputType(). This information is not required to properly ++ * decode a frame. ++ * ++ * == Special case: skippable frames == ++ * ++ * Skippable frames allow integration of user-defined data into a flow of ++ * concatenated frames. Skippable frames will be ignored (skipped) by a ++ * decompressor. The format of skippable frames is as follows: ++ * a) Skippable frame ID - 4 Bytes, Little endian format, any value from ++ * 0x184D2A50 to 0x184D2A5F ++ * b) Frame Size - 4 Bytes, Little endian format, unsigned 32-bits ++ * c) Frame Content - any content (User Data) of length equal to Frame Size ++ * For skippable frames ZSTD_decompressContinue() always returns 0. ++ * For skippable frames ZSTD_getFrameParams() returns fparamsPtr->windowLog==0 ++ * what means that a frame is skippable. ++ * Note: If fparamsPtr->frameContentSize==0, it is ambiguous: the frame might ++ * actually be a zstd encoded frame with no content. For purposes of ++ * decompression, it is valid in both cases to skip the frame using ++ * ZSTD_findFrameCompressedSize() to find its size in bytes. ++ * It also returns frame size as fparamsPtr->frameContentSize. ++ ******************************************************************************/ ++ ++/*===== Buffer-less streaming decompression functions =====*/ ++size_t ZSTD_decompressBegin(ZSTD_DCtx *dctx); ++size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx *dctx, const void *dict, ++ size_t dictSize); ++void ZSTD_copyDCtx(ZSTD_DCtx *dctx, const ZSTD_DCtx *preparedDCtx); ++size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx *dctx); ++size_t ZSTD_decompressContinue(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize); ++typedef enum { ++ ZSTDnit_frameHeader, ++ ZSTDnit_blockHeader, ++ ZSTDnit_block, ++ ZSTDnit_lastBlock, ++ ZSTDnit_checksum, ++ ZSTDnit_skippableFrame ++} ZSTD_nextInputType_e; ++ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx *dctx); ++ ++/*-***************************************************************************** ++ * Block functions ++ * ++ * Block functions produce and decode raw zstd blocks, without frame metadata. ++ * Frame metadata cost is typically ~18 bytes, which can be non-negligible for ++ * very small blocks (< 100 bytes). User will have to take in charge required ++ * information to regenerate data, such as compressed and content sizes. ++ * ++ * A few rules to respect: ++ * - Compressing and decompressing require a context structure ++ * + Use ZSTD_initCCtx() and ZSTD_initDCtx() ++ * - It is necessary to init context before starting ++ * + compression : ZSTD_compressBegin() ++ * + decompression : ZSTD_decompressBegin() ++ * + variants _usingDict() are also allowed ++ * + copyCCtx() and copyDCtx() work too ++ * - Block size is limited, it must be <= ZSTD_getBlockSizeMax() ++ * + If you need to compress more, cut data into multiple blocks ++ * + Consider using the regular ZSTD_compress() instead, as frame metadata ++ * costs become negligible when source size is large. ++ * - When a block is considered not compressible enough, ZSTD_compressBlock() ++ * result will be zero. In which case, nothing is produced into `dst`. ++ * + User must test for such outcome and deal directly with uncompressed data ++ * + ZSTD_decompressBlock() doesn't accept uncompressed data as input!!! ++ * + In case of multiple successive blocks, decoder must be informed of ++ * uncompressed block existence to follow proper history. Use ++ * ZSTD_insertBlock() in such a case. ++ ******************************************************************************/ ++ ++/* Define for static allocation */ ++#define ZSTD_BLOCKSIZE_ABSOLUTEMAX (128 * 1024) ++/*===== Raw zstd block functions =====*/ ++size_t ZSTD_getBlockSizeMax(ZSTD_CCtx *cctx); ++size_t ZSTD_compressBlock(ZSTD_CCtx *cctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize); ++size_t ZSTD_decompressBlock(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize); ++size_t ZSTD_insertBlock(ZSTD_DCtx *dctx, const void *blockStart, ++ size_t blockSize); ++ ++#endif /* ZSTD_H */ From 34e0b01ed4273b47b8f2782ded6e068162c59892 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 24 Nov 2020 19:25:50 +0000 Subject: [PATCH 057/194] stack corruption from XSA-346 change [XSA-355] --- xen.spec | 7 ++++++- xsa355.patch | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 xsa355.patch diff --git a/xen.spec b/xen.spec index 92cfe82..68052ee 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.14.0 -Release: 11%{?dist} +Release: 12%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -142,6 +142,7 @@ Patch73: xsa351-arm.patch Patch74: xsa351-x86-4.14-1.patch Patch75: xsa351-x86-4.14-2.patch Patch76: zstd-dom0.patch +Patch77: xsa355.patch %if %build_qemutrad @@ -373,6 +374,7 @@ manage Xen virtual machines. %patch74 -p1 %patch75 -p1 %patch76 -p1 +%patch77 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -966,6 +968,9 @@ fi %endif %changelog +* Tue Nov 24 2020 Michael Young - 4.14.0-12 +- stack corruption from XSA-346 change [XSA-355] + * Mon Nov 23 2020 Michael Young - 4.14.0-11 - support zstd compressed kernels (dom0 only) based on linux kernel code diff --git a/xsa355.patch b/xsa355.patch new file mode 100644 index 0000000..491dd05 --- /dev/null +++ b/xsa355.patch @@ -0,0 +1,23 @@ +From: Jan Beulich +Subject: memory: fix off-by-one in XSA-346 change + +The comparison against ARRAY_SIZE() needs to be >= in order to avoid +overrunning the pages[] array. + +This is XSA-355. + +Fixes: 5777a3742d88 ("IOMMU: hold page ref until after deferred TLB flush") +Signed-off-by: Jan Beulich +Reviewed-by: Julien Grall + +--- a/xen/common/memory.c ++++ b/xen/common/memory.c +@@ -854,7 +854,7 @@ int xenmem_add_to_physmap(struct domain + ++extra.ppage; + + /* Check for continuation if it's not the last iteration. */ +- if ( (++done > ARRAY_SIZE(pages) && extra.ppage) || ++ if ( (++done >= ARRAY_SIZE(pages) && extra.ppage) || + (xatp->size > done && hypercall_preempt_check()) ) + { + rc = start + done; From 77c10a7976c364bd6ddcd240ad745673dd8b70bb Mon Sep 17 00:00:00 2001 From: Jeff Law Date: Sat, 5 Dec 2020 08:52:08 -0700 Subject: [PATCH 058/194] Work around another gcc-11 stringop-overflow diagnostic --- xen.gcc11.fixes.patch | 17 +++++++++++++++++ xen.spec | 5 ++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/xen.gcc11.fixes.patch b/xen.gcc11.fixes.patch index f6b67ac..fd8fe15 100644 --- a/xen.gcc11.fixes.patch +++ b/xen.gcc11.fixes.patch @@ -56,3 +56,20 @@ 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.spec b/xen.spec index 68052ee..2c7ddc0 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.14.0 -Release: 12%{?dist} +Release: 13%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -968,6 +968,9 @@ fi %endif %changelog +* Sat Dec 05 2020 Jeff Law - 4.14.0-13 +- Work around another gcc-11 stringop-overflow diagnostic + * Tue Nov 24 2020 Michael Young - 4.14.0-12 - stack corruption from XSA-346 change [XSA-355] From 960733fb60b6ac190cc407b13bd16707f72d9897 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 15 Dec 2020 23:46:04 +0000 Subject: [PATCH 059/194] multiple security updates xenstore watch notifications lacking permission checks [XSA-115, CVE-2020-29480] (#1908091) Xenstore: new domains inheriting existing node permissions [XSA-322, CVE-2020-29481] (#1908095) Xenstore: wrong path length check [XSA-323, CVE-2020-29482] (#1908096) Xenstore: guests can crash xenstored via watchs [XSA-324, CVE-2020-29484] (#1908088) Xenstore: guests can disturb domain cleanup [XSA-325, CVE-2020-29483] (#1905648) oxenstored memory leak in reset_watches [XSA-330, CVE-2020-29485] (#1908000) undue recursion in x86 HVM context switch code [XSA-348, CVE-2020-29566] (#1908085) oxenstored: node ownership can be changed by unprivileged clients [XSA-352, CVE-2020-29486] (#1908003) oxenstored: permissions not checked on root node [XSA-353, CVE-2020-29479] (#1908003) infinite loop when cleaning up IRQ vectors [XSA-356, CVE-2020-29567] (#1907932) FIFO event channels control block related ordering [XSA-358, CVE-2020-29570] (#1907931) FIFO event channels control structure ordering [XSA-359, CVE-2020-29571] (#1908089) --- ...77370df2db9129c97c7305962fc5ad298728.patch | 592 ++++++++++++++++++ ...9b1c5b9e4c024f5f139d7a3d0357d2417b13.patch | 97 +++ ...989f51878bc9ba61e930b0c29b921a30dc0d.patch | 240 +++++++ ...8003fdf7bd9e28137e6683ef46902af39146.patch | 41 ++ xen.spec | 95 ++- ...llow-removing-child-of-a-node-exceed.patch | 157 +++++ ...e-ignore-transaction-id-for-un-watch.patch | 86 +++ ...ix-node-accounting-after-failed-node.patch | 104 +++ ...simplify-and-rename-check_event_node.patch | 55 ++ ...heck-privilege-for-XS_IS_DOMAIN_INTR.patch | 115 ++++ ...6-tools-xenstore-rework-node-removal.patch | 217 +++++++ ...ire-watches-only-when-removing-a-spe.patch | 118 ++++ ...store-introduce-node_perms-structure.patch | 289 +++++++++ ...llow-special-watches-for-privileged-.patch | 237 +++++++ ...void-watch-events-for-nodes-without-.patch | 374 +++++++++++ ...tored-ignore-transaction-id-for-un-w.patch | 43 ++ ...tored-check-privilege-for-XS_IS_DOMA.patch | 30 + ...s-ocaml-xenstored-unify-watch-firing.patch | 29 + ...tored-introduce-permissions-for-spec.patch | 117 ++++ ...tored-avoid-watch-events-for-nodes-w.patch | 406 ++++++++++++ ...tored-add-xenstored.conf-flag-to-tur.patch | 84 +++ xsa322-4.14-c.patch | 532 ++++++++++++++++ xsa322-o.patch | 110 ++++ xsa323.patch | 140 +++++ xsa324.patch | 48 ++ xsa325-4.14.patch | 192 ++++++ xsa330.patch | 66 ++ xsa348-1.patch | 113 ++++ xsa348-2.patch | 85 +++ xsa348-3.patch | 174 +++++ xsa352.patch | 42 ++ xsa353.patch | 89 +++ xsa356.patch | 65 ++ xsa358.patch | 57 ++ xsa359.patch | 40 ++ 35 files changed, 5278 insertions(+), 1 deletion(-) create mode 100644 xen.git-1ad177370df2db9129c97c7305962fc5ad298728.patch create mode 100644 xen.git-1cfb9b1c5b9e4c024f5f139d7a3d0357d2417b13.patch create mode 100644 xen.git-72bd989f51878bc9ba61e930b0c29b921a30dc0d.patch create mode 100644 xen.git-8d148003fdf7bd9e28137e6683ef46902af39146.patch create mode 100644 xsa115-4.14-c-0001-tools-xenstore-allow-removing-child-of-a-node-exceed.patch create mode 100644 xsa115-4.14-c-0002-tools-xenstore-ignore-transaction-id-for-un-watch.patch create mode 100644 xsa115-4.14-c-0003-tools-xenstore-fix-node-accounting-after-failed-node.patch create mode 100644 xsa115-4.14-c-0004-tools-xenstore-simplify-and-rename-check_event_node.patch create mode 100644 xsa115-4.14-c-0005-tools-xenstore-check-privilege-for-XS_IS_DOMAIN_INTR.patch create mode 100644 xsa115-4.14-c-0006-tools-xenstore-rework-node-removal.patch create mode 100644 xsa115-4.14-c-0007-tools-xenstore-fire-watches-only-when-removing-a-spe.patch create mode 100644 xsa115-4.14-c-0008-tools-xenstore-introduce-node_perms-structure.patch create mode 100644 xsa115-4.14-c-0009-tools-xenstore-allow-special-watches-for-privileged-.patch create mode 100644 xsa115-4.14-c-0010-tools-xenstore-avoid-watch-events-for-nodes-without-.patch create mode 100644 xsa115-o-0001-tools-ocaml-xenstored-ignore-transaction-id-for-un-w.patch create mode 100644 xsa115-o-0002-tools-ocaml-xenstored-check-privilege-for-XS_IS_DOMA.patch create mode 100644 xsa115-o-0003-tools-ocaml-xenstored-unify-watch-firing.patch create mode 100644 xsa115-o-0004-tools-ocaml-xenstored-introduce-permissions-for-spec.patch create mode 100644 xsa115-o-0005-tools-ocaml-xenstored-avoid-watch-events-for-nodes-w.patch create mode 100644 xsa115-o-0006-tools-ocaml-xenstored-add-xenstored.conf-flag-to-tur.patch create mode 100644 xsa322-4.14-c.patch create mode 100644 xsa322-o.patch create mode 100644 xsa323.patch create mode 100644 xsa324.patch create mode 100644 xsa325-4.14.patch create mode 100644 xsa330.patch create mode 100644 xsa348-1.patch create mode 100644 xsa348-2.patch create mode 100644 xsa348-3.patch create mode 100644 xsa352.patch create mode 100644 xsa353.patch create mode 100644 xsa356.patch create mode 100644 xsa358.patch create mode 100644 xsa359.patch diff --git a/xen.git-1ad177370df2db9129c97c7305962fc5ad298728.patch b/xen.git-1ad177370df2db9129c97c7305962fc5ad298728.patch new file mode 100644 index 0000000..a713b25 --- /dev/null +++ b/xen.git-1ad177370df2db9129c97c7305962fc5ad298728.patch @@ -0,0 +1,592 @@ +From 1ad177370df2db9129c97c7305962fc5ad298728 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 1 Dec 2020 15:31:01 +0100 +Subject: [PATCH] xen/evtchn: rework per event channel lock + +Currently the lock for a single event channel needs to be taken with +interrupts off, which causes deadlocks in some cases. + +Rework the per event channel lock to be non-blocking for the case of +sending an event and removing the need for disabling interrupts for +taking the lock. + +The lock is needed for avoiding races between event channel state +changes (creation, closing, binding) against normal operations (set +pending, [un]masking, priority changes). + +Use a rwlock, but with some restrictions: + +- Changing the state of an event channel (creation, closing, binding) + needs to use write_lock(), with ASSERT()ing that the lock is taken as + writer only when the state of the event channel is either before or + after the locked region appropriate (either free or unbound). + +- Sending an event needs to use read_trylock() mostly, in case of not + obtaining the lock the operation is omitted. This is needed as + sending an event can happen with interrupts off (at least in some + cases). + +- Dumping the event channel state for debug purposes is using + read_trylock(), too, in order to avoid blocking in case the lock is + taken as writer for a long time. + +- All other cases can use read_lock(). + +Fixes: e045199c7c9c54 ("evtchn: address races with evtchn_reset()") +Signed-off-by: Juergen Gross +Reviewed-by: Jan Beulich +Acked-by: Julien Grall + +xen/events: fix build + +Commit 5f2df45ead7c1195 ("xen/evtchn: rework per event channel lock") +introduced a build failure for NDEBUG builds. + +Fixes: 5f2df45ead7c1195 ("xen/evtchn: rework per event channel lock") +Signed-off-by: Juergen Gross +Signed-off-by: Jan Beulich +master commit: 5f2df45ead7c1195142f68b7923047a1e9479d54 +master date: 2020-11-10 14:36:15 +0100 +master commit: 53bacb86f496fdb11560d9e3b361bca7de60d268 +master date: 2020-11-11 08:56:21 +0100 +--- + xen/arch/x86/irq.c | 6 +- + xen/arch/x86/pv/shim.c | 9 +-- + xen/common/event_channel.c | 141 ++++++++++++++++++++++--------------- + xen/include/xen/event.h | 27 +++++-- + xen/include/xen/sched.h | 5 +- + 5 files changed, 116 insertions(+), 72 deletions(-) + +diff --git a/xen/arch/x86/irq.c b/xen/arch/x86/irq.c +index 93c4fb9a79..8d1f9a9fc6 100644 +--- a/xen/arch/x86/irq.c ++++ b/xen/arch/x86/irq.c +@@ -2495,14 +2495,12 @@ static void dump_irqs(unsigned char key) + pirq = domain_irq_to_pirq(d, irq); + info = pirq_info(d, pirq); + evtchn = evtchn_from_port(d, info->evtchn); +- local_irq_disable(); +- if ( spin_trylock(&evtchn->lock) ) ++ if ( evtchn_read_trylock(evtchn) ) + { + pending = evtchn_is_pending(d, evtchn); + masked = evtchn_is_masked(d, evtchn); +- spin_unlock(&evtchn->lock); ++ evtchn_read_unlock(evtchn); + } +- local_irq_enable(); + printk("d%d:%3d(%c%c%c)%c", + d->domain_id, pirq, "-P?"[pending], + "-M?"[masked], info->masked ? 'M' : '-', +diff --git a/xen/arch/x86/pv/shim.c b/xen/arch/x86/pv/shim.c +index 9aef7a860a..b4e83e0778 100644 +--- a/xen/arch/x86/pv/shim.c ++++ b/xen/arch/x86/pv/shim.c +@@ -660,11 +660,12 @@ void pv_shim_inject_evtchn(unsigned int port) + if ( port_is_valid(guest, port) ) + { + struct evtchn *chn = evtchn_from_port(guest, port); +- unsigned long flags; + +- spin_lock_irqsave(&chn->lock, flags); +- evtchn_port_set_pending(guest, chn->notify_vcpu_id, chn); +- spin_unlock_irqrestore(&chn->lock, flags); ++ if ( evtchn_read_trylock(chn) ) ++ { ++ evtchn_port_set_pending(guest, chn->notify_vcpu_id, chn); ++ evtchn_read_unlock(chn); ++ } + } + } + +diff --git a/xen/common/event_channel.c b/xen/common/event_channel.c +index 12f666cb79..181e5abaa6 100644 +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -50,6 +50,40 @@ + + #define consumer_is_xen(e) (!!(e)->xen_consumer) + ++/* ++ * Lock an event channel exclusively. This is allowed only when the channel is ++ * free or unbound either when taking or when releasing the lock, as any ++ * concurrent operation on the event channel using evtchn_read_trylock() will ++ * 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) ++{ ++ write_lock(&evtchn->lock); ++ ++#ifndef NDEBUG ++ evtchn->old_state = evtchn->state; ++#endif ++} ++ ++static inline unsigned int old_state(const struct evtchn *evtchn) ++{ ++#ifndef NDEBUG ++ return evtchn->old_state; ++#else ++ return ECS_RESERVED; /* Just to allow things to build. */ ++#endif ++} ++ ++static inline void evtchn_write_unlock(struct evtchn *evtchn) ++{ ++ /* Enforce lock discipline. */ ++ ASSERT(old_state(evtchn) == ECS_FREE || old_state(evtchn) == ECS_UNBOUND || ++ evtchn->state == ECS_FREE || evtchn->state == ECS_UNBOUND); ++ ++ write_unlock(&evtchn->lock); ++} ++ + /* + * The function alloc_unbound_xen_event_channel() allows an arbitrary + * notifier function to be specified. However, very few unique functions +@@ -131,7 +165,7 @@ static struct evtchn *alloc_evtchn_bucket(struct domain *d, unsigned int port) + return NULL; + } + chn[i].port = port + i; +- spin_lock_init(&chn[i].lock); ++ rwlock_init(&chn[i].lock); + } + return chn; + } +@@ -249,7 +283,6 @@ static long evtchn_alloc_unbound(evtchn_alloc_unbound_t *alloc) + int port; + domid_t dom = alloc->dom; + long rc; +- unsigned long flags; + + d = rcu_lock_domain_by_any_id(dom); + if ( d == NULL ) +@@ -265,14 +298,14 @@ static long evtchn_alloc_unbound(evtchn_alloc_unbound_t *alloc) + if ( rc ) + goto out; + +- spin_lock_irqsave(&chn->lock, flags); ++ evtchn_write_lock(chn); + + chn->state = ECS_UNBOUND; + if ( (chn->u.unbound.remote_domid = alloc->remote_dom) == DOMID_SELF ) + chn->u.unbound.remote_domid = current->domain->domain_id; + evtchn_port_init(d, chn); + +- spin_unlock_irqrestore(&chn->lock, flags); ++ evtchn_write_unlock(chn); + + alloc->port = port; + +@@ -285,32 +318,26 @@ static long evtchn_alloc_unbound(evtchn_alloc_unbound_t *alloc) + } + + +-static unsigned long double_evtchn_lock(struct evtchn *lchn, +- struct evtchn *rchn) ++static void double_evtchn_lock(struct evtchn *lchn, struct evtchn *rchn) + { +- unsigned long flags; +- + if ( lchn <= rchn ) + { +- spin_lock_irqsave(&lchn->lock, flags); ++ evtchn_write_lock(lchn); + if ( lchn != rchn ) +- spin_lock(&rchn->lock); ++ evtchn_write_lock(rchn); + } + else + { +- spin_lock_irqsave(&rchn->lock, flags); +- spin_lock(&lchn->lock); ++ evtchn_write_lock(rchn); ++ evtchn_write_lock(lchn); + } +- +- return flags; + } + +-static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn, +- unsigned long flags) ++static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn) + { + if ( lchn != rchn ) +- spin_unlock(&lchn->lock); +- spin_unlock_irqrestore(&rchn->lock, flags); ++ evtchn_write_unlock(lchn); ++ evtchn_write_unlock(rchn); + } + + static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) +@@ -320,7 +347,6 @@ static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) + int lport, rport = bind->remote_port; + domid_t rdom = bind->remote_dom; + long rc; +- unsigned long flags; + + if ( rdom == DOMID_SELF ) + rdom = current->domain->domain_id; +@@ -356,7 +382,7 @@ static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) + if ( rc ) + goto out; + +- flags = double_evtchn_lock(lchn, rchn); ++ double_evtchn_lock(lchn, rchn); + + lchn->u.interdomain.remote_dom = rd; + lchn->u.interdomain.remote_port = rport; +@@ -373,7 +399,7 @@ static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) + */ + evtchn_port_set_pending(ld, lchn->notify_vcpu_id, lchn); + +- double_evtchn_unlock(lchn, rchn, flags); ++ double_evtchn_unlock(lchn, rchn); + + bind->local_port = lport; + +@@ -396,7 +422,6 @@ int evtchn_bind_virq(evtchn_bind_virq_t *bind, evtchn_port_t port) + struct domain *d = current->domain; + int virq = bind->virq, vcpu = bind->vcpu; + int rc = 0; +- unsigned long flags; + + if ( (virq < 0) || (virq >= ARRAY_SIZE(v->virq_to_evtchn)) ) + return -EINVAL; +@@ -434,14 +459,14 @@ int evtchn_bind_virq(evtchn_bind_virq_t *bind, evtchn_port_t port) + + chn = evtchn_from_port(d, port); + +- spin_lock_irqsave(&chn->lock, flags); ++ evtchn_write_lock(chn); + + chn->state = ECS_VIRQ; + chn->notify_vcpu_id = vcpu; + chn->u.virq = virq; + evtchn_port_init(d, chn); + +- spin_unlock_irqrestore(&chn->lock, flags); ++ evtchn_write_unlock(chn); + + v->virq_to_evtchn[virq] = bind->port = port; + +@@ -458,7 +483,6 @@ static long evtchn_bind_ipi(evtchn_bind_ipi_t *bind) + struct domain *d = current->domain; + int port, vcpu = bind->vcpu; + long rc = 0; +- unsigned long flags; + + if ( domain_vcpu(d, vcpu) == NULL ) + return -ENOENT; +@@ -470,13 +494,13 @@ static long evtchn_bind_ipi(evtchn_bind_ipi_t *bind) + + chn = evtchn_from_port(d, port); + +- spin_lock_irqsave(&chn->lock, flags); ++ evtchn_write_lock(chn); + + chn->state = ECS_IPI; + chn->notify_vcpu_id = vcpu; + evtchn_port_init(d, chn); + +- spin_unlock_irqrestore(&chn->lock, flags); ++ evtchn_write_unlock(chn); + + bind->port = port; + +@@ -520,7 +544,6 @@ static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind) + struct pirq *info; + int port = 0, pirq = bind->pirq; + long rc; +- unsigned long flags; + + if ( (pirq < 0) || (pirq >= d->nr_pirqs) ) + return -EINVAL; +@@ -553,14 +576,14 @@ static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind) + goto out; + } + +- spin_lock_irqsave(&chn->lock, flags); ++ evtchn_write_lock(chn); + + chn->state = ECS_PIRQ; + chn->u.pirq.irq = pirq; + link_pirq_port(port, chn, v); + evtchn_port_init(d, chn); + +- spin_unlock_irqrestore(&chn->lock, flags); ++ evtchn_write_unlock(chn); + + bind->port = port; + +@@ -581,7 +604,6 @@ int evtchn_close(struct domain *d1, int port1, bool guest) + struct evtchn *chn1, *chn2; + int port2; + long rc = 0; +- unsigned long flags; + + again: + spin_lock(&d1->event_lock); +@@ -681,14 +703,14 @@ int evtchn_close(struct domain *d1, int port1, bool guest) + BUG_ON(chn2->state != ECS_INTERDOMAIN); + BUG_ON(chn2->u.interdomain.remote_dom != d1); + +- flags = double_evtchn_lock(chn1, chn2); ++ double_evtchn_lock(chn1, chn2); + + evtchn_free(d1, chn1); + + chn2->state = ECS_UNBOUND; + chn2->u.unbound.remote_domid = d1->domain_id; + +- double_evtchn_unlock(chn1, chn2, flags); ++ double_evtchn_unlock(chn1, chn2); + + goto out; + +@@ -696,9 +718,9 @@ int evtchn_close(struct domain *d1, int port1, bool guest) + BUG(); + } + +- spin_lock_irqsave(&chn1->lock, flags); ++ evtchn_write_lock(chn1); + evtchn_free(d1, chn1); +- spin_unlock_irqrestore(&chn1->lock, flags); ++ evtchn_write_unlock(chn1); + + out: + if ( d2 != NULL ) +@@ -718,7 +740,6 @@ int evtchn_send(struct domain *ld, unsigned int lport) + struct evtchn *lchn, *rchn; + struct domain *rd; + int rport, ret = 0; +- unsigned long flags; + + if ( !port_is_valid(ld, lport) ) + return -EINVAL; +@@ -731,7 +752,7 @@ int evtchn_send(struct domain *ld, unsigned int lport) + + lchn = evtchn_from_port(ld, lport); + +- spin_lock_irqsave(&lchn->lock, flags); ++ evtchn_read_lock(lchn); + + /* Guest cannot send via a Xen-attached event channel. */ + if ( unlikely(consumer_is_xen(lchn)) ) +@@ -766,7 +787,7 @@ int evtchn_send(struct domain *ld, unsigned int lport) + } + + out: +- spin_unlock_irqrestore(&lchn->lock, flags); ++ evtchn_read_unlock(lchn); + + return ret; + } +@@ -793,9 +814,11 @@ void send_guest_vcpu_virq(struct vcpu *v, uint32_t virq) + + d = v->domain; + chn = evtchn_from_port(d, port); +- spin_lock(&chn->lock); +- evtchn_port_set_pending(d, v->vcpu_id, chn); +- spin_unlock(&chn->lock); ++ if ( evtchn_read_trylock(chn) ) ++ { ++ evtchn_port_set_pending(d, v->vcpu_id, chn); ++ evtchn_read_unlock(chn); ++ } + + out: + spin_unlock_irqrestore(&v->virq_lock, flags); +@@ -824,9 +847,11 @@ void send_guest_global_virq(struct domain *d, uint32_t virq) + goto out; + + chn = evtchn_from_port(d, port); +- spin_lock(&chn->lock); +- evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); +- spin_unlock(&chn->lock); ++ if ( evtchn_read_trylock(chn) ) ++ { ++ evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); ++ evtchn_read_unlock(chn); ++ } + + out: + spin_unlock_irqrestore(&v->virq_lock, flags); +@@ -836,7 +861,6 @@ void send_guest_pirq(struct domain *d, const struct pirq *pirq) + { + int port; + struct evtchn *chn; +- unsigned long flags; + + /* + * PV guests: It should not be possible to race with __evtchn_close(). The +@@ -851,9 +875,11 @@ void send_guest_pirq(struct domain *d, const struct pirq *pirq) + } + + chn = evtchn_from_port(d, port); +- spin_lock_irqsave(&chn->lock, flags); +- evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); +- spin_unlock_irqrestore(&chn->lock, flags); ++ if ( evtchn_read_trylock(chn) ) ++ { ++ evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); ++ evtchn_read_unlock(chn); ++ } + } + + static struct domain *global_virq_handlers[NR_VIRQS] __read_mostly; +@@ -1050,15 +1076,17 @@ int evtchn_unmask(unsigned int port) + { + struct domain *d = current->domain; + struct evtchn *evtchn; +- unsigned long flags; + + if ( unlikely(!port_is_valid(d, port)) ) + return -EINVAL; + + evtchn = evtchn_from_port(d, port); +- spin_lock_irqsave(&evtchn->lock, flags); ++ ++ evtchn_read_lock(evtchn); ++ + evtchn_port_unmask(d, evtchn); +- spin_unlock_irqrestore(&evtchn->lock, flags); ++ ++ evtchn_read_unlock(evtchn); + + return 0; + } +@@ -1304,7 +1332,6 @@ int alloc_unbound_xen_event_channel( + { + struct evtchn *chn; + int port, rc; +- unsigned long flags; + + spin_lock(&ld->event_lock); + +@@ -1317,14 +1344,14 @@ int alloc_unbound_xen_event_channel( + if ( rc ) + goto out; + +- spin_lock_irqsave(&chn->lock, flags); ++ evtchn_write_lock(chn); + + chn->state = ECS_UNBOUND; + chn->xen_consumer = get_xen_consumer(notification_fn); + chn->notify_vcpu_id = lvcpu; + chn->u.unbound.remote_domid = remote_domid; + +- spin_unlock_irqrestore(&chn->lock, flags); ++ evtchn_write_unlock(chn); + + write_atomic(&ld->xen_evtchns, ld->xen_evtchns + 1); + +@@ -1356,7 +1383,6 @@ void notify_via_xen_event_channel(struct domain *ld, int lport) + { + struct evtchn *lchn, *rchn; + struct domain *rd; +- unsigned long flags; + + if ( !port_is_valid(ld, lport) ) + { +@@ -1371,7 +1397,8 @@ void notify_via_xen_event_channel(struct domain *ld, int lport) + + lchn = evtchn_from_port(ld, lport); + +- spin_lock_irqsave(&lchn->lock, flags); ++ if ( !evtchn_read_trylock(lchn) ) ++ return; + + if ( likely(lchn->state == ECS_INTERDOMAIN) ) + { +@@ -1381,7 +1408,7 @@ void notify_via_xen_event_channel(struct domain *ld, int lport) + evtchn_port_set_pending(rd, rchn->notify_vcpu_id, rchn); + } + +- spin_unlock_irqrestore(&lchn->lock, flags); ++ evtchn_read_unlock(lchn); + } + + void evtchn_check_pollers(struct domain *d, unsigned int port) +diff --git a/xen/include/xen/event.h b/xen/include/xen/event.h +index fa93a3684a..6588333f42 100644 +--- a/xen/include/xen/event.h ++++ b/xen/include/xen/event.h +@@ -111,6 +111,21 @@ static inline unsigned int max_evtchns(const struct domain *d) + : BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); + } + ++static inline void evtchn_read_lock(struct evtchn *evtchn) ++{ ++ read_lock(&evtchn->lock); ++} ++ ++static inline bool evtchn_read_trylock(struct evtchn *evtchn) ++{ ++ return read_trylock(&evtchn->lock); ++} ++ ++static inline void evtchn_read_unlock(struct evtchn *evtchn) ++{ ++ read_unlock(&evtchn->lock); ++} ++ + static inline bool_t port_is_valid(struct domain *d, unsigned int p) + { + if ( p >= read_atomic(&d->valid_evtchns) ) +@@ -244,11 +259,10 @@ static inline bool evtchn_port_is_pending(struct domain *d, evtchn_port_t port) + { + struct evtchn *evtchn = evtchn_from_port(d, port); + bool rc; +- unsigned long flags; + +- spin_lock_irqsave(&evtchn->lock, flags); ++ evtchn_read_lock(evtchn); + rc = evtchn_is_pending(d, evtchn); +- spin_unlock_irqrestore(&evtchn->lock, flags); ++ evtchn_read_unlock(evtchn); + + return rc; + } +@@ -263,11 +277,12 @@ static inline bool evtchn_port_is_masked(struct domain *d, evtchn_port_t port) + { + struct evtchn *evtchn = evtchn_from_port(d, port); + bool rc; +- unsigned long flags; + +- spin_lock_irqsave(&evtchn->lock, flags); ++ evtchn_read_lock(evtchn); ++ + rc = evtchn_is_masked(d, evtchn); +- spin_unlock_irqrestore(&evtchn->lock, flags); ++ ++ evtchn_read_unlock(evtchn); + + return rc; + } +diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h +index 97ba8e0795..f782ffeb82 100644 +--- a/xen/include/xen/sched.h ++++ b/xen/include/xen/sched.h +@@ -85,7 +85,7 @@ extern domid_t hardware_domid; + + struct evtchn + { +- spinlock_t lock; ++ rwlock_t lock; + #define ECS_FREE 0 /* Channel is available for use. */ + #define ECS_RESERVED 1 /* Channel is reserved. */ + #define ECS_UNBOUND 2 /* Channel is waiting to bind to a remote domain. */ +@@ -114,6 +114,9 @@ struct evtchn + u16 virq; /* state == ECS_VIRQ */ + } u; + u8 priority; ++#ifndef NDEBUG ++ u8 old_state; /* State when taking lock in write mode. */ ++#endif + u8 last_priority; + u16 last_vcpu_id; + #ifdef CONFIG_XSM +-- +2.20.1 + diff --git a/xen.git-1cfb9b1c5b9e4c024f5f139d7a3d0357d2417b13.patch b/xen.git-1cfb9b1c5b9e4c024f5f139d7a3d0357d2417b13.patch new file mode 100644 index 0000000..12c64ca --- /dev/null +++ b/xen.git-1cfb9b1c5b9e4c024f5f139d7a3d0357d2417b13.patch @@ -0,0 +1,97 @@ +From 1cfb9b1c5b9e4c024f5f139d7a3d0357d2417b13 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 1 Dec 2020 15:33:19 +0100 +Subject: [PATCH] xen/events: access last_priority and last_vcpu_id together + +The queue for a fifo event is depending on the vcpu_id and the +priority of the event. When sending an event it might happen the +event needs to change queues and the old queue needs to be kept for +keeping the links between queue elements intact. For this purpose +the event channel contains last_priority and last_vcpu_id values +elements for being able to identify the old queue. + +In order to avoid races always access last_priority and last_vcpu_id +with a single atomic operation avoiding any inconsistencies. + +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall +master commit: 1277cb9dc5e966f1faf665bcded02b7533e38078 +master date: 2020-11-24 11:23:42 +0100 +--- + xen/common/event_fifo.c | 25 +++++++++++++++++++------ + xen/include/xen/sched.h | 3 +-- + 2 files changed, 20 insertions(+), 8 deletions(-) + +diff --git a/xen/common/event_fifo.c b/xen/common/event_fifo.c +index 27ab3a1c3f..2037b24196 100644 +--- a/xen/common/event_fifo.c ++++ b/xen/common/event_fifo.c +@@ -21,6 +21,14 @@ + + #include + ++union evtchn_fifo_lastq { ++ uint32_t raw; ++ struct { ++ uint8_t last_priority; ++ uint16_t last_vcpu_id; ++ }; ++}; ++ + static inline event_word_t *evtchn_fifo_word_from_port(const struct domain *d, + unsigned int port) + { +@@ -65,16 +73,18 @@ static struct evtchn_fifo_queue *lock_old_queue(const struct domain *d, + struct vcpu *v; + struct evtchn_fifo_queue *q, *old_q; + unsigned int try; ++ union evtchn_fifo_lastq lastq; + + for ( try = 0; try < 3; try++ ) + { +- v = d->vcpu[evtchn->last_vcpu_id]; +- old_q = &v->evtchn_fifo->queue[evtchn->last_priority]; ++ lastq.raw = read_atomic(&evtchn->fifo_lastq); ++ v = d->vcpu[lastq.last_vcpu_id]; ++ old_q = &v->evtchn_fifo->queue[lastq.last_priority]; + + spin_lock_irqsave(&old_q->lock, *flags); + +- v = d->vcpu[evtchn->last_vcpu_id]; +- q = &v->evtchn_fifo->queue[evtchn->last_priority]; ++ v = d->vcpu[lastq.last_vcpu_id]; ++ q = &v->evtchn_fifo->queue[lastq.last_priority]; + + if ( old_q == q ) + return old_q; +@@ -225,8 +235,11 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) + /* Moved to a different queue? */ + if ( old_q != q ) + { +- evtchn->last_vcpu_id = v->vcpu_id; +- evtchn->last_priority = q->priority; ++ union evtchn_fifo_lastq lastq = { }; ++ ++ lastq.last_vcpu_id = v->vcpu_id; ++ lastq.last_priority = q->priority; ++ write_atomic(&evtchn->fifo_lastq, lastq.raw); + + spin_unlock_irqrestore(&old_q->lock, flags); + spin_lock_irqsave(&q->lock, flags); +diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h +index f782ffeb82..99e2f1aac5 100644 +--- a/xen/include/xen/sched.h ++++ b/xen/include/xen/sched.h +@@ -117,8 +117,7 @@ struct evtchn + #ifndef NDEBUG + u8 old_state; /* State when taking lock in write mode. */ + #endif +- u8 last_priority; +- u16 last_vcpu_id; ++ u32 fifo_lastq; /* Data for fifo events identifying last queue. */ + #ifdef CONFIG_XSM + union { + #ifdef XSM_NEED_GENERIC_EVTCHN_SSID +-- +2.20.1 + diff --git a/xen.git-72bd989f51878bc9ba61e930b0c29b921a30dc0d.patch b/xen.git-72bd989f51878bc9ba61e930b0c29b921a30dc0d.patch new file mode 100644 index 0000000..21c5ddd --- /dev/null +++ b/xen.git-72bd989f51878bc9ba61e930b0c29b921a30dc0d.patch @@ -0,0 +1,240 @@ +From 72bd989f51878bc9ba61e930b0c29b921a30dc0d Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 1 Dec 2020 15:34:31 +0100 +Subject: [PATCH] xen/events: rework fifo queue locking + +Two cpus entering evtchn_fifo_set_pending() for the same event channel +can race in case the first one gets interrupted after setting +EVTCHN_FIFO_PENDING and when the other one manages to set +EVTCHN_FIFO_LINKED before the first one is testing that bit. This can +lead to evtchn_check_pollers() being called before the event is put +properly into the queue, resulting eventually in the guest not seeing +the event pending and thus blocking forever afterwards. + +Note that commit 5f2df45ead7c1195 ("xen/evtchn: rework per event channel +lock") made the race just more obvious, while the fifo event channel +implementation had this race forever since the introduction and use of +per-channel locks, when an unmask operation was running in parallel with +an event channel send operation. + +Using a spinlock for the per event channel lock had turned out +problematic due to some paths needing to take the lock are called with +interrupts off, so the lock would need to disable interrupts, which in +turn broke some use cases related to vm events. + +For avoiding this race the queue locking in evtchn_fifo_set_pending() +needs to be reworked to cover the test of EVTCHN_FIFO_PENDING, +EVTCHN_FIFO_MASKED and EVTCHN_FIFO_LINKED, too. Additionally when an +event channel needs to change queues both queues need to be locked +initially, in order to avoid having a window with no lock held at all. + +Reported-by: Jan Beulich +Fixes: 5f2df45ead7c1195 ("xen/evtchn: rework per event channel lock") +Fixes: de6acb78bf0e137c ("evtchn: use a per-event channel lock for sending events") +Signed-off-by: Juergen Gross +Reviewed-by: Jan Beulich +master commit: 71ac522909e9302350a88bc378be99affa87067c +master date: 2020-11-30 14:05:39 +0100 +--- + xen/common/event_fifo.c | 128 ++++++++++++++++++++++------------------ + 1 file changed, 70 insertions(+), 58 deletions(-) + +diff --git a/xen/common/event_fifo.c b/xen/common/event_fifo.c +index 2037b24196..2f5e868b7a 100644 +--- a/xen/common/event_fifo.c ++++ b/xen/common/event_fifo.c +@@ -66,38 +66,6 @@ static void evtchn_fifo_init(struct domain *d, struct evtchn *evtchn) + d->domain_id, evtchn->port); + } + +-static struct evtchn_fifo_queue *lock_old_queue(const struct domain *d, +- struct evtchn *evtchn, +- unsigned long *flags) +-{ +- struct vcpu *v; +- struct evtchn_fifo_queue *q, *old_q; +- unsigned int try; +- union evtchn_fifo_lastq lastq; +- +- for ( try = 0; try < 3; try++ ) +- { +- lastq.raw = read_atomic(&evtchn->fifo_lastq); +- v = d->vcpu[lastq.last_vcpu_id]; +- old_q = &v->evtchn_fifo->queue[lastq.last_priority]; +- +- spin_lock_irqsave(&old_q->lock, *flags); +- +- v = d->vcpu[lastq.last_vcpu_id]; +- q = &v->evtchn_fifo->queue[lastq.last_priority]; +- +- if ( old_q == q ) +- return old_q; +- +- spin_unlock_irqrestore(&old_q->lock, *flags); +- } +- +- gprintk(XENLOG_WARNING, +- "dom%d port %d lost event (too many queue changes)\n", +- d->domain_id, evtchn->port); +- return NULL; +-} +- + static int try_set_link(event_word_t *word, event_word_t *w, uint32_t link) + { + event_word_t new, old; +@@ -169,6 +137,9 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) + event_word_t *word; + unsigned long flags; + bool_t was_pending; ++ struct evtchn_fifo_queue *q, *old_q; ++ unsigned int try; ++ bool linked = true; + + port = evtchn->port; + word = evtchn_fifo_word_from_port(d, port); +@@ -183,17 +154,67 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) + return; + } + ++ /* ++ * Lock all queues related to the event channel (in case of a queue change ++ * this might be two). ++ * It is mandatory to do that before setting and testing the PENDING bit ++ * and to hold the current queue lock until the event has been put into the ++ * list of pending events in order to avoid waking up a guest without the ++ * event being visibly pending in the guest. ++ */ ++ for ( try = 0; try < 3; try++ ) ++ { ++ union evtchn_fifo_lastq lastq; ++ const struct vcpu *old_v; ++ ++ lastq.raw = read_atomic(&evtchn->fifo_lastq); ++ old_v = d->vcpu[lastq.last_vcpu_id]; ++ ++ q = &v->evtchn_fifo->queue[evtchn->priority]; ++ old_q = &old_v->evtchn_fifo->queue[lastq.last_priority]; ++ ++ if ( q == old_q ) ++ spin_lock_irqsave(&q->lock, flags); ++ else if ( q < old_q ) ++ { ++ spin_lock_irqsave(&q->lock, flags); ++ spin_lock(&old_q->lock); ++ } ++ else ++ { ++ spin_lock_irqsave(&old_q->lock, flags); ++ spin_lock(&q->lock); ++ } ++ ++ lastq.raw = read_atomic(&evtchn->fifo_lastq); ++ old_v = d->vcpu[lastq.last_vcpu_id]; ++ if ( q == &v->evtchn_fifo->queue[evtchn->priority] && ++ old_q == &old_v->evtchn_fifo->queue[lastq.last_priority] ) ++ break; ++ ++ if ( q != old_q ) ++ spin_unlock(&old_q->lock); ++ spin_unlock_irqrestore(&q->lock, flags); ++ } ++ + was_pending = guest_test_and_set_bit(d, EVTCHN_FIFO_PENDING, word); + ++ /* If we didn't get the lock bail out. */ ++ if ( try == 3 ) ++ { ++ gprintk(XENLOG_WARNING, ++ "%pd port %u lost event (too many queue changes)\n", ++ d, evtchn->port); ++ goto done; ++ } ++ + /* + * Link the event if it unmasked and not already linked. + */ + if ( !guest_test_bit(d, EVTCHN_FIFO_MASKED, word) && + !guest_test_bit(d, EVTCHN_FIFO_LINKED, word) ) + { +- struct evtchn_fifo_queue *q, *old_q; + event_word_t *tail_word; +- bool_t linked = 0; + + /* + * Control block not mapped. The guest must not unmask an +@@ -204,25 +225,11 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) + { + printk(XENLOG_G_WARNING + "%pv has no FIFO event channel control block\n", v); +- goto done; ++ goto unlock; + } + +- /* +- * No locking around getting the queue. This may race with +- * changing the priority but we are allowed to signal the +- * event once on the old priority. +- */ +- q = &v->evtchn_fifo->queue[evtchn->priority]; +- +- old_q = lock_old_queue(d, evtchn, &flags); +- if ( !old_q ) +- goto done; +- + if ( guest_test_and_set_bit(d, EVTCHN_FIFO_LINKED, word) ) +- { +- spin_unlock_irqrestore(&old_q->lock, flags); +- goto done; +- } ++ goto unlock; + + /* + * If this event was a tail, the old queue is now empty and +@@ -241,8 +248,8 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) + lastq.last_priority = q->priority; + write_atomic(&evtchn->fifo_lastq, lastq.raw); + +- spin_unlock_irqrestore(&old_q->lock, flags); +- spin_lock_irqsave(&q->lock, flags); ++ spin_unlock(&old_q->lock); ++ old_q = q; + } + + /* +@@ -255,6 +262,7 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) + * If the queue is empty (i.e., we haven't linked to the new + * event), head must be updated. + */ ++ linked = false; + if ( q->tail ) + { + tail_word = evtchn_fifo_word_from_port(d, q->tail); +@@ -263,15 +271,19 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) + if ( !linked ) + write_atomic(q->head, port); + q->tail = port; ++ } + +- spin_unlock_irqrestore(&q->lock, flags); ++ unlock: ++ if ( q != old_q ) ++ spin_unlock(&old_q->lock); ++ spin_unlock_irqrestore(&q->lock, flags); + +- if ( !linked +- && !guest_test_and_set_bit(d, q->priority, +- &v->evtchn_fifo->control_block->ready) ) +- vcpu_mark_events_pending(v); +- } + done: ++ if ( !linked && ++ !guest_test_and_set_bit(d, q->priority, ++ &v->evtchn_fifo->control_block->ready) ) ++ vcpu_mark_events_pending(v); ++ + if ( !was_pending ) + evtchn_check_pollers(d, port); + } +-- +2.20.1 + diff --git a/xen.git-8d148003fdf7bd9e28137e6683ef46902af39146.patch b/xen.git-8d148003fdf7bd9e28137e6683ef46902af39146.patch new file mode 100644 index 0000000..dfff098 --- /dev/null +++ b/xen.git-8d148003fdf7bd9e28137e6683ef46902af39146.patch @@ -0,0 +1,41 @@ +From 8d148003fdf7bd9e28137e6683ef46902af39146 Mon Sep 17 00:00:00 2001 +From: Jan Beulich +Date: Tue, 20 Oct 2020 14:42:52 +0200 +Subject: [PATCH] evtchn/fifo: use stable fields when recording "last queue" + information + +Both evtchn->priority and evtchn->notify_vcpu_id could change behind the +back of evtchn_fifo_set_pending(), as for it - in the case of +interdomain channels - only the remote side's per-channel lock is held. +Neither the queue's priority nor the vCPU's vcpu_id fields have similar +properties, so they seem better suited for the purpose. In particular +they reflect the respective evtchn fields' values at the time they were +used to determine queue and vCPU. + +Signed-off-by: Jan Beulich +Reviewed-by: Julien Grall +Reviewed-by: Paul Durrant +master commit: 6f6f07b64cbe90e54f8e62b4d6f2404cf5306536 +master date: 2020-10-02 08:37:35 +0200 +--- + xen/common/event_fifo.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/xen/common/event_fifo.c b/xen/common/event_fifo.c +index 68d0c7a632..27ab3a1c3f 100644 +--- a/xen/common/event_fifo.c ++++ b/xen/common/event_fifo.c +@@ -225,8 +225,8 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) + /* Moved to a different queue? */ + if ( old_q != q ) + { +- evtchn->last_vcpu_id = evtchn->notify_vcpu_id; +- evtchn->last_priority = evtchn->priority; ++ evtchn->last_vcpu_id = v->vcpu_id; ++ evtchn->last_priority = q->priority; + + spin_unlock_irqrestore(&old_q->lock, flags); + spin_lock_irqsave(&q->lock, flags); +-- +2.20.1 + diff --git a/xen.spec b/xen.spec index 2c7ddc0..4325a81 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.14.0 -Release: 13%{?dist} +Release: 14%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -143,6 +143,40 @@ Patch74: xsa351-x86-4.14-1.patch Patch75: xsa351-x86-4.14-2.patch Patch76: zstd-dom0.patch Patch77: xsa355.patch +Patch78: xsa115-4.14-c-0001-tools-xenstore-allow-removing-child-of-a-node-exceed.patch +Patch79: xsa115-4.14-c-0002-tools-xenstore-ignore-transaction-id-for-un-watch.patch +Patch80: xsa115-4.14-c-0003-tools-xenstore-fix-node-accounting-after-failed-node.patch +Patch81: xsa115-4.14-c-0004-tools-xenstore-simplify-and-rename-check_event_node.patch +Patch82: xsa115-4.14-c-0005-tools-xenstore-check-privilege-for-XS_IS_DOMAIN_INTR.patch +Patch83: xsa115-4.14-c-0006-tools-xenstore-rework-node-removal.patch +Patch84: xsa115-4.14-c-0007-tools-xenstore-fire-watches-only-when-removing-a-spe.patch +Patch85: xsa115-4.14-c-0008-tools-xenstore-introduce-node_perms-structure.patch +Patch86: xsa115-4.14-c-0009-tools-xenstore-allow-special-watches-for-privileged-.patch +Patch87: xsa115-4.14-c-0010-tools-xenstore-avoid-watch-events-for-nodes-without-.patch +Patch88: xsa115-o-0001-tools-ocaml-xenstored-ignore-transaction-id-for-un-w.patch +Patch89: xsa115-o-0002-tools-ocaml-xenstored-check-privilege-for-XS_IS_DOMA.patch +Patch90: xsa115-o-0003-tools-ocaml-xenstored-unify-watch-firing.patch +Patch91: xsa115-o-0004-tools-ocaml-xenstored-introduce-permissions-for-spec.patch +Patch92: xsa115-o-0005-tools-ocaml-xenstored-avoid-watch-events-for-nodes-w.patch +Patch93: xsa115-o-0006-tools-ocaml-xenstored-add-xenstored.conf-flag-to-tur.patch +Patch94: xsa322-4.14-c.patch +Patch95: xsa322-o.patch +Patch96: xsa323.patch +Patch97: xsa324.patch +Patch98: xsa325-4.14.patch +Patch99: xsa330.patch +Patch100: xsa348-1.patch +Patch101: xsa348-2.patch +Patch102: xsa348-3.patch +Patch103: xsa352.patch +Patch104: xsa353.patch +Patch105: xsa356.patch +Patch106: xen.git-8d148003fdf7bd9e28137e6683ef46902af39146.patch +Patch107: xen.git-1ad177370df2db9129c97c7305962fc5ad298728.patch +Patch108: xen.git-1cfb9b1c5b9e4c024f5f139d7a3d0357d2417b13.patch +Patch109: xen.git-72bd989f51878bc9ba61e930b0c29b921a30dc0d.patch +Patch110: xsa358.patch +Patch111: xsa359.patch %if %build_qemutrad @@ -375,6 +409,40 @@ manage Xen virtual machines. %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 +%patch107 -p1 +%patch108 -p1 +%patch109 -p1 +%patch110 -p1 +%patch111 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -968,6 +1036,31 @@ fi %endif %changelog +* Tue Dec 15 2020 Michael Young - 4.14.0-14 +- xenstore watch notifications lacking permission checks [XSA-115, + CVE-2020-29480] (#1908091) +- Xenstore: new domains inheriting existing node permissions [XSA-322, + CVE-2020-29481] (#1908095) +- Xenstore: wrong path length check [XSA-323, CVE-2020-29482] (#1908096) +- Xenstore: guests can crash xenstored via watchs [XSA-324, CVE-2020-29484] + (#1908088) +- Xenstore: guests can disturb domain cleanup [XSA-325, CVE-2020-29483] + (#1905648) +- oxenstored memory leak in reset_watches [XSA-330, CVE-2020-29485] + (#1908000) +- undue recursion in x86 HVM context switch code [XSA-348, CVE-2020-29566] + (#1908085) +- oxenstored: node ownership can be changed by unprivileged clients + [XSA-352, CVE-2020-29486] (#1908003) +- oxenstored: permissions not checked on root node [XSA-353, CVE-2020-29479] + (#1908003) +- infinite loop when cleaning up IRQ vectors [XSA-356, CVE-2020-29567] + (#1907932) +- FIFO event channels control block related ordering [XSA-358, + CVE-2020-29570] (#1907931) +- FIFO event channels control structure ordering [XSA-359, CVE-2020-29571] + (#1908089) + * Sat Dec 05 2020 Jeff Law - 4.14.0-13 - Work around another gcc-11 stringop-overflow diagnostic diff --git a/xsa115-4.14-c-0001-tools-xenstore-allow-removing-child-of-a-node-exceed.patch b/xsa115-4.14-c-0001-tools-xenstore-allow-removing-child-of-a-node-exceed.patch new file mode 100644 index 0000000..fb29db7 --- /dev/null +++ b/xsa115-4.14-c-0001-tools-xenstore-allow-removing-child-of-a-node-exceed.patch @@ -0,0 +1,157 @@ +From 71623492f7b1b6d63ed76e2bf970c113b88ffa0b Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Thu, 11 Jun 2020 16:12:37 +0200 +Subject: [PATCH 01/10] tools/xenstore: allow removing child of a node + exceeding quota + +An unprivileged user of Xenstore is not allowed to write nodes with a +size exceeding a global quota, while privileged users like dom0 are +allowed to write such nodes. The size of a node is the needed space +to store all node specific data, this includes the names of all +children of the node. + +When deleting a node its parent has to be modified by removing the +name of the to be deleted child from it. + +This results in the strange situation that an unprivileged owner of a +node might not succeed in deleting that node in case its parent is +exceeding the quota of that unprivileged user (it might have been +written by dom0), as the user is not allowed to write the updated +parent node. + +Fix that by not checking the quota when writing a node for the +purpose of removing a child's name only. + +The same applies to transaction handling: a node being read during a +transaction is written to the transaction specific area and it should +not be tested for exceeding the quota, as it might not be owned by +the reader and presumably the original write would have failed if the +node is owned by the reader. + +This is part of XSA-115. + +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall +Reviewed-by: Paul Durrant +--- + tools/xenstore/xenstored_core.c | 20 +++++++++++--------- + tools/xenstore/xenstored_core.h | 3 ++- + tools/xenstore/xenstored_transaction.c | 2 +- + 3 files changed, 14 insertions(+), 11 deletions(-) + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 7bd959f28b39..62a17a686edc 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -419,7 +419,8 @@ static struct node *read_node(struct connection *conn, const void *ctx, + return node; + } + +-int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node) ++int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, ++ bool no_quota_check) + { + TDB_DATA data; + void *p; +@@ -429,7 +430,7 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node) + + node->num_perms*sizeof(node->perms[0]) + + node->datalen + node->childlen; + +- if (domain_is_unprivileged(conn) && ++ if (!no_quota_check && domain_is_unprivileged(conn) && + data.dsize >= quota_max_entry_size) { + errno = ENOSPC; + return errno; +@@ -457,14 +458,15 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node) + return 0; + } + +-static int write_node(struct connection *conn, struct node *node) ++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)) + return errno; + +- return write_node_raw(conn, &key, node); ++ return write_node_raw(conn, &key, node, no_quota_check); + } + + static enum xs_perm_type perm_for_conn(struct connection *conn, +@@ -1001,7 +1003,7 @@ static struct node *create_node(struct connection *conn, const void *ctx, + /* We write out the nodes down, setting destructor in case + * something goes wrong. */ + for (i = node; i; i = i->parent) { +- if (write_node(conn, i)) { ++ if (write_node(conn, i, false)) { + domain_entry_dec(conn, i); + return NULL; + } +@@ -1041,7 +1043,7 @@ static int do_write(struct connection *conn, struct buffered_data *in) + } else { + node->data = in->buffer + offset; + node->datalen = datalen; +- if (write_node(conn, node)) ++ if (write_node(conn, node, false)) + return errno; + } + +@@ -1117,7 +1119,7 @@ static int remove_child_entry(struct connection *conn, struct node *node, + size_t childlen = strlen(node->children + offset); + memdel(node->children, offset, childlen + 1, node->childlen); + node->childlen -= childlen + 1; +- return write_node(conn, node); ++ return write_node(conn, node, true); + } + + +@@ -1256,7 +1258,7 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) + node->num_perms = num; + domain_entry_inc(conn, node); + +- if (write_node(conn, node)) ++ if (write_node(conn, node, false)) + return errno; + + fire_watches(conn, in, name, false); +@@ -1516,7 +1518,7 @@ static void manual_node(const char *name, const char *child) + if (child) + node->childlen = strlen(child) + 1; + +- if (write_node(NULL, node)) ++ if (write_node(NULL, node, false)) + barf_perror("Could not create initial node %s", name); + talloc_free(node); + } +diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h +index c4c32bc88f0c..29d638fbc5a0 100644 +--- a/tools/xenstore/xenstored_core.h ++++ b/tools/xenstore/xenstored_core.h +@@ -149,7 +149,8 @@ void send_ack(struct connection *conn, enum xsd_sockmsg_type type); + char *xenstore_canonicalize(struct connection *conn, const void *ctx, const char *node); + + /* Write a node to the tdb data base. */ +-int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node); ++int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, ++ bool no_quota_check); + + /* Get this node, checking we have permissions. */ + struct node *get_node(struct connection *conn, +diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c +index 2824f7b359b8..e87897573469 100644 +--- a/tools/xenstore/xenstored_transaction.c ++++ b/tools/xenstore/xenstored_transaction.c +@@ -276,7 +276,7 @@ int access_node(struct connection *conn, struct node *node, + i->check_gen = true; + if (node->generation != NO_GENERATION) { + set_tdb_key(trans_name, &local_key); +- ret = write_node_raw(conn, &local_key, node); ++ ret = write_node_raw(conn, &local_key, node, true); + if (ret) + goto err; + i->ta_node = true; +-- +2.17.1 + diff --git a/xsa115-4.14-c-0002-tools-xenstore-ignore-transaction-id-for-un-watch.patch b/xsa115-4.14-c-0002-tools-xenstore-ignore-transaction-id-for-un-watch.patch new file mode 100644 index 0000000..42ccd5a --- /dev/null +++ b/xsa115-4.14-c-0002-tools-xenstore-ignore-transaction-id-for-un-watch.patch @@ -0,0 +1,86 @@ +From 072c729cfe90b4b09cacb12d912ba088db8274fe Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Thu, 11 Jun 2020 16:12:38 +0200 +Subject: [PATCH 02/10] tools/xenstore: ignore transaction id for [un]watch + +Instead of ignoring the transaction id for XS_WATCH and XS_UNWATCH +commands as it is documented in docs/misc/xenstore.txt, it is tested +for validity today. + +Really ignore the transaction id for XS_WATCH and XS_UNWATCH. + +This is part of XSA-115. + +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall +Reviewed-by: Paul Durrant +--- + tools/xenstore/xenstored_core.c | 26 ++++++++++++++++---------- + 1 file changed, 16 insertions(+), 10 deletions(-) + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 62a17a686edc..2f989524b497 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -1270,13 +1270,17 @@ 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); ++ unsigned int flags; ++#define XS_FLAG_NOTID (1U << 0) /* Ignore transaction id. */ + } const wire_funcs[XS_TYPE_COUNT] = { + [XS_CONTROL] = { "CONTROL", do_control }, + [XS_DIRECTORY] = { "DIRECTORY", send_directory }, + [XS_READ] = { "READ", do_read }, + [XS_GET_PERMS] = { "GET_PERMS", do_get_perms }, +- [XS_WATCH] = { "WATCH", do_watch }, +- [XS_UNWATCH] = { "UNWATCH", do_unwatch }, ++ [XS_WATCH] = ++ { "WATCH", do_watch, XS_FLAG_NOTID }, ++ [XS_UNWATCH] = ++ { "UNWATCH", do_unwatch, XS_FLAG_NOTID }, + [XS_TRANSACTION_START] = { "TRANSACTION_START", do_transaction_start }, + [XS_TRANSACTION_END] = { "TRANSACTION_END", do_transaction_end }, + [XS_INTRODUCE] = { "INTRODUCE", do_introduce }, +@@ -1298,7 +1302,7 @@ static struct { + + static const char *sockmsg_string(enum xsd_sockmsg_type type) + { +- if ((unsigned)type < XS_TYPE_COUNT && wire_funcs[type].str) ++ if ((unsigned int)type < ARRAY_SIZE(wire_funcs) && wire_funcs[type].str) + return wire_funcs[type].str; + + return "**UNKNOWN**"; +@@ -1313,7 +1317,14 @@ static void process_message(struct connection *conn, struct buffered_data *in) + enum xsd_sockmsg_type type = in->hdr.msg.type; + int ret; + +- trans = transaction_lookup(conn, in->hdr.msg.tx_id); ++ if ((unsigned int)type >= XS_TYPE_COUNT || !wire_funcs[type].func) { ++ eprintf("Client unknown operation %i", type); ++ send_error(conn, ENOSYS); ++ return; ++ } ++ ++ trans = (wire_funcs[type].flags & XS_FLAG_NOTID) ++ ? NULL : transaction_lookup(conn, in->hdr.msg.tx_id); + if (IS_ERR(trans)) { + send_error(conn, -PTR_ERR(trans)); + return; +@@ -1322,12 +1333,7 @@ static void process_message(struct connection *conn, struct buffered_data *in) + assert(conn->transaction == NULL); + conn->transaction = trans; + +- if ((unsigned)type < XS_TYPE_COUNT && wire_funcs[type].func) +- ret = wire_funcs[type].func(conn, in); +- else { +- eprintf("Client unknown operation %i", type); +- ret = ENOSYS; +- } ++ ret = wire_funcs[type].func(conn, in); + if (ret) + send_error(conn, ret); + +-- +2.17.1 + diff --git a/xsa115-4.14-c-0003-tools-xenstore-fix-node-accounting-after-failed-node.patch b/xsa115-4.14-c-0003-tools-xenstore-fix-node-accounting-after-failed-node.patch new file mode 100644 index 0000000..94c3f1f --- /dev/null +++ b/xsa115-4.14-c-0003-tools-xenstore-fix-node-accounting-after-failed-node.patch @@ -0,0 +1,104 @@ +From a133627453898759ca73dd5c1c185c3830fed754 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Thu, 11 Jun 2020 16:12:39 +0200 +Subject: [PATCH 03/10] tools/xenstore: fix node accounting after failed node + creation + +When a node creation fails the number of nodes of the domain should be +the same as before the failed node creation. In case of failure when +trying to create a node requiring to create one or more intermediate +nodes as well (e.g. when /a/b/c/d is to be created, but /a/b isn't +existing yet) it might happen that the number of nodes of the creating +domain is not reset to the value it had before. + +So move the quota accounting out of construct_node() and into the node +write loop in create_node() in order to be able to undo the accounting +in case of an error in the intermediate node destructor. + +This is part of XSA-115. + +Signed-off-by: Juergen Gross +Reviewed-by: Paul Durrant +Acked-by: Julien Grall +--- + tools/xenstore/xenstored_core.c | 37 ++++++++++++++++++++++----------- + 1 file changed, 25 insertions(+), 12 deletions(-) + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 2f989524b497..c971519e542a 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -927,11 +927,6 @@ static struct node *construct_node(struct connection *conn, const void *ctx, + if (!parent) + return NULL; + +- if (domain_entry(conn) >= quota_nb_entry_per_domain) { +- errno = ENOSPC; +- return NULL; +- } +- + /* Add child to parent. */ + base = basename(name); + baselen = strlen(base) + 1; +@@ -964,7 +959,6 @@ static struct node *construct_node(struct connection *conn, const void *ctx, + node->children = node->data = NULL; + node->childlen = node->datalen = 0; + node->parent = parent; +- domain_entry_inc(conn, node); + return node; + + nomem: +@@ -984,6 +978,9 @@ static int destroy_node(void *_node) + key.dsize = strlen(node->name); + + tdb_delete(tdb_ctx, key); ++ ++ domain_entry_dec(talloc_parent(node), node); ++ + return 0; + } + +@@ -1000,18 +997,34 @@ static struct node *create_node(struct connection *conn, const void *ctx, + node->data = data; + node->datalen = datalen; + +- /* We write out the nodes down, setting destructor in case +- * something goes wrong. */ ++ /* ++ * We write out the nodes bottom up. ++ * All new created nodes will have i->parent set, while the final ++ * node will be already existing and won't have i->parent set. ++ * New nodes are subject to quota handling. ++ * Initially set a destructor for all new nodes removing them from ++ * TDB again and undoing quota accounting for the case of an error ++ * during the write loop. ++ */ + for (i = node; i; i = i->parent) { +- if (write_node(conn, i, false)) { +- domain_entry_dec(conn, i); ++ /* 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; + } +- talloc_set_destructor(i, destroy_node); ++ if (write_node(conn, i, false)) ++ return NULL; ++ ++ /* Account for new node, set destructor for error case. */ ++ 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; i = i->parent) ++ for (i = node; i->parent; i = i->parent) + talloc_set_destructor(i, NULL); + return node; + } +-- +2.17.1 + diff --git a/xsa115-4.14-c-0004-tools-xenstore-simplify-and-rename-check_event_node.patch b/xsa115-4.14-c-0004-tools-xenstore-simplify-and-rename-check_event_node.patch new file mode 100644 index 0000000..5a7d705 --- /dev/null +++ b/xsa115-4.14-c-0004-tools-xenstore-simplify-and-rename-check_event_node.patch @@ -0,0 +1,55 @@ +From dc6cf381bdeca4013b6bfe25c27e57f010e7ca84 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Thu, 11 Jun 2020 16:12:40 +0200 +Subject: [PATCH 04/10] tools/xenstore: simplify and rename check_event_node() + +There is no path which allows to call check_event_node() without a +event name. So don't let the result depend on the name being NULL and +add an assert() covering that case. + +Rename the function to check_special_event() to better match the +semantics. + +This is part of XSA-115. + +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall +Reviewed-by: Paul Durrant +--- + tools/xenstore/xenstored_watch.c | 12 +++++------- + 1 file changed, 5 insertions(+), 7 deletions(-) + +diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c +index 7dedca60dfd6..f2f1bed47cc6 100644 +--- a/tools/xenstore/xenstored_watch.c ++++ b/tools/xenstore/xenstored_watch.c +@@ -47,13 +47,11 @@ struct watch + char *node; + }; + +-static bool check_event_node(const char *node) ++static bool check_special_event(const char *name) + { +- if (!node || !strstarts(node, "@")) { +- errno = EINVAL; +- return false; +- } +- return true; ++ assert(name); ++ ++ return strstarts(name, "@"); + } + + /* Is child a subnode of parent, or equal? */ +@@ -87,7 +85,7 @@ static void add_event(struct connection *conn, + unsigned int len; + char *data; + +- if (!check_event_node(name)) { ++ if (!check_special_event(name)) { + /* Can this conn load node, or see that it doesn't exist? */ + struct node *node = get_node(conn, ctx, name, XS_PERM_READ); + /* +-- +2.17.1 + diff --git a/xsa115-4.14-c-0005-tools-xenstore-check-privilege-for-XS_IS_DOMAIN_INTR.patch b/xsa115-4.14-c-0005-tools-xenstore-check-privilege-for-XS_IS_DOMAIN_INTR.patch new file mode 100644 index 0000000..8025401 --- /dev/null +++ b/xsa115-4.14-c-0005-tools-xenstore-check-privilege-for-XS_IS_DOMAIN_INTR.patch @@ -0,0 +1,115 @@ +From cd456dd7e3c4bbe229a0307a469c2fc3b8e7b590 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Thu, 11 Jun 2020 16:12:41 +0200 +Subject: [PATCH 05/10] tools/xenstore: check privilege for + XS_IS_DOMAIN_INTRODUCED + +The Xenstore command XS_IS_DOMAIN_INTRODUCED should be possible for +privileged domains only (the only user in the tree is the xenpaging +daemon). + +Instead of having the privilege test for each command introduce a +per-command flag for that purpose. + +This is part of XSA-115. + +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall +Reviewed-by: Paul Durrant +--- + tools/xenstore/xenstored_core.c | 24 ++++++++++++++++++------ + tools/xenstore/xenstored_domain.c | 7 ++----- + 2 files changed, 20 insertions(+), 11 deletions(-) + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index c971519e542a..f38196ae2825 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -1285,8 +1285,10 @@ static struct { + int (*func)(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. */ + } const wire_funcs[XS_TYPE_COUNT] = { +- [XS_CONTROL] = { "CONTROL", do_control }, ++ [XS_CONTROL] = ++ { "CONTROL", do_control, XS_FLAG_PRIV }, + [XS_DIRECTORY] = { "DIRECTORY", send_directory }, + [XS_READ] = { "READ", do_read }, + [XS_GET_PERMS] = { "GET_PERMS", do_get_perms }, +@@ -1296,8 +1298,10 @@ static struct { + { "UNWATCH", do_unwatch, XS_FLAG_NOTID }, + [XS_TRANSACTION_START] = { "TRANSACTION_START", do_transaction_start }, + [XS_TRANSACTION_END] = { "TRANSACTION_END", do_transaction_end }, +- [XS_INTRODUCE] = { "INTRODUCE", do_introduce }, +- [XS_RELEASE] = { "RELEASE", do_release }, ++ [XS_INTRODUCE] = ++ { "INTRODUCE", do_introduce, XS_FLAG_PRIV }, ++ [XS_RELEASE] = ++ { "RELEASE", do_release, XS_FLAG_PRIV }, + [XS_GET_DOMAIN_PATH] = { "GET_DOMAIN_PATH", do_get_domain_path }, + [XS_WRITE] = { "WRITE", do_write }, + [XS_MKDIR] = { "MKDIR", do_mkdir }, +@@ -1306,9 +1310,11 @@ static struct { + [XS_WATCH_EVENT] = { "WATCH_EVENT", NULL }, + [XS_ERROR] = { "ERROR", NULL }, + [XS_IS_DOMAIN_INTRODUCED] = +- { "IS_DOMAIN_INTRODUCED", do_is_domain_introduced }, +- [XS_RESUME] = { "RESUME", do_resume }, +- [XS_SET_TARGET] = { "SET_TARGET", do_set_target }, ++ { "IS_DOMAIN_INTRODUCED", do_is_domain_introduced, XS_FLAG_PRIV }, ++ [XS_RESUME] = ++ { "RESUME", do_resume, XS_FLAG_PRIV }, ++ [XS_SET_TARGET] = ++ { "SET_TARGET", do_set_target, XS_FLAG_PRIV }, + [XS_RESET_WATCHES] = { "RESET_WATCHES", do_reset_watches }, + [XS_DIRECTORY_PART] = { "DIRECTORY_PART", send_directory_part }, + }; +@@ -1336,6 +1342,12 @@ static void process_message(struct connection *conn, struct buffered_data *in) + return; + } + ++ if ((wire_funcs[type].flags & XS_FLAG_PRIV) && ++ domain_is_unprivileged(conn)) { ++ send_error(conn, EACCES); ++ return; ++ } ++ + trans = (wire_funcs[type].flags & XS_FLAG_NOTID) + ? NULL : transaction_lookup(conn, in->hdr.msg.tx_id); + if (IS_ERR(trans)) { +diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c +index 06359503f091..2d0d87ee89e1 100644 +--- a/tools/xenstore/xenstored_domain.c ++++ b/tools/xenstore/xenstored_domain.c +@@ -372,7 +372,7 @@ int do_introduce(struct connection *conn, struct buffered_data *in) + if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec)) + return EINVAL; + +- if (domain_is_unprivileged(conn) || !conn->can_write) ++ if (!conn->can_write) + return EACCES; + + domid = atoi(vec[0]); +@@ -438,7 +438,7 @@ int do_set_target(struct connection *conn, struct buffered_data *in) + if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec)) + return EINVAL; + +- if (domain_is_unprivileged(conn) || !conn->can_write) ++ if (!conn->can_write) + return EACCES; + + domid = atoi(vec[0]); +@@ -473,9 +473,6 @@ static struct domain *onearg_domain(struct connection *conn, + if (!domid) + return ERR_PTR(-EINVAL); + +- if (domain_is_unprivileged(conn)) +- return ERR_PTR(-EACCES); +- + return find_connected_domain(domid); + } + +-- +2.17.1 + diff --git a/xsa115-4.14-c-0006-tools-xenstore-rework-node-removal.patch b/xsa115-4.14-c-0006-tools-xenstore-rework-node-removal.patch new file mode 100644 index 0000000..f2357b8 --- /dev/null +++ b/xsa115-4.14-c-0006-tools-xenstore-rework-node-removal.patch @@ -0,0 +1,217 @@ +From a3d8089532ae573c03e1cdb2fc3c5ee5ebb52a60 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Thu, 11 Jun 2020 16:12:42 +0200 +Subject: [PATCH 06/10] tools/xenstore: rework node removal + +Today a Xenstore node is being removed by deleting it from the parent +first and then deleting itself and all its children. This results in +stale entries remaining in the data base in case e.g. a memory +allocation is failing during processing. This would result in the +rather strange behavior to be able to read a node (as its still in the +data base) while not being visible in the tree view of Xenstore. + +Fix that by deleting the nodes from the leaf side instead of starting +at the root. + +As fire_watches() is now called from _rm() the ctx parameter needs a +const attribute. + +This is part of XSA-115. + +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall +Reviewed-by: Paul Durrant +--- + tools/xenstore/xenstored_core.c | 99 ++++++++++++++++---------------- + tools/xenstore/xenstored_watch.c | 4 +- + tools/xenstore/xenstored_watch.h | 2 +- + 3 files changed, 54 insertions(+), 51 deletions(-) + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index f38196ae2825..dfdb64f3ee60 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -1089,74 +1089,76 @@ static int do_mkdir(struct connection *conn, struct buffered_data *in) + return 0; + } + +-static void delete_node(struct connection *conn, struct node *node) +-{ +- unsigned int i; +- char *name; +- +- /* Delete self, then delete children. If we crash, then the worst +- that can happen is the children will continue to take up space, but +- will otherwise be unreachable. */ +- delete_node_single(conn, node); +- +- /* Delete children, too. */ +- for (i = 0; i < node->childlen; i += strlen(node->children+i) + 1) { +- struct node *child; +- +- name = talloc_asprintf(node, "%s/%s", node->name, +- node->children + i); +- child = name ? read_node(conn, node, name) : NULL; +- if (child) { +- delete_node(conn, child); +- } +- else { +- trace("delete_node: Error deleting child '%s/%s'!\n", +- node->name, node->children + i); +- /* Skip it, we've already deleted the parent. */ +- } +- talloc_free(name); +- } +-} +- +- + /* Delete memory using memmove. */ + static void memdel(void *mem, unsigned off, unsigned len, unsigned total) + { + memmove(mem + off, mem + off + len, total - off - len); + } + +- +-static int remove_child_entry(struct connection *conn, struct node *node, +- size_t offset) ++static void 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; +- return write_node(conn, node, true); ++ if (write_node(conn, node, true)) ++ corrupt(conn, "Can't update parent node '%s'", node->name); + } + +- +-static int delete_child(struct connection *conn, +- struct node *node, const char *childname) ++static void 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)) { +- return remove_child_entry(conn, node, i); ++ remove_child_entry(conn, node, i); ++ return; + } + } + corrupt(conn, "Can't find child '%s' in %s", childname, node->name); +- return ENOENT; + } + ++static int delete_node(struct connection *conn, struct node *parent, ++ struct node *node) ++{ ++ char *name; ++ ++ /* Delete children. */ ++ while (node->childlen) { ++ struct node *child; ++ ++ name = talloc_asprintf(node, "%s/%s", node->name, ++ node->children); ++ child = name ? read_node(conn, node, name) : NULL; ++ if (child) { ++ if (delete_node(conn, node, child)) ++ 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); ++ } ++ ++ delete_node_single(conn, node); ++ delete_child(conn, parent, basename(node->name)); ++ talloc_free(node); ++ ++ return 0; ++} + + static int _rm(struct connection *conn, const void *ctx, struct node *node, + const char *name) + { +- /* Delete from parent first, then if we crash, the worst that can +- happen is the child will continue to take up space, but will +- otherwise be unreachable. */ ++ /* ++ * 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); + +@@ -1167,11 +1169,13 @@ static int _rm(struct connection *conn, const void *ctx, struct node *node, + if (!parent) + return (errno == ENOMEM) ? ENOMEM : EINVAL; + +- if (delete_child(conn, parent, basename(name))) +- return EINVAL; +- +- delete_node(conn, node); +- return 0; ++ /* ++ * 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, true); ++ return delete_node(conn, parent, node); + } + + +@@ -1209,7 +1213,6 @@ static int do_rm(struct connection *conn, struct buffered_data *in) + if (ret) + return ret; + +- fire_watches(conn, in, name, true); + send_ack(conn, XS_RM); + + return 0; +diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c +index f2f1bed47cc6..f0bbfe7a6dc6 100644 +--- a/tools/xenstore/xenstored_watch.c ++++ b/tools/xenstore/xenstored_watch.c +@@ -77,7 +77,7 @@ static bool is_child(const char *child, const char *parent) + * Temporary memory allocations are done with ctx. + */ + static void add_event(struct connection *conn, +- void *ctx, ++ const void *ctx, + struct watch *watch, + const char *name) + { +@@ -121,7 +121,7 @@ static void add_event(struct connection *conn, + * Check whether any watch events are to be sent. + * Temporary memory allocations are done with ctx. + */ +-void fire_watches(struct connection *conn, void *ctx, const char *name, ++void fire_watches(struct connection *conn, const void *ctx, const char *name, + bool recurse) + { + struct connection *i; +diff --git a/tools/xenstore/xenstored_watch.h b/tools/xenstore/xenstored_watch.h +index c72ea6a68542..54d4ea7e0d41 100644 +--- a/tools/xenstore/xenstored_watch.h ++++ b/tools/xenstore/xenstored_watch.h +@@ -25,7 +25,7 @@ int do_watch(struct connection *conn, struct buffered_data *in); + int do_unwatch(struct connection *conn, struct buffered_data *in); + + /* Fire all watches: recurse means all the children are affected (ie. rm). */ +-void fire_watches(struct connection *conn, void *tmp, const char *name, ++void fire_watches(struct connection *conn, const void *tmp, const char *name, + bool recurse); + + void conn_delete_all_watches(struct connection *conn); +-- +2.17.1 + diff --git a/xsa115-4.14-c-0007-tools-xenstore-fire-watches-only-when-removing-a-spe.patch b/xsa115-4.14-c-0007-tools-xenstore-fire-watches-only-when-removing-a-spe.patch new file mode 100644 index 0000000..008ce01 --- /dev/null +++ b/xsa115-4.14-c-0007-tools-xenstore-fire-watches-only-when-removing-a-spe.patch @@ -0,0 +1,118 @@ +From 3d4e3fd6c78795bf426947fbfbfa9af6568ece9f Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Thu, 11 Jun 2020 16:12:43 +0200 +Subject: [PATCH 07/10] tools/xenstore: fire watches only when removing a + specific node + +Instead of firing all watches for removing a subtree in one go, do so +only when the related node is being removed. + +The watches for the top-most node being removed include all watches +including that node, while watches for nodes below that are only fired +if they are matching exactly. This avoids firing any watch more than +once when removing a subtree. + +This is part of XSA-115. + +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall +Reviewed-by: Paul Durrant +--- + tools/xenstore/xenstored_core.c | 11 ++++++----- + tools/xenstore/xenstored_watch.c | 13 ++++++++----- + tools/xenstore/xenstored_watch.h | 4 ++-- + 3 files changed, 16 insertions(+), 12 deletions(-) + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index dfdb64f3ee60..20a7a3581555 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -1120,8 +1120,8 @@ static void delete_child(struct connection *conn, + corrupt(conn, "Can't find child '%s' in %s", childname, node->name); + } + +-static int delete_node(struct connection *conn, struct node *parent, +- struct node *node) ++static int delete_node(struct connection *conn, const void *ctx, ++ struct node *parent, struct node *node) + { + char *name; + +@@ -1133,7 +1133,7 @@ static int delete_node(struct connection *conn, struct node *parent, + node->children); + child = name ? read_node(conn, node, name) : NULL; + if (child) { +- if (delete_node(conn, node, child)) ++ if (delete_node(conn, ctx, node, child)) + return errno; + } else { + trace("delete_node: Error deleting child '%s/%s'!\n", +@@ -1145,6 +1145,7 @@ static int delete_node(struct connection *conn, struct node *parent, + talloc_free(name); + } + ++ fire_watches(conn, ctx, node->name, true); + delete_node_single(conn, node); + delete_child(conn, parent, basename(node->name)); + talloc_free(node); +@@ -1174,8 +1175,8 @@ static int _rm(struct connection *conn, const void *ctx, struct node *node, + * 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, true); +- return delete_node(conn, parent, node); ++ fire_watches(conn, ctx, name, false); ++ return delete_node(conn, ctx, parent, node); + } + + +diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c +index f0bbfe7a6dc6..3836675459fa 100644 +--- a/tools/xenstore/xenstored_watch.c ++++ b/tools/xenstore/xenstored_watch.c +@@ -122,7 +122,7 @@ static void add_event(struct connection *conn, + * Temporary memory allocations are done with ctx. + */ + void fire_watches(struct connection *conn, const void *ctx, const char *name, +- bool recurse) ++ bool exact) + { + struct connection *i; + struct watch *watch; +@@ -134,10 +134,13 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, + /* Create an event for each watch. */ + list_for_each_entry(i, &connections, list) { + list_for_each_entry(watch, &i->watches, list) { +- if (is_child(name, watch->node)) +- add_event(i, ctx, watch, name); +- else if (recurse && is_child(watch->node, name)) +- add_event(i, ctx, watch, watch->node); ++ if (exact) { ++ if (streq(name, watch->node)) ++ add_event(i, ctx, watch, name); ++ } else { ++ if (is_child(name, watch->node)) ++ add_event(i, ctx, watch, name); ++ } + } + } + } +diff --git a/tools/xenstore/xenstored_watch.h b/tools/xenstore/xenstored_watch.h +index 54d4ea7e0d41..1b3c80d3dda1 100644 +--- a/tools/xenstore/xenstored_watch.h ++++ b/tools/xenstore/xenstored_watch.h +@@ -24,9 +24,9 @@ + int do_watch(struct connection *conn, struct buffered_data *in); + int do_unwatch(struct connection *conn, struct buffered_data *in); + +-/* Fire all watches: recurse means all the children are affected (ie. rm). */ ++/* Fire all watches: !exact means all the children are affected (ie. rm). */ + void fire_watches(struct connection *conn, const void *tmp, const char *name, +- bool recurse); ++ bool exact); + + void conn_delete_all_watches(struct connection *conn); + +-- +2.17.1 + diff --git a/xsa115-4.14-c-0008-tools-xenstore-introduce-node_perms-structure.patch b/xsa115-4.14-c-0008-tools-xenstore-introduce-node_perms-structure.patch new file mode 100644 index 0000000..c295e8c --- /dev/null +++ b/xsa115-4.14-c-0008-tools-xenstore-introduce-node_perms-structure.patch @@ -0,0 +1,289 @@ +From 1069c600f85ff583c461cfbfee1afb1a0731796e Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Thu, 11 Jun 2020 16:12:44 +0200 +Subject: [PATCH 08/10] tools/xenstore: introduce node_perms structure + +There are several places in xenstored using a permission array and the +size of that array. Introduce a new struct node_perms containing both. + +This is part of XSA-115. + +Signed-off-by: Juergen Gross +Acked-by: Julien Grall +Reviewed-by: Paul Durrant +--- + tools/xenstore/xenstored_core.c | 79 +++++++++++++++---------------- + tools/xenstore/xenstored_core.h | 8 +++- + tools/xenstore/xenstored_domain.c | 12 ++--- + 3 files changed, 50 insertions(+), 49 deletions(-) + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 20a7a3581555..79d305fbbe58 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -403,14 +403,14 @@ static struct node *read_node(struct connection *conn, const void *ctx, + /* Datalen, childlen, number of permissions */ + hdr = (void *)data.dptr; + node->generation = hdr->generation; +- node->num_perms = hdr->num_perms; ++ node->perms.num = hdr->num_perms; + node->datalen = hdr->datalen; + node->childlen = hdr->childlen; + + /* Permissions are struct xs_permissions. */ +- node->perms = hdr->perms; ++ node->perms.p = hdr->perms; + /* Data is binary blob (usually ascii, no nul). */ +- node->data = node->perms + node->num_perms; ++ node->data = node->perms.p + node->perms.num; + /* Children is strings, nul separated. */ + node->children = node->data + node->datalen; + +@@ -427,7 +427,7 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, + struct xs_tdb_record_hdr *hdr; + + data.dsize = sizeof(*hdr) +- + node->num_perms*sizeof(node->perms[0]) ++ + node->perms.num * sizeof(node->perms.p[0]) + + node->datalen + node->childlen; + + if (!no_quota_check && domain_is_unprivileged(conn) && +@@ -439,12 +439,13 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, + data.dptr = talloc_size(node, data.dsize); + hdr = (void *)data.dptr; + hdr->generation = node->generation; +- hdr->num_perms = node->num_perms; ++ hdr->num_perms = node->perms.num; + hdr->datalen = node->datalen; + hdr->childlen = node->childlen; + +- memcpy(hdr->perms, node->perms, node->num_perms*sizeof(node->perms[0])); +- p = hdr->perms + node->num_perms; ++ memcpy(hdr->perms, node->perms.p, ++ node->perms.num * sizeof(*node->perms.p)); ++ p = hdr->perms + node->perms.num; + memcpy(p, node->data, node->datalen); + p += node->datalen; + memcpy(p, node->children, node->childlen); +@@ -470,8 +471,7 @@ static int write_node(struct connection *conn, struct node *node, + } + + static enum xs_perm_type perm_for_conn(struct connection *conn, +- struct xs_permissions *perms, +- unsigned int num) ++ const struct node_perms *perms) + { + unsigned int i; + enum xs_perm_type mask = XS_PERM_READ|XS_PERM_WRITE|XS_PERM_OWNER; +@@ -480,16 +480,16 @@ static enum xs_perm_type perm_for_conn(struct connection *conn, + mask &= ~XS_PERM_WRITE; + + /* Owners and tools get it all... */ +- if (!domain_is_unprivileged(conn) || perms[0].id == conn->id +- || (conn->target && perms[0].id == conn->target->id)) ++ if (!domain_is_unprivileged(conn) || perms->p[0].id == conn->id ++ || (conn->target && perms->p[0].id == conn->target->id)) + return (XS_PERM_READ|XS_PERM_WRITE|XS_PERM_OWNER) & mask; + +- for (i = 1; i < num; i++) +- if (perms[i].id == conn->id +- || (conn->target && perms[i].id == conn->target->id)) +- return perms[i].perms & mask; ++ for (i = 1; i < perms->num; i++) ++ if (perms->p[i].id == conn->id ++ || (conn->target && perms->p[i].id == conn->target->id)) ++ return perms->p[i].perms & mask; + +- return perms[0].perms & mask; ++ return perms->p[0].perms & mask; + } + + /* +@@ -536,7 +536,7 @@ static int ask_parents(struct connection *conn, const void *ctx, + return 0; + } + +- *perm = perm_for_conn(conn, node->perms, node->num_perms); ++ *perm = perm_for_conn(conn, &node->perms); + return 0; + } + +@@ -582,8 +582,7 @@ struct node *get_node(struct connection *conn, + node = read_node(conn, ctx, name); + /* If we don't have permission, we don't have node. */ + if (node) { +- if ((perm_for_conn(conn, node->perms, node->num_perms) & perm) +- != perm) { ++ if ((perm_for_conn(conn, &node->perms) & perm) != perm) { + errno = EACCES; + node = NULL; + } +@@ -759,16 +758,15 @@ const char *onearg(struct buffered_data *in) + return in->buffer; + } + +-static char *perms_to_strings(const void *ctx, +- struct xs_permissions *perms, unsigned int num, ++static char *perms_to_strings(const void *ctx, const struct node_perms *perms, + unsigned int *len) + { + unsigned int i; + char *strings = NULL; + char buffer[MAX_STRLEN(unsigned int) + 1]; + +- for (*len = 0, i = 0; i < num; i++) { +- if (!xs_perm_to_string(&perms[i], buffer, sizeof(buffer))) ++ for (*len = 0, i = 0; i < perms->num; i++) { ++ if (!xs_perm_to_string(&perms->p[i], buffer, sizeof(buffer))) + return NULL; + + strings = talloc_realloc(ctx, strings, char, +@@ -947,13 +945,13 @@ static struct node *construct_node(struct connection *conn, const void *ctx, + goto nomem; + + /* Inherit permissions, except unprivileged domains own what they create */ +- node->num_perms = parent->num_perms; +- node->perms = talloc_memdup(node, parent->perms, +- node->num_perms * sizeof(node->perms[0])); +- if (!node->perms) ++ 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[0].id = conn->id; ++ node->perms.p[0].id = conn->id; + + /* No children, no data */ + node->children = node->data = NULL; +@@ -1230,7 +1228,7 @@ static int do_get_perms(struct connection *conn, struct buffered_data *in) + if (!node) + return errno; + +- strings = perms_to_strings(node, node->perms, node->num_perms, &len); ++ strings = perms_to_strings(node, &node->perms, &len); + if (!strings) + return errno; + +@@ -1241,13 +1239,12 @@ static int do_get_perms(struct connection *conn, struct buffered_data *in) + + static int do_set_perms(struct connection *conn, struct buffered_data *in) + { +- unsigned int num; +- struct xs_permissions *perms; ++ struct node_perms perms; + char *name, *permstr; + struct node *node; + +- num = xs_count_strings(in->buffer, in->used); +- if (num < 2) ++ perms.num = xs_count_strings(in->buffer, in->used); ++ if (perms.num < 2) + return EINVAL; + + /* First arg is node name. */ +@@ -1258,21 +1255,21 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) + return errno; + + permstr = in->buffer + strlen(in->buffer) + 1; +- num--; ++ perms.num--; + +- perms = talloc_array(node, struct xs_permissions, num); +- if (!perms) ++ perms.p = talloc_array(node, struct xs_permissions, perms.num); ++ if (!perms.p) + return ENOMEM; +- if (!xs_strings_to_perms(perms, num, permstr)) ++ if (!xs_strings_to_perms(perms.p, perms.num, permstr)) + return errno; + + /* Unprivileged domains may not change the owner. */ +- if (domain_is_unprivileged(conn) && perms[0].id != node->perms[0].id) ++ if (domain_is_unprivileged(conn) && ++ perms.p[0].id != node->perms.p[0].id) + return EPERM; + + domain_entry_dec(conn, node); + node->perms = perms; +- node->num_perms = num; + domain_entry_inc(conn, node); + + if (write_node(conn, node, false)) +@@ -1547,8 +1544,8 @@ static void manual_node(const char *name, const char *child) + barf_perror("Could not allocate initial node %s", name); + + node->name = name; +- node->perms = &perms; +- node->num_perms = 1; ++ node->perms.p = &perms; ++ node->perms.num = 1; + node->children = (char *)child; + if (child) + node->childlen = strlen(child) + 1; +diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h +index 29d638fbc5a0..47ba0916dbe2 100644 +--- a/tools/xenstore/xenstored_core.h ++++ b/tools/xenstore/xenstored_core.h +@@ -109,6 +109,11 @@ struct connection + }; + extern struct list_head connections; + ++struct node_perms { ++ unsigned int num; ++ struct xs_permissions *p; ++}; ++ + struct node { + const char *name; + +@@ -120,8 +125,7 @@ struct node { + #define NO_GENERATION ~((uint64_t)0) + + /* Permissions. */ +- unsigned int num_perms; +- struct xs_permissions *perms; ++ struct node_perms perms; + + /* Contents. */ + unsigned int datalen; +diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c +index 2d0d87ee89e1..aa9942fcc267 100644 +--- a/tools/xenstore/xenstored_domain.c ++++ b/tools/xenstore/xenstored_domain.c +@@ -650,12 +650,12 @@ void domain_entry_inc(struct connection *conn, struct node *node) + if (!conn) + return; + +- if (node->perms && node->perms[0].id != conn->id) { ++ if (node->perms.p && node->perms.p[0].id != conn->id) { + if (conn->transaction) { + transaction_entry_inc(conn->transaction, +- node->perms[0].id); ++ node->perms.p[0].id); + } else { +- d = find_domain_by_domid(node->perms[0].id); ++ d = find_domain_by_domid(node->perms.p[0].id); + if (d) + d->nbentry++; + } +@@ -676,12 +676,12 @@ void domain_entry_dec(struct connection *conn, struct node *node) + if (!conn) + return; + +- if (node->perms && node->perms[0].id != conn->id) { ++ if (node->perms.p && node->perms.p[0].id != conn->id) { + if (conn->transaction) { + transaction_entry_dec(conn->transaction, +- node->perms[0].id); ++ node->perms.p[0].id); + } else { +- d = find_domain_by_domid(node->perms[0].id); ++ d = find_domain_by_domid(node->perms.p[0].id); + if (d && d->nbentry) + d->nbentry--; + } +-- +2.17.1 + diff --git a/xsa115-4.14-c-0009-tools-xenstore-allow-special-watches-for-privileged-.patch b/xsa115-4.14-c-0009-tools-xenstore-allow-special-watches-for-privileged-.patch new file mode 100644 index 0000000..e1e8942 --- /dev/null +++ b/xsa115-4.14-c-0009-tools-xenstore-allow-special-watches-for-privileged-.patch @@ -0,0 +1,237 @@ +From b9fff4b7ad6b41db860a43d35c401847fef789cb Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Thu, 11 Jun 2020 16:12:45 +0200 +Subject: [PATCH 09/10] tools/xenstore: allow special watches for privileged + callers only + +The special watches "@introduceDomain" and "@releaseDomain" should be +allowed for privileged callers only, as they allow to gain information +about presence of other guests on the host. So send watch events for +those watches via privileged connections only. + +In order to allow for disaggregated setups where e.g. driver domains +need to make use of those special watches add support for calling +"set permissions" for those special nodes, too. + +This is part of XSA-115. + +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall +Reviewed-by: Paul Durrant +--- + docs/misc/xenstore.txt | 5 +++ + tools/xenstore/xenstored_core.c | 27 ++++++++------ + tools/xenstore/xenstored_core.h | 2 ++ + tools/xenstore/xenstored_domain.c | 60 +++++++++++++++++++++++++++++++ + tools/xenstore/xenstored_domain.h | 5 +++ + tools/xenstore/xenstored_watch.c | 4 +++ + 6 files changed, 93 insertions(+), 10 deletions(-) + +diff --git a/docs/misc/xenstore.txt b/docs/misc/xenstore.txt +index cb8009cb686d..2081f20f55e4 100644 +--- a/docs/misc/xenstore.txt ++++ b/docs/misc/xenstore.txt +@@ -170,6 +170,9 @@ SET_PERMS ||+? + n no access + See https://wiki.xen.org/wiki/XenBus section + `Permissions' for details of the permissions system. ++ It is possible to set permissions for the special watch paths ++ "@introduceDomain" and "@releaseDomain" to enable receiving those ++ watches in unprivileged domains. + + ---------- Watches ---------- + +@@ -194,6 +197,8 @@ WATCH ||? + @releaseDomain occurs on any domain crash or + shutdown, and also on RELEASE + and domain destruction ++ events are sent to privileged callers or explicitly ++ via SET_PERMS enabled domains only. + + When a watch is first set up it is triggered once straight + away, with equal to . Watches may be triggered +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 79d305fbbe58..15ffbeb30f19 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -470,8 +470,8 @@ static int write_node(struct connection *conn, struct node *node, + return write_node_raw(conn, &key, node, no_quota_check); + } + +-static enum xs_perm_type perm_for_conn(struct connection *conn, +- const struct node_perms *perms) ++enum xs_perm_type perm_for_conn(struct connection *conn, ++ const struct node_perms *perms) + { + unsigned int i; + enum xs_perm_type mask = XS_PERM_READ|XS_PERM_WRITE|XS_PERM_OWNER; +@@ -1247,22 +1247,29 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) + if (perms.num < 2) + return EINVAL; + +- /* First arg is node name. */ +- /* We must own node to do this (tools can do this too). */ +- node = get_node_canonicalized(conn, in, in->buffer, &name, +- XS_PERM_WRITE | XS_PERM_OWNER); +- if (!node) +- return errno; +- + permstr = in->buffer + strlen(in->buffer) + 1; + perms.num--; + +- perms.p = talloc_array(node, struct xs_permissions, perms.num); ++ perms.p = talloc_array(in, struct xs_permissions, perms.num); + if (!perms.p) + return ENOMEM; + if (!xs_strings_to_perms(perms.p, perms.num, permstr)) + return errno; + ++ /* First arg is node name. */ ++ if (strstarts(in->buffer, "@")) { ++ if (set_perms_special(conn, in->buffer, &perms)) ++ return errno; ++ send_ack(conn, XS_SET_PERMS); ++ return 0; ++ } ++ ++ /* We must own node to do this (tools can do this too). */ ++ node = get_node_canonicalized(conn, in, in->buffer, &name, ++ XS_PERM_WRITE | XS_PERM_OWNER); ++ if (!node) ++ return errno; ++ + /* Unprivileged domains may not change the owner. */ + if (domain_is_unprivileged(conn) && + perms.p[0].id != node->perms.p[0].id) +diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h +index 47ba0916dbe2..53f1050859fc 100644 +--- a/tools/xenstore/xenstored_core.h ++++ b/tools/xenstore/xenstored_core.h +@@ -165,6 +165,8 @@ struct node *get_node(struct connection *conn, + struct connection *new_connection(connwritefn_t *write, connreadfn_t *read); + void check_store(void); + void corrupt(struct connection *conn, const char *fmt, ...); ++enum xs_perm_type perm_for_conn(struct connection *conn, ++ const struct node_perms *perms); + + /* Is this a valid node name? */ + bool is_valid_nodename(const char *node); +diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c +index aa9942fcc267..a0d1a11c837f 100644 +--- a/tools/xenstore/xenstored_domain.c ++++ b/tools/xenstore/xenstored_domain.c +@@ -41,6 +41,9 @@ static evtchn_port_t virq_port; + + xenevtchn_handle *xce_handle = NULL; + ++static struct node_perms dom_release_perms; ++static struct node_perms dom_introduce_perms; ++ + struct domain + { + struct list_head list; +@@ -582,6 +585,59 @@ void restore_existing_connections(void) + { + } + ++static int set_dom_perms_default(struct node_perms *perms) ++{ ++ perms->num = 1; ++ perms->p = talloc_array(NULL, struct xs_permissions, perms->num); ++ if (!perms->p) ++ return -1; ++ perms->p->id = 0; ++ perms->p->perms = XS_PERM_NONE; ++ ++ return 0; ++} ++ ++static struct node_perms *get_perms_special(const char *name) ++{ ++ if (!strcmp(name, "@releaseDomain")) ++ return &dom_release_perms; ++ if (!strcmp(name, "@introduceDomain")) ++ return &dom_introduce_perms; ++ return NULL; ++} ++ ++int set_perms_special(struct connection *conn, const char *name, ++ struct node_perms *perms) ++{ ++ struct node_perms *p; ++ ++ p = get_perms_special(name); ++ if (!p) ++ return EINVAL; ++ ++ if ((perm_for_conn(conn, p) & (XS_PERM_WRITE | XS_PERM_OWNER)) != ++ (XS_PERM_WRITE | XS_PERM_OWNER)) ++ return EACCES; ++ ++ p->num = perms->num; ++ talloc_free(p->p); ++ p->p = perms->p; ++ talloc_steal(NULL, perms->p); ++ ++ return 0; ++} ++ ++bool check_perms_special(const char *name, struct connection *conn) ++{ ++ struct node_perms *p; ++ ++ p = get_perms_special(name); ++ if (!p) ++ return false; ++ ++ return perm_for_conn(conn, p) & XS_PERM_READ; ++} ++ + static int dom0_init(void) + { + evtchn_port_t port; +@@ -603,6 +659,10 @@ static int dom0_init(void) + + xenevtchn_notify(xce_handle, dom0->port); + ++ if (set_dom_perms_default(&dom_release_perms) || ++ set_dom_perms_default(&dom_introduce_perms)) ++ return -1; ++ + return 0; + } + +diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h +index 56ae01597475..259183962a9c 100644 +--- a/tools/xenstore/xenstored_domain.h ++++ b/tools/xenstore/xenstored_domain.h +@@ -65,6 +65,11 @@ void domain_watch_inc(struct connection *conn); + void domain_watch_dec(struct connection *conn); + int domain_watch(struct connection *conn); + ++/* Special node permission handling. */ ++int set_perms_special(struct connection *conn, const char *name, ++ struct node_perms *perms); ++bool check_perms_special(const char *name, struct connection *conn); ++ + /* Write rate limiting */ + + #define WRL_FACTOR 1000 /* for fixed-point arithmetic */ +diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c +index 3836675459fa..f4e289362eb6 100644 +--- a/tools/xenstore/xenstored_watch.c ++++ b/tools/xenstore/xenstored_watch.c +@@ -133,6 +133,10 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, + + /* Create an event for each watch. */ + list_for_each_entry(i, &connections, list) { ++ /* introduce/release domain watches */ ++ if (check_special_event(name) && !check_perms_special(name, i)) ++ continue; ++ + list_for_each_entry(watch, &i->watches, list) { + if (exact) { + if (streq(name, watch->node)) +-- +2.17.1 + diff --git a/xsa115-4.14-c-0010-tools-xenstore-avoid-watch-events-for-nodes-without-.patch b/xsa115-4.14-c-0010-tools-xenstore-avoid-watch-events-for-nodes-without-.patch new file mode 100644 index 0000000..b09153c --- /dev/null +++ b/xsa115-4.14-c-0010-tools-xenstore-avoid-watch-events-for-nodes-without-.patch @@ -0,0 +1,374 @@ +From f1cc47b0572b337269af7e34bd019584f4b8c98e Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Thu, 11 Jun 2020 16:12:46 +0200 +Subject: [PATCH 10/10] tools/xenstore: avoid watch events for nodes without + access + +Today watch events are sent regardless of the access rights of the +node the event is sent for. This enables any guest to e.g. setup a +watch for "/" in order to have a detailed record of all Xenstore +modifications. + +Modify that by sending only watch events for nodes that the watcher +has a chance to see otherwise (either via direct reads or by querying +the children of a node). This includes cases where the visibility of +a node for a watcher is changing (permissions being removed). + +This is part of XSA-115. + +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall +Reviewed-by: Paul Durrant +--- + tools/xenstore/xenstored_core.c | 28 +++++----- + tools/xenstore/xenstored_core.h | 15 ++++-- + tools/xenstore/xenstored_domain.c | 6 +-- + tools/xenstore/xenstored_transaction.c | 21 +++++++- + tools/xenstore/xenstored_watch.c | 75 +++++++++++++++++++------- + tools/xenstore/xenstored_watch.h | 2 +- + 6 files changed, 104 insertions(+), 43 deletions(-) + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 15ffbeb30f19..92bfd54cff62 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -360,8 +360,8 @@ static void initialize_fds(int *p_sock_pollfd_idx, int *p_ro_sock_pollfd_idx, + * If it fails, returns NULL and sets errno. + * Temporary memory allocations will be done with ctx. + */ +-static struct node *read_node(struct connection *conn, const void *ctx, +- const char *name) ++struct node *read_node(struct connection *conn, const void *ctx, ++ const char *name) + { + TDB_DATA key, data; + struct xs_tdb_record_hdr *hdr; +@@ -496,7 +496,7 @@ enum xs_perm_type perm_for_conn(struct connection *conn, + * Get name of node parent. + * Temporary memory allocations are done with ctx. + */ +-static char *get_parent(const void *ctx, const char *node) ++char *get_parent(const void *ctx, const char *node) + { + char *parent; + char *slash = strrchr(node + 1, '/'); +@@ -568,10 +568,10 @@ static int errno_from_parents(struct connection *conn, const void *ctx, + * If it fails, returns NULL and sets errno. + * Temporary memory allocations are done with ctx. + */ +-struct node *get_node(struct connection *conn, +- const void *ctx, +- const char *name, +- enum xs_perm_type perm) ++static struct node *get_node(struct connection *conn, ++ const void *ctx, ++ const char *name, ++ enum xs_perm_type perm) + { + struct node *node; + +@@ -1058,7 +1058,7 @@ static int do_write(struct connection *conn, struct buffered_data *in) + return errno; + } + +- fire_watches(conn, in, name, false); ++ fire_watches(conn, in, name, node, false, NULL); + send_ack(conn, XS_WRITE); + + return 0; +@@ -1080,7 +1080,7 @@ static int do_mkdir(struct connection *conn, struct buffered_data *in) + node = create_node(conn, in, name, NULL, 0); + if (!node) + return errno; +- fire_watches(conn, in, name, false); ++ fire_watches(conn, in, name, node, false, NULL); + } + send_ack(conn, XS_MKDIR); + +@@ -1143,7 +1143,7 @@ static int delete_node(struct connection *conn, const void *ctx, + talloc_free(name); + } + +- fire_watches(conn, ctx, node->name, true); ++ fire_watches(conn, ctx, node->name, node, true, NULL); + delete_node_single(conn, node); + delete_child(conn, parent, basename(node->name)); + talloc_free(node); +@@ -1167,13 +1167,14 @@ 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; ++ 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, false); ++ fire_watches(conn, ctx, name, node, false, NULL); + return delete_node(conn, ctx, parent, node); + } + +@@ -1239,7 +1240,7 @@ static int do_get_perms(struct connection *conn, struct buffered_data *in) + + static int do_set_perms(struct connection *conn, struct buffered_data *in) + { +- struct node_perms perms; ++ struct node_perms perms, old_perms; + char *name, *permstr; + struct node *node; + +@@ -1275,6 +1276,7 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) + perms.p[0].id != node->perms.p[0].id) + return EPERM; + ++ old_perms = node->perms; + domain_entry_dec(conn, node); + node->perms = perms; + domain_entry_inc(conn, node); +@@ -1282,7 +1284,7 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) + if (write_node(conn, node, false)) + return errno; + +- fire_watches(conn, in, name, false); ++ fire_watches(conn, in, name, node, false, &old_perms); + send_ack(conn, XS_SET_PERMS); + + return 0; +diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h +index 53f1050859fc..eb19b71f5f46 100644 +--- a/tools/xenstore/xenstored_core.h ++++ b/tools/xenstore/xenstored_core.h +@@ -152,15 +152,17 @@ void send_ack(struct connection *conn, enum xsd_sockmsg_type type); + /* Canonicalize this path if possible. */ + 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, ++ const struct node_perms *perms); ++ + /* Write a node to the tdb data base. */ + int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, + bool no_quota_check); + +-/* Get this node, checking we have permissions. */ +-struct node *get_node(struct connection *conn, +- const void *ctx, +- const char *name, +- enum xs_perm_type perm); ++/* Get a node from the tdb data base. */ ++struct node *read_node(struct connection *conn, const void *ctx, ++ const char *name); + + struct connection *new_connection(connwritefn_t *write, connreadfn_t *read); + void check_store(void); +@@ -171,6 +173,9 @@ enum xs_perm_type perm_for_conn(struct connection *conn, + /* Is this a valid node name? */ + bool is_valid_nodename(const char *node); + ++/* Get name of parent node. */ ++char *get_parent(const void *ctx, const char *node); ++ + /* Tracing infrastructure. */ + void trace_create(const void *data, const char *type); + void trace_destroy(const void *data, const char *type); +diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c +index a0d1a11c837f..9fad470f8331 100644 +--- a/tools/xenstore/xenstored_domain.c ++++ b/tools/xenstore/xenstored_domain.c +@@ -202,7 +202,7 @@ static int destroy_domain(void *_domain) + unmap_interface(domain->interface); + } + +- fire_watches(NULL, domain, "@releaseDomain", false); ++ fire_watches(NULL, domain, "@releaseDomain", NULL, false, NULL); + + wrl_domain_destroy(domain); + +@@ -240,7 +240,7 @@ static void domain_cleanup(void) + } + + if (notify) +- fire_watches(NULL, NULL, "@releaseDomain", false); ++ fire_watches(NULL, NULL, "@releaseDomain", NULL, false, NULL); + } + + /* We scan all domains rather than use the information given here. */ +@@ -404,7 +404,7 @@ int do_introduce(struct connection *conn, struct buffered_data *in) + /* Now domain belongs to its connection. */ + talloc_steal(domain->conn, domain); + +- fire_watches(NULL, in, "@introduceDomain", false); ++ fire_watches(NULL, in, "@introduceDomain", NULL, false, NULL); + } else { + /* Use XS_INTRODUCE for recreating the xenbus event-channel. */ + if (domain->port) +diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c +index e87897573469..a7d8c5d475ec 100644 +--- a/tools/xenstore/xenstored_transaction.c ++++ b/tools/xenstore/xenstored_transaction.c +@@ -114,6 +114,9 @@ struct accessed_node + /* Generation count (or NO_GENERATION) for conflict checking. */ + uint64_t generation; + ++ /* Original node permissions. */ ++ struct node_perms perms; ++ + /* Generation count checking required? */ + bool check_gen; + +@@ -260,6 +263,15 @@ int access_node(struct connection *conn, struct node *node, + i->node = talloc_strdup(i, node->name); + if (!i->node) + goto nomem; ++ if (node->generation != NO_GENERATION && node->perms.num) { ++ i->perms.p = talloc_array(i, struct xs_permissions, ++ node->perms.num); ++ if (!i->perms.p) ++ goto nomem; ++ i->perms.num = node->perms.num; ++ memcpy(i->perms.p, node->perms.p, ++ i->perms.num * sizeof(*i->perms.p)); ++ } + + introduce = true; + i->ta_node = false; +@@ -368,9 +380,14 @@ static int finalize_transaction(struct connection *conn, + talloc_free(data.dptr); + if (ret) + goto err; +- } else if (tdb_delete(tdb_ctx, key)) ++ fire_watches(conn, trans, i->node, NULL, false, ++ i->perms.p ? &i->perms : NULL); ++ } else { ++ fire_watches(conn, trans, i->node, NULL, false, ++ i->perms.p ? &i->perms : NULL); ++ if (tdb_delete(tdb_ctx, key)) + goto err; +- fire_watches(conn, trans, i->node, false); ++ } + } + + if (i->ta_node && tdb_delete(tdb_ctx, ta_key)) +diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c +index f4e289362eb6..71c108ea99f1 100644 +--- a/tools/xenstore/xenstored_watch.c ++++ b/tools/xenstore/xenstored_watch.c +@@ -85,22 +85,6 @@ static void add_event(struct connection *conn, + unsigned int len; + char *data; + +- if (!check_special_event(name)) { +- /* Can this conn load node, or see that it doesn't exist? */ +- struct node *node = get_node(conn, ctx, name, XS_PERM_READ); +- /* +- * XXX We allow EACCES here because otherwise a non-dom0 +- * backend driver cannot watch for disappearance of a frontend +- * xenstore directory. When the directory disappears, we +- * revert to permissions of the parent directory for that path, +- * which will typically disallow access for the backend. +- * But this breaks device-channel teardown! +- * Really we should fix this better... +- */ +- if (!node && errno != ENOENT && errno != EACCES) +- return; +- } +- + if (watch->relative_path) { + name += strlen(watch->relative_path); + if (*name == '/') /* Could be "" */ +@@ -117,12 +101,60 @@ static void add_event(struct connection *conn, + 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 ++ * changed permissions we need to take the old permissions into account, too. ++ */ ++static bool watch_permitted(struct connection *conn, const void *ctx, ++ const char *name, struct node *node, ++ struct node_perms *perms) ++{ ++ enum xs_perm_type perm; ++ struct node *parent; ++ char *parent_name; ++ ++ if (perms) { ++ perm = perm_for_conn(conn, perms); ++ if (perm & XS_PERM_READ) ++ return true; ++ } ++ ++ if (!node) { ++ node = read_node(conn, ctx, name); ++ if (!node) ++ return false; ++ } ++ ++ perm = perm_for_conn(conn, &node->perms); ++ if (perm & XS_PERM_READ) ++ return true; ++ ++ parent = node->parent; ++ if (!parent) { ++ parent_name = get_parent(ctx, node->name); ++ if (!parent_name) ++ return false; ++ parent = read_node(conn, ctx, parent_name); ++ if (!parent) ++ return false; ++ } ++ ++ perm = perm_for_conn(conn, &parent->perms); ++ ++ return perm & XS_PERM_READ; ++} ++ + /* + * Check whether any watch events are to be sent. + * Temporary memory allocations are done with ctx. ++ * We need to take the (potential) old permissions of the node into account ++ * as a watcher losing permissions to access a node should receive the ++ * watch event, too. + */ + void fire_watches(struct connection *conn, const void *ctx, const char *name, +- bool exact) ++ struct node *node, bool exact, struct node_perms *perms) + { + struct connection *i; + struct watch *watch; +@@ -134,8 +166,13 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, + /* Create an event for each watch. */ + list_for_each_entry(i, &connections, list) { + /* introduce/release domain watches */ +- if (check_special_event(name) && !check_perms_special(name, i)) +- continue; ++ if (check_special_event(name)) { ++ if (!check_perms_special(name, i)) ++ continue; ++ } else { ++ if (!watch_permitted(i, ctx, name, node, perms)) ++ continue; ++ } + + list_for_each_entry(watch, &i->watches, list) { + if (exact) { +diff --git a/tools/xenstore/xenstored_watch.h b/tools/xenstore/xenstored_watch.h +index 1b3c80d3dda1..03094374f379 100644 +--- a/tools/xenstore/xenstored_watch.h ++++ b/tools/xenstore/xenstored_watch.h +@@ -26,7 +26,7 @@ int do_unwatch(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, +- bool exact); ++ struct node *node, bool exact, struct node_perms *perms); + + void conn_delete_all_watches(struct connection *conn); + +-- +2.17.1 + diff --git a/xsa115-o-0001-tools-ocaml-xenstored-ignore-transaction-id-for-un-w.patch b/xsa115-o-0001-tools-ocaml-xenstored-ignore-transaction-id-for-un-w.patch new file mode 100644 index 0000000..0072c68 --- /dev/null +++ b/xsa115-o-0001-tools-ocaml-xenstored-ignore-transaction-id-for-un-w.patch @@ -0,0 +1,43 @@ +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Subject: tools/ocaml/xenstored: ignore transaction id for [un]watch +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Instead of ignoring the transaction id for XS_WATCH and XS_UNWATCH +commands as it is documented in docs/misc/xenstore.txt, it is tested +for validity today. + +Really ignore the transaction id for XS_WATCH and XS_UNWATCH. + +This is part of XSA-115. + +Signed-off-by: Edwin Török +Acked-by: Christian Lindig +Reviewed-by: Andrew Cooper + +diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml +index ff5c9484fc..2fa6798e3b 100644 +--- a/tools/ocaml/xenstored/process.ml ++++ b/tools/ocaml/xenstored/process.ml +@@ -498,12 +498,19 @@ let retain_op_in_history ty = + | Xenbus.Xb.Op.Reset_watches + | Xenbus.Xb.Op.Invalid -> false + ++let maybe_ignore_transaction = function ++ | Xenbus.Xb.Op.Watch | Xenbus.Xb.Op.Unwatch -> fun tid -> ++ if tid <> Transaction.none then ++ debug "Ignoring transaction ID %d for watch/unwatch" tid; ++ Transaction.none ++ | _ -> fun x -> x ++ + (** + * Nothrow guarantee. + *) + let process_packet ~store ~cons ~doms ~con ~req = + let ty = req.Packet.ty in +- let tid = req.Packet.tid in ++ let tid = maybe_ignore_transaction ty req.Packet.tid in + let rid = req.Packet.rid in + try + let fct = function_of_type ty in diff --git a/xsa115-o-0002-tools-ocaml-xenstored-check-privilege-for-XS_IS_DOMA.patch b/xsa115-o-0002-tools-ocaml-xenstored-check-privilege-for-XS_IS_DOMA.patch new file mode 100644 index 0000000..26033c7 --- /dev/null +++ b/xsa115-o-0002-tools-ocaml-xenstored-check-privilege-for-XS_IS_DOMA.patch @@ -0,0 +1,30 @@ +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Subject: tools/ocaml/xenstored: check privilege for XS_IS_DOMAIN_INTRODUCED +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The Xenstore command XS_IS_DOMAIN_INTRODUCED should be possible for privileged +domains only (the only user in the tree is the xenpaging daemon). + +This is part of XSA-115. + +Signed-off-by: Edwin Török +Acked-by: Christian Lindig +Reviewed-by: Andrew Cooper + +diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml +index 2fa6798e3b..fd79ef564f 100644 +--- a/tools/ocaml/xenstored/process.ml ++++ b/tools/ocaml/xenstored/process.ml +@@ -166,7 +166,9 @@ let do_setperms con t _domains _cons data = + let do_error _con _t _domains _cons _data = + raise Define.Unknown_operation + +-let do_isintroduced _con _t domains _cons data = ++let do_isintroduced con _t domains _cons data = ++ if not (Connection.is_dom0 con) ++ then raise Define.Permission_denied; + let domid = + match (split None '\000' data) with + | domid :: _ -> int_of_string domid diff --git a/xsa115-o-0003-tools-ocaml-xenstored-unify-watch-firing.patch b/xsa115-o-0003-tools-ocaml-xenstored-unify-watch-firing.patch new file mode 100644 index 0000000..fea94a9 --- /dev/null +++ b/xsa115-o-0003-tools-ocaml-xenstored-unify-watch-firing.patch @@ -0,0 +1,29 @@ +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Subject: tools/ocaml/xenstored: unify watch firing +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This will make it easier insert additional checks in a follow-up patch. +All watches are now fired from a single function. + +This is part of XSA-115. + +Signed-off-by: Edwin Török +Acked-by: Christian Lindig +Reviewed-by: Andrew Cooper + +diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/connection.ml +index 24750ada43..e5df62d9e7 100644 +--- a/tools/ocaml/xenstored/connection.ml ++++ b/tools/ocaml/xenstored/connection.ml +@@ -210,8 +210,7 @@ let fire_watch watch path = + end else + path + in +- let data = Utils.join_by_null [ new_path; watch.token; "" ] in +- send_reply watch.con Transaction.none 0 Xenbus.Xb.Op.Watchevent data ++ fire_single_watch { watch with path = new_path } + + (* Search for a valid unused transaction id. *) + let rec valid_transaction_id con proposed_id = diff --git a/xsa115-o-0004-tools-ocaml-xenstored-introduce-permissions-for-spec.patch b/xsa115-o-0004-tools-ocaml-xenstored-introduce-permissions-for-spec.patch new file mode 100644 index 0000000..76f98e9 --- /dev/null +++ b/xsa115-o-0004-tools-ocaml-xenstored-introduce-permissions-for-spec.patch @@ -0,0 +1,117 @@ +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Subject: tools/ocaml/xenstored: introduce permissions for special watches +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The special watches "@introduceDomain" and "@releaseDomain" should be +allowed for privileged callers only, as they allow to gain information +about presence of other guests on the host. So send watch events for +those watches via privileged connections only. + +Start to address this by treating the special watches as regular nodes +in the tree, which gives them normal semantics for permissions. A later +change will restrict the handling, so that they can't be listed, etc. + +This is part of XSA-115. + +Signed-off-by: Edwin Török +Acked-by: Christian Lindig +Reviewed-by: Andrew Cooper + +diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml +index fd79ef564f..e528d1ecb2 100644 +--- a/tools/ocaml/xenstored/process.ml ++++ b/tools/ocaml/xenstored/process.ml +@@ -420,7 +420,7 @@ let do_introduce con _t domains cons data = + else try + let ndom = Domains.create domains domid mfn port in + Connections.add_domain cons ndom; +- Connections.fire_spec_watches cons "@introduceDomain"; ++ Connections.fire_spec_watches cons Store.Path.introduce_domain; + ndom + with _ -> raise Invalid_Cmd_Args + in +@@ -439,7 +439,7 @@ let do_release con _t domains cons data = + Domains.del domains domid; + Connections.del_domain cons domid; + if fire_spec_watches +- then Connections.fire_spec_watches cons "@releaseDomain" ++ then Connections.fire_spec_watches cons Store.Path.release_domain + else raise Invalid_Cmd_Args + + let do_resume con _t domains _cons data = +diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml +index 92b6289b5e..52b88b3ee1 100644 +--- a/tools/ocaml/xenstored/store.ml ++++ b/tools/ocaml/xenstored/store.ml +@@ -214,6 +214,11 @@ let rec lookup node path fct = + + let apply rnode path fct = + lookup rnode path fct ++ ++let introduce_domain = "@introduceDomain" ++let release_domain = "@releaseDomain" ++let specials = List.map of_string [ introduce_domain; release_domain ] ++ + end + + (* The Store.t type *) +diff --git a/tools/ocaml/xenstored/utils.ml b/tools/ocaml/xenstored/utils.ml +index b252db799b..e8c9fe4e94 100644 +--- a/tools/ocaml/xenstored/utils.ml ++++ b/tools/ocaml/xenstored/utils.ml +@@ -88,19 +88,17 @@ let read_file_single_integer filename = + Unix.close fd; + int_of_string (Bytes.sub_string buf 0 sz) + +-let path_complete path connection_path = +- if String.get path 0 <> '/' then +- connection_path ^ path +- else +- path +- ++(* @path may be guest data and needs its length validating. @connection_path ++ * is generated locally in xenstored and always of the form "/local/domain/$N/" *) + let path_validate path connection_path = +- if String.length path = 0 || String.length path > 1024 then +- raise Define.Invalid_path +- else +- let cpath = path_complete path connection_path in +- if String.get cpath 0 <> '/' then +- raise Define.Invalid_path +- else +- cpath ++ let len = String.length path in ++ ++ if len = 0 || len > 1024 then raise Define.Invalid_path; ++ ++ let abs_path = ++ match String.get path 0 with ++ | '/' | '@' -> path ++ | _ -> connection_path ^ path ++ in + ++ abs_path +diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml +index 7e7824761b..8d0c50bfa4 100644 +--- a/tools/ocaml/xenstored/xenstored.ml ++++ b/tools/ocaml/xenstored/xenstored.ml +@@ -286,6 +286,8 @@ let _ = + let quit = ref false in + + Logging.init_xenstored_log(); ++ List.iter (fun path -> ++ Store.write store Perms.Connection.full_rights path "") Store.Path.specials; + + let filename = Paths.xen_run_stored ^ "/db" in + if cf.restart && Sys.file_exists filename then ( +@@ -335,7 +337,7 @@ let _ = + let (notify, deaddom) = Domains.cleanup domains in + List.iter (Connections.del_domain cons) deaddom; + if deaddom <> [] || notify then +- Connections.fire_spec_watches cons "@releaseDomain" ++ Connections.fire_spec_watches cons Store.Path.release_domain + ) + else + let c = Connections.find_domain_by_port cons port in diff --git a/xsa115-o-0005-tools-ocaml-xenstored-avoid-watch-events-for-nodes-w.patch b/xsa115-o-0005-tools-ocaml-xenstored-avoid-watch-events-for-nodes-w.patch new file mode 100644 index 0000000..866d415 --- /dev/null +++ b/xsa115-o-0005-tools-ocaml-xenstored-avoid-watch-events-for-nodes-w.patch @@ -0,0 +1,406 @@ +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Subject: tools/ocaml/xenstored: avoid watch events for nodes without access +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Today watch events are sent regardless of the access rights of the +node the event is sent for. This enables any guest to e.g. setup a +watch for "/" in order to have a detailed record of all Xenstore +modifications. + +Modify that by sending only watch events for nodes that the watcher +has a chance to see otherwise (either via direct reads or by querying +the children of a node). This includes cases where the visibility of +a node for a watcher is changing (permissions being removed). + +Permissions for nodes are looked up either in the old (pre +transaction/command) or current trees (post transaction). If +permissions are changed multiple times in a transaction only the final +version is checked, because considering a transaction atomic the +individual permission changes would not be noticable to an outside +observer. + +Two trees are only needed for set_perms: here we can either notice the +node disappearing (if we loose permission), appearing +(if we gain permission), or changing (if we preserve permission). + +RM needs to only look at the old tree: in the new tree the node would be +gone, or could have different permissions if it was recreated (the +recreation would get its own watch fired). + +Inside a tree we lookup the watch path's parent, and then the watch path +child itself. This gets us 4 sets of permissions in worst case, and if +either of these allows a watch, then we permit it to fire. The +permission lookups are done without logging the failures, otherwise we'd +get confusing errors about permission denied for some paths, but a watch +still firing. The actual result is logged in xenstored-access log: + + 'w event ...' as usual if watch was fired + 'w notfired...' if the watch was not fired, together with path and + permission set to help in troubleshooting + +Adding a watch bypasses permission checks and always fires the watch +once immediately. This is consistent with the specification, and no +information is gained (the watch is fired both if the path exists or +doesn't, and both if you have or don't have access, i.e. it reflects the +path a domain gave it back to that domain). + +There are some semantic changes here: + + * Write+rm in a single transaction of the same path is unobservable + now via watches: both before and after a transaction the path + doesn't exist, thus both tree lookups come up with the empty + permission set, and noone, not even Dom0 can see this. This is + consistent with transaction atomicity though. + * Similar to above if we temporarily grant and then revoke permission + on a path any watches fired inbetween are ignored as well + * There is a new log event (w notfired) which shows the permission set + of the path, and the path. + * Watches on paths that a domain doesn't have access to are now not + seen, which is the purpose of the security fix. + +This is part of XSA-115. + +Signed-off-by: Edwin Török +Acked-by: Christian Lindig +Reviewed-by: Andrew Cooper + +diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/connection.ml +index e5df62d9e7..644a448f2e 100644 +--- a/tools/ocaml/xenstored/connection.ml ++++ b/tools/ocaml/xenstored/connection.ml +@@ -196,11 +196,36 @@ let list_watches con = + con.watches [] in + List.concat ll + +-let fire_single_watch watch = ++let dbg fmt = Logging.debug "connection" fmt ++let info fmt = Logging.info "connection" fmt ++ ++let lookup_watch_perm path = function ++| None -> [] ++| Some root -> ++ try Store.Path.apply root path @@ fun parent name -> ++ Store.Node.get_perms parent :: ++ try [Store.Node.get_perms (Store.Node.find parent name)] ++ with Not_found -> [] ++ with Define.Invalid_path | Not_found -> [] ++ ++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 data = Utils.join_by_null [watch.path; watch.token; ""] in + send_reply watch.con Transaction.none 0 Xenbus.Xb.Op.Watchevent data + +-let fire_watch watch path = ++let fire_single_watch (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 List.exists (Perms.has watch.con.perm READ) perms then ++ fire_single_watch_unchecked 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 new_path = + if watch.is_relative && path.[0] = '/' + then begin +@@ -210,7 +235,7 @@ let fire_watch watch path = + end else + path + in +- fire_single_watch { watch with path = new_path } ++ fire_single_watch roots { watch with path = new_path } + + (* Search for a valid unused transaction id. *) + let rec valid_transaction_id con proposed_id = +diff --git a/tools/ocaml/xenstored/connections.ml b/tools/ocaml/xenstored/connections.ml +index f2c4318c88..9f9f7ee2f0 100644 +--- a/tools/ocaml/xenstored/connections.ml ++++ b/tools/ocaml/xenstored/connections.ml +@@ -135,25 +135,26 @@ let del_watch cons con path token = + watch + + (* path is absolute *) +-let fire_watches cons path recurse = ++let fire_watches ?oldroot 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 w path) watches ++ | Some watches -> List.iter (fun w -> Connection.fire_watch roots w path) watches + in + let fire_rec _x = function + | None -> () + | Some watches -> +- List.iter (fun w -> Connection.fire_single_watch w) watches ++ List.iter (Connection.fire_single_watch roots) watches + in + Trie.iter_path fire_watch cons.watches key; + if recurse then + Trie.iter fire_rec (Trie.sub cons.watches key) + +-let fire_spec_watches cons specpath = ++let fire_spec_watches root cons specpath = + iter cons (fun con -> +- List.iter (fun w -> Connection.fire_single_watch w) (Connection.get_watches con specpath)) ++ List.iter (Connection.fire_single_watch (None, root)) (Connection.get_watches con specpath)) + + let set_target cons domain target_domain = + let con = find_domain cons domain in +diff --git a/tools/ocaml/xenstored/logging.ml b/tools/ocaml/xenstored/logging.ml +index c5cba79e92..1ede131329 100644 +--- a/tools/ocaml/xenstored/logging.ml ++++ b/tools/ocaml/xenstored/logging.ml +@@ -161,6 +161,8 @@ let xenstored_log_nb_lines = ref 13215 + let xenstored_log_nb_chars = ref (-1) + let xenstored_logger = ref (None: logger option) + ++let debug_enabled () = !xenstored_log_level = Debug ++ + let set_xenstored_log_destination s = + xenstored_log_destination := log_destination_of_string s + +@@ -204,6 +206,7 @@ type access_type = + | Commit + | Newconn + | Endconn ++ | Watch_not_fired + | XbOp of Xenbus.Xb.Op.operation + + let string_of_tid ~con tid = +@@ -217,6 +220,7 @@ let string_of_access_type = function + | Commit -> "commit " + | Newconn -> "newconn " + | Endconn -> "endconn " ++ | Watch_not_fired -> "w notfired" + + | XbOp op -> match op with + | Xenbus.Xb.Op.Debug -> "debug " +@@ -331,3 +335,7 @@ let xb_answer ~tid ~con ~ty data = + | _ -> false, Debug + in + if print then access_logging ~tid ~con ~data (XbOp ty) ~level ++ ++let watch_not_fired ~con perms path = ++ let data = Printf.sprintf "EPERM perms=[%s] path=%s" perms path in ++ access_logging ~tid:0 ~con ~data Watch_not_fired ~level:Info +diff --git a/tools/ocaml/xenstored/perms.ml b/tools/ocaml/xenstored/perms.ml +index 3ea193ea14..23b80aba3d 100644 +--- a/tools/ocaml/xenstored/perms.ml ++++ b/tools/ocaml/xenstored/perms.ml +@@ -79,9 +79,9 @@ let of_string s = + let string_of_perm perm = + Printf.sprintf "%c%u" (char_of_permty (snd perm)) (fst perm) + +-let to_string permvec = ++let to_string ?(sep="\000") permvec = + let l = ((permvec.owner, permvec.other) :: permvec.acl) in +- String.concat "\000" (List.map string_of_perm l) ++ String.concat sep (List.map string_of_perm l) + + end + +@@ -132,8 +132,8 @@ let check_owner (connection:Connection.t) (node:Node.t) = + then Connection.is_owner connection (Node.get_owner node) + else true + +-(* check if the current connection has the requested perm on the current node *) +-let check (connection:Connection.t) request (node:Node.t) = ++(* check if the current connection lacks the requested perm on the current node *) ++let lacks (connection:Connection.t) request (node:Node.t) = + let check_acl domainid = + let perm = + if List.mem_assoc domainid (Node.get_acl node) +@@ -154,11 +154,19 @@ let check (connection:Connection.t) request (node:Node.t) = + info "Permission denied: Domain %d has write only access" domainid; + false + in +- if !activate ++ !activate + && not (Connection.is_dom0 connection) + && not (check_owner connection node) + && not (List.exists check_acl (Connection.get_owners connection)) ++ ++(* check if the current connection has the requested perm on the current node. ++* Raises an exception if it doesn't. *) ++let check connection request node = ++ if lacks connection request node + then raise Define.Permission_denied + ++(* check if the current connection has the requested perm on the current node *) ++let has connection request node = not (lacks connection request node) ++ + let equiv perm1 perm2 = + (Node.to_string perm1) = (Node.to_string perm2) +diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml +index e528d1ecb2..f99b9e935c 100644 +--- a/tools/ocaml/xenstored/process.ml ++++ b/tools/ocaml/xenstored/process.ml +@@ -56,15 +56,17 @@ let split_one_path data con = + | path :: "" :: [] -> Store.Path.create path (Connection.get_path con) + | _ -> raise Invalid_Cmd_Args + +-let process_watch ops cons = ++let process_watch 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 + let do_op_watch op cons = +- let recurse = match (fst op) with +- | Xenbus.Xb.Op.Write -> false +- | Xenbus.Xb.Op.Mkdir -> false +- | Xenbus.Xb.Op.Rm -> true +- | Xenbus.Xb.Op.Setperms -> false ++ let recurse, oldroot, root = match (fst op) with ++ | Xenbus.Xb.Op.Write|Xenbus.Xb.Op.Mkdir -> false, None, newroot ++ | Xenbus.Xb.Op.Rm -> true, None, oldroot ++ | Xenbus.Xb.Op.Setperms -> false, Some oldroot, newroot + | _ -> raise (Failure "huh ?") in +- Connections.fire_watches cons (snd op) recurse in ++ Connections.fire_watches ?oldroot root cons (snd op) recurse in + List.iter (fun op -> do_op_watch op cons) ops + + let create_implicit_path t perm path = +@@ -205,7 +207,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 (Transaction.get_paths t) cons ++ process_watch t cons + ) + + let reply_data fct con t doms cons data = +@@ -353,14 +355,17 @@ let transaction_replay c t doms cons = + ignore @@ Connection.end_transaction c tid None + ) + +-let do_watch con _t _domains cons data = ++let do_watch con t _domains cons data = + let (node, token) = + match (split None '\000' data) with + | [node; token; ""] -> node, token + | _ -> raise Invalid_Cmd_Args + in + let watch = Connections.add_watch cons con node token in +- Packet.Ack (fun () -> Connection.fire_single_watch watch) ++ 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) + + let do_unwatch con _t _domains cons data = + let (node, token) = +@@ -391,7 +396,7 @@ let do_transaction_end con t domains cons data = + if not success then + raise Transaction_again; + if commit then begin +- process_watch (List.rev (Transaction.get_paths t)) cons; ++ process_watch t cons; + match t.Transaction.ty with + | Transaction.No -> + () (* no need to record anything *) +@@ -399,7 +404,7 @@ let do_transaction_end con t domains cons data = + record_commit ~con ~tid:id ~before:oldstore ~after:cstore + end + +-let do_introduce con _t domains cons data = ++let do_introduce con t domains cons data = + if not (Connection.is_dom0 con) + then raise Define.Permission_denied; + let (domid, mfn, port) = +@@ -420,14 +425,14 @@ let do_introduce con _t domains cons data = + else try + let ndom = Domains.create domains domid mfn port in + Connections.add_domain cons ndom; +- Connections.fire_spec_watches cons Store.Path.introduce_domain; ++ Connections.fire_spec_watches (Transaction.get_root t) cons Store.Path.introduce_domain; + ndom + with _ -> raise Invalid_Cmd_Args + in + if (Domain.get_remote_port dom) <> port || (Domain.get_mfn dom) <> mfn then + raise Domain_not_match + +-let do_release con _t domains cons data = ++let do_release con t domains cons data = + if not (Connection.is_dom0 con) + then raise Define.Permission_denied; + let domid = +@@ -439,7 +444,7 @@ let do_release con _t domains cons data = + Domains.del domains domid; + Connections.del_domain cons domid; + if fire_spec_watches +- then Connections.fire_spec_watches cons Store.Path.release_domain ++ then Connections.fire_spec_watches (Transaction.get_root t) cons Store.Path.release_domain + else raise Invalid_Cmd_Args + + let do_resume con _t domains _cons data = +@@ -507,6 +512,8 @@ let maybe_ignore_transaction = function + Transaction.none + | _ -> fun x -> x + ++ ++let () = Printexc.record_backtrace true + (** + * Nothrow guarantee. + *) +@@ -548,7 +555,8 @@ let process_packet ~store ~cons ~doms ~con ~req = + (* Put the response on the wire *) + send_response ty con t rid response + with exn -> +- error "process packet: %s" (Printexc.to_string exn); ++ let bt = Printexc.get_backtrace () in ++ error "process packet: %s. %s" (Printexc.to_string exn) bt; + Connection.send_error con tid rid "EIO" + + let do_input store cons doms con = +diff --git a/tools/ocaml/xenstored/transaction.ml b/tools/ocaml/xenstored/transaction.ml +index 963734a653..25bc8c3b4a 100644 +--- a/tools/ocaml/xenstored/transaction.ml ++++ b/tools/ocaml/xenstored/transaction.ml +@@ -82,6 +82,7 @@ type t = { + start_count: int64; + store: Store.t; (* This is the store that we change in write operations. *) + quota: Quota.t; ++ oldroot: Store.Node.t; + mutable paths: (Xenbus.Xb.Op.operation * Store.Path.t) list; + mutable operations: (Packet.request * Packet.response) list; + mutable read_lowpath: Store.Path.t option; +@@ -123,6 +124,7 @@ let make ?(internal=false) id store = + start_count = !counter; + store = if id = none then store else Store.copy store; + quota = Quota.copy store.Store.quota; ++ oldroot = Store.get_root store; + paths = []; + operations = []; + read_lowpath = None; +@@ -137,6 +139,8 @@ let make ?(internal=false) id store = + let get_store t = t.store + let get_paths t = t.paths + ++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 = +diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml +index 8d0c50bfa4..f7b88065bb 100644 +--- a/tools/ocaml/xenstored/xenstored.ml ++++ b/tools/ocaml/xenstored/xenstored.ml +@@ -337,7 +337,9 @@ let _ = + let (notify, deaddom) = Domains.cleanup domains in + List.iter (Connections.del_domain cons) deaddom; + if deaddom <> [] || notify then +- Connections.fire_spec_watches cons Store.Path.release_domain ++ Connections.fire_spec_watches ++ (Store.get_root store) ++ cons Store.Path.release_domain + ) + else + let c = Connections.find_domain_by_port cons port in diff --git a/xsa115-o-0006-tools-ocaml-xenstored-add-xenstored.conf-flag-to-tur.patch b/xsa115-o-0006-tools-ocaml-xenstored-add-xenstored.conf-flag-to-tur.patch new file mode 100644 index 0000000..d1fa8b2 --- /dev/null +++ b/xsa115-o-0006-tools-ocaml-xenstored-add-xenstored.conf-flag-to-tur.patch @@ -0,0 +1,84 @@ +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Subject: tools/ocaml/xenstored: add xenstored.conf flag to turn off watch + permission checks +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +There are flags to turn off quotas and the permission system, so add one +that turns off the newly introduced watch permission checks as well. + +This is part of XSA-115. + +Signed-off-by: Edwin Török +Acked-by: Christian Lindig +Reviewed-by: Andrew Cooper + +diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/connection.ml +index 644a448f2e..fa0d3c4d92 100644 +--- a/tools/ocaml/xenstored/connection.ml ++++ b/tools/ocaml/xenstored/connection.ml +@@ -218,7 +218,7 @@ let fire_single_watch_unchecked watch = + let fire_single_watch (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 List.exists (Perms.has watch.con.perm READ) perms then ++ if Perms.can_fire_watch watch.con.perm perms then + fire_single_watch_unchecked watch + else + let perms = perms |> List.map (Perms.Node.to_string ~sep:" ") |> String.concat ", " in +diff --git a/tools/ocaml/xenstored/oxenstored.conf.in b/tools/ocaml/xenstored/oxenstored.conf.in +index 151b65b72d..f843482981 100644 +--- a/tools/ocaml/xenstored/oxenstored.conf.in ++++ b/tools/ocaml/xenstored/oxenstored.conf.in +@@ -44,6 +44,16 @@ conflict-rate-limit-is-aggregate = true + # Activate node permission system + perms-activate = true + ++# Activate the watch permission system ++# When this is enabled unprivileged guests can only get watch events ++# for xenstore entries that they would've been able to read. ++# ++# When this is disabled unprivileged guests may get watch events ++# for xenstore entries that they cannot read. The watch event contains ++# only the entry name, not the value. ++# This restores behaviour prior to XSA-115. ++perms-watch-activate = true ++ + # Activate quota + quota-activate = true + quota-maxentity = 1000 +diff --git a/tools/ocaml/xenstored/perms.ml b/tools/ocaml/xenstored/perms.ml +index 23b80aba3d..ee7fee6bda 100644 +--- a/tools/ocaml/xenstored/perms.ml ++++ b/tools/ocaml/xenstored/perms.ml +@@ -20,6 +20,7 @@ let info fmt = Logging.info "perms" fmt + open Stdext + + let activate = ref true ++let watch_activate = ref true + + type permty = READ | WRITE | RDWR | NONE + +@@ -168,5 +169,9 @@ let check connection request node = + (* check if the current connection has the requested perm on the current node *) + let has connection request node = not (lacks connection request node) + ++let can_fire_watch connection perms = ++ not !watch_activate ++ || List.exists (has connection READ) perms ++ + let equiv perm1 perm2 = + (Node.to_string perm1) = (Node.to_string perm2) +diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml +index f7b88065bb..0d355bbcb8 100644 +--- a/tools/ocaml/xenstored/xenstored.ml ++++ b/tools/ocaml/xenstored/xenstored.ml +@@ -95,6 +95,7 @@ let parse_config filename = + ("conflict-max-history-seconds", Config.Set_float Define.conflict_max_history_seconds); + ("conflict-rate-limit-is-aggregate", Config.Set_bool Define.conflict_rate_limit_is_aggregate); + ("perms-activate", Config.Set_bool Perms.activate); ++ ("perms-watch-activate", Config.Set_bool Perms.watch_activate); + ("quota-activate", Config.Set_bool Quota.activate); + ("quota-maxwatch", Config.Set_int Define.maxwatch); + ("quota-transaction", Config.Set_int Define.maxtransaction); diff --git a/xsa322-4.14-c.patch b/xsa322-4.14-c.patch new file mode 100644 index 0000000..5059f24 --- /dev/null +++ b/xsa322-4.14-c.patch @@ -0,0 +1,532 @@ +From: Juergen Gross +Subject: tools/xenstore: revoke access rights for removed domains + +Access rights of Xenstore nodes are per domid. Unfortunately existing +granted access rights are not removed when a domain is being destroyed. +This means that a new domain created with the same domid will inherit +the access rights to Xenstore nodes from the previous domain(s) with +the same domid. + +This can be avoided by adding a generation counter to each domain. +The generation counter of the domain is set to the global generation +counter when a domain structure is being allocated. When reading or +writing a node all permissions of domains which are younger than the +node itself are dropped. This is done by flagging the related entry +as invalid in order to avoid modifying permissions in a way the user +could detect. + +A special case has to be considered: for a new domain the first +Xenstore entries are already written before the domain is officially +introduced in Xenstore. In order not to drop the permissions for the +new domain a domain struct is allocated even before introduction if +the hypervisor is aware of the domain. This requires adding another +bool "introduced" to struct domain in xenstored. In order to avoid +additional padding holes convert the shutdown flag to bool, too. + +As verifying permissions has its price regarding runtime add a new +quota for limiting the number of permissions an unprivileged domain +can set for a node. The default for that new quota is 5. + +This is part of XSA-322. + +Signed-off-by: Juergen Gross +Reviewed-by: Paul Durrant +Acked-by: Julien Grall + +diff --git a/tools/xenstore/include/xenstore_lib.h b/tools/xenstore/include/xenstore_lib.h +index 0ffbae9eb5..4c9b6d1685 100644 +--- a/tools/xenstore/include/xenstore_lib.h ++++ b/tools/xenstore/include/xenstore_lib.h +@@ -34,6 +34,7 @@ enum xs_perm_type { + /* Internal use. */ + XS_PERM_ENOENT_OK = 4, + XS_PERM_OWNER = 8, ++ XS_PERM_IGNORE = 16, + }; + + struct xs_permissions +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 92bfd54cff..505560a5de 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -104,6 +104,7 @@ int quota_nb_entry_per_domain = 1000; + 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; + + void trace(const char *fmt, ...) + { +@@ -409,8 +410,13 @@ 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)) { ++ talloc_free(node); ++ return NULL; ++ } ++ + /* Data is binary blob (usually ascii, no nul). */ +- node->data = node->perms.p + node->perms.num; ++ node->data = node->perms.p + hdr->num_perms; + /* Children is strings, nul separated. */ + node->children = node->data + node->datalen; + +@@ -426,6 +432,9 @@ 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)) ++ return errno; ++ + data.dsize = sizeof(*hdr) + + node->perms.num * sizeof(node->perms.p[0]) + + node->datalen + node->childlen; +@@ -485,8 +494,9 @@ enum xs_perm_type perm_for_conn(struct connection *conn, + return (XS_PERM_READ|XS_PERM_WRITE|XS_PERM_OWNER) & mask; + + for (i = 1; i < perms->num; i++) +- if (perms->p[i].id == conn->id +- || (conn->target && perms->p[i].id == conn->target->id)) ++ if (!(perms->p[i].perms & XS_PERM_IGNORE) && ++ (perms->p[i].id == conn->id || ++ (conn->target && perms->p[i].id == conn->target->id))) + return perms->p[i].perms & mask; + + return perms->p[0].perms & mask; +@@ -1248,8 +1258,12 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) + if (perms.num < 2) + return EINVAL; + +- permstr = in->buffer + strlen(in->buffer) + 1; + perms.num--; ++ if (domain_is_unprivileged(conn) && ++ perms.num > quota_nb_perms_per_node) ++ return ENOSPC; ++ ++ permstr = in->buffer + strlen(in->buffer) + 1; + + perms.p = talloc_array(in, struct xs_permissions, perms.num); + if (!perms.p) +@@ -1904,6 +1918,7 @@ static void usage(void) + " -S, --entry-size limit the size of entry per domain, and\n" + " -W, --watch-nb limit the number of watches per domain,\n" + " -t, --transaction limit the number of transaction allowed per domain,\n" ++" -A, --perm-nb limit the number of permissions per node,\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" +@@ -1924,6 +1939,7 @@ static struct option options[] = { + { "entry-size", 1, NULL, 'S' }, + { "trace-file", 1, NULL, 'T' }, + { "transaction", 1, NULL, 't' }, ++ { "perm-nb", 1, NULL, 'A' }, + { "no-recovery", 0, NULL, 'R' }, + { "internal-db", 0, NULL, 'I' }, + { "verbose", 0, NULL, 'V' }, +@@ -1946,7 +1962,7 @@ int main(int argc, char *argv[]) + int timeout; + + +- while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:T:RVW:", options, ++ while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:T:RVW:", options, + NULL)) != -1) { + switch (opt) { + case 'D': +@@ -1988,6 +2004,9 @@ int main(int argc, char *argv[]) + case 'W': + quota_nb_watch_per_domain = strtol(optarg, NULL, 10); + break; ++ case 'A': ++ quota_nb_perms_per_node = strtol(optarg, NULL, 10); ++ break; + case 'e': + dom0_event = strtol(optarg, NULL, 10); + break; +diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c +index 9fad470f83..dc635e9be3 100644 +--- a/tools/xenstore/xenstored_domain.c ++++ b/tools/xenstore/xenstored_domain.c +@@ -67,8 +67,14 @@ struct domain + /* The connection associated with this. */ + struct connection *conn; + ++ /* Generation count at domain introduction time. */ ++ uint64_t generation; ++ + /* Have we noticed that this domain is shutdown? */ +- int shutdown; ++ bool shutdown; ++ ++ /* Has domain been officially introduced? */ ++ bool introduced; + + /* number of entry from this domain in the store */ + int nbentry; +@@ -188,6 +194,9 @@ static int destroy_domain(void *_domain) + + list_del(&domain->list); + ++ if (!domain->introduced) ++ return 0; ++ + if (domain->port) { + if (xenevtchn_unbind(xce_handle, domain->port) == -1) + eprintf("> Unbinding port %i failed!\n", domain->port); +@@ -209,21 +218,34 @@ static int destroy_domain(void *_domain) + return 0; + } + ++static bool get_domain_info(unsigned int domid, xc_dominfo_t *dominfo) ++{ ++ return xc_domain_getinfo(*xc_handle, domid, 1, dominfo) == 1 && ++ dominfo->domid == domid; ++} ++ + static void domain_cleanup(void) + { + xc_dominfo_t dominfo; + struct domain *domain; + struct connection *conn; + int notify = 0; ++ bool dom_valid; + + again: + list_for_each_entry(domain, &domains, list) { +- if (xc_domain_getinfo(*xc_handle, domain->domid, 1, +- &dominfo) == 1 && +- dominfo.domid == domain->domid) { ++ dom_valid = get_domain_info(domain->domid, &dominfo); ++ if (!domain->introduced) { ++ if (!dom_valid) { ++ talloc_free(domain); ++ goto again; ++ } ++ continue; ++ } ++ if (dom_valid) { + if ((dominfo.crashed || dominfo.shutdown) + && !domain->shutdown) { +- domain->shutdown = 1; ++ domain->shutdown = true; + notify = 1; + } + if (!dominfo.dying) +@@ -289,58 +311,84 @@ static char *talloc_domain_path(void *context, unsigned int domid) + return talloc_asprintf(context, "/local/domain/%u", domid); + } + +-static struct domain *new_domain(void *context, unsigned int domid, +- int port) ++static struct domain *find_domain_struct(unsigned int domid) ++{ ++ struct domain *i; ++ ++ list_for_each_entry(i, &domains, list) { ++ if (i->domid == domid) ++ return i; ++ } ++ return NULL; ++} ++ ++static struct domain *alloc_domain(void *context, unsigned int domid) + { + struct domain *domain; +- int rc; + + domain = talloc(context, struct domain); +- if (!domain) ++ if (!domain) { ++ errno = ENOMEM; + return NULL; ++ } + +- domain->port = 0; +- domain->shutdown = 0; + domain->domid = domid; +- domain->path = talloc_domain_path(domain, domid); +- if (!domain->path) +- return NULL; ++ domain->generation = generation; ++ domain->introduced = false; + +- wrl_domain_new(domain); ++ talloc_set_destructor(domain, destroy_domain); + + list_add(&domain->list, &domains); +- talloc_set_destructor(domain, destroy_domain); ++ ++ return domain; ++} ++ ++static int new_domain(struct domain *domain, int port) ++{ ++ int rc; ++ ++ domain->port = 0; ++ domain->shutdown = false; ++ domain->path = talloc_domain_path(domain, domain->domid); ++ if (!domain->path) { ++ errno = ENOMEM; ++ return errno; ++ } ++ ++ wrl_domain_new(domain); + + /* Tell kernel we're interested in this event. */ +- rc = xenevtchn_bind_interdomain(xce_handle, domid, port); ++ rc = xenevtchn_bind_interdomain(xce_handle, domain->domid, port); + if (rc == -1) +- return NULL; ++ return errno; + domain->port = rc; + ++ domain->introduced = true; ++ + domain->conn = new_connection(writechn, readchn); +- if (!domain->conn) +- return NULL; ++ if (!domain->conn) { ++ errno = ENOMEM; ++ return errno; ++ } + + domain->conn->domain = domain; +- domain->conn->id = domid; ++ domain->conn->id = domain->domid; + + domain->remote_port = port; + domain->nbentry = 0; + domain->nbwatch = 0; + +- return domain; ++ return 0; + } + + + static struct domain *find_domain_by_domid(unsigned int domid) + { +- struct domain *i; ++ struct domain *d; + +- list_for_each_entry(i, &domains, list) { +- if (i->domid == domid) +- return i; +- } +- return NULL; ++ d = find_domain_struct(domid); ++ ++ return (d && d->introduced) ? d : NULL; + } + + static void domain_conn_reset(struct domain *domain) +@@ -386,15 +434,21 @@ int do_introduce(struct connection *conn, struct buffered_data *in) + if (port <= 0) + return EINVAL; + +- domain = find_domain_by_domid(domid); ++ domain = find_domain_struct(domid); + + if (domain == NULL) { ++ /* Hang domain off "in" until we're finished. */ ++ domain = alloc_domain(in, domid); ++ if (domain == NULL) ++ return ENOMEM; ++ } ++ ++ if (!domain->introduced) { + interface = map_interface(domid); + if (!interface) + return errno; + /* Hang domain off "in" until we're finished. */ +- domain = new_domain(in, domid, port); +- if (!domain) { ++ if (new_domain(domain, port)) { + rc = errno; + unmap_interface(interface); + return rc; +@@ -503,8 +557,8 @@ int do_resume(struct connection *conn, struct buffered_data *in) + if (IS_ERR(domain)) + return -PTR_ERR(domain); + +- domain->shutdown = 0; +- ++ domain->shutdown = false; ++ + send_ack(conn, XS_RESUME); + + return 0; +@@ -647,8 +701,10 @@ static int dom0_init(void) + if (port == -1) + return -1; + +- dom0 = new_domain(NULL, xenbus_master_domid(), port); +- if (dom0 == NULL) ++ dom0 = alloc_domain(NULL, xenbus_master_domid()); ++ if (!dom0) ++ return -1; ++ if (new_domain(dom0, port)) + return -1; + + dom0->interface = xenbus_map(); +@@ -729,6 +785,66 @@ void domain_entry_inc(struct connection *conn, struct node *node) + } + } + ++/* ++ * Check whether a domain was created before or after a specific generation ++ * 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 ++ */ ++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; ++ ++ d = alloc_domain(NULL, domid); ++ return d ? 1 : -1; ++} ++ ++/* ++ * 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) ++{ ++ unsigned int i; ++ 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) ++ 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; ++ 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; ++ } ++ ++ return 0; ++} ++ + void domain_entry_dec(struct connection *conn, struct node *node) + { + struct domain *d; +diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h +index 259183962a..5e00087206 100644 +--- a/tools/xenstore/xenstored_domain.h ++++ b/tools/xenstore/xenstored_domain.h +@@ -56,6 +56,9 @@ bool domain_can_write(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); ++ + /* Quota manipulation */ + void domain_entry_inc(struct connection *conn, struct node *); + void domain_entry_dec(struct connection *conn, struct node *); +diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c +index a7d8c5d475..2881f3b2e4 100644 +--- a/tools/xenstore/xenstored_transaction.c ++++ b/tools/xenstore/xenstored_transaction.c +@@ -47,7 +47,12 @@ + * transaction. + * Each time the global generation count is copied to either a node or a + * transaction it is incremented. This ensures all nodes and/or transactions +- * are having a unique generation count. ++ * are having a unique generation count. The increment is done _before_ the ++ * copy as that is needed for checking whether a domain was created before ++ * or after a node has been written (the domain's generation is set with the ++ * actual generation count without incrementing it, in order to support ++ * writing a node for a domain before the domain has been officially ++ * introduced). + * + * Transaction conflicts are detected by checking the generation count of all + * nodes read in the transaction to match with the generation count in the +@@ -161,7 +166,7 @@ struct transaction + }; + + extern int quota_max_transaction; +-static uint64_t generation; ++uint64_t generation; + + static void set_tdb_key(const char *name, TDB_DATA *key) + { +@@ -237,7 +242,7 @@ int access_node(struct connection *conn, struct node *node, + bool introduce = false; + + if (type != NODE_ACCESS_READ) { +- node->generation = generation++; ++ node->generation = ++generation; + if (conn && !conn->transaction) + wrl_apply_debit_direct(conn); + } +@@ -374,7 +379,7 @@ static int finalize_transaction(struct connection *conn, + if (!data.dptr) + goto err; + hdr = (void *)data.dptr; +- hdr->generation = generation++; ++ hdr->generation = ++generation; + ret = tdb_store(tdb_ctx, key, data, + TDB_REPLACE); + talloc_free(data.dptr); +@@ -462,7 +467,7 @@ int do_transaction_start(struct connection *conn, struct buffered_data *in) + INIT_LIST_HEAD(&trans->accessed); + INIT_LIST_HEAD(&trans->changed_domains); + trans->fail = false; +- trans->generation = generation++; ++ trans->generation = ++generation; + + /* Pick an unused transaction identifier. */ + do { +diff --git a/tools/xenstore/xenstored_transaction.h b/tools/xenstore/xenstored_transaction.h +index 3386bac565..43a162bea3 100644 +--- a/tools/xenstore/xenstored_transaction.h ++++ b/tools/xenstore/xenstored_transaction.h +@@ -27,6 +27,8 @@ enum node_access_type { + + 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); + +diff --git a/tools/xenstore/xs_lib.c b/tools/xenstore/xs_lib.c +index 3e43f8809d..d407d5713a 100644 +--- a/tools/xenstore/xs_lib.c ++++ b/tools/xenstore/xs_lib.c +@@ -152,7 +152,7 @@ bool xs_strings_to_perms(struct xs_permissions *perms, unsigned int num, + bool xs_perm_to_string(const struct xs_permissions *perm, + char *buffer, size_t buf_len) + { +- switch ((int)perm->perms) { ++ switch ((int)perm->perms & ~XS_PERM_IGNORE) { + case XS_PERM_WRITE: + *buffer = 'w'; + break; diff --git a/xsa322-o.patch b/xsa322-o.patch new file mode 100644 index 0000000..75f7c20 --- /dev/null +++ b/xsa322-o.patch @@ -0,0 +1,110 @@ +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Subject: tools/ocaml/xenstored: clean up permissions for dead domains +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +domain ids are prone to wrapping (15-bits), and with sufficient number +of VMs in a reboot loop it is possible to trigger it. Xenstore entries +may linger after a domain dies, until a toolstack cleans it up. During +this time there is a window where a wrapped domid could access these +xenstore keys (that belonged to another VM). + +To prevent this do a cleanup when a domain dies: + * walk the entire xenstore tree and update permissions for all nodes + * if the dead domain had an ACL entry: remove it + * if the dead domain was the owner: change the owner to Dom0 + +This is done without quota checks or a transaction. Quota checks would +be a no-op (either the domain is dead, or it is Dom0 where they are not +enforced). Transactions are not needed, because this is all done +atomically by oxenstored's single thread. + +The xenstore entries owned by the dead domain are not deleted, because +that could confuse a toolstack / backends that are still bound to it +(or generate unexpected watch events). It is the responsibility of a +toolstack to remove the xenstore entries themselves. + +This is part of XSA-322. + +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 ee7fee6bda..e8a16221f8 100644 +--- a/tools/ocaml/xenstored/perms.ml ++++ b/tools/ocaml/xenstored/perms.ml +@@ -58,6 +58,15 @@ let get_other perms = perms.other + let get_acl perms = perms.acl + let get_owner perm = perm.owner + ++(** [remote_domid ~domid perm] removes all ACLs for [domid] from perm. ++* If [domid] was the owner then it is changed to Dom0. ++* This is used for cleaning up after dead domains. ++* *) ++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 } ++ + let default0 = create 0 NONE [] + + let perm_of_string s = +diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml +index f99b9e935c..73e04cc18b 100644 +--- a/tools/ocaml/xenstored/process.ml ++++ b/tools/ocaml/xenstored/process.ml +@@ -443,6 +443,7 @@ let do_release con t domains cons data = + let fire_spec_watches = Domains.exist domains domid in + Domains.del domains domid; + Connections.del_domain cons domid; ++ Store.reset_permissions (Transaction.get_store t) domid; + if fire_spec_watches + then Connections.fire_spec_watches (Transaction.get_root t) cons Store.Path.release_domain + else raise Invalid_Cmd_Args +diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml +index 6b6e440e98..3b05128f1b 100644 +--- a/tools/ocaml/xenstored/store.ml ++++ b/tools/ocaml/xenstored/store.ml +@@ -89,6 +89,13 @@ let check_owner node connection = + + let rec recurse fct node = fct node; List.iter (recurse fct) node.children + ++(** [recurse_map f tree] applies [f] on each node in the tree recursively *) ++let recurse_map f = ++ let rec walk node = ++ f { node with children = List.rev_map walk node.children |> List.rev } ++ in ++ walk ++ + let unpack node = (Symbol.to_string node.name, node.perms, node.value) + + end +@@ -405,6 +412,15 @@ let setperms store perm path nperms = + Quota.del_entry store.quota old_owner; + Quota.add_entry store.quota new_owner + ++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 ++ + type ops = { + store: t; + write: Path.t -> string -> unit; +diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml +index 0d355bbcb8..ff9fbbbac2 100644 +--- a/tools/ocaml/xenstored/xenstored.ml ++++ b/tools/ocaml/xenstored/xenstored.ml +@@ -336,6 +336,7 @@ let _ = + finally (fun () -> + if Some port = eventchn.Event.virq_port then ( + let (notify, deaddom) = Domains.cleanup domains in ++ List.iter (Store.reset_permissions store) deaddom; + List.iter (Connections.del_domain cons) deaddom; + if deaddom <> [] || notify then + Connections.fire_spec_watches diff --git a/xsa323.patch b/xsa323.patch new file mode 100644 index 0000000..aadf5c7 --- /dev/null +++ b/xsa323.patch @@ -0,0 +1,140 @@ +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Subject: tools/ocaml/xenstored: Fix path length validation +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Currently, oxenstored checks the length of paths against 1024, then +prepends "/local/domain/$DOMID/" to relative paths. This allows a domU +to create paths which can't subsequently be read by anyone, even dom0. +This also interferes with listing directories, etc. + +Define a new oxenstored.conf entry: quota-path-max, defaulting to 1024 +as before. For paths that begin with "/local/domain/$DOMID/" check the +relative path length against this quota. For all other paths check the +entire path length. + +This ensures that if the domid changes (and thus the length of a prefix +changes) a path that used to be valid stays valid (e.g. after a +live-migration). It also ensures that regardless how the client tries +to access a path (domid-relative or absolute) it will get consistent +results, since the limit is always applied on the final canonicalized +path. + +Delete the unused Domain.get_path to avoid it being confused with +Connection.get_path (which differs by a trailing slash only). + +Rewrite Util.path_validate to apply the appropriate length restriction +based on whether the path is relative or not. Remove the check for +connection_path being absolute, because it is not guest controlled data. + +This is part of XSA-323. + +Signed-off-by: Andrew Cooper +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 d4d1c7bdec..b6e2a716e2 100644 +--- a/tools/ocaml/libs/xb/partial.ml ++++ b/tools/ocaml/libs/xb/partial.ml +@@ -28,6 +28,7 @@ external header_of_string_internal: string -> int * int * int * int + = "stub_header_of_string" + + let xenstore_payload_max = 4096 (* xen/include/public/io/xs_wire.h *) ++let xenstore_rel_path_max = 2048 (* xen/include/public/io/xs_wire.h *) + + let of_string s = + let tid, rid, opint, dlen = header_of_string_internal s in +diff --git a/tools/ocaml/libs/xb/partial.mli b/tools/ocaml/libs/xb/partial.mli +index 359a75e88d..b9216018f5 100644 +--- a/tools/ocaml/libs/xb/partial.mli ++++ b/tools/ocaml/libs/xb/partial.mli +@@ -9,6 +9,7 @@ external header_size : unit -> int = "stub_header_size" + external header_of_string_internal : string -> int * int * int * int + = "stub_header_of_string" + val xenstore_payload_max : int ++val xenstore_rel_path_max : int + val of_string : string -> pkt + val append : pkt -> string -> int -> unit + val to_complete : pkt -> int +diff --git a/tools/ocaml/xenstored/define.ml b/tools/ocaml/xenstored/define.ml +index ea9e1b7620..ebe18b8e31 100644 +--- a/tools/ocaml/xenstored/define.ml ++++ b/tools/ocaml/xenstored/define.ml +@@ -31,6 +31,8 @@ let conflict_rate_limit_is_aggregate = ref true + + let domid_self = 0x7FF0 + ++let path_max = ref Xenbus.Partial.xenstore_rel_path_max ++ + exception Not_a_directory of string + exception Not_a_value of string + exception Already_exist +diff --git a/tools/ocaml/xenstored/domain.ml b/tools/ocaml/xenstored/domain.ml +index aeb185ff7e..81cb59b8f1 100644 +--- a/tools/ocaml/xenstored/domain.ml ++++ b/tools/ocaml/xenstored/domain.ml +@@ -38,7 +38,6 @@ type t = + } + + let is_dom0 d = d.id = 0 +-let get_path dom = "/local/domain/" ^ (sprintf "%u" dom.id) + let get_id domain = domain.id + let get_interface d = d.interface + let get_mfn d = d.mfn +diff --git a/tools/ocaml/xenstored/oxenstored.conf.in b/tools/ocaml/xenstored/oxenstored.conf.in +index f843482981..4ae48e42d4 100644 +--- a/tools/ocaml/xenstored/oxenstored.conf.in ++++ b/tools/ocaml/xenstored/oxenstored.conf.in +@@ -61,6 +61,7 @@ quota-maxsize = 2048 + quota-maxwatch = 100 + quota-transaction = 10 + quota-maxrequests = 1024 ++quota-path-max = 1024 + + # Activate filed base backend + persistent = false +diff --git a/tools/ocaml/xenstored/utils.ml b/tools/ocaml/xenstored/utils.ml +index e8c9fe4e94..eb79bf0146 100644 +--- a/tools/ocaml/xenstored/utils.ml ++++ b/tools/ocaml/xenstored/utils.ml +@@ -93,7 +93,7 @@ let read_file_single_integer filename = + let path_validate path connection_path = + let len = String.length path in + +- if len = 0 || len > 1024 then raise Define.Invalid_path; ++ if len = 0 then raise Define.Invalid_path; + + let abs_path = + match String.get path 0 with +@@ -101,4 +101,17 @@ let path_validate path connection_path = + | _ -> connection_path ^ path + in + ++ (* Regardless whether client specified absolute or relative path, ++ canonicalize it (above) and, for domain-relative paths, check the ++ length of the relative part. ++ ++ This prevents paths becoming invalid across migrate when the length ++ of the domid changes in @param connection_path. ++ *) ++ let len = String.length abs_path in ++ let on_absolute _ _ = len in ++ let on_relative _ offset = len - offset in ++ let len = Scanf.ksscanf abs_path on_absolute "/local/domain/%d/%n" on_relative in ++ if len > !Define.path_max then raise Define.Invalid_path; ++ + abs_path +diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml +index ff9fbbbac2..39d6d767e4 100644 +--- a/tools/ocaml/xenstored/xenstored.ml ++++ b/tools/ocaml/xenstored/xenstored.ml +@@ -102,6 +102,7 @@ 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-path-max", Config.Set_int Define.path_max); + ("test-eagain", Config.Set_bool Transaction.test_eagain); + ("persistent", Config.Set_bool Disk.enable); + ("xenstored-log-file", Config.String Logging.set_xenstored_log_destination); diff --git a/xsa324.patch b/xsa324.patch new file mode 100644 index 0000000..c5e542d --- /dev/null +++ b/xsa324.patch @@ -0,0 +1,48 @@ +From: Juergen Gross +Subject: tools/xenstore: drop watch event messages exceeding maximum size + +By setting a watch with a very large tag it is possible to trick +xenstored to send watch event messages exceeding the maximum allowed +payload size. This might in turn lead to a crash of xenstored as the +resulting error can cause dereferencing a NULL pointer in case there +is no active request being handled by the guest the watch event is +being sent to. + +Fix that by just dropping such watch events. Additionally modify the +error handling to test the pointer to be not NULL before dereferencing +it. + +This is XSA-324. + +Signed-off-by: Juergen Gross +Acked-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 33f95dcf3c..3d74dbbb40 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -674,6 +674,9 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, + /* 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; +diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c +index 71c108ea99..9ff20690c0 100644 +--- a/tools/xenstore/xenstored_watch.c ++++ b/tools/xenstore/xenstored_watch.c +@@ -92,6 +92,10 @@ static void add_event(struct connection *conn, + } + + 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; diff --git a/xsa325-4.14.patch b/xsa325-4.14.patch new file mode 100644 index 0000000..a17f546 --- /dev/null +++ b/xsa325-4.14.patch @@ -0,0 +1,192 @@ +From: Harsha Shamsundara Havanur +Subject: tools/xenstore: Preserve bad client until they are destroyed + +XenStored will kill any connection that it thinks has misbehaved, +this is currently happening in two places: + * In `handle_input()` if the sanity check on the ring and the message + fails. + * In `handle_output()` when failing to write the response in the ring. + +As the domain structure is a child of the connection, XenStored will +destroy its view of the domain when killing the connection. This will +result in sending @releaseDomain event to all the watchers. + +As the watch event doesn't carry which domain has been released, +the watcher (such as XenStored) will generally go through the list of +domains registers and check if one of them is shutting down/dying. +In the case of a client misbehaving, the domain will likely to be +running, so no action will be performed. + +When the domain is effectively destroyed, XenStored will not be aware of +the domain anymore. So the watch event is not going to be sent. +By consequence, the watchers of the event will not release mappings +they may have on the domain. This will result in a zombie domain. + +In order to send @releaseDomain event at the correct time, we want +to keep the domain structure until the domain is effectively +shutting-down/dying. + +We also want to keep the connection around so we could possibly revive +the connection in the future. + +A new flag 'is_ignored' is added to mark whether a connection should be +ignored when checking if there are work to do. Additionally any +transactions, watches, buffers associated to the connection will be +freed as you can't do much with them (restarting the connection will +likely need a reset). + +As a side note, when the device model were running in a stubdomain, a +guest would have been able to introduce a use-after-free because there +is two parents for a guest connection. + +This is XSA-325. + +Reported-by: Pawel Wieczorkiewicz +Signed-off-by: Harsha Shamsundara Havanur +Signed-off-by: Julien Grall +Reviewed-by: Juergen Gross +Reviewed-by: Paul Durrant + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index af3d17004b3f..27d8f15b6b76 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -1355,6 +1355,32 @@ static struct { + [XS_DIRECTORY_PART] = { "DIRECTORY_PART", send_directory_part }, + }; + ++/* ++ * Keep the connection alive but stop processing any new request or sending ++ * reponse. This is to allow sending @releaseDomain watch event at the correct ++ * moment and/or to allow the connection to restart (not yet implemented). ++ * ++ * All watches, transactions, buffers will be freed. ++ */ ++static 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); ++ } ++ ++ talloc_free(conn->in); ++ conn->in = NULL; ++} ++ + static const char *sockmsg_string(enum xsd_sockmsg_type type) + { + if ((unsigned int)type < ARRAY_SIZE(wire_funcs) && wire_funcs[type].str) +@@ -1413,8 +1439,10 @@ static void consider_message(struct connection *conn) + assert(conn->in == NULL); + } + +-/* Errors in reading or allocating here mean we get out of sync, so we +- * drop the whole client connection. */ ++/* ++ * Errors in reading or allocating here means we get out of sync, so we mark ++ * the connection as ignored. ++ */ + static void handle_input(struct connection *conn) + { + int bytes; +@@ -1471,14 +1499,14 @@ static void handle_input(struct connection *conn) + return; + + bad_client: +- /* Kill it. */ +- talloc_free(conn); ++ ignore_connection(conn); + } + + static void handle_output(struct connection *conn) + { ++ /* Ignore the connection if an error occured */ + if (!write_messages(conn)) +- talloc_free(conn); ++ ignore_connection(conn); + } + + struct connection *new_connection(connwritefn_t *write, connreadfn_t *read) +@@ -1494,6 +1522,7 @@ struct connection *new_connection(connwritefn_t *write, connreadfn_t *read) + new->write = write; + new->read = read; + new->can_write = true; ++ new->is_ignored = false; + new->transaction_started = 0; + INIT_LIST_HEAD(&new->out_list); + INIT_LIST_HEAD(&new->watches); +@@ -2186,8 +2215,9 @@ int main(int argc, char *argv[]) + if (fds[conn->pollfd_idx].revents + & ~(POLLIN|POLLOUT)) + talloc_free(conn); +- else if (fds[conn->pollfd_idx].revents +- & POLLIN) ++ else if ((fds[conn->pollfd_idx].revents ++ & POLLIN) && ++ !conn->is_ignored) + handle_input(conn); + } + if (talloc_free(conn) == 0) +@@ -2199,8 +2229,9 @@ int main(int argc, char *argv[]) + if (fds[conn->pollfd_idx].revents + & ~(POLLIN|POLLOUT)) + talloc_free(conn); +- else if (fds[conn->pollfd_idx].revents +- & POLLOUT) ++ else if ((fds[conn->pollfd_idx].revents ++ & POLLOUT) && ++ !conn->is_ignored) + handle_output(conn); + } + if (talloc_free(conn) == 0) +diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h +index eb19b71f5f46..196a6fd2b0be 100644 +--- a/tools/xenstore/xenstored_core.h ++++ b/tools/xenstore/xenstored_core.h +@@ -80,6 +80,9 @@ struct connection + /* Is this a read-only connection? */ + bool can_write; + ++ /* Is this connection ignored? */ ++ bool is_ignored; ++ + /* Buffered incoming data. */ + struct buffered_data *in; + +diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c +index dc635e9be30c..d5e1e3e9d42d 100644 +--- a/tools/xenstore/xenstored_domain.c ++++ b/tools/xenstore/xenstored_domain.c +@@ -286,6 +286,10 @@ bool domain_can_read(struct connection *conn) + + if (domain_is_unprivileged(conn) && conn->domain->wrl_credit < 0) + return false; ++ ++ if (conn->is_ignored) ++ return false; ++ + return (intf->req_cons != intf->req_prod); + } + +@@ -303,6 +307,10 @@ bool domain_is_unprivileged(struct connection *conn) + bool domain_can_write(struct connection *conn) + { + struct xenstore_domain_interface *intf = conn->domain->interface; ++ ++ if (conn->is_ignored) ++ return false; ++ + return ((intf->rsp_prod - intf->rsp_cons) != XENSTORE_RING_SIZE); + } + +-- +2.17.1 + diff --git a/xsa330.patch b/xsa330.patch new file mode 100644 index 0000000..c834516 --- /dev/null +++ b/xsa330.patch @@ -0,0 +1,66 @@ +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Subject: tools/ocaml/xenstored: delete watch from trie too when resetting + watches +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +c/s f8c72b526129 "oxenstored: implement XS_RESET_WATCHES" from Xen 4.6 +introduced reset watches support in oxenstored by mirroring the change +in cxenstored. + +However the OCaml version has some additional data structures to +optimize watch firing, and just resetting the watches in one of the data +structures creates a security bug where a malicious guest kernel can +exceed its watch quota, driving oxenstored into OOM: + * create watches + * reset watches (this still keeps the watches lingering in another data + structure, using memory) + * create some more watches + * loop until oxenstored dies + +The guest kernel doesn't necessarily have to be malicious to trigger +this: + * if control/platform-feature-xs_reset_watches is set + * the guest kexecs (e.g. because it crashes) + * on boot more watches are set up + * this will slowly "leak" memory for watches in oxenstored, driving it + towards OOM. + +This is XSA-330. + +Fixes: f8c72b526129 ("oxenstored: implement XS_RESET_WATCHES") +Signed-off-by: Edwin Török +Acked-by: Christian Lindig +Reviewed-by: Andrew Cooper + +diff --git a/tools/ocaml/xenstored/connections.ml b/tools/ocaml/xenstored/connections.ml +index 9f9f7ee2f0..6ee3552ec2 100644 +--- a/tools/ocaml/xenstored/connections.ml ++++ b/tools/ocaml/xenstored/connections.ml +@@ -134,6 +134,10 @@ 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 key = key_of_path path in +diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml +index 73e04cc18b..437d2dcf9e 100644 +--- a/tools/ocaml/xenstored/process.ml ++++ b/tools/ocaml/xenstored/process.ml +@@ -179,8 +179,8 @@ let do_isintroduced con _t domains _cons data = + if domid = Define.domid_self || Domains.exist domains domid then "T\000" else "F\000" + + (* only in xen >= 4.2 *) +-let do_reset_watches con _t _domains _cons _data = +- Connection.del_watches con; ++let do_reset_watches con _t _domains cons _data = ++ Connections.del_watches cons con; + Connection.del_transactions con + + (* only in >= xen3.3 *) diff --git a/xsa348-1.patch b/xsa348-1.patch new file mode 100644 index 0000000..721c047 --- /dev/null +++ b/xsa348-1.patch @@ -0,0 +1,113 @@ +From: Jan Beulich +Subject: x86: replace reset_stack_and_jump_nolp() + +Move the necessary check into check_for_livepatch_work(), rather than +mostly duplicating reset_stack_and_jump() for this purpose. This is to +prevent an inflation of reset_stack_and_jump() flavors. + +Signed-off-by: Jan Beulich +Reviewed-by: Juergen Gross +--- +Of course instead of adding the check right into +check_for_livepatch_work(), a wrapper could be introduced. + +--- a/xen/arch/x86/domain.c ++++ b/xen/arch/x86/domain.c +@@ -192,7 +192,7 @@ static void noreturn continue_idle_domai + { + /* Idle vcpus might be attached to non-idle units! */ + if ( !is_idle_domain(v->sched_unit->domain) ) +- reset_stack_and_jump_nolp(guest_idle_loop); ++ reset_stack_and_jump(guest_idle_loop); + + reset_stack_and_jump(idle_loop); + } +--- a/xen/arch/x86/hvm/svm/svm.c ++++ b/xen/arch/x86/hvm/svm/svm.c +@@ -1036,7 +1036,7 @@ static void noreturn svm_do_resume(struc + + hvm_do_resume(v); + +- reset_stack_and_jump_nolp(svm_asm_do_resume); ++ reset_stack_and_jump(svm_asm_do_resume); + } + + void svm_vmenter_helper(const struct cpu_user_regs *regs) +--- a/xen/arch/x86/hvm/vmx/vmcs.c ++++ b/xen/arch/x86/hvm/vmx/vmcs.c +@@ -1909,7 +1909,7 @@ void vmx_do_resume(struct vcpu *v) + if ( host_cr4 != read_cr4() ) + __vmwrite(HOST_CR4, read_cr4()); + +- reset_stack_and_jump_nolp(vmx_asm_do_vmentry); ++ reset_stack_and_jump(vmx_asm_do_vmentry); + } + + static inline unsigned long vmr(unsigned long field) +--- a/xen/arch/x86/pv/domain.c ++++ b/xen/arch/x86/pv/domain.c +@@ -113,7 +113,7 @@ static int parse_pcid(const char *s) + static void noreturn continue_nonidle_domain(struct vcpu *v) + { + check_wakeup_from_wait(); +- reset_stack_and_jump_nolp(ret_from_intr); ++ reset_stack_and_jump(ret_from_intr); + } + + static int setup_compat_l4(struct vcpu *v) +--- a/xen/arch/x86/setup.c ++++ b/xen/arch/x86/setup.c +@@ -676,7 +676,7 @@ static void __init noreturn reinit_bsp_s + asm volatile ("setssbsy" ::: "memory"); + } + +- reset_stack_and_jump_nolp(init_done); ++ reset_stack_and_jump(init_done); + } + + /* +--- a/xen/common/livepatch.c ++++ b/xen/common/livepatch.c +@@ -1635,6 +1635,11 @@ void check_for_livepatch_work(void) + s_time_t timeout; + unsigned long flags; + ++ /* Only do any work when invoked in truly idle state. */ ++ if ( system_state != SYS_STATE_active || ++ !is_idle_domain(current->sched_unit->domain) ) ++ return; ++ + /* Fast path: no work to do. */ + if ( !per_cpu(work_to_do, cpu ) ) + return; +--- a/xen/include/asm-x86/current.h ++++ b/xen/include/asm-x86/current.h +@@ -155,13 +155,13 @@ unsigned long get_stack_dump_bottom (uns + # define SHADOW_STACK_WORK "" + #endif + +-#define switch_stack_and_jump(fn, instr) \ ++#define reset_stack_and_jump(fn) \ + ({ \ + unsigned int tmp; \ + __asm__ __volatile__ ( \ + SHADOW_STACK_WORK \ + "mov %[stk], %%rsp;" \ +- instr \ ++ CHECK_FOR_LIVEPATCH_WORK \ + "jmp %c[fun];" \ + : [val] "=&r" (tmp), \ + [ssp] "=&r" (tmp) \ +@@ -176,12 +176,6 @@ unsigned long get_stack_dump_bottom (uns + unreachable(); \ + }) + +-#define reset_stack_and_jump(fn) \ +- switch_stack_and_jump(fn, CHECK_FOR_LIVEPATCH_WORK) +- +-#define reset_stack_and_jump_nolp(fn) \ +- switch_stack_and_jump(fn, "") +- + /* + * Which VCPU's state is currently running on each CPU? + * This is not necesasrily the same as 'current' as a CPU may be diff --git a/xsa348-2.patch b/xsa348-2.patch new file mode 100644 index 0000000..a8106bf --- /dev/null +++ b/xsa348-2.patch @@ -0,0 +1,85 @@ +From: Jan Beulich +Subject: x86: fold guest_idle_loop() into idle_loop() + +The latter can easily be made cover both cases. This is in preparation +of using idle_loop directly for populating idle_csw.tail. + +Take the liberty and also adjust indentation / spacing in involved code. + +Signed-off-by: Jan Beulich +Reviewed-by: Juergen Gross + +--- a/xen/arch/x86/domain.c ++++ b/xen/arch/x86/domain.c +@@ -133,14 +133,22 @@ void play_dead(void) + static void idle_loop(void) + { + unsigned int cpu = smp_processor_id(); ++ /* ++ * Idle vcpus might be attached to non-idle units! We don't do any ++ * standard idle work like tasklets or livepatching in this case. ++ */ ++ bool guest = !is_idle_domain(current->sched_unit->domain); + + for ( ; ; ) + { + if ( cpu_is_offline(cpu) ) ++ { ++ ASSERT(!guest); + play_dead(); ++ } + + /* Are we here for running vcpu context tasklets, or for idling? */ +- if ( unlikely(tasklet_work_to_do(cpu)) ) ++ if ( !guest && unlikely(tasklet_work_to_do(cpu)) ) + { + do_tasklet(); + /* Livepatch work is always kicked off via a tasklet. */ +@@ -151,28 +159,14 @@ static void idle_loop(void) + * and then, after it is done, whether softirqs became pending + * while we were scrubbing. + */ +- else if ( !softirq_pending(cpu) && !scrub_free_pages() && +- !softirq_pending(cpu) ) +- pm_idle(); +- do_softirq(); +- } +-} +- +-/* +- * Idle loop for siblings in active schedule units. +- * We don't do any standard idle work like tasklets or livepatching. +- */ +-static void guest_idle_loop(void) +-{ +- unsigned int cpu = smp_processor_id(); +- +- for ( ; ; ) +- { +- ASSERT(!cpu_is_offline(cpu)); +- +- if ( !softirq_pending(cpu) && !scrub_free_pages() && +- !softirq_pending(cpu)) +- sched_guest_idle(pm_idle, cpu); ++ else if ( !softirq_pending(cpu) && !scrub_free_pages() && ++ !softirq_pending(cpu) ) ++ { ++ if ( guest ) ++ sched_guest_idle(pm_idle, cpu); ++ else ++ pm_idle(); ++ } + do_softirq(); + } + } +@@ -190,10 +184,6 @@ void startup_cpu_idle_loop(void) + + static void noreturn continue_idle_domain(struct vcpu *v) + { +- /* Idle vcpus might be attached to non-idle units! */ +- if ( !is_idle_domain(v->sched_unit->domain) ) +- reset_stack_and_jump(guest_idle_loop); +- + reset_stack_and_jump(idle_loop); + } + diff --git a/xsa348-3.patch b/xsa348-3.patch new file mode 100644 index 0000000..43b8bbc --- /dev/null +++ b/xsa348-3.patch @@ -0,0 +1,174 @@ +From: Jan Beulich +Subject: x86: avoid calling {svm,vmx}_do_resume() + +These functions follow the following path: hvm_do_resume() -> +handle_hvm_io_completion() -> hvm_wait_for_io() -> +wait_on_xen_event_channel() -> do_softirq() -> schedule() -> +sched_context_switch() -> continue_running() and hence may +recursively invoke themselves. If this ends up happening a couple of +times, a stack overflow would result. + +Prevent this by also resetting the stack at the +->arch.ctxt_switch->tail() invocations (in both places for consistency) +and thus jumping to the functions instead of calling them. + +This is XSA-348 / CVE-2020-29566. + +Reported-by: Julien Grall +Signed-off-by: Jan Beulich +Reviewed-by: Juergen Gross +--- +v2: Fix LIVEPATCH builds crashing. + +--- a/xen/arch/x86/domain.c ++++ b/xen/arch/x86/domain.c +@@ -130,7 +130,7 @@ void play_dead(void) + dead_idle(); + } + +-static void idle_loop(void) ++static void noreturn idle_loop(void) + { + unsigned int cpu = smp_processor_id(); + /* +@@ -182,11 +182,6 @@ void startup_cpu_idle_loop(void) + reset_stack_and_jump(idle_loop); + } + +-static void noreturn continue_idle_domain(struct vcpu *v) +-{ +- reset_stack_and_jump(idle_loop); +-} +- + void init_hypercall_page(struct domain *d, void *ptr) + { + memset(ptr, 0xcc, PAGE_SIZE); +@@ -710,7 +705,7 @@ int arch_domain_create(struct domain *d, + static const struct arch_csw idle_csw = { + .from = paravirt_ctxt_switch_from, + .to = paravirt_ctxt_switch_to, +- .tail = continue_idle_domain, ++ .tail = idle_loop, + }; + + d->arch.ctxt_switch = &idle_csw; +@@ -2047,20 +2042,12 @@ void context_switch(struct vcpu *prev, s + /* Ensure that the vcpu has an up-to-date time base. */ + update_vcpu_system_time(next); + +- /* +- * Schedule tail *should* be a terminal function pointer, but leave a +- * bug frame around just in case it returns, to save going back into the +- * context switching code and leaving a far more subtle crash to diagnose. +- */ +- nextd->arch.ctxt_switch->tail(next); +- BUG(); ++ reset_stack_and_jump_ind(nextd->arch.ctxt_switch->tail); + } + + void continue_running(struct vcpu *same) + { +- /* See the comment above. */ +- same->domain->arch.ctxt_switch->tail(same); +- BUG(); ++ reset_stack_and_jump_ind(same->domain->arch.ctxt_switch->tail); + } + + int __sync_local_execstate(void) +--- a/xen/arch/x86/hvm/svm/svm.c ++++ b/xen/arch/x86/hvm/svm/svm.c +@@ -991,8 +991,9 @@ static void svm_ctxt_switch_to(struct vc + wrmsr_tsc_aux(v->arch.msrs->tsc_aux); + } + +-static void noreturn svm_do_resume(struct vcpu *v) ++static void noreturn svm_do_resume(void) + { ++ struct vcpu *v = current; + struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb; + bool debug_state = (v->domain->debugger_attached || + v->domain->arch.monitor.software_breakpoint_enabled || +--- a/xen/arch/x86/hvm/vmx/vmcs.c ++++ b/xen/arch/x86/hvm/vmx/vmcs.c +@@ -1850,8 +1850,9 @@ void vmx_vmentry_failure(void) + domain_crash(curr->domain); + } + +-void vmx_do_resume(struct vcpu *v) ++void vmx_do_resume(void) + { ++ struct vcpu *v = current; + bool_t debug_state; + unsigned long host_cr4; + +--- a/xen/arch/x86/pv/domain.c ++++ b/xen/arch/x86/pv/domain.c +@@ -110,7 +110,7 @@ static int parse_pcid(const char *s) + return rc; + } + +-static void noreturn continue_nonidle_domain(struct vcpu *v) ++static void noreturn continue_nonidle_domain(void) + { + check_wakeup_from_wait(); + reset_stack_and_jump(ret_from_intr); +--- a/xen/include/asm-x86/current.h ++++ b/xen/include/asm-x86/current.h +@@ -155,18 +155,18 @@ unsigned long get_stack_dump_bottom (uns + # define SHADOW_STACK_WORK "" + #endif + +-#define reset_stack_and_jump(fn) \ ++#define switch_stack_and_jump(fn, instr, constr) \ + ({ \ + unsigned int tmp; \ + __asm__ __volatile__ ( \ + SHADOW_STACK_WORK \ + "mov %[stk], %%rsp;" \ + CHECK_FOR_LIVEPATCH_WORK \ +- "jmp %c[fun];" \ ++ instr "[fun]" \ + : [val] "=&r" (tmp), \ + [ssp] "=&r" (tmp) \ + : [stk] "r" (guest_cpu_user_regs()), \ +- [fun] "i" (fn), \ ++ [fun] constr (fn), \ + [skstk_base] "i" \ + ((PRIMARY_SHSTK_SLOT + 1) * PAGE_SIZE - 8), \ + [stack_mask] "i" (STACK_SIZE - 1), \ +@@ -176,6 +176,13 @@ unsigned long get_stack_dump_bottom (uns + unreachable(); \ + }) + ++#define reset_stack_and_jump(fn) \ ++ switch_stack_and_jump(fn, "jmp %c", "i") ++ ++/* The constraint may only specify non-call-clobbered registers. */ ++#define reset_stack_and_jump_ind(fn) \ ++ switch_stack_and_jump(fn, "INDIRECT_JMP %", "b") ++ + /* + * Which VCPU's state is currently running on each CPU? + * This is not necesasrily the same as 'current' as a CPU may be +--- a/xen/include/asm-x86/domain.h ++++ b/xen/include/asm-x86/domain.h +@@ -337,7 +337,7 @@ struct arch_domain + const struct arch_csw { + void (*from)(struct vcpu *); + void (*to)(struct vcpu *); +- void (*tail)(struct vcpu *); ++ void noreturn (*tail)(void); + } *ctxt_switch; + + #ifdef CONFIG_HVM +--- a/xen/include/asm-x86/hvm/vmx/vmx.h ++++ b/xen/include/asm-x86/hvm/vmx/vmx.h +@@ -95,7 +95,7 @@ typedef enum { + void vmx_asm_vmexit_handler(struct cpu_user_regs); + void vmx_asm_do_vmentry(void); + void vmx_intr_assist(void); +-void noreturn vmx_do_resume(struct vcpu *); ++void noreturn vmx_do_resume(void); + void vmx_vlapic_msr_changed(struct vcpu *v); + struct hvm_emulate_ctxt; + void vmx_realmode_emulate_one(struct hvm_emulate_ctxt *hvmemul_ctxt); diff --git a/xsa352.patch b/xsa352.patch new file mode 100644 index 0000000..e21d21a --- /dev/null +++ b/xsa352.patch @@ -0,0 +1,42 @@ +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Subject: tools/ocaml/xenstored: only Dom0 can change node owner +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Otherwise we can give quota away to another domain, either causing it to run +out of quota, or in case of Dom0 use unbounded amounts of memory and bypass +the quota system entirely. + +This was fixed in the C version of xenstored in 2006 (c/s db34d2aaa5f5, +predating the XSA process by 5 years). + +It was also fixed in the mirage version of xenstore in 2012, with a unit test +demonstrating the vulnerability: + + https://github.com/mirage/ocaml-xenstore/commit/6b91f3ac46b885d0530a51d57a9b3a57d64923a7 + https://github.com/mirage/ocaml-xenstore/commit/22ee5417c90b8fda905c38de0d534506152eace6 + +but possibly without realising that the vulnerability still affected the +in-tree oxenstored (added c/s f44af660412 in 2010). + +This is XSA-352. + +Signed-off-by: Edwin Török +Acked-by: Christian Lindig +Reviewed-by: Andrew Cooper + +diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml +index 3b05128f1b..5f915f2bbe 100644 +--- a/tools/ocaml/xenstored/store.ml ++++ b/tools/ocaml/xenstored/store.ml +@@ -407,7 +407,8 @@ let setperms store perm path nperms = + | Some node -> + let old_owner = Node.get_owner node in + let new_owner = Perms.Node.get_owner nperms in +- if not ((old_owner = new_owner) || (Perms.Connection.is_dom0 perm)) then Quota.check store.quota new_owner 0; ++ if not ((old_owner = new_owner) || (Perms.Connection.is_dom0 perm)) then ++ raise Define.Permission_denied; + store.root <- path_setperms store perm path nperms; + Quota.del_entry store.quota old_owner; + Quota.add_entry store.quota new_owner diff --git a/xsa353.patch b/xsa353.patch new file mode 100644 index 0000000..764f93c --- /dev/null +++ b/xsa353.patch @@ -0,0 +1,89 @@ +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Subject: tools/ocaml/xenstored: do permission checks on xenstore root +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This was lacking in a disappointing number of places. + +The xenstore root node is treated differently from all other nodes, because it +doesn't have a parent, and mutation requires changing the parent. + +Unfortunately this lead to open-coding the special case for root into every +single xenstore operation, and out of all the xenstore operations only read +did a permission check when handling the root node. + +This means that an unprivileged guest can: + + * xenstore-chmod / to its liking and subsequently write new arbitrary nodes + there (subject to quota) + * xenstore-rm -r / deletes almost the entire xenstore tree (xenopsd quickly + refills some, but you are left with a broken system) + * DIRECTORY on / lists all children when called through python + bindings (xenstore-ls stops at /local because it tries to list recursively) + * get-perms on / works too, but that is just a minor information leak + +Add the missing permission checks, but this should really be refactored to do +the root handling and permission checks on the node only once from a single +function, instead of getting it wrong nearly everywhere. + +This is XSA-353. + +Signed-off-by: Edwin Török +Acked-by: Christian Lindig +Reviewed-by: Andrew Cooper + +diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml +index f299ec6461..92b6289b5e 100644 +--- a/tools/ocaml/xenstored/store.ml ++++ b/tools/ocaml/xenstored/store.ml +@@ -273,15 +273,17 @@ let path_rm store perm path = + Node.del_childname node name + with Not_found -> + raise Define.Doesnt_exist in +- if path = [] then ++ if path = [] then ( ++ Node.check_perm store.root perm Perms.WRITE; + Node.del_all_children store.root +- else ++ ) else + Path.apply_modify store.root path do_rm + + let path_setperms store perm path perms = +- if path = [] then ++ if path = [] then ( ++ Node.check_perm store.root perm Perms.WRITE; + Node.set_perms store.root perms +- else ++ ) else + let do_setperms node name = + let c = Node.find node name in + Node.check_owner c perm; +@@ -313,9 +315,10 @@ let read store perm path = + + let ls store perm path = + let children = +- if path = [] then +- (Node.get_children store.root) +- else ++ if path = [] then ( ++ Node.check_perm store.root perm Perms.READ; ++ Node.get_children store.root ++ ) else + let do_ls node name = + let cnode = Node.find node name in + Node.check_perm cnode perm Perms.READ; +@@ -324,9 +327,10 @@ let ls store perm path = + List.rev (List.map (fun n -> Symbol.to_string n.Node.name) children) + + let getperms store perm path = +- if path = [] then +- (Node.get_perms store.root) +- else ++ if path = [] then ( ++ Node.check_perm store.root perm Perms.READ; ++ Node.get_perms store.root ++ ) else + let fct n name = + let c = Node.find n name in + Node.check_perm c perm Perms.READ; diff --git a/xsa356.patch b/xsa356.patch new file mode 100644 index 0000000..f18d220 --- /dev/null +++ b/xsa356.patch @@ -0,0 +1,65 @@ +From: Roger Pau Monné +Subject: x86/irq: fix infinite loop in irq_move_cleanup_interrupt + +If Xen enters irq_move_cleanup_interrupt with a dynamic vector below +IRQ_MOVE_CLEANUP_VECTOR pending in IRR (0x20 or 0x21) that's also +designated for a cleanup it will enter a loop where +irq_move_cleanup_interrupt continuously sends a cleanup IPI (vector +0x22) to itself while waiting for the vector with lower priority to be +injected - which will never happen because IRQ_MOVE_CLEANUP_VECTOR +takes precedence and it's always injected first. + +Fix this by making sure vectors below IRQ_MOVE_CLEANUP_VECTOR are +marked as used and thus not available for APs. Also add some logic to +assert and prevent irq_move_cleanup_interrupt from entering such an +infinite loop, albeit that should never happen given the current code. + +This is XSA-356 / CVE-2020-29567. + +Fixes: 3fba06ba9f8 ('x86/IRQ: re-use legacy vector ranges on APs') +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich + +--- a/xen/arch/x86/irq.c ++++ b/xen/arch/x86/irq.c +@@ -441,8 +441,15 @@ int __init init_irq_data(void) + set_bit(HYPERCALL_VECTOR, used_vectors); + #endif + +- /* IRQ_MOVE_CLEANUP_VECTOR used for clean up vectors */ +- set_bit(IRQ_MOVE_CLEANUP_VECTOR, used_vectors); ++ /* ++ * Mark vectors up to the cleanup one as used, to prevent an infinite loop ++ * invoking irq_move_cleanup_interrupt. ++ */ ++ BUILD_BUG_ON(IRQ_MOVE_CLEANUP_VECTOR < FIRST_DYNAMIC_VECTOR); ++ for ( vector = FIRST_DYNAMIC_VECTOR; ++ vector <= IRQ_MOVE_CLEANUP_VECTOR; ++ vector++ ) ++ __set_bit(vector, used_vectors); + + return 0; + } +@@ -727,10 +734,6 @@ void irq_move_cleanup_interrupt(struct cpu_user_regs *regs) + { + unsigned vector, me; + +- /* This interrupt should not nest inside others. */ +- BUILD_BUG_ON(APIC_PRIO_CLASS(IRQ_MOVE_CLEANUP_VECTOR) != +- APIC_PRIO_CLASS(FIRST_DYNAMIC_VECTOR)); +- + ack_APIC_irq(); + + me = smp_processor_id(); +@@ -774,6 +777,11 @@ void irq_move_cleanup_interrupt(struct cpu_user_regs *regs) + */ + if ( irr & (1u << (vector % 32)) ) + { ++ if ( vector < IRQ_MOVE_CLEANUP_VECTOR ) ++ { ++ ASSERT_UNREACHABLE(); ++ goto unlock; ++ } + send_IPI_self(IRQ_MOVE_CLEANUP_VECTOR); + TRACE_3D(TRC_HW_IRQ_MOVE_CLEANUP_DELAY, + irq, vector, smp_processor_id()); diff --git a/xsa358.patch b/xsa358.patch new file mode 100644 index 0000000..a9ff89a --- /dev/null +++ b/xsa358.patch @@ -0,0 +1,57 @@ +From: Jan Beulich +Subject: evtchn/FIFO: re-order and synchronize (with) map_control_block() + +For evtchn_fifo_set_pending()'s check of the control block having been +set to be effective, ordering of respective reads and writes needs to be +ensured: The control block pointer needs to be recorded strictly after +the setting of all the queue heads, and it needs checking strictly +before any uses of them (this latter aspect was already guaranteed). + +This is XSA-358 / CVE-2020-29570. + +Reported-by: Julien Grall +Signed-off-by: Jan Beulich +Acked-by: Julien Grall +--- +v3: Drop read-side barrier again, leveraging guest_test_and_set_bit(). +v2: Re-base over queue locking re-work. + +--- a/xen/common/event_fifo.c ++++ b/xen/common/event_fifo.c +@@ -249,6 +249,10 @@ static void evtchn_fifo_set_pending(stru + goto unlock; + } + ++ /* ++ * This also acts as the read counterpart of the smp_wmb() in ++ * map_control_block(). ++ */ + if ( guest_test_and_set_bit(d, EVTCHN_FIFO_LINKED, word) ) + goto unlock; + +@@ -474,6 +478,7 @@ static int setup_control_block(struct vc + static int map_control_block(struct vcpu *v, uint64_t gfn, uint32_t offset) + { + void *virt; ++ struct evtchn_fifo_control_block *control_block; + unsigned int i; + int rc; + +@@ -484,10 +489,15 @@ static int map_control_block(struct vcpu + if ( rc < 0 ) + return rc; + +- v->evtchn_fifo->control_block = virt + offset; ++ control_block = virt + offset; + + for ( i = 0; i <= EVTCHN_FIFO_PRIORITY_MIN; i++ ) +- v->evtchn_fifo->queue[i].head = &v->evtchn_fifo->control_block->head[i]; ++ v->evtchn_fifo->queue[i].head = &control_block->head[i]; ++ ++ /* All queue heads must have been set before setting the control block. */ ++ smp_wmb(); ++ ++ v->evtchn_fifo->control_block = control_block; + + return 0; + } diff --git a/xsa359.patch b/xsa359.patch new file mode 100644 index 0000000..231810b --- /dev/null +++ b/xsa359.patch @@ -0,0 +1,40 @@ +From: Jan Beulich +Subject: evtchn/FIFO: add 2nd smp_rmb() to evtchn_fifo_word_from_port() + +Besides with add_page_to_event_array() the function also needs to +synchronize with evtchn_fifo_init_control() setting both d->evtchn_fifo +and (subsequently) d->evtchn_port_ops. + +This is XSA-359 / CVE-2020-29571. + +Reported-by: Julien Grall +Signed-off-by: Jan Beulich +Reviewed-by: Julien Grall + +--- a/xen/common/event_fifo.c ++++ b/xen/common/event_fifo.c +@@ -55,6 +55,13 @@ static inline event_word_t *evtchn_fifo_ + { + unsigned int p, w; + ++ /* ++ * Callers aren't required to hold d->event_lock, so we need to synchronize ++ * with evtchn_fifo_init_control() setting d->evtchn_port_ops /after/ ++ * d->evtchn_fifo. ++ */ ++ smp_rmb(); ++ + if ( unlikely(port >= d->evtchn_fifo->num_evtchns) ) + return NULL; + +@@ -606,6 +613,10 @@ int evtchn_fifo_init_control(struct evtc + if ( rc < 0 ) + goto error; + ++ /* ++ * This call, as a side effect, synchronizes with ++ * evtchn_fifo_word_from_port(). ++ */ + rc = map_control_block(v, gfn, offset); + if ( rc < 0 ) + goto error; From 3f30b99783d97b7d248c787aa529387b11be453f Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 16 Dec 2020 19:52:11 +0000 Subject: [PATCH 060/194] fix bug references --- xen.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xen.spec b/xen.spec index 4325a81..122da22 100644 --- a/xen.spec +++ b/xen.spec @@ -1045,7 +1045,7 @@ fi - Xenstore: guests can crash xenstored via watchs [XSA-324, CVE-2020-29484] (#1908088) - Xenstore: guests can disturb domain cleanup [XSA-325, CVE-2020-29483] - (#1905648) + (#1908087) - oxenstored memory leak in reset_watches [XSA-330, CVE-2020-29485] (#1908000) - undue recursion in x86 HVM context switch code [XSA-348, CVE-2020-29566] @@ -1053,7 +1053,7 @@ fi - oxenstored: node ownership can be changed by unprivileged clients [XSA-352, CVE-2020-29486] (#1908003) - oxenstored: permissions not checked on root node [XSA-353, CVE-2020-29479] - (#1908003) + (#1908002) - infinite loop when cleaning up IRQ vectors [XSA-356, CVE-2020-29567] (#1907932) - FIFO event channels control block related ordering [XSA-358, From 05650b8adfcc83ec984afc1caa90ee168ea61b12 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Sun, 20 Dec 2020 18:59:11 +0000 Subject: [PATCH 061/194] update to xen 4.14.1 --- .gitignore | 2 +- sources | 2 +- xen.canonicalize.patch | 22 +- xen.gcc10.fixes.patch | 23 - ...77370df2db9129c97c7305962fc5ad298728.patch | 592 ------------------ ...9b1c5b9e4c024f5f139d7a3d0357d2417b13.patch | 97 --- ...989f51878bc9ba61e930b0c29b921a30dc0d.patch | 240 ------- ...8003fdf7bd9e28137e6683ef46902af39146.patch | 41 -- xen.spec | 137 +--- ...llow-removing-child-of-a-node-exceed.patch | 157 ----- ...e-ignore-transaction-id-for-un-watch.patch | 86 --- ...ix-node-accounting-after-failed-node.patch | 104 --- ...simplify-and-rename-check_event_node.patch | 55 -- ...heck-privilege-for-XS_IS_DOMAIN_INTR.patch | 115 ---- ...6-tools-xenstore-rework-node-removal.patch | 217 ------- ...ire-watches-only-when-removing-a-spe.patch | 118 ---- ...store-introduce-node_perms-structure.patch | 289 --------- ...llow-special-watches-for-privileged-.patch | 237 ------- ...void-watch-events-for-nodes-without-.patch | 374 ----------- ...tored-ignore-transaction-id-for-un-w.patch | 43 -- ...tored-check-privilege-for-XS_IS_DOMA.patch | 30 - ...s-ocaml-xenstored-unify-watch-firing.patch | 29 - ...tored-introduce-permissions-for-spec.patch | 117 ---- ...tored-avoid-watch-events-for-nodes-w.patch | 406 ------------ ...tored-add-xenstored.conf-flag-to-tur.patch | 84 --- ...H_TLB_GLOBAL-in-do_mmu_update-for-XP.patch | 58 -- ...-in-response-to-paging-structure-cha.patch | 166 ----- xsa322-4.14-c.patch | 532 ---------------- xsa322-o.patch | 110 ---- xsa323.patch | 140 ----- xsa324.patch | 48 -- xsa325-4.14.patch | 192 ------ xsa330.patch | 66 -- xsa333.patch | 39 -- xsa334.patch | 51 -- xsa335-qemu.patch | 84 --- xsa335-trad.patch | 45 -- xsa336.patch | 283 --------- xsa337-1.patch | 87 --- xsa337-2.patch | 184 ------ xsa338.patch | 42 -- xsa339.patch | 76 --- xsa340.patch | 65 -- xsa342.patch | 152 ----- xsa343-1.patch | 199 ------ xsa343-2.patch | 295 --------- xsa343-3.patch | 392 ------------ xsa344-1.patch | 137 ---- xsa344-2.patch | 210 ------- ...map_pages_to_xen-to-have-only-a-sing.patch | 94 --- ...modify_xen_mappings-to-have-one-exit.patch | 68 -- ...ome-races-in-hypervisor-mapping-upda.patch | 249 -------- xsa346-1.patch | 50 -- xsa346-2.patch | 204 ------ xsa347-4.14-1.patch | 149 ----- xsa347-4.14-2.patch | 72 --- xsa347-4.14-3.patch | 59 -- xsa348-1.patch | 113 ---- xsa348-2.patch | 85 --- xsa348-3.patch | 174 ----- xsa351-arm.patch | 58 -- xsa351-x86-4.14-1.patch | 155 ----- xsa351-x86-4.14-2.patch | 126 ---- xsa352.patch | 42 -- xsa353.patch | 89 --- xsa355.patch | 23 - xsa356.patch | 65 -- xsa358.patch | 57 -- xsa359.patch | 40 -- 69 files changed, 20 insertions(+), 9222 deletions(-) delete mode 100644 xen.git-1ad177370df2db9129c97c7305962fc5ad298728.patch delete mode 100644 xen.git-1cfb9b1c5b9e4c024f5f139d7a3d0357d2417b13.patch delete mode 100644 xen.git-72bd989f51878bc9ba61e930b0c29b921a30dc0d.patch delete mode 100644 xen.git-8d148003fdf7bd9e28137e6683ef46902af39146.patch delete mode 100644 xsa115-4.14-c-0001-tools-xenstore-allow-removing-child-of-a-node-exceed.patch delete mode 100644 xsa115-4.14-c-0002-tools-xenstore-ignore-transaction-id-for-un-watch.patch delete mode 100644 xsa115-4.14-c-0003-tools-xenstore-fix-node-accounting-after-failed-node.patch delete mode 100644 xsa115-4.14-c-0004-tools-xenstore-simplify-and-rename-check_event_node.patch delete mode 100644 xsa115-4.14-c-0005-tools-xenstore-check-privilege-for-XS_IS_DOMAIN_INTR.patch delete mode 100644 xsa115-4.14-c-0006-tools-xenstore-rework-node-removal.patch delete mode 100644 xsa115-4.14-c-0007-tools-xenstore-fire-watches-only-when-removing-a-spe.patch delete mode 100644 xsa115-4.14-c-0008-tools-xenstore-introduce-node_perms-structure.patch delete mode 100644 xsa115-4.14-c-0009-tools-xenstore-allow-special-watches-for-privileged-.patch delete mode 100644 xsa115-4.14-c-0010-tools-xenstore-avoid-watch-events-for-nodes-without-.patch delete mode 100644 xsa115-o-0001-tools-ocaml-xenstored-ignore-transaction-id-for-un-w.patch delete mode 100644 xsa115-o-0002-tools-ocaml-xenstored-check-privilege-for-XS_IS_DOMA.patch delete mode 100644 xsa115-o-0003-tools-ocaml-xenstored-unify-watch-firing.patch delete mode 100644 xsa115-o-0004-tools-ocaml-xenstored-introduce-permissions-for-spec.patch delete mode 100644 xsa115-o-0005-tools-ocaml-xenstored-avoid-watch-events-for-nodes-w.patch delete mode 100644 xsa115-o-0006-tools-ocaml-xenstored-add-xenstored.conf-flag-to-tur.patch delete mode 100644 xsa286-4.14-0001-x86-pv-Drop-FLUSH_TLB_GLOBAL-in-do_mmu_update-for-XP.patch delete mode 100644 xsa286-4.14-0002-x86-pv-Flush-TLB-in-response-to-paging-structure-cha.patch delete mode 100644 xsa322-4.14-c.patch delete mode 100644 xsa322-o.patch delete mode 100644 xsa323.patch delete mode 100644 xsa324.patch delete mode 100644 xsa325-4.14.patch delete mode 100644 xsa330.patch delete mode 100644 xsa333.patch delete mode 100644 xsa334.patch delete mode 100644 xsa335-qemu.patch delete mode 100644 xsa335-trad.patch delete mode 100644 xsa336.patch delete mode 100644 xsa337-1.patch delete mode 100644 xsa337-2.patch delete mode 100644 xsa338.patch delete mode 100644 xsa339.patch delete mode 100644 xsa340.patch delete mode 100644 xsa342.patch delete mode 100644 xsa343-1.patch delete mode 100644 xsa343-2.patch delete mode 100644 xsa343-3.patch delete mode 100644 xsa344-1.patch delete mode 100644 xsa344-2.patch delete mode 100644 xsa345-4.14-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch delete mode 100644 xsa345-4.14-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch delete mode 100644 xsa345-4.14-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch delete mode 100644 xsa346-1.patch delete mode 100644 xsa346-2.patch delete mode 100644 xsa347-4.14-1.patch delete mode 100644 xsa347-4.14-2.patch delete mode 100644 xsa347-4.14-3.patch delete mode 100644 xsa348-1.patch delete mode 100644 xsa348-2.patch delete mode 100644 xsa348-3.patch delete mode 100644 xsa351-arm.patch delete mode 100644 xsa351-x86-4.14-1.patch delete mode 100644 xsa351-x86-4.14-2.patch delete mode 100644 xsa352.patch delete mode 100644 xsa353.patch delete mode 100644 xsa355.patch delete mode 100644 xsa356.patch delete mode 100644 xsa358.patch delete mode 100644 xsa359.patch diff --git a/.gitignore b/.gitignore index f112132..155f9cb 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.0.tar.gz +/xen-4.14.1.tar.gz diff --git a/sources b/sources index 660c881..e3f8b84 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.0.tar.gz) = ebce47a2f754955d8517123d69f62006634d97620fbbe3784869a0667466e586a249f57ffaf7846d5bcb45d69377cde43354c82c233fbb5407e55653b9a33ac0 +SHA512 (xen-4.14.1.tar.gz) = c75cbec82793435f5a7026626ffdb2e9a2166b42d2be4b2f1194240e0312458124f0ebd53eeb02ce7330c22afe402a28a96b32f8af66e41e9416fe94535724c9 diff --git a/xen.canonicalize.patch b/xen.canonicalize.patch index 43ccf02..500cc72 100644 --- a/xen.canonicalize.patch +++ b/xen.canonicalize.patch @@ -38,17 +38,6 @@ return get_node(conn, ctx, *canonical_name, perm); } ---- xen-4.9.0-rc1.2/tools/xenstore/xenstored_core.h.orig 2017-04-12 16:18:57.000000000 +0100 -+++ xen-4.9.0-rc1.2/tools/xenstore/xenstored_core.h 2017-04-13 21:20:29.146368478 +0100 -@@ -148,7 +148,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); - - /* Write a node to the tdb data base. */ - int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node); --- 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 @@ @@ -107,3 +96,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 @@ + 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); + + /* Get access permissions. */ + enum xs_perm_type perm_for_conn(struct connection *conn, diff --git a/xen.gcc10.fixes.patch b/xen.gcc10.fixes.patch index 0bd895f..657989b 100644 --- a/xen.gcc10.fixes.patch +++ b/xen.gcc10.fixes.patch @@ -1,26 +1,3 @@ ---- xen-4.13.0/tools/libxl/libxlu_pci.c.orig 2019-12-17 14:23:09.000000000 +0000 -+++ xen-4.13.0/tools/libxl/libxlu_pci.c 2020-01-21 21:56:26.812212916 +0000 -@@ -48,7 +48,7 @@ - int xlu_pci_parse_bdf(XLU_Config *cfg, libxl_device_pci *pcidev, const char *str) - { - unsigned state = STATE_DOMAIN; -- unsigned dom, bus, dev, func, vslot = 0; -+ unsigned dom = 0, bus = 0, dev = 0, func = 0, vslot = 0; - char *buf2, *tok, *ptr, *end, *optkey = NULL; - - if ( NULL == (buf2 = ptr = strdup(str)) ) ---- xen-4.13.0/tools/libxl/libxl_utils.c.orig 2019-12-17 14:23:09.000000000 +0000 -+++ xen-4.13.0/tools/libxl/libxl_utils.c 2020-01-21 22:34:52.096300774 +0000 -@@ -1259,7 +1259,7 @@ - } - memset(un, 0, sizeof(struct sockaddr_un)); - un->sun_family = AF_UNIX; -- strncpy(un->sun_path, path, sizeof(un->sun_path)); -+ strncpy(un->sun_path, path, sizeof(un->sun_path)-1); - return 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 @@ diff --git a/xen.git-1ad177370df2db9129c97c7305962fc5ad298728.patch b/xen.git-1ad177370df2db9129c97c7305962fc5ad298728.patch deleted file mode 100644 index a713b25..0000000 --- a/xen.git-1ad177370df2db9129c97c7305962fc5ad298728.patch +++ /dev/null @@ -1,592 +0,0 @@ -From 1ad177370df2db9129c97c7305962fc5ad298728 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 1 Dec 2020 15:31:01 +0100 -Subject: [PATCH] xen/evtchn: rework per event channel lock - -Currently the lock for a single event channel needs to be taken with -interrupts off, which causes deadlocks in some cases. - -Rework the per event channel lock to be non-blocking for the case of -sending an event and removing the need for disabling interrupts for -taking the lock. - -The lock is needed for avoiding races between event channel state -changes (creation, closing, binding) against normal operations (set -pending, [un]masking, priority changes). - -Use a rwlock, but with some restrictions: - -- Changing the state of an event channel (creation, closing, binding) - needs to use write_lock(), with ASSERT()ing that the lock is taken as - writer only when the state of the event channel is either before or - after the locked region appropriate (either free or unbound). - -- Sending an event needs to use read_trylock() mostly, in case of not - obtaining the lock the operation is omitted. This is needed as - sending an event can happen with interrupts off (at least in some - cases). - -- Dumping the event channel state for debug purposes is using - read_trylock(), too, in order to avoid blocking in case the lock is - taken as writer for a long time. - -- All other cases can use read_lock(). - -Fixes: e045199c7c9c54 ("evtchn: address races with evtchn_reset()") -Signed-off-by: Juergen Gross -Reviewed-by: Jan Beulich -Acked-by: Julien Grall - -xen/events: fix build - -Commit 5f2df45ead7c1195 ("xen/evtchn: rework per event channel lock") -introduced a build failure for NDEBUG builds. - -Fixes: 5f2df45ead7c1195 ("xen/evtchn: rework per event channel lock") -Signed-off-by: Juergen Gross -Signed-off-by: Jan Beulich -master commit: 5f2df45ead7c1195142f68b7923047a1e9479d54 -master date: 2020-11-10 14:36:15 +0100 -master commit: 53bacb86f496fdb11560d9e3b361bca7de60d268 -master date: 2020-11-11 08:56:21 +0100 ---- - xen/arch/x86/irq.c | 6 +- - xen/arch/x86/pv/shim.c | 9 +-- - xen/common/event_channel.c | 141 ++++++++++++++++++++++--------------- - xen/include/xen/event.h | 27 +++++-- - xen/include/xen/sched.h | 5 +- - 5 files changed, 116 insertions(+), 72 deletions(-) - -diff --git a/xen/arch/x86/irq.c b/xen/arch/x86/irq.c -index 93c4fb9a79..8d1f9a9fc6 100644 ---- a/xen/arch/x86/irq.c -+++ b/xen/arch/x86/irq.c -@@ -2495,14 +2495,12 @@ static void dump_irqs(unsigned char key) - pirq = domain_irq_to_pirq(d, irq); - info = pirq_info(d, pirq); - evtchn = evtchn_from_port(d, info->evtchn); -- local_irq_disable(); -- if ( spin_trylock(&evtchn->lock) ) -+ if ( evtchn_read_trylock(evtchn) ) - { - pending = evtchn_is_pending(d, evtchn); - masked = evtchn_is_masked(d, evtchn); -- spin_unlock(&evtchn->lock); -+ evtchn_read_unlock(evtchn); - } -- local_irq_enable(); - printk("d%d:%3d(%c%c%c)%c", - d->domain_id, pirq, "-P?"[pending], - "-M?"[masked], info->masked ? 'M' : '-', -diff --git a/xen/arch/x86/pv/shim.c b/xen/arch/x86/pv/shim.c -index 9aef7a860a..b4e83e0778 100644 ---- a/xen/arch/x86/pv/shim.c -+++ b/xen/arch/x86/pv/shim.c -@@ -660,11 +660,12 @@ void pv_shim_inject_evtchn(unsigned int port) - if ( port_is_valid(guest, port) ) - { - struct evtchn *chn = evtchn_from_port(guest, port); -- unsigned long flags; - -- spin_lock_irqsave(&chn->lock, flags); -- evtchn_port_set_pending(guest, chn->notify_vcpu_id, chn); -- spin_unlock_irqrestore(&chn->lock, flags); -+ if ( evtchn_read_trylock(chn) ) -+ { -+ evtchn_port_set_pending(guest, chn->notify_vcpu_id, chn); -+ evtchn_read_unlock(chn); -+ } - } - } - -diff --git a/xen/common/event_channel.c b/xen/common/event_channel.c -index 12f666cb79..181e5abaa6 100644 ---- a/xen/common/event_channel.c -+++ b/xen/common/event_channel.c -@@ -50,6 +50,40 @@ - - #define consumer_is_xen(e) (!!(e)->xen_consumer) - -+/* -+ * Lock an event channel exclusively. This is allowed only when the channel is -+ * free or unbound either when taking or when releasing the lock, as any -+ * concurrent operation on the event channel using evtchn_read_trylock() will -+ * 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) -+{ -+ write_lock(&evtchn->lock); -+ -+#ifndef NDEBUG -+ evtchn->old_state = evtchn->state; -+#endif -+} -+ -+static inline unsigned int old_state(const struct evtchn *evtchn) -+{ -+#ifndef NDEBUG -+ return evtchn->old_state; -+#else -+ return ECS_RESERVED; /* Just to allow things to build. */ -+#endif -+} -+ -+static inline void evtchn_write_unlock(struct evtchn *evtchn) -+{ -+ /* Enforce lock discipline. */ -+ ASSERT(old_state(evtchn) == ECS_FREE || old_state(evtchn) == ECS_UNBOUND || -+ evtchn->state == ECS_FREE || evtchn->state == ECS_UNBOUND); -+ -+ write_unlock(&evtchn->lock); -+} -+ - /* - * The function alloc_unbound_xen_event_channel() allows an arbitrary - * notifier function to be specified. However, very few unique functions -@@ -131,7 +165,7 @@ static struct evtchn *alloc_evtchn_bucket(struct domain *d, unsigned int port) - return NULL; - } - chn[i].port = port + i; -- spin_lock_init(&chn[i].lock); -+ rwlock_init(&chn[i].lock); - } - return chn; - } -@@ -249,7 +283,6 @@ static long evtchn_alloc_unbound(evtchn_alloc_unbound_t *alloc) - int port; - domid_t dom = alloc->dom; - long rc; -- unsigned long flags; - - d = rcu_lock_domain_by_any_id(dom); - if ( d == NULL ) -@@ -265,14 +298,14 @@ static long evtchn_alloc_unbound(evtchn_alloc_unbound_t *alloc) - if ( rc ) - goto out; - -- spin_lock_irqsave(&chn->lock, flags); -+ evtchn_write_lock(chn); - - chn->state = ECS_UNBOUND; - if ( (chn->u.unbound.remote_domid = alloc->remote_dom) == DOMID_SELF ) - chn->u.unbound.remote_domid = current->domain->domain_id; - evtchn_port_init(d, chn); - -- spin_unlock_irqrestore(&chn->lock, flags); -+ evtchn_write_unlock(chn); - - alloc->port = port; - -@@ -285,32 +318,26 @@ static long evtchn_alloc_unbound(evtchn_alloc_unbound_t *alloc) - } - - --static unsigned long double_evtchn_lock(struct evtchn *lchn, -- struct evtchn *rchn) -+static void double_evtchn_lock(struct evtchn *lchn, struct evtchn *rchn) - { -- unsigned long flags; -- - if ( lchn <= rchn ) - { -- spin_lock_irqsave(&lchn->lock, flags); -+ evtchn_write_lock(lchn); - if ( lchn != rchn ) -- spin_lock(&rchn->lock); -+ evtchn_write_lock(rchn); - } - else - { -- spin_lock_irqsave(&rchn->lock, flags); -- spin_lock(&lchn->lock); -+ evtchn_write_lock(rchn); -+ evtchn_write_lock(lchn); - } -- -- return flags; - } - --static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn, -- unsigned long flags) -+static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn) - { - if ( lchn != rchn ) -- spin_unlock(&lchn->lock); -- spin_unlock_irqrestore(&rchn->lock, flags); -+ evtchn_write_unlock(lchn); -+ evtchn_write_unlock(rchn); - } - - static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) -@@ -320,7 +347,6 @@ static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) - int lport, rport = bind->remote_port; - domid_t rdom = bind->remote_dom; - long rc; -- unsigned long flags; - - if ( rdom == DOMID_SELF ) - rdom = current->domain->domain_id; -@@ -356,7 +382,7 @@ static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) - if ( rc ) - goto out; - -- flags = double_evtchn_lock(lchn, rchn); -+ double_evtchn_lock(lchn, rchn); - - lchn->u.interdomain.remote_dom = rd; - lchn->u.interdomain.remote_port = rport; -@@ -373,7 +399,7 @@ static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) - */ - evtchn_port_set_pending(ld, lchn->notify_vcpu_id, lchn); - -- double_evtchn_unlock(lchn, rchn, flags); -+ double_evtchn_unlock(lchn, rchn); - - bind->local_port = lport; - -@@ -396,7 +422,6 @@ int evtchn_bind_virq(evtchn_bind_virq_t *bind, evtchn_port_t port) - struct domain *d = current->domain; - int virq = bind->virq, vcpu = bind->vcpu; - int rc = 0; -- unsigned long flags; - - if ( (virq < 0) || (virq >= ARRAY_SIZE(v->virq_to_evtchn)) ) - return -EINVAL; -@@ -434,14 +459,14 @@ int evtchn_bind_virq(evtchn_bind_virq_t *bind, evtchn_port_t port) - - chn = evtchn_from_port(d, port); - -- spin_lock_irqsave(&chn->lock, flags); -+ evtchn_write_lock(chn); - - chn->state = ECS_VIRQ; - chn->notify_vcpu_id = vcpu; - chn->u.virq = virq; - evtchn_port_init(d, chn); - -- spin_unlock_irqrestore(&chn->lock, flags); -+ evtchn_write_unlock(chn); - - v->virq_to_evtchn[virq] = bind->port = port; - -@@ -458,7 +483,6 @@ static long evtchn_bind_ipi(evtchn_bind_ipi_t *bind) - struct domain *d = current->domain; - int port, vcpu = bind->vcpu; - long rc = 0; -- unsigned long flags; - - if ( domain_vcpu(d, vcpu) == NULL ) - return -ENOENT; -@@ -470,13 +494,13 @@ static long evtchn_bind_ipi(evtchn_bind_ipi_t *bind) - - chn = evtchn_from_port(d, port); - -- spin_lock_irqsave(&chn->lock, flags); -+ evtchn_write_lock(chn); - - chn->state = ECS_IPI; - chn->notify_vcpu_id = vcpu; - evtchn_port_init(d, chn); - -- spin_unlock_irqrestore(&chn->lock, flags); -+ evtchn_write_unlock(chn); - - bind->port = port; - -@@ -520,7 +544,6 @@ static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind) - struct pirq *info; - int port = 0, pirq = bind->pirq; - long rc; -- unsigned long flags; - - if ( (pirq < 0) || (pirq >= d->nr_pirqs) ) - return -EINVAL; -@@ -553,14 +576,14 @@ static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind) - goto out; - } - -- spin_lock_irqsave(&chn->lock, flags); -+ evtchn_write_lock(chn); - - chn->state = ECS_PIRQ; - chn->u.pirq.irq = pirq; - link_pirq_port(port, chn, v); - evtchn_port_init(d, chn); - -- spin_unlock_irqrestore(&chn->lock, flags); -+ evtchn_write_unlock(chn); - - bind->port = port; - -@@ -581,7 +604,6 @@ int evtchn_close(struct domain *d1, int port1, bool guest) - struct evtchn *chn1, *chn2; - int port2; - long rc = 0; -- unsigned long flags; - - again: - spin_lock(&d1->event_lock); -@@ -681,14 +703,14 @@ int evtchn_close(struct domain *d1, int port1, bool guest) - BUG_ON(chn2->state != ECS_INTERDOMAIN); - BUG_ON(chn2->u.interdomain.remote_dom != d1); - -- flags = double_evtchn_lock(chn1, chn2); -+ double_evtchn_lock(chn1, chn2); - - evtchn_free(d1, chn1); - - chn2->state = ECS_UNBOUND; - chn2->u.unbound.remote_domid = d1->domain_id; - -- double_evtchn_unlock(chn1, chn2, flags); -+ double_evtchn_unlock(chn1, chn2); - - goto out; - -@@ -696,9 +718,9 @@ int evtchn_close(struct domain *d1, int port1, bool guest) - BUG(); - } - -- spin_lock_irqsave(&chn1->lock, flags); -+ evtchn_write_lock(chn1); - evtchn_free(d1, chn1); -- spin_unlock_irqrestore(&chn1->lock, flags); -+ evtchn_write_unlock(chn1); - - out: - if ( d2 != NULL ) -@@ -718,7 +740,6 @@ int evtchn_send(struct domain *ld, unsigned int lport) - struct evtchn *lchn, *rchn; - struct domain *rd; - int rport, ret = 0; -- unsigned long flags; - - if ( !port_is_valid(ld, lport) ) - return -EINVAL; -@@ -731,7 +752,7 @@ int evtchn_send(struct domain *ld, unsigned int lport) - - lchn = evtchn_from_port(ld, lport); - -- spin_lock_irqsave(&lchn->lock, flags); -+ evtchn_read_lock(lchn); - - /* Guest cannot send via a Xen-attached event channel. */ - if ( unlikely(consumer_is_xen(lchn)) ) -@@ -766,7 +787,7 @@ int evtchn_send(struct domain *ld, unsigned int lport) - } - - out: -- spin_unlock_irqrestore(&lchn->lock, flags); -+ evtchn_read_unlock(lchn); - - return ret; - } -@@ -793,9 +814,11 @@ void send_guest_vcpu_virq(struct vcpu *v, uint32_t virq) - - d = v->domain; - chn = evtchn_from_port(d, port); -- spin_lock(&chn->lock); -- evtchn_port_set_pending(d, v->vcpu_id, chn); -- spin_unlock(&chn->lock); -+ if ( evtchn_read_trylock(chn) ) -+ { -+ evtchn_port_set_pending(d, v->vcpu_id, chn); -+ evtchn_read_unlock(chn); -+ } - - out: - spin_unlock_irqrestore(&v->virq_lock, flags); -@@ -824,9 +847,11 @@ void send_guest_global_virq(struct domain *d, uint32_t virq) - goto out; - - chn = evtchn_from_port(d, port); -- spin_lock(&chn->lock); -- evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); -- spin_unlock(&chn->lock); -+ if ( evtchn_read_trylock(chn) ) -+ { -+ evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); -+ evtchn_read_unlock(chn); -+ } - - out: - spin_unlock_irqrestore(&v->virq_lock, flags); -@@ -836,7 +861,6 @@ void send_guest_pirq(struct domain *d, const struct pirq *pirq) - { - int port; - struct evtchn *chn; -- unsigned long flags; - - /* - * PV guests: It should not be possible to race with __evtchn_close(). The -@@ -851,9 +875,11 @@ void send_guest_pirq(struct domain *d, const struct pirq *pirq) - } - - chn = evtchn_from_port(d, port); -- spin_lock_irqsave(&chn->lock, flags); -- evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); -- spin_unlock_irqrestore(&chn->lock, flags); -+ if ( evtchn_read_trylock(chn) ) -+ { -+ evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); -+ evtchn_read_unlock(chn); -+ } - } - - static struct domain *global_virq_handlers[NR_VIRQS] __read_mostly; -@@ -1050,15 +1076,17 @@ int evtchn_unmask(unsigned int port) - { - struct domain *d = current->domain; - struct evtchn *evtchn; -- unsigned long flags; - - if ( unlikely(!port_is_valid(d, port)) ) - return -EINVAL; - - evtchn = evtchn_from_port(d, port); -- spin_lock_irqsave(&evtchn->lock, flags); -+ -+ evtchn_read_lock(evtchn); -+ - evtchn_port_unmask(d, evtchn); -- spin_unlock_irqrestore(&evtchn->lock, flags); -+ -+ evtchn_read_unlock(evtchn); - - return 0; - } -@@ -1304,7 +1332,6 @@ int alloc_unbound_xen_event_channel( - { - struct evtchn *chn; - int port, rc; -- unsigned long flags; - - spin_lock(&ld->event_lock); - -@@ -1317,14 +1344,14 @@ int alloc_unbound_xen_event_channel( - if ( rc ) - goto out; - -- spin_lock_irqsave(&chn->lock, flags); -+ evtchn_write_lock(chn); - - chn->state = ECS_UNBOUND; - chn->xen_consumer = get_xen_consumer(notification_fn); - chn->notify_vcpu_id = lvcpu; - chn->u.unbound.remote_domid = remote_domid; - -- spin_unlock_irqrestore(&chn->lock, flags); -+ evtchn_write_unlock(chn); - - write_atomic(&ld->xen_evtchns, ld->xen_evtchns + 1); - -@@ -1356,7 +1383,6 @@ void notify_via_xen_event_channel(struct domain *ld, int lport) - { - struct evtchn *lchn, *rchn; - struct domain *rd; -- unsigned long flags; - - if ( !port_is_valid(ld, lport) ) - { -@@ -1371,7 +1397,8 @@ void notify_via_xen_event_channel(struct domain *ld, int lport) - - lchn = evtchn_from_port(ld, lport); - -- spin_lock_irqsave(&lchn->lock, flags); -+ if ( !evtchn_read_trylock(lchn) ) -+ return; - - if ( likely(lchn->state == ECS_INTERDOMAIN) ) - { -@@ -1381,7 +1408,7 @@ void notify_via_xen_event_channel(struct domain *ld, int lport) - evtchn_port_set_pending(rd, rchn->notify_vcpu_id, rchn); - } - -- spin_unlock_irqrestore(&lchn->lock, flags); -+ evtchn_read_unlock(lchn); - } - - void evtchn_check_pollers(struct domain *d, unsigned int port) -diff --git a/xen/include/xen/event.h b/xen/include/xen/event.h -index fa93a3684a..6588333f42 100644 ---- a/xen/include/xen/event.h -+++ b/xen/include/xen/event.h -@@ -111,6 +111,21 @@ static inline unsigned int max_evtchns(const struct domain *d) - : BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); - } - -+static inline void evtchn_read_lock(struct evtchn *evtchn) -+{ -+ read_lock(&evtchn->lock); -+} -+ -+static inline bool evtchn_read_trylock(struct evtchn *evtchn) -+{ -+ return read_trylock(&evtchn->lock); -+} -+ -+static inline void evtchn_read_unlock(struct evtchn *evtchn) -+{ -+ read_unlock(&evtchn->lock); -+} -+ - static inline bool_t port_is_valid(struct domain *d, unsigned int p) - { - if ( p >= read_atomic(&d->valid_evtchns) ) -@@ -244,11 +259,10 @@ static inline bool evtchn_port_is_pending(struct domain *d, evtchn_port_t port) - { - struct evtchn *evtchn = evtchn_from_port(d, port); - bool rc; -- unsigned long flags; - -- spin_lock_irqsave(&evtchn->lock, flags); -+ evtchn_read_lock(evtchn); - rc = evtchn_is_pending(d, evtchn); -- spin_unlock_irqrestore(&evtchn->lock, flags); -+ evtchn_read_unlock(evtchn); - - return rc; - } -@@ -263,11 +277,12 @@ static inline bool evtchn_port_is_masked(struct domain *d, evtchn_port_t port) - { - struct evtchn *evtchn = evtchn_from_port(d, port); - bool rc; -- unsigned long flags; - -- spin_lock_irqsave(&evtchn->lock, flags); -+ evtchn_read_lock(evtchn); -+ - rc = evtchn_is_masked(d, evtchn); -- spin_unlock_irqrestore(&evtchn->lock, flags); -+ -+ evtchn_read_unlock(evtchn); - - return rc; - } -diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h -index 97ba8e0795..f782ffeb82 100644 ---- a/xen/include/xen/sched.h -+++ b/xen/include/xen/sched.h -@@ -85,7 +85,7 @@ extern domid_t hardware_domid; - - struct evtchn - { -- spinlock_t lock; -+ rwlock_t lock; - #define ECS_FREE 0 /* Channel is available for use. */ - #define ECS_RESERVED 1 /* Channel is reserved. */ - #define ECS_UNBOUND 2 /* Channel is waiting to bind to a remote domain. */ -@@ -114,6 +114,9 @@ struct evtchn - u16 virq; /* state == ECS_VIRQ */ - } u; - u8 priority; -+#ifndef NDEBUG -+ u8 old_state; /* State when taking lock in write mode. */ -+#endif - u8 last_priority; - u16 last_vcpu_id; - #ifdef CONFIG_XSM --- -2.20.1 - diff --git a/xen.git-1cfb9b1c5b9e4c024f5f139d7a3d0357d2417b13.patch b/xen.git-1cfb9b1c5b9e4c024f5f139d7a3d0357d2417b13.patch deleted file mode 100644 index 12c64ca..0000000 --- a/xen.git-1cfb9b1c5b9e4c024f5f139d7a3d0357d2417b13.patch +++ /dev/null @@ -1,97 +0,0 @@ -From 1cfb9b1c5b9e4c024f5f139d7a3d0357d2417b13 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 1 Dec 2020 15:33:19 +0100 -Subject: [PATCH] xen/events: access last_priority and last_vcpu_id together - -The queue for a fifo event is depending on the vcpu_id and the -priority of the event. When sending an event it might happen the -event needs to change queues and the old queue needs to be kept for -keeping the links between queue elements intact. For this purpose -the event channel contains last_priority and last_vcpu_id values -elements for being able to identify the old queue. - -In order to avoid races always access last_priority and last_vcpu_id -with a single atomic operation avoiding any inconsistencies. - -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall -master commit: 1277cb9dc5e966f1faf665bcded02b7533e38078 -master date: 2020-11-24 11:23:42 +0100 ---- - xen/common/event_fifo.c | 25 +++++++++++++++++++------ - xen/include/xen/sched.h | 3 +-- - 2 files changed, 20 insertions(+), 8 deletions(-) - -diff --git a/xen/common/event_fifo.c b/xen/common/event_fifo.c -index 27ab3a1c3f..2037b24196 100644 ---- a/xen/common/event_fifo.c -+++ b/xen/common/event_fifo.c -@@ -21,6 +21,14 @@ - - #include - -+union evtchn_fifo_lastq { -+ uint32_t raw; -+ struct { -+ uint8_t last_priority; -+ uint16_t last_vcpu_id; -+ }; -+}; -+ - static inline event_word_t *evtchn_fifo_word_from_port(const struct domain *d, - unsigned int port) - { -@@ -65,16 +73,18 @@ static struct evtchn_fifo_queue *lock_old_queue(const struct domain *d, - struct vcpu *v; - struct evtchn_fifo_queue *q, *old_q; - unsigned int try; -+ union evtchn_fifo_lastq lastq; - - for ( try = 0; try < 3; try++ ) - { -- v = d->vcpu[evtchn->last_vcpu_id]; -- old_q = &v->evtchn_fifo->queue[evtchn->last_priority]; -+ lastq.raw = read_atomic(&evtchn->fifo_lastq); -+ v = d->vcpu[lastq.last_vcpu_id]; -+ old_q = &v->evtchn_fifo->queue[lastq.last_priority]; - - spin_lock_irqsave(&old_q->lock, *flags); - -- v = d->vcpu[evtchn->last_vcpu_id]; -- q = &v->evtchn_fifo->queue[evtchn->last_priority]; -+ v = d->vcpu[lastq.last_vcpu_id]; -+ q = &v->evtchn_fifo->queue[lastq.last_priority]; - - if ( old_q == q ) - return old_q; -@@ -225,8 +235,11 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) - /* Moved to a different queue? */ - if ( old_q != q ) - { -- evtchn->last_vcpu_id = v->vcpu_id; -- evtchn->last_priority = q->priority; -+ union evtchn_fifo_lastq lastq = { }; -+ -+ lastq.last_vcpu_id = v->vcpu_id; -+ lastq.last_priority = q->priority; -+ write_atomic(&evtchn->fifo_lastq, lastq.raw); - - spin_unlock_irqrestore(&old_q->lock, flags); - spin_lock_irqsave(&q->lock, flags); -diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h -index f782ffeb82..99e2f1aac5 100644 ---- a/xen/include/xen/sched.h -+++ b/xen/include/xen/sched.h -@@ -117,8 +117,7 @@ struct evtchn - #ifndef NDEBUG - u8 old_state; /* State when taking lock in write mode. */ - #endif -- u8 last_priority; -- u16 last_vcpu_id; -+ u32 fifo_lastq; /* Data for fifo events identifying last queue. */ - #ifdef CONFIG_XSM - union { - #ifdef XSM_NEED_GENERIC_EVTCHN_SSID --- -2.20.1 - diff --git a/xen.git-72bd989f51878bc9ba61e930b0c29b921a30dc0d.patch b/xen.git-72bd989f51878bc9ba61e930b0c29b921a30dc0d.patch deleted file mode 100644 index 21c5ddd..0000000 --- a/xen.git-72bd989f51878bc9ba61e930b0c29b921a30dc0d.patch +++ /dev/null @@ -1,240 +0,0 @@ -From 72bd989f51878bc9ba61e930b0c29b921a30dc0d Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 1 Dec 2020 15:34:31 +0100 -Subject: [PATCH] xen/events: rework fifo queue locking - -Two cpus entering evtchn_fifo_set_pending() for the same event channel -can race in case the first one gets interrupted after setting -EVTCHN_FIFO_PENDING and when the other one manages to set -EVTCHN_FIFO_LINKED before the first one is testing that bit. This can -lead to evtchn_check_pollers() being called before the event is put -properly into the queue, resulting eventually in the guest not seeing -the event pending and thus blocking forever afterwards. - -Note that commit 5f2df45ead7c1195 ("xen/evtchn: rework per event channel -lock") made the race just more obvious, while the fifo event channel -implementation had this race forever since the introduction and use of -per-channel locks, when an unmask operation was running in parallel with -an event channel send operation. - -Using a spinlock for the per event channel lock had turned out -problematic due to some paths needing to take the lock are called with -interrupts off, so the lock would need to disable interrupts, which in -turn broke some use cases related to vm events. - -For avoiding this race the queue locking in evtchn_fifo_set_pending() -needs to be reworked to cover the test of EVTCHN_FIFO_PENDING, -EVTCHN_FIFO_MASKED and EVTCHN_FIFO_LINKED, too. Additionally when an -event channel needs to change queues both queues need to be locked -initially, in order to avoid having a window with no lock held at all. - -Reported-by: Jan Beulich -Fixes: 5f2df45ead7c1195 ("xen/evtchn: rework per event channel lock") -Fixes: de6acb78bf0e137c ("evtchn: use a per-event channel lock for sending events") -Signed-off-by: Juergen Gross -Reviewed-by: Jan Beulich -master commit: 71ac522909e9302350a88bc378be99affa87067c -master date: 2020-11-30 14:05:39 +0100 ---- - xen/common/event_fifo.c | 128 ++++++++++++++++++++++------------------ - 1 file changed, 70 insertions(+), 58 deletions(-) - -diff --git a/xen/common/event_fifo.c b/xen/common/event_fifo.c -index 2037b24196..2f5e868b7a 100644 ---- a/xen/common/event_fifo.c -+++ b/xen/common/event_fifo.c -@@ -66,38 +66,6 @@ static void evtchn_fifo_init(struct domain *d, struct evtchn *evtchn) - d->domain_id, evtchn->port); - } - --static struct evtchn_fifo_queue *lock_old_queue(const struct domain *d, -- struct evtchn *evtchn, -- unsigned long *flags) --{ -- struct vcpu *v; -- struct evtchn_fifo_queue *q, *old_q; -- unsigned int try; -- union evtchn_fifo_lastq lastq; -- -- for ( try = 0; try < 3; try++ ) -- { -- lastq.raw = read_atomic(&evtchn->fifo_lastq); -- v = d->vcpu[lastq.last_vcpu_id]; -- old_q = &v->evtchn_fifo->queue[lastq.last_priority]; -- -- spin_lock_irqsave(&old_q->lock, *flags); -- -- v = d->vcpu[lastq.last_vcpu_id]; -- q = &v->evtchn_fifo->queue[lastq.last_priority]; -- -- if ( old_q == q ) -- return old_q; -- -- spin_unlock_irqrestore(&old_q->lock, *flags); -- } -- -- gprintk(XENLOG_WARNING, -- "dom%d port %d lost event (too many queue changes)\n", -- d->domain_id, evtchn->port); -- return NULL; --} -- - static int try_set_link(event_word_t *word, event_word_t *w, uint32_t link) - { - event_word_t new, old; -@@ -169,6 +137,9 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) - event_word_t *word; - unsigned long flags; - bool_t was_pending; -+ struct evtchn_fifo_queue *q, *old_q; -+ unsigned int try; -+ bool linked = true; - - port = evtchn->port; - word = evtchn_fifo_word_from_port(d, port); -@@ -183,17 +154,67 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) - return; - } - -+ /* -+ * Lock all queues related to the event channel (in case of a queue change -+ * this might be two). -+ * It is mandatory to do that before setting and testing the PENDING bit -+ * and to hold the current queue lock until the event has been put into the -+ * list of pending events in order to avoid waking up a guest without the -+ * event being visibly pending in the guest. -+ */ -+ for ( try = 0; try < 3; try++ ) -+ { -+ union evtchn_fifo_lastq lastq; -+ const struct vcpu *old_v; -+ -+ lastq.raw = read_atomic(&evtchn->fifo_lastq); -+ old_v = d->vcpu[lastq.last_vcpu_id]; -+ -+ q = &v->evtchn_fifo->queue[evtchn->priority]; -+ old_q = &old_v->evtchn_fifo->queue[lastq.last_priority]; -+ -+ if ( q == old_q ) -+ spin_lock_irqsave(&q->lock, flags); -+ else if ( q < old_q ) -+ { -+ spin_lock_irqsave(&q->lock, flags); -+ spin_lock(&old_q->lock); -+ } -+ else -+ { -+ spin_lock_irqsave(&old_q->lock, flags); -+ spin_lock(&q->lock); -+ } -+ -+ lastq.raw = read_atomic(&evtchn->fifo_lastq); -+ old_v = d->vcpu[lastq.last_vcpu_id]; -+ if ( q == &v->evtchn_fifo->queue[evtchn->priority] && -+ old_q == &old_v->evtchn_fifo->queue[lastq.last_priority] ) -+ break; -+ -+ if ( q != old_q ) -+ spin_unlock(&old_q->lock); -+ spin_unlock_irqrestore(&q->lock, flags); -+ } -+ - was_pending = guest_test_and_set_bit(d, EVTCHN_FIFO_PENDING, word); - -+ /* If we didn't get the lock bail out. */ -+ if ( try == 3 ) -+ { -+ gprintk(XENLOG_WARNING, -+ "%pd port %u lost event (too many queue changes)\n", -+ d, evtchn->port); -+ goto done; -+ } -+ - /* - * Link the event if it unmasked and not already linked. - */ - if ( !guest_test_bit(d, EVTCHN_FIFO_MASKED, word) && - !guest_test_bit(d, EVTCHN_FIFO_LINKED, word) ) - { -- struct evtchn_fifo_queue *q, *old_q; - event_word_t *tail_word; -- bool_t linked = 0; - - /* - * Control block not mapped. The guest must not unmask an -@@ -204,25 +225,11 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) - { - printk(XENLOG_G_WARNING - "%pv has no FIFO event channel control block\n", v); -- goto done; -+ goto unlock; - } - -- /* -- * No locking around getting the queue. This may race with -- * changing the priority but we are allowed to signal the -- * event once on the old priority. -- */ -- q = &v->evtchn_fifo->queue[evtchn->priority]; -- -- old_q = lock_old_queue(d, evtchn, &flags); -- if ( !old_q ) -- goto done; -- - if ( guest_test_and_set_bit(d, EVTCHN_FIFO_LINKED, word) ) -- { -- spin_unlock_irqrestore(&old_q->lock, flags); -- goto done; -- } -+ goto unlock; - - /* - * If this event was a tail, the old queue is now empty and -@@ -241,8 +248,8 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) - lastq.last_priority = q->priority; - write_atomic(&evtchn->fifo_lastq, lastq.raw); - -- spin_unlock_irqrestore(&old_q->lock, flags); -- spin_lock_irqsave(&q->lock, flags); -+ spin_unlock(&old_q->lock); -+ old_q = q; - } - - /* -@@ -255,6 +262,7 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) - * If the queue is empty (i.e., we haven't linked to the new - * event), head must be updated. - */ -+ linked = false; - if ( q->tail ) - { - tail_word = evtchn_fifo_word_from_port(d, q->tail); -@@ -263,15 +271,19 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) - if ( !linked ) - write_atomic(q->head, port); - q->tail = port; -+ } - -- spin_unlock_irqrestore(&q->lock, flags); -+ unlock: -+ if ( q != old_q ) -+ spin_unlock(&old_q->lock); -+ spin_unlock_irqrestore(&q->lock, flags); - -- if ( !linked -- && !guest_test_and_set_bit(d, q->priority, -- &v->evtchn_fifo->control_block->ready) ) -- vcpu_mark_events_pending(v); -- } - done: -+ if ( !linked && -+ !guest_test_and_set_bit(d, q->priority, -+ &v->evtchn_fifo->control_block->ready) ) -+ vcpu_mark_events_pending(v); -+ - if ( !was_pending ) - evtchn_check_pollers(d, port); - } --- -2.20.1 - diff --git a/xen.git-8d148003fdf7bd9e28137e6683ef46902af39146.patch b/xen.git-8d148003fdf7bd9e28137e6683ef46902af39146.patch deleted file mode 100644 index dfff098..0000000 --- a/xen.git-8d148003fdf7bd9e28137e6683ef46902af39146.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 8d148003fdf7bd9e28137e6683ef46902af39146 Mon Sep 17 00:00:00 2001 -From: Jan Beulich -Date: Tue, 20 Oct 2020 14:42:52 +0200 -Subject: [PATCH] evtchn/fifo: use stable fields when recording "last queue" - information - -Both evtchn->priority and evtchn->notify_vcpu_id could change behind the -back of evtchn_fifo_set_pending(), as for it - in the case of -interdomain channels - only the remote side's per-channel lock is held. -Neither the queue's priority nor the vCPU's vcpu_id fields have similar -properties, so they seem better suited for the purpose. In particular -they reflect the respective evtchn fields' values at the time they were -used to determine queue and vCPU. - -Signed-off-by: Jan Beulich -Reviewed-by: Julien Grall -Reviewed-by: Paul Durrant -master commit: 6f6f07b64cbe90e54f8e62b4d6f2404cf5306536 -master date: 2020-10-02 08:37:35 +0200 ---- - xen/common/event_fifo.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/xen/common/event_fifo.c b/xen/common/event_fifo.c -index 68d0c7a632..27ab3a1c3f 100644 ---- a/xen/common/event_fifo.c -+++ b/xen/common/event_fifo.c -@@ -225,8 +225,8 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) - /* Moved to a different queue? */ - if ( old_q != q ) - { -- evtchn->last_vcpu_id = evtchn->notify_vcpu_id; -- evtchn->last_priority = evtchn->priority; -+ evtchn->last_vcpu_id = v->vcpu_id; -+ evtchn->last_priority = q->priority; - - spin_unlock_irqrestore(&old_q->lock, flags); - spin_lock_irqsave(&q->lock, flags); --- -2.20.1 - diff --git a/xen.spec b/xen.spec index 122da22..40f2d33 100644 --- a/xen.spec +++ b/xen.spec @@ -57,8 +57,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.14.0 -Release: 14%{?dist} +Version: 4.14.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 @@ -111,72 +111,8 @@ Patch39: qemu.trad.CVE-2017-9330.patch Patch40: xen.drop.brctl.patch Patch42: xen.gcc9.fixes.patch Patch45: xen.gcc10.fixes.patch -Patch46: xsa335-qemu.patch -Patch47: xsa335-trad.patch -Patch48: xsa333.patch -Patch49: xsa334.patch -Patch50: xsa336.patch -Patch51: xsa337-1.patch -Patch52: xsa337-2.patch -Patch53: xsa338.patch -Patch54: xsa339.patch -Patch55: xsa340.patch -Patch56: xsa342.patch -Patch57: xsa343-1.patch -Patch58: xsa343-2.patch -Patch59: xsa343-3.patch -Patch60: xsa344-1.patch -Patch61: xsa344-2.patch -Patch62: xsa345-4.14-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch -Patch63: xsa345-4.14-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch -Patch64: xsa345-4.14-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch -Patch65: xsa346-1.patch -Patch66: xsa346-2.patch -Patch67: xsa347-4.14-1.patch -Patch68: xsa347-4.14-2.patch -Patch69: xsa347-4.14-3.patch Patch70: xen.gcc11.fixes.patch -Patch71: xsa286-4.14-0001-x86-pv-Drop-FLUSH_TLB_GLOBAL-in-do_mmu_update-for-XP.patch -Patch72: xsa286-4.14-0002-x86-pv-Flush-TLB-in-response-to-paging-structure-cha.patch -Patch73: xsa351-arm.patch -Patch74: xsa351-x86-4.14-1.patch -Patch75: xsa351-x86-4.14-2.patch Patch76: zstd-dom0.patch -Patch77: xsa355.patch -Patch78: xsa115-4.14-c-0001-tools-xenstore-allow-removing-child-of-a-node-exceed.patch -Patch79: xsa115-4.14-c-0002-tools-xenstore-ignore-transaction-id-for-un-watch.patch -Patch80: xsa115-4.14-c-0003-tools-xenstore-fix-node-accounting-after-failed-node.patch -Patch81: xsa115-4.14-c-0004-tools-xenstore-simplify-and-rename-check_event_node.patch -Patch82: xsa115-4.14-c-0005-tools-xenstore-check-privilege-for-XS_IS_DOMAIN_INTR.patch -Patch83: xsa115-4.14-c-0006-tools-xenstore-rework-node-removal.patch -Patch84: xsa115-4.14-c-0007-tools-xenstore-fire-watches-only-when-removing-a-spe.patch -Patch85: xsa115-4.14-c-0008-tools-xenstore-introduce-node_perms-structure.patch -Patch86: xsa115-4.14-c-0009-tools-xenstore-allow-special-watches-for-privileged-.patch -Patch87: xsa115-4.14-c-0010-tools-xenstore-avoid-watch-events-for-nodes-without-.patch -Patch88: xsa115-o-0001-tools-ocaml-xenstored-ignore-transaction-id-for-un-w.patch -Patch89: xsa115-o-0002-tools-ocaml-xenstored-check-privilege-for-XS_IS_DOMA.patch -Patch90: xsa115-o-0003-tools-ocaml-xenstored-unify-watch-firing.patch -Patch91: xsa115-o-0004-tools-ocaml-xenstored-introduce-permissions-for-spec.patch -Patch92: xsa115-o-0005-tools-ocaml-xenstored-avoid-watch-events-for-nodes-w.patch -Patch93: xsa115-o-0006-tools-ocaml-xenstored-add-xenstored.conf-flag-to-tur.patch -Patch94: xsa322-4.14-c.patch -Patch95: xsa322-o.patch -Patch96: xsa323.patch -Patch97: xsa324.patch -Patch98: xsa325-4.14.patch -Patch99: xsa330.patch -Patch100: xsa348-1.patch -Patch101: xsa348-2.patch -Patch102: xsa348-3.patch -Patch103: xsa352.patch -Patch104: xsa353.patch -Patch105: xsa356.patch -Patch106: xen.git-8d148003fdf7bd9e28137e6683ef46902af39146.patch -Patch107: xen.git-1ad177370df2db9129c97c7305962fc5ad298728.patch -Patch108: xen.git-1cfb9b1c5b9e4c024f5f139d7a3d0357d2417b13.patch -Patch109: xen.git-72bd989f51878bc9ba61e930b0c29b921a30dc0d.patch -Patch110: xsa358.patch -Patch111: xsa359.patch %if %build_qemutrad @@ -378,71 +314,8 @@ manage Xen virtual machines. %patch40 -p1 %patch42 -p1 %patch45 -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 -%patch107 -p1 -%patch108 -p1 -%patch109 -p1 -%patch110 -p1 -%patch111 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -459,7 +332,6 @@ popd # qemu-xen patches pushd tools/qemu-xen -%patch46 -p1 popd # stubdom sources @@ -1036,6 +908,11 @@ fi %endif %changelog +* Sun Dec 20 2020 Michael Young - 4.14.1-1 +- update to 4.14.1 + adjust xen.canonicalize.patch + remove or adjust patches now included or superceded upstream + * Tue Dec 15 2020 Michael Young - 4.14.0-14 - xenstore watch notifications lacking permission checks [XSA-115, CVE-2020-29480] (#1908091) diff --git a/xsa115-4.14-c-0001-tools-xenstore-allow-removing-child-of-a-node-exceed.patch b/xsa115-4.14-c-0001-tools-xenstore-allow-removing-child-of-a-node-exceed.patch deleted file mode 100644 index fb29db7..0000000 --- a/xsa115-4.14-c-0001-tools-xenstore-allow-removing-child-of-a-node-exceed.patch +++ /dev/null @@ -1,157 +0,0 @@ -From 71623492f7b1b6d63ed76e2bf970c113b88ffa0b Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Thu, 11 Jun 2020 16:12:37 +0200 -Subject: [PATCH 01/10] tools/xenstore: allow removing child of a node - exceeding quota - -An unprivileged user of Xenstore is not allowed to write nodes with a -size exceeding a global quota, while privileged users like dom0 are -allowed to write such nodes. The size of a node is the needed space -to store all node specific data, this includes the names of all -children of the node. - -When deleting a node its parent has to be modified by removing the -name of the to be deleted child from it. - -This results in the strange situation that an unprivileged owner of a -node might not succeed in deleting that node in case its parent is -exceeding the quota of that unprivileged user (it might have been -written by dom0), as the user is not allowed to write the updated -parent node. - -Fix that by not checking the quota when writing a node for the -purpose of removing a child's name only. - -The same applies to transaction handling: a node being read during a -transaction is written to the transaction specific area and it should -not be tested for exceeding the quota, as it might not be owned by -the reader and presumably the original write would have failed if the -node is owned by the reader. - -This is part of XSA-115. - -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall -Reviewed-by: Paul Durrant ---- - tools/xenstore/xenstored_core.c | 20 +++++++++++--------- - tools/xenstore/xenstored_core.h | 3 ++- - tools/xenstore/xenstored_transaction.c | 2 +- - 3 files changed, 14 insertions(+), 11 deletions(-) - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 7bd959f28b39..62a17a686edc 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -419,7 +419,8 @@ static struct node *read_node(struct connection *conn, const void *ctx, - return node; - } - --int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node) -+int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, -+ bool no_quota_check) - { - TDB_DATA data; - void *p; -@@ -429,7 +430,7 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node) - + node->num_perms*sizeof(node->perms[0]) - + node->datalen + node->childlen; - -- if (domain_is_unprivileged(conn) && -+ if (!no_quota_check && domain_is_unprivileged(conn) && - data.dsize >= quota_max_entry_size) { - errno = ENOSPC; - return errno; -@@ -457,14 +458,15 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node) - return 0; - } - --static int write_node(struct connection *conn, struct node *node) -+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)) - return errno; - -- return write_node_raw(conn, &key, node); -+ return write_node_raw(conn, &key, node, no_quota_check); - } - - static enum xs_perm_type perm_for_conn(struct connection *conn, -@@ -1001,7 +1003,7 @@ static struct node *create_node(struct connection *conn, const void *ctx, - /* We write out the nodes down, setting destructor in case - * something goes wrong. */ - for (i = node; i; i = i->parent) { -- if (write_node(conn, i)) { -+ if (write_node(conn, i, false)) { - domain_entry_dec(conn, i); - return NULL; - } -@@ -1041,7 +1043,7 @@ static int do_write(struct connection *conn, struct buffered_data *in) - } else { - node->data = in->buffer + offset; - node->datalen = datalen; -- if (write_node(conn, node)) -+ if (write_node(conn, node, false)) - return errno; - } - -@@ -1117,7 +1119,7 @@ static int remove_child_entry(struct connection *conn, struct node *node, - size_t childlen = strlen(node->children + offset); - memdel(node->children, offset, childlen + 1, node->childlen); - node->childlen -= childlen + 1; -- return write_node(conn, node); -+ return write_node(conn, node, true); - } - - -@@ -1256,7 +1258,7 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) - node->num_perms = num; - domain_entry_inc(conn, node); - -- if (write_node(conn, node)) -+ if (write_node(conn, node, false)) - return errno; - - fire_watches(conn, in, name, false); -@@ -1516,7 +1518,7 @@ static void manual_node(const char *name, const char *child) - if (child) - node->childlen = strlen(child) + 1; - -- if (write_node(NULL, node)) -+ if (write_node(NULL, node, false)) - barf_perror("Could not create initial node %s", name); - talloc_free(node); - } -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index c4c32bc88f0c..29d638fbc5a0 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -149,7 +149,8 @@ void send_ack(struct connection *conn, enum xsd_sockmsg_type type); - char *xenstore_canonicalize(struct connection *conn, const void *ctx, const char *node); - - /* Write a node to the tdb data base. */ --int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node); -+int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, -+ bool no_quota_check); - - /* Get this node, checking we have permissions. */ - struct node *get_node(struct connection *conn, -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index 2824f7b359b8..e87897573469 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -276,7 +276,7 @@ int access_node(struct connection *conn, struct node *node, - i->check_gen = true; - if (node->generation != NO_GENERATION) { - set_tdb_key(trans_name, &local_key); -- ret = write_node_raw(conn, &local_key, node); -+ ret = write_node_raw(conn, &local_key, node, true); - if (ret) - goto err; - i->ta_node = true; --- -2.17.1 - diff --git a/xsa115-4.14-c-0002-tools-xenstore-ignore-transaction-id-for-un-watch.patch b/xsa115-4.14-c-0002-tools-xenstore-ignore-transaction-id-for-un-watch.patch deleted file mode 100644 index 42ccd5a..0000000 --- a/xsa115-4.14-c-0002-tools-xenstore-ignore-transaction-id-for-un-watch.patch +++ /dev/null @@ -1,86 +0,0 @@ -From 072c729cfe90b4b09cacb12d912ba088db8274fe Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Thu, 11 Jun 2020 16:12:38 +0200 -Subject: [PATCH 02/10] tools/xenstore: ignore transaction id for [un]watch - -Instead of ignoring the transaction id for XS_WATCH and XS_UNWATCH -commands as it is documented in docs/misc/xenstore.txt, it is tested -for validity today. - -Really ignore the transaction id for XS_WATCH and XS_UNWATCH. - -This is part of XSA-115. - -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall -Reviewed-by: Paul Durrant ---- - tools/xenstore/xenstored_core.c | 26 ++++++++++++++++---------- - 1 file changed, 16 insertions(+), 10 deletions(-) - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 62a17a686edc..2f989524b497 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1270,13 +1270,17 @@ 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); -+ unsigned int flags; -+#define XS_FLAG_NOTID (1U << 0) /* Ignore transaction id. */ - } const wire_funcs[XS_TYPE_COUNT] = { - [XS_CONTROL] = { "CONTROL", do_control }, - [XS_DIRECTORY] = { "DIRECTORY", send_directory }, - [XS_READ] = { "READ", do_read }, - [XS_GET_PERMS] = { "GET_PERMS", do_get_perms }, -- [XS_WATCH] = { "WATCH", do_watch }, -- [XS_UNWATCH] = { "UNWATCH", do_unwatch }, -+ [XS_WATCH] = -+ { "WATCH", do_watch, XS_FLAG_NOTID }, -+ [XS_UNWATCH] = -+ { "UNWATCH", do_unwatch, XS_FLAG_NOTID }, - [XS_TRANSACTION_START] = { "TRANSACTION_START", do_transaction_start }, - [XS_TRANSACTION_END] = { "TRANSACTION_END", do_transaction_end }, - [XS_INTRODUCE] = { "INTRODUCE", do_introduce }, -@@ -1298,7 +1302,7 @@ static struct { - - static const char *sockmsg_string(enum xsd_sockmsg_type type) - { -- if ((unsigned)type < XS_TYPE_COUNT && wire_funcs[type].str) -+ if ((unsigned int)type < ARRAY_SIZE(wire_funcs) && wire_funcs[type].str) - return wire_funcs[type].str; - - return "**UNKNOWN**"; -@@ -1313,7 +1317,14 @@ static void process_message(struct connection *conn, struct buffered_data *in) - enum xsd_sockmsg_type type = in->hdr.msg.type; - int ret; - -- trans = transaction_lookup(conn, in->hdr.msg.tx_id); -+ if ((unsigned int)type >= XS_TYPE_COUNT || !wire_funcs[type].func) { -+ eprintf("Client unknown operation %i", type); -+ send_error(conn, ENOSYS); -+ return; -+ } -+ -+ trans = (wire_funcs[type].flags & XS_FLAG_NOTID) -+ ? NULL : transaction_lookup(conn, in->hdr.msg.tx_id); - if (IS_ERR(trans)) { - send_error(conn, -PTR_ERR(trans)); - return; -@@ -1322,12 +1333,7 @@ static void process_message(struct connection *conn, struct buffered_data *in) - assert(conn->transaction == NULL); - conn->transaction = trans; - -- if ((unsigned)type < XS_TYPE_COUNT && wire_funcs[type].func) -- ret = wire_funcs[type].func(conn, in); -- else { -- eprintf("Client unknown operation %i", type); -- ret = ENOSYS; -- } -+ ret = wire_funcs[type].func(conn, in); - if (ret) - send_error(conn, ret); - --- -2.17.1 - diff --git a/xsa115-4.14-c-0003-tools-xenstore-fix-node-accounting-after-failed-node.patch b/xsa115-4.14-c-0003-tools-xenstore-fix-node-accounting-after-failed-node.patch deleted file mode 100644 index 94c3f1f..0000000 --- a/xsa115-4.14-c-0003-tools-xenstore-fix-node-accounting-after-failed-node.patch +++ /dev/null @@ -1,104 +0,0 @@ -From a133627453898759ca73dd5c1c185c3830fed754 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Thu, 11 Jun 2020 16:12:39 +0200 -Subject: [PATCH 03/10] tools/xenstore: fix node accounting after failed node - creation - -When a node creation fails the number of nodes of the domain should be -the same as before the failed node creation. In case of failure when -trying to create a node requiring to create one or more intermediate -nodes as well (e.g. when /a/b/c/d is to be created, but /a/b isn't -existing yet) it might happen that the number of nodes of the creating -domain is not reset to the value it had before. - -So move the quota accounting out of construct_node() and into the node -write loop in create_node() in order to be able to undo the accounting -in case of an error in the intermediate node destructor. - -This is part of XSA-115. - -Signed-off-by: Juergen Gross -Reviewed-by: Paul Durrant -Acked-by: Julien Grall ---- - tools/xenstore/xenstored_core.c | 37 ++++++++++++++++++++++----------- - 1 file changed, 25 insertions(+), 12 deletions(-) - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 2f989524b497..c971519e542a 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -927,11 +927,6 @@ static struct node *construct_node(struct connection *conn, const void *ctx, - if (!parent) - return NULL; - -- if (domain_entry(conn) >= quota_nb_entry_per_domain) { -- errno = ENOSPC; -- return NULL; -- } -- - /* Add child to parent. */ - base = basename(name); - baselen = strlen(base) + 1; -@@ -964,7 +959,6 @@ static struct node *construct_node(struct connection *conn, const void *ctx, - node->children = node->data = NULL; - node->childlen = node->datalen = 0; - node->parent = parent; -- domain_entry_inc(conn, node); - return node; - - nomem: -@@ -984,6 +978,9 @@ static int destroy_node(void *_node) - key.dsize = strlen(node->name); - - tdb_delete(tdb_ctx, key); -+ -+ domain_entry_dec(talloc_parent(node), node); -+ - return 0; - } - -@@ -1000,18 +997,34 @@ static struct node *create_node(struct connection *conn, const void *ctx, - node->data = data; - node->datalen = datalen; - -- /* We write out the nodes down, setting destructor in case -- * something goes wrong. */ -+ /* -+ * We write out the nodes bottom up. -+ * All new created nodes will have i->parent set, while the final -+ * node will be already existing and won't have i->parent set. -+ * New nodes are subject to quota handling. -+ * Initially set a destructor for all new nodes removing them from -+ * TDB again and undoing quota accounting for the case of an error -+ * during the write loop. -+ */ - for (i = node; i; i = i->parent) { -- if (write_node(conn, i, false)) { -- domain_entry_dec(conn, i); -+ /* 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; - } -- talloc_set_destructor(i, destroy_node); -+ if (write_node(conn, i, false)) -+ return NULL; -+ -+ /* Account for new node, set destructor for error case. */ -+ 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; i = i->parent) -+ for (i = node; i->parent; i = i->parent) - talloc_set_destructor(i, NULL); - return node; - } --- -2.17.1 - diff --git a/xsa115-4.14-c-0004-tools-xenstore-simplify-and-rename-check_event_node.patch b/xsa115-4.14-c-0004-tools-xenstore-simplify-and-rename-check_event_node.patch deleted file mode 100644 index 5a7d705..0000000 --- a/xsa115-4.14-c-0004-tools-xenstore-simplify-and-rename-check_event_node.patch +++ /dev/null @@ -1,55 +0,0 @@ -From dc6cf381bdeca4013b6bfe25c27e57f010e7ca84 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Thu, 11 Jun 2020 16:12:40 +0200 -Subject: [PATCH 04/10] tools/xenstore: simplify and rename check_event_node() - -There is no path which allows to call check_event_node() without a -event name. So don't let the result depend on the name being NULL and -add an assert() covering that case. - -Rename the function to check_special_event() to better match the -semantics. - -This is part of XSA-115. - -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall -Reviewed-by: Paul Durrant ---- - tools/xenstore/xenstored_watch.c | 12 +++++------- - 1 file changed, 5 insertions(+), 7 deletions(-) - -diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c -index 7dedca60dfd6..f2f1bed47cc6 100644 ---- a/tools/xenstore/xenstored_watch.c -+++ b/tools/xenstore/xenstored_watch.c -@@ -47,13 +47,11 @@ struct watch - char *node; - }; - --static bool check_event_node(const char *node) -+static bool check_special_event(const char *name) - { -- if (!node || !strstarts(node, "@")) { -- errno = EINVAL; -- return false; -- } -- return true; -+ assert(name); -+ -+ return strstarts(name, "@"); - } - - /* Is child a subnode of parent, or equal? */ -@@ -87,7 +85,7 @@ static void add_event(struct connection *conn, - unsigned int len; - char *data; - -- if (!check_event_node(name)) { -+ if (!check_special_event(name)) { - /* Can this conn load node, or see that it doesn't exist? */ - struct node *node = get_node(conn, ctx, name, XS_PERM_READ); - /* --- -2.17.1 - diff --git a/xsa115-4.14-c-0005-tools-xenstore-check-privilege-for-XS_IS_DOMAIN_INTR.patch b/xsa115-4.14-c-0005-tools-xenstore-check-privilege-for-XS_IS_DOMAIN_INTR.patch deleted file mode 100644 index 8025401..0000000 --- a/xsa115-4.14-c-0005-tools-xenstore-check-privilege-for-XS_IS_DOMAIN_INTR.patch +++ /dev/null @@ -1,115 +0,0 @@ -From cd456dd7e3c4bbe229a0307a469c2fc3b8e7b590 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Thu, 11 Jun 2020 16:12:41 +0200 -Subject: [PATCH 05/10] tools/xenstore: check privilege for - XS_IS_DOMAIN_INTRODUCED - -The Xenstore command XS_IS_DOMAIN_INTRODUCED should be possible for -privileged domains only (the only user in the tree is the xenpaging -daemon). - -Instead of having the privilege test for each command introduce a -per-command flag for that purpose. - -This is part of XSA-115. - -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall -Reviewed-by: Paul Durrant ---- - tools/xenstore/xenstored_core.c | 24 ++++++++++++++++++------ - tools/xenstore/xenstored_domain.c | 7 ++----- - 2 files changed, 20 insertions(+), 11 deletions(-) - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index c971519e542a..f38196ae2825 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1285,8 +1285,10 @@ static struct { - int (*func)(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. */ - } const wire_funcs[XS_TYPE_COUNT] = { -- [XS_CONTROL] = { "CONTROL", do_control }, -+ [XS_CONTROL] = -+ { "CONTROL", do_control, XS_FLAG_PRIV }, - [XS_DIRECTORY] = { "DIRECTORY", send_directory }, - [XS_READ] = { "READ", do_read }, - [XS_GET_PERMS] = { "GET_PERMS", do_get_perms }, -@@ -1296,8 +1298,10 @@ static struct { - { "UNWATCH", do_unwatch, XS_FLAG_NOTID }, - [XS_TRANSACTION_START] = { "TRANSACTION_START", do_transaction_start }, - [XS_TRANSACTION_END] = { "TRANSACTION_END", do_transaction_end }, -- [XS_INTRODUCE] = { "INTRODUCE", do_introduce }, -- [XS_RELEASE] = { "RELEASE", do_release }, -+ [XS_INTRODUCE] = -+ { "INTRODUCE", do_introduce, XS_FLAG_PRIV }, -+ [XS_RELEASE] = -+ { "RELEASE", do_release, XS_FLAG_PRIV }, - [XS_GET_DOMAIN_PATH] = { "GET_DOMAIN_PATH", do_get_domain_path }, - [XS_WRITE] = { "WRITE", do_write }, - [XS_MKDIR] = { "MKDIR", do_mkdir }, -@@ -1306,9 +1310,11 @@ static struct { - [XS_WATCH_EVENT] = { "WATCH_EVENT", NULL }, - [XS_ERROR] = { "ERROR", NULL }, - [XS_IS_DOMAIN_INTRODUCED] = -- { "IS_DOMAIN_INTRODUCED", do_is_domain_introduced }, -- [XS_RESUME] = { "RESUME", do_resume }, -- [XS_SET_TARGET] = { "SET_TARGET", do_set_target }, -+ { "IS_DOMAIN_INTRODUCED", do_is_domain_introduced, XS_FLAG_PRIV }, -+ [XS_RESUME] = -+ { "RESUME", do_resume, XS_FLAG_PRIV }, -+ [XS_SET_TARGET] = -+ { "SET_TARGET", do_set_target, XS_FLAG_PRIV }, - [XS_RESET_WATCHES] = { "RESET_WATCHES", do_reset_watches }, - [XS_DIRECTORY_PART] = { "DIRECTORY_PART", send_directory_part }, - }; -@@ -1336,6 +1342,12 @@ static void process_message(struct connection *conn, struct buffered_data *in) - return; - } - -+ if ((wire_funcs[type].flags & XS_FLAG_PRIV) && -+ domain_is_unprivileged(conn)) { -+ send_error(conn, EACCES); -+ return; -+ } -+ - trans = (wire_funcs[type].flags & XS_FLAG_NOTID) - ? NULL : transaction_lookup(conn, in->hdr.msg.tx_id); - if (IS_ERR(trans)) { -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index 06359503f091..2d0d87ee89e1 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -372,7 +372,7 @@ int do_introduce(struct connection *conn, struct buffered_data *in) - if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec)) - return EINVAL; - -- if (domain_is_unprivileged(conn) || !conn->can_write) -+ if (!conn->can_write) - return EACCES; - - domid = atoi(vec[0]); -@@ -438,7 +438,7 @@ int do_set_target(struct connection *conn, struct buffered_data *in) - if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec)) - return EINVAL; - -- if (domain_is_unprivileged(conn) || !conn->can_write) -+ if (!conn->can_write) - return EACCES; - - domid = atoi(vec[0]); -@@ -473,9 +473,6 @@ static struct domain *onearg_domain(struct connection *conn, - if (!domid) - return ERR_PTR(-EINVAL); - -- if (domain_is_unprivileged(conn)) -- return ERR_PTR(-EACCES); -- - return find_connected_domain(domid); - } - --- -2.17.1 - diff --git a/xsa115-4.14-c-0006-tools-xenstore-rework-node-removal.patch b/xsa115-4.14-c-0006-tools-xenstore-rework-node-removal.patch deleted file mode 100644 index f2357b8..0000000 --- a/xsa115-4.14-c-0006-tools-xenstore-rework-node-removal.patch +++ /dev/null @@ -1,217 +0,0 @@ -From a3d8089532ae573c03e1cdb2fc3c5ee5ebb52a60 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Thu, 11 Jun 2020 16:12:42 +0200 -Subject: [PATCH 06/10] tools/xenstore: rework node removal - -Today a Xenstore node is being removed by deleting it from the parent -first and then deleting itself and all its children. This results in -stale entries remaining in the data base in case e.g. a memory -allocation is failing during processing. This would result in the -rather strange behavior to be able to read a node (as its still in the -data base) while not being visible in the tree view of Xenstore. - -Fix that by deleting the nodes from the leaf side instead of starting -at the root. - -As fire_watches() is now called from _rm() the ctx parameter needs a -const attribute. - -This is part of XSA-115. - -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall -Reviewed-by: Paul Durrant ---- - tools/xenstore/xenstored_core.c | 99 ++++++++++++++++---------------- - tools/xenstore/xenstored_watch.c | 4 +- - tools/xenstore/xenstored_watch.h | 2 +- - 3 files changed, 54 insertions(+), 51 deletions(-) - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index f38196ae2825..dfdb64f3ee60 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1089,74 +1089,76 @@ static int do_mkdir(struct connection *conn, struct buffered_data *in) - return 0; - } - --static void delete_node(struct connection *conn, struct node *node) --{ -- unsigned int i; -- char *name; -- -- /* Delete self, then delete children. If we crash, then the worst -- that can happen is the children will continue to take up space, but -- will otherwise be unreachable. */ -- delete_node_single(conn, node); -- -- /* Delete children, too. */ -- for (i = 0; i < node->childlen; i += strlen(node->children+i) + 1) { -- struct node *child; -- -- name = talloc_asprintf(node, "%s/%s", node->name, -- node->children + i); -- child = name ? read_node(conn, node, name) : NULL; -- if (child) { -- delete_node(conn, child); -- } -- else { -- trace("delete_node: Error deleting child '%s/%s'!\n", -- node->name, node->children + i); -- /* Skip it, we've already deleted the parent. */ -- } -- talloc_free(name); -- } --} -- -- - /* Delete memory using memmove. */ - static void memdel(void *mem, unsigned off, unsigned len, unsigned total) - { - memmove(mem + off, mem + off + len, total - off - len); - } - -- --static int remove_child_entry(struct connection *conn, struct node *node, -- size_t offset) -+static void 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; -- return write_node(conn, node, true); -+ if (write_node(conn, node, true)) -+ corrupt(conn, "Can't update parent node '%s'", node->name); - } - -- --static int delete_child(struct connection *conn, -- struct node *node, const char *childname) -+static void 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)) { -- return remove_child_entry(conn, node, i); -+ remove_child_entry(conn, node, i); -+ return; - } - } - corrupt(conn, "Can't find child '%s' in %s", childname, node->name); -- return ENOENT; - } - -+static int delete_node(struct connection *conn, struct node *parent, -+ struct node *node) -+{ -+ char *name; -+ -+ /* Delete children. */ -+ while (node->childlen) { -+ struct node *child; -+ -+ name = talloc_asprintf(node, "%s/%s", node->name, -+ node->children); -+ child = name ? read_node(conn, node, name) : NULL; -+ if (child) { -+ if (delete_node(conn, node, child)) -+ 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); -+ } -+ -+ delete_node_single(conn, node); -+ delete_child(conn, parent, basename(node->name)); -+ talloc_free(node); -+ -+ return 0; -+} - - static int _rm(struct connection *conn, const void *ctx, struct node *node, - const char *name) - { -- /* Delete from parent first, then if we crash, the worst that can -- happen is the child will continue to take up space, but will -- otherwise be unreachable. */ -+ /* -+ * 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); - -@@ -1167,11 +1169,13 @@ static int _rm(struct connection *conn, const void *ctx, struct node *node, - if (!parent) - return (errno == ENOMEM) ? ENOMEM : EINVAL; - -- if (delete_child(conn, parent, basename(name))) -- return EINVAL; -- -- delete_node(conn, node); -- return 0; -+ /* -+ * 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, true); -+ return delete_node(conn, parent, node); - } - - -@@ -1209,7 +1213,6 @@ static int do_rm(struct connection *conn, struct buffered_data *in) - if (ret) - return ret; - -- fire_watches(conn, in, name, true); - send_ack(conn, XS_RM); - - return 0; -diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c -index f2f1bed47cc6..f0bbfe7a6dc6 100644 ---- a/tools/xenstore/xenstored_watch.c -+++ b/tools/xenstore/xenstored_watch.c -@@ -77,7 +77,7 @@ static bool is_child(const char *child, const char *parent) - * Temporary memory allocations are done with ctx. - */ - static void add_event(struct connection *conn, -- void *ctx, -+ const void *ctx, - struct watch *watch, - const char *name) - { -@@ -121,7 +121,7 @@ static void add_event(struct connection *conn, - * Check whether any watch events are to be sent. - * Temporary memory allocations are done with ctx. - */ --void fire_watches(struct connection *conn, void *ctx, const char *name, -+void fire_watches(struct connection *conn, const void *ctx, const char *name, - bool recurse) - { - struct connection *i; -diff --git a/tools/xenstore/xenstored_watch.h b/tools/xenstore/xenstored_watch.h -index c72ea6a68542..54d4ea7e0d41 100644 ---- a/tools/xenstore/xenstored_watch.h -+++ b/tools/xenstore/xenstored_watch.h -@@ -25,7 +25,7 @@ int do_watch(struct connection *conn, struct buffered_data *in); - int do_unwatch(struct connection *conn, struct buffered_data *in); - - /* Fire all watches: recurse means all the children are affected (ie. rm). */ --void fire_watches(struct connection *conn, void *tmp, const char *name, -+void fire_watches(struct connection *conn, const void *tmp, const char *name, - bool recurse); - - void conn_delete_all_watches(struct connection *conn); --- -2.17.1 - diff --git a/xsa115-4.14-c-0007-tools-xenstore-fire-watches-only-when-removing-a-spe.patch b/xsa115-4.14-c-0007-tools-xenstore-fire-watches-only-when-removing-a-spe.patch deleted file mode 100644 index 008ce01..0000000 --- a/xsa115-4.14-c-0007-tools-xenstore-fire-watches-only-when-removing-a-spe.patch +++ /dev/null @@ -1,118 +0,0 @@ -From 3d4e3fd6c78795bf426947fbfbfa9af6568ece9f Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Thu, 11 Jun 2020 16:12:43 +0200 -Subject: [PATCH 07/10] tools/xenstore: fire watches only when removing a - specific node - -Instead of firing all watches for removing a subtree in one go, do so -only when the related node is being removed. - -The watches for the top-most node being removed include all watches -including that node, while watches for nodes below that are only fired -if they are matching exactly. This avoids firing any watch more than -once when removing a subtree. - -This is part of XSA-115. - -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall -Reviewed-by: Paul Durrant ---- - tools/xenstore/xenstored_core.c | 11 ++++++----- - tools/xenstore/xenstored_watch.c | 13 ++++++++----- - tools/xenstore/xenstored_watch.h | 4 ++-- - 3 files changed, 16 insertions(+), 12 deletions(-) - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index dfdb64f3ee60..20a7a3581555 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1120,8 +1120,8 @@ static void delete_child(struct connection *conn, - corrupt(conn, "Can't find child '%s' in %s", childname, node->name); - } - --static int delete_node(struct connection *conn, struct node *parent, -- struct node *node) -+static int delete_node(struct connection *conn, const void *ctx, -+ struct node *parent, struct node *node) - { - char *name; - -@@ -1133,7 +1133,7 @@ static int delete_node(struct connection *conn, struct node *parent, - node->children); - child = name ? read_node(conn, node, name) : NULL; - if (child) { -- if (delete_node(conn, node, child)) -+ if (delete_node(conn, ctx, node, child)) - return errno; - } else { - trace("delete_node: Error deleting child '%s/%s'!\n", -@@ -1145,6 +1145,7 @@ static int delete_node(struct connection *conn, struct node *parent, - talloc_free(name); - } - -+ fire_watches(conn, ctx, node->name, true); - delete_node_single(conn, node); - delete_child(conn, parent, basename(node->name)); - talloc_free(node); -@@ -1174,8 +1175,8 @@ static int _rm(struct connection *conn, const void *ctx, struct node *node, - * 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, true); -- return delete_node(conn, parent, node); -+ fire_watches(conn, ctx, name, false); -+ return delete_node(conn, ctx, parent, node); - } - - -diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c -index f0bbfe7a6dc6..3836675459fa 100644 ---- a/tools/xenstore/xenstored_watch.c -+++ b/tools/xenstore/xenstored_watch.c -@@ -122,7 +122,7 @@ static void add_event(struct connection *conn, - * Temporary memory allocations are done with ctx. - */ - void fire_watches(struct connection *conn, const void *ctx, const char *name, -- bool recurse) -+ bool exact) - { - struct connection *i; - struct watch *watch; -@@ -134,10 +134,13 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, - /* Create an event for each watch. */ - list_for_each_entry(i, &connections, list) { - list_for_each_entry(watch, &i->watches, list) { -- if (is_child(name, watch->node)) -- add_event(i, ctx, watch, name); -- else if (recurse && is_child(watch->node, name)) -- add_event(i, ctx, watch, watch->node); -+ if (exact) { -+ if (streq(name, watch->node)) -+ add_event(i, ctx, watch, name); -+ } else { -+ if (is_child(name, watch->node)) -+ add_event(i, ctx, watch, name); -+ } - } - } - } -diff --git a/tools/xenstore/xenstored_watch.h b/tools/xenstore/xenstored_watch.h -index 54d4ea7e0d41..1b3c80d3dda1 100644 ---- a/tools/xenstore/xenstored_watch.h -+++ b/tools/xenstore/xenstored_watch.h -@@ -24,9 +24,9 @@ - int do_watch(struct connection *conn, struct buffered_data *in); - int do_unwatch(struct connection *conn, struct buffered_data *in); - --/* Fire all watches: recurse means all the children are affected (ie. rm). */ -+/* Fire all watches: !exact means all the children are affected (ie. rm). */ - void fire_watches(struct connection *conn, const void *tmp, const char *name, -- bool recurse); -+ bool exact); - - void conn_delete_all_watches(struct connection *conn); - --- -2.17.1 - diff --git a/xsa115-4.14-c-0008-tools-xenstore-introduce-node_perms-structure.patch b/xsa115-4.14-c-0008-tools-xenstore-introduce-node_perms-structure.patch deleted file mode 100644 index c295e8c..0000000 --- a/xsa115-4.14-c-0008-tools-xenstore-introduce-node_perms-structure.patch +++ /dev/null @@ -1,289 +0,0 @@ -From 1069c600f85ff583c461cfbfee1afb1a0731796e Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Thu, 11 Jun 2020 16:12:44 +0200 -Subject: [PATCH 08/10] tools/xenstore: introduce node_perms structure - -There are several places in xenstored using a permission array and the -size of that array. Introduce a new struct node_perms containing both. - -This is part of XSA-115. - -Signed-off-by: Juergen Gross -Acked-by: Julien Grall -Reviewed-by: Paul Durrant ---- - tools/xenstore/xenstored_core.c | 79 +++++++++++++++---------------- - tools/xenstore/xenstored_core.h | 8 +++- - tools/xenstore/xenstored_domain.c | 12 ++--- - 3 files changed, 50 insertions(+), 49 deletions(-) - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 20a7a3581555..79d305fbbe58 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -403,14 +403,14 @@ static struct node *read_node(struct connection *conn, const void *ctx, - /* Datalen, childlen, number of permissions */ - hdr = (void *)data.dptr; - node->generation = hdr->generation; -- node->num_perms = hdr->num_perms; -+ node->perms.num = hdr->num_perms; - node->datalen = hdr->datalen; - node->childlen = hdr->childlen; - - /* Permissions are struct xs_permissions. */ -- node->perms = hdr->perms; -+ node->perms.p = hdr->perms; - /* Data is binary blob (usually ascii, no nul). */ -- node->data = node->perms + node->num_perms; -+ node->data = node->perms.p + node->perms.num; - /* Children is strings, nul separated. */ - node->children = node->data + node->datalen; - -@@ -427,7 +427,7 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, - struct xs_tdb_record_hdr *hdr; - - data.dsize = sizeof(*hdr) -- + node->num_perms*sizeof(node->perms[0]) -+ + node->perms.num * sizeof(node->perms.p[0]) - + node->datalen + node->childlen; - - if (!no_quota_check && domain_is_unprivileged(conn) && -@@ -439,12 +439,13 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, - data.dptr = talloc_size(node, data.dsize); - hdr = (void *)data.dptr; - hdr->generation = node->generation; -- hdr->num_perms = node->num_perms; -+ hdr->num_perms = node->perms.num; - hdr->datalen = node->datalen; - hdr->childlen = node->childlen; - -- memcpy(hdr->perms, node->perms, node->num_perms*sizeof(node->perms[0])); -- p = hdr->perms + node->num_perms; -+ memcpy(hdr->perms, node->perms.p, -+ node->perms.num * sizeof(*node->perms.p)); -+ p = hdr->perms + node->perms.num; - memcpy(p, node->data, node->datalen); - p += node->datalen; - memcpy(p, node->children, node->childlen); -@@ -470,8 +471,7 @@ static int write_node(struct connection *conn, struct node *node, - } - - static enum xs_perm_type perm_for_conn(struct connection *conn, -- struct xs_permissions *perms, -- unsigned int num) -+ const struct node_perms *perms) - { - unsigned int i; - enum xs_perm_type mask = XS_PERM_READ|XS_PERM_WRITE|XS_PERM_OWNER; -@@ -480,16 +480,16 @@ static enum xs_perm_type perm_for_conn(struct connection *conn, - mask &= ~XS_PERM_WRITE; - - /* Owners and tools get it all... */ -- if (!domain_is_unprivileged(conn) || perms[0].id == conn->id -- || (conn->target && perms[0].id == conn->target->id)) -+ if (!domain_is_unprivileged(conn) || perms->p[0].id == conn->id -+ || (conn->target && perms->p[0].id == conn->target->id)) - return (XS_PERM_READ|XS_PERM_WRITE|XS_PERM_OWNER) & mask; - -- for (i = 1; i < num; i++) -- if (perms[i].id == conn->id -- || (conn->target && perms[i].id == conn->target->id)) -- return perms[i].perms & mask; -+ for (i = 1; i < perms->num; i++) -+ if (perms->p[i].id == conn->id -+ || (conn->target && perms->p[i].id == conn->target->id)) -+ return perms->p[i].perms & mask; - -- return perms[0].perms & mask; -+ return perms->p[0].perms & mask; - } - - /* -@@ -536,7 +536,7 @@ static int ask_parents(struct connection *conn, const void *ctx, - return 0; - } - -- *perm = perm_for_conn(conn, node->perms, node->num_perms); -+ *perm = perm_for_conn(conn, &node->perms); - return 0; - } - -@@ -582,8 +582,7 @@ struct node *get_node(struct connection *conn, - node = read_node(conn, ctx, name); - /* If we don't have permission, we don't have node. */ - if (node) { -- if ((perm_for_conn(conn, node->perms, node->num_perms) & perm) -- != perm) { -+ if ((perm_for_conn(conn, &node->perms) & perm) != perm) { - errno = EACCES; - node = NULL; - } -@@ -759,16 +758,15 @@ const char *onearg(struct buffered_data *in) - return in->buffer; - } - --static char *perms_to_strings(const void *ctx, -- struct xs_permissions *perms, unsigned int num, -+static char *perms_to_strings(const void *ctx, const struct node_perms *perms, - unsigned int *len) - { - unsigned int i; - char *strings = NULL; - char buffer[MAX_STRLEN(unsigned int) + 1]; - -- for (*len = 0, i = 0; i < num; i++) { -- if (!xs_perm_to_string(&perms[i], buffer, sizeof(buffer))) -+ for (*len = 0, i = 0; i < perms->num; i++) { -+ if (!xs_perm_to_string(&perms->p[i], buffer, sizeof(buffer))) - return NULL; - - strings = talloc_realloc(ctx, strings, char, -@@ -947,13 +945,13 @@ static struct node *construct_node(struct connection *conn, const void *ctx, - goto nomem; - - /* Inherit permissions, except unprivileged domains own what they create */ -- node->num_perms = parent->num_perms; -- node->perms = talloc_memdup(node, parent->perms, -- node->num_perms * sizeof(node->perms[0])); -- if (!node->perms) -+ 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[0].id = conn->id; -+ node->perms.p[0].id = conn->id; - - /* No children, no data */ - node->children = node->data = NULL; -@@ -1230,7 +1228,7 @@ static int do_get_perms(struct connection *conn, struct buffered_data *in) - if (!node) - return errno; - -- strings = perms_to_strings(node, node->perms, node->num_perms, &len); -+ strings = perms_to_strings(node, &node->perms, &len); - if (!strings) - return errno; - -@@ -1241,13 +1239,12 @@ static int do_get_perms(struct connection *conn, struct buffered_data *in) - - static int do_set_perms(struct connection *conn, struct buffered_data *in) - { -- unsigned int num; -- struct xs_permissions *perms; -+ struct node_perms perms; - char *name, *permstr; - struct node *node; - -- num = xs_count_strings(in->buffer, in->used); -- if (num < 2) -+ perms.num = xs_count_strings(in->buffer, in->used); -+ if (perms.num < 2) - return EINVAL; - - /* First arg is node name. */ -@@ -1258,21 +1255,21 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) - return errno; - - permstr = in->buffer + strlen(in->buffer) + 1; -- num--; -+ perms.num--; - -- perms = talloc_array(node, struct xs_permissions, num); -- if (!perms) -+ perms.p = talloc_array(node, struct xs_permissions, perms.num); -+ if (!perms.p) - return ENOMEM; -- if (!xs_strings_to_perms(perms, num, permstr)) -+ if (!xs_strings_to_perms(perms.p, perms.num, permstr)) - return errno; - - /* Unprivileged domains may not change the owner. */ -- if (domain_is_unprivileged(conn) && perms[0].id != node->perms[0].id) -+ if (domain_is_unprivileged(conn) && -+ perms.p[0].id != node->perms.p[0].id) - return EPERM; - - domain_entry_dec(conn, node); - node->perms = perms; -- node->num_perms = num; - domain_entry_inc(conn, node); - - if (write_node(conn, node, false)) -@@ -1547,8 +1544,8 @@ static void manual_node(const char *name, const char *child) - barf_perror("Could not allocate initial node %s", name); - - node->name = name; -- node->perms = &perms; -- node->num_perms = 1; -+ node->perms.p = &perms; -+ node->perms.num = 1; - node->children = (char *)child; - if (child) - node->childlen = strlen(child) + 1; -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index 29d638fbc5a0..47ba0916dbe2 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -109,6 +109,11 @@ struct connection - }; - extern struct list_head connections; - -+struct node_perms { -+ unsigned int num; -+ struct xs_permissions *p; -+}; -+ - struct node { - const char *name; - -@@ -120,8 +125,7 @@ struct node { - #define NO_GENERATION ~((uint64_t)0) - - /* Permissions. */ -- unsigned int num_perms; -- struct xs_permissions *perms; -+ struct node_perms perms; - - /* Contents. */ - unsigned int datalen; -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index 2d0d87ee89e1..aa9942fcc267 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -650,12 +650,12 @@ void domain_entry_inc(struct connection *conn, struct node *node) - if (!conn) - return; - -- if (node->perms && node->perms[0].id != conn->id) { -+ if (node->perms.p && node->perms.p[0].id != conn->id) { - if (conn->transaction) { - transaction_entry_inc(conn->transaction, -- node->perms[0].id); -+ node->perms.p[0].id); - } else { -- d = find_domain_by_domid(node->perms[0].id); -+ d = find_domain_by_domid(node->perms.p[0].id); - if (d) - d->nbentry++; - } -@@ -676,12 +676,12 @@ void domain_entry_dec(struct connection *conn, struct node *node) - if (!conn) - return; - -- if (node->perms && node->perms[0].id != conn->id) { -+ if (node->perms.p && node->perms.p[0].id != conn->id) { - if (conn->transaction) { - transaction_entry_dec(conn->transaction, -- node->perms[0].id); -+ node->perms.p[0].id); - } else { -- d = find_domain_by_domid(node->perms[0].id); -+ d = find_domain_by_domid(node->perms.p[0].id); - if (d && d->nbentry) - d->nbentry--; - } --- -2.17.1 - diff --git a/xsa115-4.14-c-0009-tools-xenstore-allow-special-watches-for-privileged-.patch b/xsa115-4.14-c-0009-tools-xenstore-allow-special-watches-for-privileged-.patch deleted file mode 100644 index e1e8942..0000000 --- a/xsa115-4.14-c-0009-tools-xenstore-allow-special-watches-for-privileged-.patch +++ /dev/null @@ -1,237 +0,0 @@ -From b9fff4b7ad6b41db860a43d35c401847fef789cb Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Thu, 11 Jun 2020 16:12:45 +0200 -Subject: [PATCH 09/10] tools/xenstore: allow special watches for privileged - callers only - -The special watches "@introduceDomain" and "@releaseDomain" should be -allowed for privileged callers only, as they allow to gain information -about presence of other guests on the host. So send watch events for -those watches via privileged connections only. - -In order to allow for disaggregated setups where e.g. driver domains -need to make use of those special watches add support for calling -"set permissions" for those special nodes, too. - -This is part of XSA-115. - -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall -Reviewed-by: Paul Durrant ---- - docs/misc/xenstore.txt | 5 +++ - tools/xenstore/xenstored_core.c | 27 ++++++++------ - tools/xenstore/xenstored_core.h | 2 ++ - tools/xenstore/xenstored_domain.c | 60 +++++++++++++++++++++++++++++++ - tools/xenstore/xenstored_domain.h | 5 +++ - tools/xenstore/xenstored_watch.c | 4 +++ - 6 files changed, 93 insertions(+), 10 deletions(-) - -diff --git a/docs/misc/xenstore.txt b/docs/misc/xenstore.txt -index cb8009cb686d..2081f20f55e4 100644 ---- a/docs/misc/xenstore.txt -+++ b/docs/misc/xenstore.txt -@@ -170,6 +170,9 @@ SET_PERMS ||+? - n no access - See https://wiki.xen.org/wiki/XenBus section - `Permissions' for details of the permissions system. -+ It is possible to set permissions for the special watch paths -+ "@introduceDomain" and "@releaseDomain" to enable receiving those -+ watches in unprivileged domains. - - ---------- Watches ---------- - -@@ -194,6 +197,8 @@ WATCH ||? - @releaseDomain occurs on any domain crash or - shutdown, and also on RELEASE - and domain destruction -+ events are sent to privileged callers or explicitly -+ via SET_PERMS enabled domains only. - - When a watch is first set up it is triggered once straight - away, with equal to . Watches may be triggered -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 79d305fbbe58..15ffbeb30f19 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -470,8 +470,8 @@ static int write_node(struct connection *conn, struct node *node, - return write_node_raw(conn, &key, node, no_quota_check); - } - --static enum xs_perm_type perm_for_conn(struct connection *conn, -- const struct node_perms *perms) -+enum xs_perm_type perm_for_conn(struct connection *conn, -+ const struct node_perms *perms) - { - unsigned int i; - enum xs_perm_type mask = XS_PERM_READ|XS_PERM_WRITE|XS_PERM_OWNER; -@@ -1247,22 +1247,29 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) - if (perms.num < 2) - return EINVAL; - -- /* First arg is node name. */ -- /* We must own node to do this (tools can do this too). */ -- node = get_node_canonicalized(conn, in, in->buffer, &name, -- XS_PERM_WRITE | XS_PERM_OWNER); -- if (!node) -- return errno; -- - permstr = in->buffer + strlen(in->buffer) + 1; - perms.num--; - -- perms.p = talloc_array(node, struct xs_permissions, perms.num); -+ perms.p = talloc_array(in, struct xs_permissions, perms.num); - if (!perms.p) - return ENOMEM; - if (!xs_strings_to_perms(perms.p, perms.num, permstr)) - return errno; - -+ /* First arg is node name. */ -+ if (strstarts(in->buffer, "@")) { -+ if (set_perms_special(conn, in->buffer, &perms)) -+ return errno; -+ send_ack(conn, XS_SET_PERMS); -+ return 0; -+ } -+ -+ /* We must own node to do this (tools can do this too). */ -+ node = get_node_canonicalized(conn, in, in->buffer, &name, -+ XS_PERM_WRITE | XS_PERM_OWNER); -+ if (!node) -+ return errno; -+ - /* Unprivileged domains may not change the owner. */ - if (domain_is_unprivileged(conn) && - perms.p[0].id != node->perms.p[0].id) -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index 47ba0916dbe2..53f1050859fc 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -165,6 +165,8 @@ struct node *get_node(struct connection *conn, - struct connection *new_connection(connwritefn_t *write, connreadfn_t *read); - void check_store(void); - void corrupt(struct connection *conn, const char *fmt, ...); -+enum xs_perm_type perm_for_conn(struct connection *conn, -+ const struct node_perms *perms); - - /* Is this a valid node name? */ - bool is_valid_nodename(const char *node); -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index aa9942fcc267..a0d1a11c837f 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -41,6 +41,9 @@ static evtchn_port_t virq_port; - - xenevtchn_handle *xce_handle = NULL; - -+static struct node_perms dom_release_perms; -+static struct node_perms dom_introduce_perms; -+ - struct domain - { - struct list_head list; -@@ -582,6 +585,59 @@ void restore_existing_connections(void) - { - } - -+static int set_dom_perms_default(struct node_perms *perms) -+{ -+ perms->num = 1; -+ perms->p = talloc_array(NULL, struct xs_permissions, perms->num); -+ if (!perms->p) -+ return -1; -+ perms->p->id = 0; -+ perms->p->perms = XS_PERM_NONE; -+ -+ return 0; -+} -+ -+static struct node_perms *get_perms_special(const char *name) -+{ -+ if (!strcmp(name, "@releaseDomain")) -+ return &dom_release_perms; -+ if (!strcmp(name, "@introduceDomain")) -+ return &dom_introduce_perms; -+ return NULL; -+} -+ -+int set_perms_special(struct connection *conn, const char *name, -+ struct node_perms *perms) -+{ -+ struct node_perms *p; -+ -+ p = get_perms_special(name); -+ if (!p) -+ return EINVAL; -+ -+ if ((perm_for_conn(conn, p) & (XS_PERM_WRITE | XS_PERM_OWNER)) != -+ (XS_PERM_WRITE | XS_PERM_OWNER)) -+ return EACCES; -+ -+ p->num = perms->num; -+ talloc_free(p->p); -+ p->p = perms->p; -+ talloc_steal(NULL, perms->p); -+ -+ return 0; -+} -+ -+bool check_perms_special(const char *name, struct connection *conn) -+{ -+ struct node_perms *p; -+ -+ p = get_perms_special(name); -+ if (!p) -+ return false; -+ -+ return perm_for_conn(conn, p) & XS_PERM_READ; -+} -+ - static int dom0_init(void) - { - evtchn_port_t port; -@@ -603,6 +659,10 @@ static int dom0_init(void) - - xenevtchn_notify(xce_handle, dom0->port); - -+ if (set_dom_perms_default(&dom_release_perms) || -+ set_dom_perms_default(&dom_introduce_perms)) -+ return -1; -+ - return 0; - } - -diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h -index 56ae01597475..259183962a9c 100644 ---- a/tools/xenstore/xenstored_domain.h -+++ b/tools/xenstore/xenstored_domain.h -@@ -65,6 +65,11 @@ void domain_watch_inc(struct connection *conn); - void domain_watch_dec(struct connection *conn); - int domain_watch(struct connection *conn); - -+/* Special node permission handling. */ -+int set_perms_special(struct connection *conn, const char *name, -+ struct node_perms *perms); -+bool check_perms_special(const char *name, struct connection *conn); -+ - /* Write rate limiting */ - - #define WRL_FACTOR 1000 /* for fixed-point arithmetic */ -diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c -index 3836675459fa..f4e289362eb6 100644 ---- a/tools/xenstore/xenstored_watch.c -+++ b/tools/xenstore/xenstored_watch.c -@@ -133,6 +133,10 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, - - /* Create an event for each watch. */ - list_for_each_entry(i, &connections, list) { -+ /* introduce/release domain watches */ -+ if (check_special_event(name) && !check_perms_special(name, i)) -+ continue; -+ - list_for_each_entry(watch, &i->watches, list) { - if (exact) { - if (streq(name, watch->node)) --- -2.17.1 - diff --git a/xsa115-4.14-c-0010-tools-xenstore-avoid-watch-events-for-nodes-without-.patch b/xsa115-4.14-c-0010-tools-xenstore-avoid-watch-events-for-nodes-without-.patch deleted file mode 100644 index b09153c..0000000 --- a/xsa115-4.14-c-0010-tools-xenstore-avoid-watch-events-for-nodes-without-.patch +++ /dev/null @@ -1,374 +0,0 @@ -From f1cc47b0572b337269af7e34bd019584f4b8c98e Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Thu, 11 Jun 2020 16:12:46 +0200 -Subject: [PATCH 10/10] tools/xenstore: avoid watch events for nodes without - access - -Today watch events are sent regardless of the access rights of the -node the event is sent for. This enables any guest to e.g. setup a -watch for "/" in order to have a detailed record of all Xenstore -modifications. - -Modify that by sending only watch events for nodes that the watcher -has a chance to see otherwise (either via direct reads or by querying -the children of a node). This includes cases where the visibility of -a node for a watcher is changing (permissions being removed). - -This is part of XSA-115. - -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall -Reviewed-by: Paul Durrant ---- - tools/xenstore/xenstored_core.c | 28 +++++----- - tools/xenstore/xenstored_core.h | 15 ++++-- - tools/xenstore/xenstored_domain.c | 6 +-- - tools/xenstore/xenstored_transaction.c | 21 +++++++- - tools/xenstore/xenstored_watch.c | 75 +++++++++++++++++++------- - tools/xenstore/xenstored_watch.h | 2 +- - 6 files changed, 104 insertions(+), 43 deletions(-) - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 15ffbeb30f19..92bfd54cff62 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -360,8 +360,8 @@ static void initialize_fds(int *p_sock_pollfd_idx, int *p_ro_sock_pollfd_idx, - * If it fails, returns NULL and sets errno. - * Temporary memory allocations will be done with ctx. - */ --static struct node *read_node(struct connection *conn, const void *ctx, -- const char *name) -+struct node *read_node(struct connection *conn, const void *ctx, -+ const char *name) - { - TDB_DATA key, data; - struct xs_tdb_record_hdr *hdr; -@@ -496,7 +496,7 @@ enum xs_perm_type perm_for_conn(struct connection *conn, - * Get name of node parent. - * Temporary memory allocations are done with ctx. - */ --static char *get_parent(const void *ctx, const char *node) -+char *get_parent(const void *ctx, const char *node) - { - char *parent; - char *slash = strrchr(node + 1, '/'); -@@ -568,10 +568,10 @@ static int errno_from_parents(struct connection *conn, const void *ctx, - * If it fails, returns NULL and sets errno. - * Temporary memory allocations are done with ctx. - */ --struct node *get_node(struct connection *conn, -- const void *ctx, -- const char *name, -- enum xs_perm_type perm) -+static struct node *get_node(struct connection *conn, -+ const void *ctx, -+ const char *name, -+ enum xs_perm_type perm) - { - struct node *node; - -@@ -1058,7 +1058,7 @@ static int do_write(struct connection *conn, struct buffered_data *in) - return errno; - } - -- fire_watches(conn, in, name, false); -+ fire_watches(conn, in, name, node, false, NULL); - send_ack(conn, XS_WRITE); - - return 0; -@@ -1080,7 +1080,7 @@ static int do_mkdir(struct connection *conn, struct buffered_data *in) - node = create_node(conn, in, name, NULL, 0); - if (!node) - return errno; -- fire_watches(conn, in, name, false); -+ fire_watches(conn, in, name, node, false, NULL); - } - send_ack(conn, XS_MKDIR); - -@@ -1143,7 +1143,7 @@ static int delete_node(struct connection *conn, const void *ctx, - talloc_free(name); - } - -- fire_watches(conn, ctx, node->name, true); -+ fire_watches(conn, ctx, node->name, node, true, NULL); - delete_node_single(conn, node); - delete_child(conn, parent, basename(node->name)); - talloc_free(node); -@@ -1167,13 +1167,14 @@ 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; -+ 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, false); -+ fire_watches(conn, ctx, name, node, false, NULL); - return delete_node(conn, ctx, parent, node); - } - -@@ -1239,7 +1240,7 @@ static int do_get_perms(struct connection *conn, struct buffered_data *in) - - static int do_set_perms(struct connection *conn, struct buffered_data *in) - { -- struct node_perms perms; -+ struct node_perms perms, old_perms; - char *name, *permstr; - struct node *node; - -@@ -1275,6 +1276,7 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) - perms.p[0].id != node->perms.p[0].id) - return EPERM; - -+ old_perms = node->perms; - domain_entry_dec(conn, node); - node->perms = perms; - domain_entry_inc(conn, node); -@@ -1282,7 +1284,7 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) - if (write_node(conn, node, false)) - return errno; - -- fire_watches(conn, in, name, false); -+ fire_watches(conn, in, name, node, false, &old_perms); - send_ack(conn, XS_SET_PERMS); - - return 0; -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index 53f1050859fc..eb19b71f5f46 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -152,15 +152,17 @@ void send_ack(struct connection *conn, enum xsd_sockmsg_type type); - /* Canonicalize this path if possible. */ - 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, -+ const struct node_perms *perms); -+ - /* Write a node to the tdb data base. */ - int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, - bool no_quota_check); - --/* Get this node, checking we have permissions. */ --struct node *get_node(struct connection *conn, -- const void *ctx, -- const char *name, -- enum xs_perm_type perm); -+/* Get a node from the tdb data base. */ -+struct node *read_node(struct connection *conn, const void *ctx, -+ const char *name); - - struct connection *new_connection(connwritefn_t *write, connreadfn_t *read); - void check_store(void); -@@ -171,6 +173,9 @@ enum xs_perm_type perm_for_conn(struct connection *conn, - /* Is this a valid node name? */ - bool is_valid_nodename(const char *node); - -+/* Get name of parent node. */ -+char *get_parent(const void *ctx, const char *node); -+ - /* Tracing infrastructure. */ - void trace_create(const void *data, const char *type); - void trace_destroy(const void *data, const char *type); -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index a0d1a11c837f..9fad470f8331 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -202,7 +202,7 @@ static int destroy_domain(void *_domain) - unmap_interface(domain->interface); - } - -- fire_watches(NULL, domain, "@releaseDomain", false); -+ fire_watches(NULL, domain, "@releaseDomain", NULL, false, NULL); - - wrl_domain_destroy(domain); - -@@ -240,7 +240,7 @@ static void domain_cleanup(void) - } - - if (notify) -- fire_watches(NULL, NULL, "@releaseDomain", false); -+ fire_watches(NULL, NULL, "@releaseDomain", NULL, false, NULL); - } - - /* We scan all domains rather than use the information given here. */ -@@ -404,7 +404,7 @@ int do_introduce(struct connection *conn, struct buffered_data *in) - /* Now domain belongs to its connection. */ - talloc_steal(domain->conn, domain); - -- fire_watches(NULL, in, "@introduceDomain", false); -+ fire_watches(NULL, in, "@introduceDomain", NULL, false, NULL); - } else { - /* Use XS_INTRODUCE for recreating the xenbus event-channel. */ - if (domain->port) -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index e87897573469..a7d8c5d475ec 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -114,6 +114,9 @@ struct accessed_node - /* Generation count (or NO_GENERATION) for conflict checking. */ - uint64_t generation; - -+ /* Original node permissions. */ -+ struct node_perms perms; -+ - /* Generation count checking required? */ - bool check_gen; - -@@ -260,6 +263,15 @@ int access_node(struct connection *conn, struct node *node, - i->node = talloc_strdup(i, node->name); - if (!i->node) - goto nomem; -+ if (node->generation != NO_GENERATION && node->perms.num) { -+ i->perms.p = talloc_array(i, struct xs_permissions, -+ node->perms.num); -+ if (!i->perms.p) -+ goto nomem; -+ i->perms.num = node->perms.num; -+ memcpy(i->perms.p, node->perms.p, -+ i->perms.num * sizeof(*i->perms.p)); -+ } - - introduce = true; - i->ta_node = false; -@@ -368,9 +380,14 @@ static int finalize_transaction(struct connection *conn, - talloc_free(data.dptr); - if (ret) - goto err; -- } else if (tdb_delete(tdb_ctx, key)) -+ fire_watches(conn, trans, i->node, NULL, false, -+ i->perms.p ? &i->perms : NULL); -+ } else { -+ fire_watches(conn, trans, i->node, NULL, false, -+ i->perms.p ? &i->perms : NULL); -+ if (tdb_delete(tdb_ctx, key)) - goto err; -- fire_watches(conn, trans, i->node, false); -+ } - } - - if (i->ta_node && tdb_delete(tdb_ctx, ta_key)) -diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c -index f4e289362eb6..71c108ea99f1 100644 ---- a/tools/xenstore/xenstored_watch.c -+++ b/tools/xenstore/xenstored_watch.c -@@ -85,22 +85,6 @@ static void add_event(struct connection *conn, - unsigned int len; - char *data; - -- if (!check_special_event(name)) { -- /* Can this conn load node, or see that it doesn't exist? */ -- struct node *node = get_node(conn, ctx, name, XS_PERM_READ); -- /* -- * XXX We allow EACCES here because otherwise a non-dom0 -- * backend driver cannot watch for disappearance of a frontend -- * xenstore directory. When the directory disappears, we -- * revert to permissions of the parent directory for that path, -- * which will typically disallow access for the backend. -- * But this breaks device-channel teardown! -- * Really we should fix this better... -- */ -- if (!node && errno != ENOENT && errno != EACCES) -- return; -- } -- - if (watch->relative_path) { - name += strlen(watch->relative_path); - if (*name == '/') /* Could be "" */ -@@ -117,12 +101,60 @@ static void add_event(struct connection *conn, - 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 -+ * changed permissions we need to take the old permissions into account, too. -+ */ -+static bool watch_permitted(struct connection *conn, const void *ctx, -+ const char *name, struct node *node, -+ struct node_perms *perms) -+{ -+ enum xs_perm_type perm; -+ struct node *parent; -+ char *parent_name; -+ -+ if (perms) { -+ perm = perm_for_conn(conn, perms); -+ if (perm & XS_PERM_READ) -+ return true; -+ } -+ -+ if (!node) { -+ node = read_node(conn, ctx, name); -+ if (!node) -+ return false; -+ } -+ -+ perm = perm_for_conn(conn, &node->perms); -+ if (perm & XS_PERM_READ) -+ return true; -+ -+ parent = node->parent; -+ if (!parent) { -+ parent_name = get_parent(ctx, node->name); -+ if (!parent_name) -+ return false; -+ parent = read_node(conn, ctx, parent_name); -+ if (!parent) -+ return false; -+ } -+ -+ perm = perm_for_conn(conn, &parent->perms); -+ -+ return perm & XS_PERM_READ; -+} -+ - /* - * Check whether any watch events are to be sent. - * Temporary memory allocations are done with ctx. -+ * We need to take the (potential) old permissions of the node into account -+ * as a watcher losing permissions to access a node should receive the -+ * watch event, too. - */ - void fire_watches(struct connection *conn, const void *ctx, const char *name, -- bool exact) -+ struct node *node, bool exact, struct node_perms *perms) - { - struct connection *i; - struct watch *watch; -@@ -134,8 +166,13 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, - /* Create an event for each watch. */ - list_for_each_entry(i, &connections, list) { - /* introduce/release domain watches */ -- if (check_special_event(name) && !check_perms_special(name, i)) -- continue; -+ if (check_special_event(name)) { -+ if (!check_perms_special(name, i)) -+ continue; -+ } else { -+ if (!watch_permitted(i, ctx, name, node, perms)) -+ continue; -+ } - - list_for_each_entry(watch, &i->watches, list) { - if (exact) { -diff --git a/tools/xenstore/xenstored_watch.h b/tools/xenstore/xenstored_watch.h -index 1b3c80d3dda1..03094374f379 100644 ---- a/tools/xenstore/xenstored_watch.h -+++ b/tools/xenstore/xenstored_watch.h -@@ -26,7 +26,7 @@ int do_unwatch(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, -- bool exact); -+ struct node *node, bool exact, struct node_perms *perms); - - void conn_delete_all_watches(struct connection *conn); - --- -2.17.1 - diff --git a/xsa115-o-0001-tools-ocaml-xenstored-ignore-transaction-id-for-un-w.patch b/xsa115-o-0001-tools-ocaml-xenstored-ignore-transaction-id-for-un-w.patch deleted file mode 100644 index 0072c68..0000000 --- a/xsa115-o-0001-tools-ocaml-xenstored-ignore-transaction-id-for-un-w.patch +++ /dev/null @@ -1,43 +0,0 @@ -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Subject: tools/ocaml/xenstored: ignore transaction id for [un]watch -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Instead of ignoring the transaction id for XS_WATCH and XS_UNWATCH -commands as it is documented in docs/misc/xenstore.txt, it is tested -for validity today. - -Really ignore the transaction id for XS_WATCH and XS_UNWATCH. - -This is part of XSA-115. - -Signed-off-by: Edwin Török -Acked-by: Christian Lindig -Reviewed-by: Andrew Cooper - -diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml -index ff5c9484fc..2fa6798e3b 100644 ---- a/tools/ocaml/xenstored/process.ml -+++ b/tools/ocaml/xenstored/process.ml -@@ -498,12 +498,19 @@ let retain_op_in_history ty = - | Xenbus.Xb.Op.Reset_watches - | Xenbus.Xb.Op.Invalid -> false - -+let maybe_ignore_transaction = function -+ | Xenbus.Xb.Op.Watch | Xenbus.Xb.Op.Unwatch -> fun tid -> -+ if tid <> Transaction.none then -+ debug "Ignoring transaction ID %d for watch/unwatch" tid; -+ Transaction.none -+ | _ -> fun x -> x -+ - (** - * Nothrow guarantee. - *) - let process_packet ~store ~cons ~doms ~con ~req = - let ty = req.Packet.ty in -- let tid = req.Packet.tid in -+ let tid = maybe_ignore_transaction ty req.Packet.tid in - let rid = req.Packet.rid in - try - let fct = function_of_type ty in diff --git a/xsa115-o-0002-tools-ocaml-xenstored-check-privilege-for-XS_IS_DOMA.patch b/xsa115-o-0002-tools-ocaml-xenstored-check-privilege-for-XS_IS_DOMA.patch deleted file mode 100644 index 26033c7..0000000 --- a/xsa115-o-0002-tools-ocaml-xenstored-check-privilege-for-XS_IS_DOMA.patch +++ /dev/null @@ -1,30 +0,0 @@ -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Subject: tools/ocaml/xenstored: check privilege for XS_IS_DOMAIN_INTRODUCED -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The Xenstore command XS_IS_DOMAIN_INTRODUCED should be possible for privileged -domains only (the only user in the tree is the xenpaging daemon). - -This is part of XSA-115. - -Signed-off-by: Edwin Török -Acked-by: Christian Lindig -Reviewed-by: Andrew Cooper - -diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml -index 2fa6798e3b..fd79ef564f 100644 ---- a/tools/ocaml/xenstored/process.ml -+++ b/tools/ocaml/xenstored/process.ml -@@ -166,7 +166,9 @@ let do_setperms con t _domains _cons data = - let do_error _con _t _domains _cons _data = - raise Define.Unknown_operation - --let do_isintroduced _con _t domains _cons data = -+let do_isintroduced con _t domains _cons data = -+ if not (Connection.is_dom0 con) -+ then raise Define.Permission_denied; - let domid = - match (split None '\000' data) with - | domid :: _ -> int_of_string domid diff --git a/xsa115-o-0003-tools-ocaml-xenstored-unify-watch-firing.patch b/xsa115-o-0003-tools-ocaml-xenstored-unify-watch-firing.patch deleted file mode 100644 index fea94a9..0000000 --- a/xsa115-o-0003-tools-ocaml-xenstored-unify-watch-firing.patch +++ /dev/null @@ -1,29 +0,0 @@ -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Subject: tools/ocaml/xenstored: unify watch firing -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -This will make it easier insert additional checks in a follow-up patch. -All watches are now fired from a single function. - -This is part of XSA-115. - -Signed-off-by: Edwin Török -Acked-by: Christian Lindig -Reviewed-by: Andrew Cooper - -diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/connection.ml -index 24750ada43..e5df62d9e7 100644 ---- a/tools/ocaml/xenstored/connection.ml -+++ b/tools/ocaml/xenstored/connection.ml -@@ -210,8 +210,7 @@ let fire_watch watch path = - end else - path - in -- let data = Utils.join_by_null [ new_path; watch.token; "" ] in -- send_reply watch.con Transaction.none 0 Xenbus.Xb.Op.Watchevent data -+ fire_single_watch { watch with path = new_path } - - (* Search for a valid unused transaction id. *) - let rec valid_transaction_id con proposed_id = diff --git a/xsa115-o-0004-tools-ocaml-xenstored-introduce-permissions-for-spec.patch b/xsa115-o-0004-tools-ocaml-xenstored-introduce-permissions-for-spec.patch deleted file mode 100644 index 76f98e9..0000000 --- a/xsa115-o-0004-tools-ocaml-xenstored-introduce-permissions-for-spec.patch +++ /dev/null @@ -1,117 +0,0 @@ -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Subject: tools/ocaml/xenstored: introduce permissions for special watches -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The special watches "@introduceDomain" and "@releaseDomain" should be -allowed for privileged callers only, as they allow to gain information -about presence of other guests on the host. So send watch events for -those watches via privileged connections only. - -Start to address this by treating the special watches as regular nodes -in the tree, which gives them normal semantics for permissions. A later -change will restrict the handling, so that they can't be listed, etc. - -This is part of XSA-115. - -Signed-off-by: Edwin Török -Acked-by: Christian Lindig -Reviewed-by: Andrew Cooper - -diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml -index fd79ef564f..e528d1ecb2 100644 ---- a/tools/ocaml/xenstored/process.ml -+++ b/tools/ocaml/xenstored/process.ml -@@ -420,7 +420,7 @@ let do_introduce con _t domains cons data = - else try - let ndom = Domains.create domains domid mfn port in - Connections.add_domain cons ndom; -- Connections.fire_spec_watches cons "@introduceDomain"; -+ Connections.fire_spec_watches cons Store.Path.introduce_domain; - ndom - with _ -> raise Invalid_Cmd_Args - in -@@ -439,7 +439,7 @@ let do_release con _t domains cons data = - Domains.del domains domid; - Connections.del_domain cons domid; - if fire_spec_watches -- then Connections.fire_spec_watches cons "@releaseDomain" -+ then Connections.fire_spec_watches cons Store.Path.release_domain - else raise Invalid_Cmd_Args - - let do_resume con _t domains _cons data = -diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml -index 92b6289b5e..52b88b3ee1 100644 ---- a/tools/ocaml/xenstored/store.ml -+++ b/tools/ocaml/xenstored/store.ml -@@ -214,6 +214,11 @@ let rec lookup node path fct = - - let apply rnode path fct = - lookup rnode path fct -+ -+let introduce_domain = "@introduceDomain" -+let release_domain = "@releaseDomain" -+let specials = List.map of_string [ introduce_domain; release_domain ] -+ - end - - (* The Store.t type *) -diff --git a/tools/ocaml/xenstored/utils.ml b/tools/ocaml/xenstored/utils.ml -index b252db799b..e8c9fe4e94 100644 ---- a/tools/ocaml/xenstored/utils.ml -+++ b/tools/ocaml/xenstored/utils.ml -@@ -88,19 +88,17 @@ let read_file_single_integer filename = - Unix.close fd; - int_of_string (Bytes.sub_string buf 0 sz) - --let path_complete path connection_path = -- if String.get path 0 <> '/' then -- connection_path ^ path -- else -- path -- -+(* @path may be guest data and needs its length validating. @connection_path -+ * is generated locally in xenstored and always of the form "/local/domain/$N/" *) - let path_validate path connection_path = -- if String.length path = 0 || String.length path > 1024 then -- raise Define.Invalid_path -- else -- let cpath = path_complete path connection_path in -- if String.get cpath 0 <> '/' then -- raise Define.Invalid_path -- else -- cpath -+ let len = String.length path in -+ -+ if len = 0 || len > 1024 then raise Define.Invalid_path; -+ -+ let abs_path = -+ match String.get path 0 with -+ | '/' | '@' -> path -+ | _ -> connection_path ^ path -+ in - -+ abs_path -diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml -index 7e7824761b..8d0c50bfa4 100644 ---- a/tools/ocaml/xenstored/xenstored.ml -+++ b/tools/ocaml/xenstored/xenstored.ml -@@ -286,6 +286,8 @@ let _ = - let quit = ref false in - - Logging.init_xenstored_log(); -+ List.iter (fun path -> -+ Store.write store Perms.Connection.full_rights path "") Store.Path.specials; - - let filename = Paths.xen_run_stored ^ "/db" in - if cf.restart && Sys.file_exists filename then ( -@@ -335,7 +337,7 @@ let _ = - let (notify, deaddom) = Domains.cleanup domains in - List.iter (Connections.del_domain cons) deaddom; - if deaddom <> [] || notify then -- Connections.fire_spec_watches cons "@releaseDomain" -+ Connections.fire_spec_watches cons Store.Path.release_domain - ) - else - let c = Connections.find_domain_by_port cons port in diff --git a/xsa115-o-0005-tools-ocaml-xenstored-avoid-watch-events-for-nodes-w.patch b/xsa115-o-0005-tools-ocaml-xenstored-avoid-watch-events-for-nodes-w.patch deleted file mode 100644 index 866d415..0000000 --- a/xsa115-o-0005-tools-ocaml-xenstored-avoid-watch-events-for-nodes-w.patch +++ /dev/null @@ -1,406 +0,0 @@ -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Subject: tools/ocaml/xenstored: avoid watch events for nodes without access -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Today watch events are sent regardless of the access rights of the -node the event is sent for. This enables any guest to e.g. setup a -watch for "/" in order to have a detailed record of all Xenstore -modifications. - -Modify that by sending only watch events for nodes that the watcher -has a chance to see otherwise (either via direct reads or by querying -the children of a node). This includes cases where the visibility of -a node for a watcher is changing (permissions being removed). - -Permissions for nodes are looked up either in the old (pre -transaction/command) or current trees (post transaction). If -permissions are changed multiple times in a transaction only the final -version is checked, because considering a transaction atomic the -individual permission changes would not be noticable to an outside -observer. - -Two trees are only needed for set_perms: here we can either notice the -node disappearing (if we loose permission), appearing -(if we gain permission), or changing (if we preserve permission). - -RM needs to only look at the old tree: in the new tree the node would be -gone, or could have different permissions if it was recreated (the -recreation would get its own watch fired). - -Inside a tree we lookup the watch path's parent, and then the watch path -child itself. This gets us 4 sets of permissions in worst case, and if -either of these allows a watch, then we permit it to fire. The -permission lookups are done without logging the failures, otherwise we'd -get confusing errors about permission denied for some paths, but a watch -still firing. The actual result is logged in xenstored-access log: - - 'w event ...' as usual if watch was fired - 'w notfired...' if the watch was not fired, together with path and - permission set to help in troubleshooting - -Adding a watch bypasses permission checks and always fires the watch -once immediately. This is consistent with the specification, and no -information is gained (the watch is fired both if the path exists or -doesn't, and both if you have or don't have access, i.e. it reflects the -path a domain gave it back to that domain). - -There are some semantic changes here: - - * Write+rm in a single transaction of the same path is unobservable - now via watches: both before and after a transaction the path - doesn't exist, thus both tree lookups come up with the empty - permission set, and noone, not even Dom0 can see this. This is - consistent with transaction atomicity though. - * Similar to above if we temporarily grant and then revoke permission - on a path any watches fired inbetween are ignored as well - * There is a new log event (w notfired) which shows the permission set - of the path, and the path. - * Watches on paths that a domain doesn't have access to are now not - seen, which is the purpose of the security fix. - -This is part of XSA-115. - -Signed-off-by: Edwin Török -Acked-by: Christian Lindig -Reviewed-by: Andrew Cooper - -diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/connection.ml -index e5df62d9e7..644a448f2e 100644 ---- a/tools/ocaml/xenstored/connection.ml -+++ b/tools/ocaml/xenstored/connection.ml -@@ -196,11 +196,36 @@ let list_watches con = - con.watches [] in - List.concat ll - --let fire_single_watch watch = -+let dbg fmt = Logging.debug "connection" fmt -+let info fmt = Logging.info "connection" fmt -+ -+let lookup_watch_perm path = function -+| None -> [] -+| Some root -> -+ try Store.Path.apply root path @@ fun parent name -> -+ Store.Node.get_perms parent :: -+ try [Store.Node.get_perms (Store.Node.find parent name)] -+ with Not_found -> [] -+ with Define.Invalid_path | Not_found -> [] -+ -+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 data = Utils.join_by_null [watch.path; watch.token; ""] in - send_reply watch.con Transaction.none 0 Xenbus.Xb.Op.Watchevent data - --let fire_watch watch path = -+let fire_single_watch (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 List.exists (Perms.has watch.con.perm READ) perms then -+ fire_single_watch_unchecked 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 new_path = - if watch.is_relative && path.[0] = '/' - then begin -@@ -210,7 +235,7 @@ let fire_watch watch path = - end else - path - in -- fire_single_watch { watch with path = new_path } -+ fire_single_watch roots { watch with path = new_path } - - (* Search for a valid unused transaction id. *) - let rec valid_transaction_id con proposed_id = -diff --git a/tools/ocaml/xenstored/connections.ml b/tools/ocaml/xenstored/connections.ml -index f2c4318c88..9f9f7ee2f0 100644 ---- a/tools/ocaml/xenstored/connections.ml -+++ b/tools/ocaml/xenstored/connections.ml -@@ -135,25 +135,26 @@ let del_watch cons con path token = - watch - - (* path is absolute *) --let fire_watches cons path recurse = -+let fire_watches ?oldroot 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 w path) watches -+ | Some watches -> List.iter (fun w -> Connection.fire_watch roots w path) watches - in - let fire_rec _x = function - | None -> () - | Some watches -> -- List.iter (fun w -> Connection.fire_single_watch w) watches -+ List.iter (Connection.fire_single_watch roots) watches - in - Trie.iter_path fire_watch cons.watches key; - if recurse then - Trie.iter fire_rec (Trie.sub cons.watches key) - --let fire_spec_watches cons specpath = -+let fire_spec_watches root cons specpath = - iter cons (fun con -> -- List.iter (fun w -> Connection.fire_single_watch w) (Connection.get_watches con specpath)) -+ List.iter (Connection.fire_single_watch (None, root)) (Connection.get_watches con specpath)) - - let set_target cons domain target_domain = - let con = find_domain cons domain in -diff --git a/tools/ocaml/xenstored/logging.ml b/tools/ocaml/xenstored/logging.ml -index c5cba79e92..1ede131329 100644 ---- a/tools/ocaml/xenstored/logging.ml -+++ b/tools/ocaml/xenstored/logging.ml -@@ -161,6 +161,8 @@ let xenstored_log_nb_lines = ref 13215 - let xenstored_log_nb_chars = ref (-1) - let xenstored_logger = ref (None: logger option) - -+let debug_enabled () = !xenstored_log_level = Debug -+ - let set_xenstored_log_destination s = - xenstored_log_destination := log_destination_of_string s - -@@ -204,6 +206,7 @@ type access_type = - | Commit - | Newconn - | Endconn -+ | Watch_not_fired - | XbOp of Xenbus.Xb.Op.operation - - let string_of_tid ~con tid = -@@ -217,6 +220,7 @@ let string_of_access_type = function - | Commit -> "commit " - | Newconn -> "newconn " - | Endconn -> "endconn " -+ | Watch_not_fired -> "w notfired" - - | XbOp op -> match op with - | Xenbus.Xb.Op.Debug -> "debug " -@@ -331,3 +335,7 @@ let xb_answer ~tid ~con ~ty data = - | _ -> false, Debug - in - if print then access_logging ~tid ~con ~data (XbOp ty) ~level -+ -+let watch_not_fired ~con perms path = -+ let data = Printf.sprintf "EPERM perms=[%s] path=%s" perms path in -+ access_logging ~tid:0 ~con ~data Watch_not_fired ~level:Info -diff --git a/tools/ocaml/xenstored/perms.ml b/tools/ocaml/xenstored/perms.ml -index 3ea193ea14..23b80aba3d 100644 ---- a/tools/ocaml/xenstored/perms.ml -+++ b/tools/ocaml/xenstored/perms.ml -@@ -79,9 +79,9 @@ let of_string s = - let string_of_perm perm = - Printf.sprintf "%c%u" (char_of_permty (snd perm)) (fst perm) - --let to_string permvec = -+let to_string ?(sep="\000") permvec = - let l = ((permvec.owner, permvec.other) :: permvec.acl) in -- String.concat "\000" (List.map string_of_perm l) -+ String.concat sep (List.map string_of_perm l) - - end - -@@ -132,8 +132,8 @@ let check_owner (connection:Connection.t) (node:Node.t) = - then Connection.is_owner connection (Node.get_owner node) - else true - --(* check if the current connection has the requested perm on the current node *) --let check (connection:Connection.t) request (node:Node.t) = -+(* check if the current connection lacks the requested perm on the current node *) -+let lacks (connection:Connection.t) request (node:Node.t) = - let check_acl domainid = - let perm = - if List.mem_assoc domainid (Node.get_acl node) -@@ -154,11 +154,19 @@ let check (connection:Connection.t) request (node:Node.t) = - info "Permission denied: Domain %d has write only access" domainid; - false - in -- if !activate -+ !activate - && not (Connection.is_dom0 connection) - && not (check_owner connection node) - && not (List.exists check_acl (Connection.get_owners connection)) -+ -+(* check if the current connection has the requested perm on the current node. -+* Raises an exception if it doesn't. *) -+let check connection request node = -+ if lacks connection request node - then raise Define.Permission_denied - -+(* check if the current connection has the requested perm on the current node *) -+let has connection request node = not (lacks connection request node) -+ - let equiv perm1 perm2 = - (Node.to_string perm1) = (Node.to_string perm2) -diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml -index e528d1ecb2..f99b9e935c 100644 ---- a/tools/ocaml/xenstored/process.ml -+++ b/tools/ocaml/xenstored/process.ml -@@ -56,15 +56,17 @@ let split_one_path data con = - | path :: "" :: [] -> Store.Path.create path (Connection.get_path con) - | _ -> raise Invalid_Cmd_Args - --let process_watch ops cons = -+let process_watch 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 - let do_op_watch op cons = -- let recurse = match (fst op) with -- | Xenbus.Xb.Op.Write -> false -- | Xenbus.Xb.Op.Mkdir -> false -- | Xenbus.Xb.Op.Rm -> true -- | Xenbus.Xb.Op.Setperms -> false -+ let recurse, oldroot, root = match (fst op) with -+ | Xenbus.Xb.Op.Write|Xenbus.Xb.Op.Mkdir -> false, None, newroot -+ | Xenbus.Xb.Op.Rm -> true, None, oldroot -+ | Xenbus.Xb.Op.Setperms -> false, Some oldroot, newroot - | _ -> raise (Failure "huh ?") in -- Connections.fire_watches cons (snd op) recurse in -+ Connections.fire_watches ?oldroot root cons (snd op) recurse in - List.iter (fun op -> do_op_watch op cons) ops - - let create_implicit_path t perm path = -@@ -205,7 +207,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 (Transaction.get_paths t) cons -+ process_watch t cons - ) - - let reply_data fct con t doms cons data = -@@ -353,14 +355,17 @@ let transaction_replay c t doms cons = - ignore @@ Connection.end_transaction c tid None - ) - --let do_watch con _t _domains cons data = -+let do_watch con t _domains cons data = - let (node, token) = - match (split None '\000' data) with - | [node; token; ""] -> node, token - | _ -> raise Invalid_Cmd_Args - in - let watch = Connections.add_watch cons con node token in -- Packet.Ack (fun () -> Connection.fire_single_watch watch) -+ 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) - - let do_unwatch con _t _domains cons data = - let (node, token) = -@@ -391,7 +396,7 @@ let do_transaction_end con t domains cons data = - if not success then - raise Transaction_again; - if commit then begin -- process_watch (List.rev (Transaction.get_paths t)) cons; -+ process_watch t cons; - match t.Transaction.ty with - | Transaction.No -> - () (* no need to record anything *) -@@ -399,7 +404,7 @@ let do_transaction_end con t domains cons data = - record_commit ~con ~tid:id ~before:oldstore ~after:cstore - end - --let do_introduce con _t domains cons data = -+let do_introduce con t domains cons data = - if not (Connection.is_dom0 con) - then raise Define.Permission_denied; - let (domid, mfn, port) = -@@ -420,14 +425,14 @@ let do_introduce con _t domains cons data = - else try - let ndom = Domains.create domains domid mfn port in - Connections.add_domain cons ndom; -- Connections.fire_spec_watches cons Store.Path.introduce_domain; -+ Connections.fire_spec_watches (Transaction.get_root t) cons Store.Path.introduce_domain; - ndom - with _ -> raise Invalid_Cmd_Args - in - if (Domain.get_remote_port dom) <> port || (Domain.get_mfn dom) <> mfn then - raise Domain_not_match - --let do_release con _t domains cons data = -+let do_release con t domains cons data = - if not (Connection.is_dom0 con) - then raise Define.Permission_denied; - let domid = -@@ -439,7 +444,7 @@ let do_release con _t domains cons data = - Domains.del domains domid; - Connections.del_domain cons domid; - if fire_spec_watches -- then Connections.fire_spec_watches cons Store.Path.release_domain -+ then Connections.fire_spec_watches (Transaction.get_root t) cons Store.Path.release_domain - else raise Invalid_Cmd_Args - - let do_resume con _t domains _cons data = -@@ -507,6 +512,8 @@ let maybe_ignore_transaction = function - Transaction.none - | _ -> fun x -> x - -+ -+let () = Printexc.record_backtrace true - (** - * Nothrow guarantee. - *) -@@ -548,7 +555,8 @@ let process_packet ~store ~cons ~doms ~con ~req = - (* Put the response on the wire *) - send_response ty con t rid response - with exn -> -- error "process packet: %s" (Printexc.to_string exn); -+ let bt = Printexc.get_backtrace () in -+ error "process packet: %s. %s" (Printexc.to_string exn) bt; - Connection.send_error con tid rid "EIO" - - let do_input store cons doms con = -diff --git a/tools/ocaml/xenstored/transaction.ml b/tools/ocaml/xenstored/transaction.ml -index 963734a653..25bc8c3b4a 100644 ---- a/tools/ocaml/xenstored/transaction.ml -+++ b/tools/ocaml/xenstored/transaction.ml -@@ -82,6 +82,7 @@ type t = { - start_count: int64; - store: Store.t; (* This is the store that we change in write operations. *) - quota: Quota.t; -+ oldroot: Store.Node.t; - mutable paths: (Xenbus.Xb.Op.operation * Store.Path.t) list; - mutable operations: (Packet.request * Packet.response) list; - mutable read_lowpath: Store.Path.t option; -@@ -123,6 +124,7 @@ let make ?(internal=false) id store = - start_count = !counter; - store = if id = none then store else Store.copy store; - quota = Quota.copy store.Store.quota; -+ oldroot = Store.get_root store; - paths = []; - operations = []; - read_lowpath = None; -@@ -137,6 +139,8 @@ let make ?(internal=false) id store = - let get_store t = t.store - let get_paths t = t.paths - -+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 = -diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml -index 8d0c50bfa4..f7b88065bb 100644 ---- a/tools/ocaml/xenstored/xenstored.ml -+++ b/tools/ocaml/xenstored/xenstored.ml -@@ -337,7 +337,9 @@ let _ = - let (notify, deaddom) = Domains.cleanup domains in - List.iter (Connections.del_domain cons) deaddom; - if deaddom <> [] || notify then -- Connections.fire_spec_watches cons Store.Path.release_domain -+ Connections.fire_spec_watches -+ (Store.get_root store) -+ cons Store.Path.release_domain - ) - else - let c = Connections.find_domain_by_port cons port in diff --git a/xsa115-o-0006-tools-ocaml-xenstored-add-xenstored.conf-flag-to-tur.patch b/xsa115-o-0006-tools-ocaml-xenstored-add-xenstored.conf-flag-to-tur.patch deleted file mode 100644 index d1fa8b2..0000000 --- a/xsa115-o-0006-tools-ocaml-xenstored-add-xenstored.conf-flag-to-tur.patch +++ /dev/null @@ -1,84 +0,0 @@ -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Subject: tools/ocaml/xenstored: add xenstored.conf flag to turn off watch - permission checks -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -There are flags to turn off quotas and the permission system, so add one -that turns off the newly introduced watch permission checks as well. - -This is part of XSA-115. - -Signed-off-by: Edwin Török -Acked-by: Christian Lindig -Reviewed-by: Andrew Cooper - -diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/connection.ml -index 644a448f2e..fa0d3c4d92 100644 ---- a/tools/ocaml/xenstored/connection.ml -+++ b/tools/ocaml/xenstored/connection.ml -@@ -218,7 +218,7 @@ let fire_single_watch_unchecked watch = - let fire_single_watch (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 List.exists (Perms.has watch.con.perm READ) perms then -+ if Perms.can_fire_watch watch.con.perm perms then - fire_single_watch_unchecked watch - else - let perms = perms |> List.map (Perms.Node.to_string ~sep:" ") |> String.concat ", " in -diff --git a/tools/ocaml/xenstored/oxenstored.conf.in b/tools/ocaml/xenstored/oxenstored.conf.in -index 151b65b72d..f843482981 100644 ---- a/tools/ocaml/xenstored/oxenstored.conf.in -+++ b/tools/ocaml/xenstored/oxenstored.conf.in -@@ -44,6 +44,16 @@ conflict-rate-limit-is-aggregate = true - # Activate node permission system - perms-activate = true - -+# Activate the watch permission system -+# When this is enabled unprivileged guests can only get watch events -+# for xenstore entries that they would've been able to read. -+# -+# When this is disabled unprivileged guests may get watch events -+# for xenstore entries that they cannot read. The watch event contains -+# only the entry name, not the value. -+# This restores behaviour prior to XSA-115. -+perms-watch-activate = true -+ - # Activate quota - quota-activate = true - quota-maxentity = 1000 -diff --git a/tools/ocaml/xenstored/perms.ml b/tools/ocaml/xenstored/perms.ml -index 23b80aba3d..ee7fee6bda 100644 ---- a/tools/ocaml/xenstored/perms.ml -+++ b/tools/ocaml/xenstored/perms.ml -@@ -20,6 +20,7 @@ let info fmt = Logging.info "perms" fmt - open Stdext - - let activate = ref true -+let watch_activate = ref true - - type permty = READ | WRITE | RDWR | NONE - -@@ -168,5 +169,9 @@ let check connection request node = - (* check if the current connection has the requested perm on the current node *) - let has connection request node = not (lacks connection request node) - -+let can_fire_watch connection perms = -+ not !watch_activate -+ || List.exists (has connection READ) perms -+ - let equiv perm1 perm2 = - (Node.to_string perm1) = (Node.to_string perm2) -diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml -index f7b88065bb..0d355bbcb8 100644 ---- a/tools/ocaml/xenstored/xenstored.ml -+++ b/tools/ocaml/xenstored/xenstored.ml -@@ -95,6 +95,7 @@ let parse_config filename = - ("conflict-max-history-seconds", Config.Set_float Define.conflict_max_history_seconds); - ("conflict-rate-limit-is-aggregate", Config.Set_bool Define.conflict_rate_limit_is_aggregate); - ("perms-activate", Config.Set_bool Perms.activate); -+ ("perms-watch-activate", Config.Set_bool Perms.watch_activate); - ("quota-activate", Config.Set_bool Quota.activate); - ("quota-maxwatch", Config.Set_int Define.maxwatch); - ("quota-transaction", Config.Set_int Define.maxtransaction); diff --git a/xsa286-4.14-0001-x86-pv-Drop-FLUSH_TLB_GLOBAL-in-do_mmu_update-for-XP.patch b/xsa286-4.14-0001-x86-pv-Drop-FLUSH_TLB_GLOBAL-in-do_mmu_update-for-XP.patch deleted file mode 100644 index 5e9109d..0000000 --- a/xsa286-4.14-0001-x86-pv-Drop-FLUSH_TLB_GLOBAL-in-do_mmu_update-for-XP.patch +++ /dev/null @@ -1,58 +0,0 @@ -From 941f69a428cd989144300519e548e346c681a1b3 Mon Sep 17 00:00:00 2001 -From: Andrew Cooper -Date: Thu, 22 Oct 2020 11:28:58 +0100 -Subject: [PATCH 1/2] x86/pv: Drop FLUSH_TLB_GLOBAL in do_mmu_update() for XPTI - -c/s 9d1d31ad9498 "x86: slightly reduce Meltdown band-aid overhead" removed the -use of Global TLB flushes on the Xen entry path, but added a FLUSH_TLB_GLOBAL -to the L4 path in do_mmu_update(). - -However, this was unnecessary. - -It is the guests responsibility to perform appropriate TLB flushing if the L4 -modification altered an established mapping in a flush-relevant way. In this -case, an MMUEXT_OP hypercall will follow. The case which Xen needs to cover -is when new mappings are created, and the resync on the exit-to-guest path -covers this correctly. - -There is a corner case with multiple vCPUs in hypercalls at the same time, -which 9d1d31ad9498 changed, and this patch changes back to its original XPTI -behaviour. - -Architecturally, established TLB entries can continue to be used until the -broadcast flush has completed. Therefore, even with concurrent hypercalls, -the guest cannot depend on older mappings not being used until an MMUEXT_OP -hypercall completes. Xen's implementation of guest-initiated flushes will -take correct effect on top of an in-progress hypercall, picking up new mapping -setting before the other vCPU's MMUEXT_OP completes. - -Note: The correctness of this change is not impacted by whether XPTI uses -global mappings or not. Correctness there depends on the behaviour of Xen on -the entry/exit paths when switching two/from the XPTI "shadow" pagetables. - -This is (not really) XSA-286 (but necessary to simplify the logic). - -Fixes: 9d1d31ad9498 ("x86: slightly reduce Meltdown band-aid overhead") -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -(cherry picked from commit 055e1c3a3d95b1e753148369fbc4ba48782dd602) ---- - xen/arch/x86/mm.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 3cb6fabdae..1caa2df0a5 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -4193,7 +4193,7 @@ long do_mmu_update( - - cpumask_andnot(mask, pt_owner->dirty_cpumask, cpumask_of(cpu)); - if ( !cpumask_empty(mask) ) -- flush_mask(mask, FLUSH_TLB_GLOBAL | FLUSH_ROOT_PGTBL); -+ flush_mask(mask, FLUSH_ROOT_PGTBL); - } - - perfc_add(num_page_updates, i); --- -2.20.1 - diff --git a/xsa286-4.14-0002-x86-pv-Flush-TLB-in-response-to-paging-structure-cha.patch b/xsa286-4.14-0002-x86-pv-Flush-TLB-in-response-to-paging-structure-cha.patch deleted file mode 100644 index 22af1ca..0000000 --- a/xsa286-4.14-0002-x86-pv-Flush-TLB-in-response-to-paging-structure-cha.patch +++ /dev/null @@ -1,166 +0,0 @@ -From 10bb63c203f42d931fa1fa7dbbae7ce1765cecf2 Mon Sep 17 00:00:00 2001 -From: Andrew Cooper -Date: Mon, 19 Oct 2020 15:51:22 +0100 -Subject: [PATCH 2/2] x86/pv: Flush TLB in response to paging structure changes - -With MMU_UPDATE, a PV guest can make changes to higher level pagetables. This -is safe from Xen's point of view (as the update only affects guest mappings), -and the guest is required to flush (if necessary) after making updates. - -However, Xen's use of linear pagetables (UPDATE_VA_MAPPING, GNTTABOP_map, -writeable pagetables, etc.) is an implementation detail outside of the -API/ABI. - -Changes in the paging structure require invalidations in the linear pagetable -range for subsequent accesses into the linear pagetables to access non-stale -mappings. Xen must provide suitable flushing to prevent intermixed guest -actions from accidentally accessing/modifying the wrong pagetable. - -For all L2 and higher modifications, flush the TLB. PV guests cannot create -L2 or higher entries with the Global bit set, so no mappings established in -the linear range can be global. (This could in principle be an order 39 flush -starting at LINEAR_PT_VIRT_START, but no such mechanism exists in practice.) - -Express the necessary flushes as a set of booleans which accumulate across the -operation. Comment the flushing logic extensively. - -This is XSA-286. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -(cherry picked from commit 16a20963b3209788f2c0d3a3eebb7d92f03f5883) ---- - xen/arch/x86/mm.c | 69 ++++++++++++++++++++++++++++++++++++++++------- - 1 file changed, 59 insertions(+), 10 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 1caa2df0a5..61cf6a7b9b 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -3896,7 +3896,8 @@ long do_mmu_update( - struct vcpu *curr = current, *v = curr; - struct domain *d = v->domain, *pt_owner = d, *pg_owner; - mfn_t map_mfn = INVALID_MFN, mfn; -- bool sync_guest = false; -+ bool flush_linear_pt = false, flush_root_pt_local = false, -+ flush_root_pt_others = false; - uint32_t xsm_needed = 0; - uint32_t xsm_checked = 0; - int rc = put_old_guest_table(curr); -@@ -4046,6 +4047,8 @@ long do_mmu_update( - break; - rc = mod_l2_entry(va, l2e_from_intpte(req.val), mfn, - cmd == MMU_PT_UPDATE_PRESERVE_AD, v); -+ if ( !rc ) -+ flush_linear_pt = true; - break; - - case PGT_l3_page_table: -@@ -4053,6 +4056,8 @@ long do_mmu_update( - break; - rc = mod_l3_entry(va, l3e_from_intpte(req.val), mfn, - cmd == MMU_PT_UPDATE_PRESERVE_AD, v); -+ if ( !rc ) -+ flush_linear_pt = true; - break; - - case PGT_l4_page_table: -@@ -4060,6 +4065,8 @@ long do_mmu_update( - break; - rc = mod_l4_entry(va, l4e_from_intpte(req.val), mfn, - cmd == MMU_PT_UPDATE_PRESERVE_AD, v); -+ if ( !rc ) -+ flush_linear_pt = true; - if ( !rc && pt_owner->arch.pv.xpti ) - { - bool local_in_use = false; -@@ -4068,7 +4075,7 @@ long do_mmu_update( - mfn) ) - { - local_in_use = true; -- get_cpu_info()->root_pgt_changed = true; -+ flush_root_pt_local = true; - } - - /* -@@ -4080,7 +4087,7 @@ long do_mmu_update( - (1 + !!(page->u.inuse.type_info & PGT_pinned) + - mfn_eq(pagetable_get_mfn(curr->arch.guest_table_user), - mfn) + local_in_use) ) -- sync_guest = true; -+ flush_root_pt_others = true; - } - break; - -@@ -4182,19 +4189,61 @@ long do_mmu_update( - if ( va ) - unmap_domain_page(va); - -- if ( sync_guest ) -+ /* -+ * Perform required TLB maintenance. -+ * -+ * This logic currently depend on flush_linear_pt being a superset of the -+ * flush_root_pt_* conditions. -+ * -+ * pt_owner may not be current->domain. This may occur during -+ * construction of 32bit PV guests, or debugging of PV guests. The -+ * behaviour cannot be correct with domain unpaused. We therefore expect -+ * pt_owner->dirty_cpumask to be empty, but it is a waste of effort to -+ * explicitly check for, and exclude, this corner case. -+ * -+ * flush_linear_pt requires a FLUSH_TLB to all dirty CPUs. The flush must -+ * be performed now to maintain correct behaviour across a multicall. -+ * i.e. we cannot relax FLUSH_TLB to FLUSH_ROOT_PGTBL, given that the -+ * former is a side effect of the latter, because the resync (which is in -+ * the return-to-guest path) happens too late. -+ * -+ * flush_root_pt_* requires FLUSH_ROOT_PGTBL on either the local CPU -+ * (implies pt_owner == current->domain and current->processor set in -+ * pt_owner->dirty_cpumask), and/or all *other* dirty CPUs as there are -+ * references we can't account for locally. -+ */ -+ if ( flush_linear_pt /* || flush_root_pt_local || flush_root_pt_others */ ) - { -+ unsigned int cpu = smp_processor_id(); -+ cpumask_t *mask = pt_owner->dirty_cpumask; -+ - /* -- * Force other vCPU-s of the affected guest to pick up L4 entry -- * changes (if any). -+ * Always handle local flushing separately (if applicable), to -+ * separate the flush invocations appropriately for scope of the two -+ * flush_root_pt_* variables. - */ -- unsigned int cpu = smp_processor_id(); -- cpumask_t *mask = per_cpu(scratch_cpumask, cpu); -+ if ( likely(cpumask_test_cpu(cpu, mask)) ) -+ { -+ mask = per_cpu(scratch_cpumask, cpu); - -- cpumask_andnot(mask, pt_owner->dirty_cpumask, cpumask_of(cpu)); -+ cpumask_copy(mask, pt_owner->dirty_cpumask); -+ __cpumask_clear_cpu(cpu, mask); -+ -+ flush_local(FLUSH_TLB | -+ (flush_root_pt_local ? FLUSH_ROOT_PGTBL : 0)); -+ } -+ else -+ /* Sanity check. flush_root_pt_local implies local cpu is dirty. */ -+ ASSERT(!flush_root_pt_local); -+ -+ /* Flush the remote dirty CPUs. Does not include the local CPU. */ - if ( !cpumask_empty(mask) ) -- flush_mask(mask, FLUSH_ROOT_PGTBL); -+ flush_mask(mask, FLUSH_TLB | -+ (flush_root_pt_others ? FLUSH_ROOT_PGTBL : 0)); - } -+ else -+ /* Sanity check. flush_root_pt_* implies flush_linear_pt. */ -+ ASSERT(!flush_root_pt_local && !flush_root_pt_others); - - perfc_add(num_page_updates, i); - --- -2.20.1 - diff --git a/xsa322-4.14-c.patch b/xsa322-4.14-c.patch deleted file mode 100644 index 5059f24..0000000 --- a/xsa322-4.14-c.patch +++ /dev/null @@ -1,532 +0,0 @@ -From: Juergen Gross -Subject: tools/xenstore: revoke access rights for removed domains - -Access rights of Xenstore nodes are per domid. Unfortunately existing -granted access rights are not removed when a domain is being destroyed. -This means that a new domain created with the same domid will inherit -the access rights to Xenstore nodes from the previous domain(s) with -the same domid. - -This can be avoided by adding a generation counter to each domain. -The generation counter of the domain is set to the global generation -counter when a domain structure is being allocated. When reading or -writing a node all permissions of domains which are younger than the -node itself are dropped. This is done by flagging the related entry -as invalid in order to avoid modifying permissions in a way the user -could detect. - -A special case has to be considered: for a new domain the first -Xenstore entries are already written before the domain is officially -introduced in Xenstore. In order not to drop the permissions for the -new domain a domain struct is allocated even before introduction if -the hypervisor is aware of the domain. This requires adding another -bool "introduced" to struct domain in xenstored. In order to avoid -additional padding holes convert the shutdown flag to bool, too. - -As verifying permissions has its price regarding runtime add a new -quota for limiting the number of permissions an unprivileged domain -can set for a node. The default for that new quota is 5. - -This is part of XSA-322. - -Signed-off-by: Juergen Gross -Reviewed-by: Paul Durrant -Acked-by: Julien Grall - -diff --git a/tools/xenstore/include/xenstore_lib.h b/tools/xenstore/include/xenstore_lib.h -index 0ffbae9eb5..4c9b6d1685 100644 ---- a/tools/xenstore/include/xenstore_lib.h -+++ b/tools/xenstore/include/xenstore_lib.h -@@ -34,6 +34,7 @@ enum xs_perm_type { - /* Internal use. */ - XS_PERM_ENOENT_OK = 4, - XS_PERM_OWNER = 8, -+ XS_PERM_IGNORE = 16, - }; - - struct xs_permissions -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 92bfd54cff..505560a5de 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -104,6 +104,7 @@ int quota_nb_entry_per_domain = 1000; - 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; - - void trace(const char *fmt, ...) - { -@@ -409,8 +410,13 @@ 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)) { -+ talloc_free(node); -+ return NULL; -+ } -+ - /* Data is binary blob (usually ascii, no nul). */ -- node->data = node->perms.p + node->perms.num; -+ node->data = node->perms.p + hdr->num_perms; - /* Children is strings, nul separated. */ - node->children = node->data + node->datalen; - -@@ -426,6 +432,9 @@ 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)) -+ return errno; -+ - data.dsize = sizeof(*hdr) - + node->perms.num * sizeof(node->perms.p[0]) - + node->datalen + node->childlen; -@@ -485,8 +494,9 @@ enum xs_perm_type perm_for_conn(struct connection *conn, - return (XS_PERM_READ|XS_PERM_WRITE|XS_PERM_OWNER) & mask; - - for (i = 1; i < perms->num; i++) -- if (perms->p[i].id == conn->id -- || (conn->target && perms->p[i].id == conn->target->id)) -+ if (!(perms->p[i].perms & XS_PERM_IGNORE) && -+ (perms->p[i].id == conn->id || -+ (conn->target && perms->p[i].id == conn->target->id))) - return perms->p[i].perms & mask; - - return perms->p[0].perms & mask; -@@ -1248,8 +1258,12 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) - if (perms.num < 2) - return EINVAL; - -- permstr = in->buffer + strlen(in->buffer) + 1; - perms.num--; -+ if (domain_is_unprivileged(conn) && -+ perms.num > quota_nb_perms_per_node) -+ return ENOSPC; -+ -+ permstr = in->buffer + strlen(in->buffer) + 1; - - perms.p = talloc_array(in, struct xs_permissions, perms.num); - if (!perms.p) -@@ -1904,6 +1918,7 @@ static void usage(void) - " -S, --entry-size limit the size of entry per domain, and\n" - " -W, --watch-nb limit the number of watches per domain,\n" - " -t, --transaction limit the number of transaction allowed per domain,\n" -+" -A, --perm-nb limit the number of permissions per node,\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" -@@ -1924,6 +1939,7 @@ static struct option options[] = { - { "entry-size", 1, NULL, 'S' }, - { "trace-file", 1, NULL, 'T' }, - { "transaction", 1, NULL, 't' }, -+ { "perm-nb", 1, NULL, 'A' }, - { "no-recovery", 0, NULL, 'R' }, - { "internal-db", 0, NULL, 'I' }, - { "verbose", 0, NULL, 'V' }, -@@ -1946,7 +1962,7 @@ int main(int argc, char *argv[]) - int timeout; - - -- while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:T:RVW:", options, -+ while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:T:RVW:", options, - NULL)) != -1) { - switch (opt) { - case 'D': -@@ -1988,6 +2004,9 @@ int main(int argc, char *argv[]) - case 'W': - quota_nb_watch_per_domain = strtol(optarg, NULL, 10); - break; -+ case 'A': -+ quota_nb_perms_per_node = strtol(optarg, NULL, 10); -+ break; - case 'e': - dom0_event = strtol(optarg, NULL, 10); - break; -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index 9fad470f83..dc635e9be3 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -67,8 +67,14 @@ struct domain - /* The connection associated with this. */ - struct connection *conn; - -+ /* Generation count at domain introduction time. */ -+ uint64_t generation; -+ - /* Have we noticed that this domain is shutdown? */ -- int shutdown; -+ bool shutdown; -+ -+ /* Has domain been officially introduced? */ -+ bool introduced; - - /* number of entry from this domain in the store */ - int nbentry; -@@ -188,6 +194,9 @@ static int destroy_domain(void *_domain) - - list_del(&domain->list); - -+ if (!domain->introduced) -+ return 0; -+ - if (domain->port) { - if (xenevtchn_unbind(xce_handle, domain->port) == -1) - eprintf("> Unbinding port %i failed!\n", domain->port); -@@ -209,21 +218,34 @@ static int destroy_domain(void *_domain) - return 0; - } - -+static bool get_domain_info(unsigned int domid, xc_dominfo_t *dominfo) -+{ -+ return xc_domain_getinfo(*xc_handle, domid, 1, dominfo) == 1 && -+ dominfo->domid == domid; -+} -+ - static void domain_cleanup(void) - { - xc_dominfo_t dominfo; - struct domain *domain; - struct connection *conn; - int notify = 0; -+ bool dom_valid; - - again: - list_for_each_entry(domain, &domains, list) { -- if (xc_domain_getinfo(*xc_handle, domain->domid, 1, -- &dominfo) == 1 && -- dominfo.domid == domain->domid) { -+ dom_valid = get_domain_info(domain->domid, &dominfo); -+ if (!domain->introduced) { -+ if (!dom_valid) { -+ talloc_free(domain); -+ goto again; -+ } -+ continue; -+ } -+ if (dom_valid) { - if ((dominfo.crashed || dominfo.shutdown) - && !domain->shutdown) { -- domain->shutdown = 1; -+ domain->shutdown = true; - notify = 1; - } - if (!dominfo.dying) -@@ -289,58 +311,84 @@ static char *talloc_domain_path(void *context, unsigned int domid) - return talloc_asprintf(context, "/local/domain/%u", domid); - } - --static struct domain *new_domain(void *context, unsigned int domid, -- int port) -+static struct domain *find_domain_struct(unsigned int domid) -+{ -+ struct domain *i; -+ -+ list_for_each_entry(i, &domains, list) { -+ if (i->domid == domid) -+ return i; -+ } -+ return NULL; -+} -+ -+static struct domain *alloc_domain(void *context, unsigned int domid) - { - struct domain *domain; -- int rc; - - domain = talloc(context, struct domain); -- if (!domain) -+ if (!domain) { -+ errno = ENOMEM; - return NULL; -+ } - -- domain->port = 0; -- domain->shutdown = 0; - domain->domid = domid; -- domain->path = talloc_domain_path(domain, domid); -- if (!domain->path) -- return NULL; -+ domain->generation = generation; -+ domain->introduced = false; - -- wrl_domain_new(domain); -+ talloc_set_destructor(domain, destroy_domain); - - list_add(&domain->list, &domains); -- talloc_set_destructor(domain, destroy_domain); -+ -+ return domain; -+} -+ -+static int new_domain(struct domain *domain, int port) -+{ -+ int rc; -+ -+ domain->port = 0; -+ domain->shutdown = false; -+ domain->path = talloc_domain_path(domain, domain->domid); -+ if (!domain->path) { -+ errno = ENOMEM; -+ return errno; -+ } -+ -+ wrl_domain_new(domain); - - /* Tell kernel we're interested in this event. */ -- rc = xenevtchn_bind_interdomain(xce_handle, domid, port); -+ rc = xenevtchn_bind_interdomain(xce_handle, domain->domid, port); - if (rc == -1) -- return NULL; -+ return errno; - domain->port = rc; - -+ domain->introduced = true; -+ - domain->conn = new_connection(writechn, readchn); -- if (!domain->conn) -- return NULL; -+ if (!domain->conn) { -+ errno = ENOMEM; -+ return errno; -+ } - - domain->conn->domain = domain; -- domain->conn->id = domid; -+ domain->conn->id = domain->domid; - - domain->remote_port = port; - domain->nbentry = 0; - domain->nbwatch = 0; - -- return domain; -+ return 0; - } - - - static struct domain *find_domain_by_domid(unsigned int domid) - { -- struct domain *i; -+ struct domain *d; - -- list_for_each_entry(i, &domains, list) { -- if (i->domid == domid) -- return i; -- } -- return NULL; -+ d = find_domain_struct(domid); -+ -+ return (d && d->introduced) ? d : NULL; - } - - static void domain_conn_reset(struct domain *domain) -@@ -386,15 +434,21 @@ int do_introduce(struct connection *conn, struct buffered_data *in) - if (port <= 0) - return EINVAL; - -- domain = find_domain_by_domid(domid); -+ domain = find_domain_struct(domid); - - if (domain == NULL) { -+ /* Hang domain off "in" until we're finished. */ -+ domain = alloc_domain(in, domid); -+ if (domain == NULL) -+ return ENOMEM; -+ } -+ -+ if (!domain->introduced) { - interface = map_interface(domid); - if (!interface) - return errno; - /* Hang domain off "in" until we're finished. */ -- domain = new_domain(in, domid, port); -- if (!domain) { -+ if (new_domain(domain, port)) { - rc = errno; - unmap_interface(interface); - return rc; -@@ -503,8 +557,8 @@ int do_resume(struct connection *conn, struct buffered_data *in) - if (IS_ERR(domain)) - return -PTR_ERR(domain); - -- domain->shutdown = 0; -- -+ domain->shutdown = false; -+ - send_ack(conn, XS_RESUME); - - return 0; -@@ -647,8 +701,10 @@ static int dom0_init(void) - if (port == -1) - return -1; - -- dom0 = new_domain(NULL, xenbus_master_domid(), port); -- if (dom0 == NULL) -+ dom0 = alloc_domain(NULL, xenbus_master_domid()); -+ if (!dom0) -+ return -1; -+ if (new_domain(dom0, port)) - return -1; - - dom0->interface = xenbus_map(); -@@ -729,6 +785,66 @@ void domain_entry_inc(struct connection *conn, struct node *node) - } - } - -+/* -+ * Check whether a domain was created before or after a specific generation -+ * 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 -+ */ -+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; -+ -+ d = alloc_domain(NULL, domid); -+ return d ? 1 : -1; -+} -+ -+/* -+ * 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) -+{ -+ unsigned int i; -+ 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) -+ 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; -+ 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; -+ } -+ -+ return 0; -+} -+ - void domain_entry_dec(struct connection *conn, struct node *node) - { - struct domain *d; -diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h -index 259183962a..5e00087206 100644 ---- a/tools/xenstore/xenstored_domain.h -+++ b/tools/xenstore/xenstored_domain.h -@@ -56,6 +56,9 @@ bool domain_can_write(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); -+ - /* Quota manipulation */ - void domain_entry_inc(struct connection *conn, struct node *); - void domain_entry_dec(struct connection *conn, struct node *); -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index a7d8c5d475..2881f3b2e4 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -47,7 +47,12 @@ - * transaction. - * Each time the global generation count is copied to either a node or a - * transaction it is incremented. This ensures all nodes and/or transactions -- * are having a unique generation count. -+ * are having a unique generation count. The increment is done _before_ the -+ * copy as that is needed for checking whether a domain was created before -+ * or after a node has been written (the domain's generation is set with the -+ * actual generation count without incrementing it, in order to support -+ * writing a node for a domain before the domain has been officially -+ * introduced). - * - * Transaction conflicts are detected by checking the generation count of all - * nodes read in the transaction to match with the generation count in the -@@ -161,7 +166,7 @@ struct transaction - }; - - extern int quota_max_transaction; --static uint64_t generation; -+uint64_t generation; - - static void set_tdb_key(const char *name, TDB_DATA *key) - { -@@ -237,7 +242,7 @@ int access_node(struct connection *conn, struct node *node, - bool introduce = false; - - if (type != NODE_ACCESS_READ) { -- node->generation = generation++; -+ node->generation = ++generation; - if (conn && !conn->transaction) - wrl_apply_debit_direct(conn); - } -@@ -374,7 +379,7 @@ static int finalize_transaction(struct connection *conn, - if (!data.dptr) - goto err; - hdr = (void *)data.dptr; -- hdr->generation = generation++; -+ hdr->generation = ++generation; - ret = tdb_store(tdb_ctx, key, data, - TDB_REPLACE); - talloc_free(data.dptr); -@@ -462,7 +467,7 @@ int do_transaction_start(struct connection *conn, struct buffered_data *in) - INIT_LIST_HEAD(&trans->accessed); - INIT_LIST_HEAD(&trans->changed_domains); - trans->fail = false; -- trans->generation = generation++; -+ trans->generation = ++generation; - - /* Pick an unused transaction identifier. */ - do { -diff --git a/tools/xenstore/xenstored_transaction.h b/tools/xenstore/xenstored_transaction.h -index 3386bac565..43a162bea3 100644 ---- a/tools/xenstore/xenstored_transaction.h -+++ b/tools/xenstore/xenstored_transaction.h -@@ -27,6 +27,8 @@ enum node_access_type { - - 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); - -diff --git a/tools/xenstore/xs_lib.c b/tools/xenstore/xs_lib.c -index 3e43f8809d..d407d5713a 100644 ---- a/tools/xenstore/xs_lib.c -+++ b/tools/xenstore/xs_lib.c -@@ -152,7 +152,7 @@ bool xs_strings_to_perms(struct xs_permissions *perms, unsigned int num, - bool xs_perm_to_string(const struct xs_permissions *perm, - char *buffer, size_t buf_len) - { -- switch ((int)perm->perms) { -+ switch ((int)perm->perms & ~XS_PERM_IGNORE) { - case XS_PERM_WRITE: - *buffer = 'w'; - break; diff --git a/xsa322-o.patch b/xsa322-o.patch deleted file mode 100644 index 75f7c20..0000000 --- a/xsa322-o.patch +++ /dev/null @@ -1,110 +0,0 @@ -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Subject: tools/ocaml/xenstored: clean up permissions for dead domains -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -domain ids are prone to wrapping (15-bits), and with sufficient number -of VMs in a reboot loop it is possible to trigger it. Xenstore entries -may linger after a domain dies, until a toolstack cleans it up. During -this time there is a window where a wrapped domid could access these -xenstore keys (that belonged to another VM). - -To prevent this do a cleanup when a domain dies: - * walk the entire xenstore tree and update permissions for all nodes - * if the dead domain had an ACL entry: remove it - * if the dead domain was the owner: change the owner to Dom0 - -This is done without quota checks or a transaction. Quota checks would -be a no-op (either the domain is dead, or it is Dom0 where they are not -enforced). Transactions are not needed, because this is all done -atomically by oxenstored's single thread. - -The xenstore entries owned by the dead domain are not deleted, because -that could confuse a toolstack / backends that are still bound to it -(or generate unexpected watch events). It is the responsibility of a -toolstack to remove the xenstore entries themselves. - -This is part of XSA-322. - -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 ee7fee6bda..e8a16221f8 100644 ---- a/tools/ocaml/xenstored/perms.ml -+++ b/tools/ocaml/xenstored/perms.ml -@@ -58,6 +58,15 @@ let get_other perms = perms.other - let get_acl perms = perms.acl - let get_owner perm = perm.owner - -+(** [remote_domid ~domid perm] removes all ACLs for [domid] from perm. -+* If [domid] was the owner then it is changed to Dom0. -+* This is used for cleaning up after dead domains. -+* *) -+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 } -+ - let default0 = create 0 NONE [] - - let perm_of_string s = -diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml -index f99b9e935c..73e04cc18b 100644 ---- a/tools/ocaml/xenstored/process.ml -+++ b/tools/ocaml/xenstored/process.ml -@@ -443,6 +443,7 @@ let do_release con t domains cons data = - let fire_spec_watches = Domains.exist domains domid in - Domains.del domains domid; - Connections.del_domain cons domid; -+ Store.reset_permissions (Transaction.get_store t) domid; - if fire_spec_watches - then Connections.fire_spec_watches (Transaction.get_root t) cons Store.Path.release_domain - else raise Invalid_Cmd_Args -diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml -index 6b6e440e98..3b05128f1b 100644 ---- a/tools/ocaml/xenstored/store.ml -+++ b/tools/ocaml/xenstored/store.ml -@@ -89,6 +89,13 @@ let check_owner node connection = - - let rec recurse fct node = fct node; List.iter (recurse fct) node.children - -+(** [recurse_map f tree] applies [f] on each node in the tree recursively *) -+let recurse_map f = -+ let rec walk node = -+ f { node with children = List.rev_map walk node.children |> List.rev } -+ in -+ walk -+ - let unpack node = (Symbol.to_string node.name, node.perms, node.value) - - end -@@ -405,6 +412,15 @@ let setperms store perm path nperms = - Quota.del_entry store.quota old_owner; - Quota.add_entry store.quota new_owner - -+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 -+ - type ops = { - store: t; - write: Path.t -> string -> unit; -diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml -index 0d355bbcb8..ff9fbbbac2 100644 ---- a/tools/ocaml/xenstored/xenstored.ml -+++ b/tools/ocaml/xenstored/xenstored.ml -@@ -336,6 +336,7 @@ let _ = - finally (fun () -> - if Some port = eventchn.Event.virq_port then ( - let (notify, deaddom) = Domains.cleanup domains in -+ List.iter (Store.reset_permissions store) deaddom; - List.iter (Connections.del_domain cons) deaddom; - if deaddom <> [] || notify then - Connections.fire_spec_watches diff --git a/xsa323.patch b/xsa323.patch deleted file mode 100644 index aadf5c7..0000000 --- a/xsa323.patch +++ /dev/null @@ -1,140 +0,0 @@ -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Subject: tools/ocaml/xenstored: Fix path length validation -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Currently, oxenstored checks the length of paths against 1024, then -prepends "/local/domain/$DOMID/" to relative paths. This allows a domU -to create paths which can't subsequently be read by anyone, even dom0. -This also interferes with listing directories, etc. - -Define a new oxenstored.conf entry: quota-path-max, defaulting to 1024 -as before. For paths that begin with "/local/domain/$DOMID/" check the -relative path length against this quota. For all other paths check the -entire path length. - -This ensures that if the domid changes (and thus the length of a prefix -changes) a path that used to be valid stays valid (e.g. after a -live-migration). It also ensures that regardless how the client tries -to access a path (domid-relative or absolute) it will get consistent -results, since the limit is always applied on the final canonicalized -path. - -Delete the unused Domain.get_path to avoid it being confused with -Connection.get_path (which differs by a trailing slash only). - -Rewrite Util.path_validate to apply the appropriate length restriction -based on whether the path is relative or not. Remove the check for -connection_path being absolute, because it is not guest controlled data. - -This is part of XSA-323. - -Signed-off-by: Andrew Cooper -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 d4d1c7bdec..b6e2a716e2 100644 ---- a/tools/ocaml/libs/xb/partial.ml -+++ b/tools/ocaml/libs/xb/partial.ml -@@ -28,6 +28,7 @@ external header_of_string_internal: string -> int * int * int * int - = "stub_header_of_string" - - let xenstore_payload_max = 4096 (* xen/include/public/io/xs_wire.h *) -+let xenstore_rel_path_max = 2048 (* xen/include/public/io/xs_wire.h *) - - let of_string s = - let tid, rid, opint, dlen = header_of_string_internal s in -diff --git a/tools/ocaml/libs/xb/partial.mli b/tools/ocaml/libs/xb/partial.mli -index 359a75e88d..b9216018f5 100644 ---- a/tools/ocaml/libs/xb/partial.mli -+++ b/tools/ocaml/libs/xb/partial.mli -@@ -9,6 +9,7 @@ external header_size : unit -> int = "stub_header_size" - external header_of_string_internal : string -> int * int * int * int - = "stub_header_of_string" - val xenstore_payload_max : int -+val xenstore_rel_path_max : int - val of_string : string -> pkt - val append : pkt -> string -> int -> unit - val to_complete : pkt -> int -diff --git a/tools/ocaml/xenstored/define.ml b/tools/ocaml/xenstored/define.ml -index ea9e1b7620..ebe18b8e31 100644 ---- a/tools/ocaml/xenstored/define.ml -+++ b/tools/ocaml/xenstored/define.ml -@@ -31,6 +31,8 @@ let conflict_rate_limit_is_aggregate = ref true - - let domid_self = 0x7FF0 - -+let path_max = ref Xenbus.Partial.xenstore_rel_path_max -+ - exception Not_a_directory of string - exception Not_a_value of string - exception Already_exist -diff --git a/tools/ocaml/xenstored/domain.ml b/tools/ocaml/xenstored/domain.ml -index aeb185ff7e..81cb59b8f1 100644 ---- a/tools/ocaml/xenstored/domain.ml -+++ b/tools/ocaml/xenstored/domain.ml -@@ -38,7 +38,6 @@ type t = - } - - let is_dom0 d = d.id = 0 --let get_path dom = "/local/domain/" ^ (sprintf "%u" dom.id) - let get_id domain = domain.id - let get_interface d = d.interface - let get_mfn d = d.mfn -diff --git a/tools/ocaml/xenstored/oxenstored.conf.in b/tools/ocaml/xenstored/oxenstored.conf.in -index f843482981..4ae48e42d4 100644 ---- a/tools/ocaml/xenstored/oxenstored.conf.in -+++ b/tools/ocaml/xenstored/oxenstored.conf.in -@@ -61,6 +61,7 @@ quota-maxsize = 2048 - quota-maxwatch = 100 - quota-transaction = 10 - quota-maxrequests = 1024 -+quota-path-max = 1024 - - # Activate filed base backend - persistent = false -diff --git a/tools/ocaml/xenstored/utils.ml b/tools/ocaml/xenstored/utils.ml -index e8c9fe4e94..eb79bf0146 100644 ---- a/tools/ocaml/xenstored/utils.ml -+++ b/tools/ocaml/xenstored/utils.ml -@@ -93,7 +93,7 @@ let read_file_single_integer filename = - let path_validate path connection_path = - let len = String.length path in - -- if len = 0 || len > 1024 then raise Define.Invalid_path; -+ if len = 0 then raise Define.Invalid_path; - - let abs_path = - match String.get path 0 with -@@ -101,4 +101,17 @@ let path_validate path connection_path = - | _ -> connection_path ^ path - in - -+ (* Regardless whether client specified absolute or relative path, -+ canonicalize it (above) and, for domain-relative paths, check the -+ length of the relative part. -+ -+ This prevents paths becoming invalid across migrate when the length -+ of the domid changes in @param connection_path. -+ *) -+ let len = String.length abs_path in -+ let on_absolute _ _ = len in -+ let on_relative _ offset = len - offset in -+ let len = Scanf.ksscanf abs_path on_absolute "/local/domain/%d/%n" on_relative in -+ if len > !Define.path_max then raise Define.Invalid_path; -+ - abs_path -diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml -index ff9fbbbac2..39d6d767e4 100644 ---- a/tools/ocaml/xenstored/xenstored.ml -+++ b/tools/ocaml/xenstored/xenstored.ml -@@ -102,6 +102,7 @@ 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-path-max", Config.Set_int Define.path_max); - ("test-eagain", Config.Set_bool Transaction.test_eagain); - ("persistent", Config.Set_bool Disk.enable); - ("xenstored-log-file", Config.String Logging.set_xenstored_log_destination); diff --git a/xsa324.patch b/xsa324.patch deleted file mode 100644 index c5e542d..0000000 --- a/xsa324.patch +++ /dev/null @@ -1,48 +0,0 @@ -From: Juergen Gross -Subject: tools/xenstore: drop watch event messages exceeding maximum size - -By setting a watch with a very large tag it is possible to trick -xenstored to send watch event messages exceeding the maximum allowed -payload size. This might in turn lead to a crash of xenstored as the -resulting error can cause dereferencing a NULL pointer in case there -is no active request being handled by the guest the watch event is -being sent to. - -Fix that by just dropping such watch events. Additionally modify the -error handling to test the pointer to be not NULL before dereferencing -it. - -This is XSA-324. - -Signed-off-by: Juergen Gross -Acked-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 33f95dcf3c..3d74dbbb40 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -674,6 +674,9 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, - /* 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; -diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c -index 71c108ea99..9ff20690c0 100644 ---- a/tools/xenstore/xenstored_watch.c -+++ b/tools/xenstore/xenstored_watch.c -@@ -92,6 +92,10 @@ static void add_event(struct connection *conn, - } - - 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; diff --git a/xsa325-4.14.patch b/xsa325-4.14.patch deleted file mode 100644 index a17f546..0000000 --- a/xsa325-4.14.patch +++ /dev/null @@ -1,192 +0,0 @@ -From: Harsha Shamsundara Havanur -Subject: tools/xenstore: Preserve bad client until they are destroyed - -XenStored will kill any connection that it thinks has misbehaved, -this is currently happening in two places: - * In `handle_input()` if the sanity check on the ring and the message - fails. - * In `handle_output()` when failing to write the response in the ring. - -As the domain structure is a child of the connection, XenStored will -destroy its view of the domain when killing the connection. This will -result in sending @releaseDomain event to all the watchers. - -As the watch event doesn't carry which domain has been released, -the watcher (such as XenStored) will generally go through the list of -domains registers and check if one of them is shutting down/dying. -In the case of a client misbehaving, the domain will likely to be -running, so no action will be performed. - -When the domain is effectively destroyed, XenStored will not be aware of -the domain anymore. So the watch event is not going to be sent. -By consequence, the watchers of the event will not release mappings -they may have on the domain. This will result in a zombie domain. - -In order to send @releaseDomain event at the correct time, we want -to keep the domain structure until the domain is effectively -shutting-down/dying. - -We also want to keep the connection around so we could possibly revive -the connection in the future. - -A new flag 'is_ignored' is added to mark whether a connection should be -ignored when checking if there are work to do. Additionally any -transactions, watches, buffers associated to the connection will be -freed as you can't do much with them (restarting the connection will -likely need a reset). - -As a side note, when the device model were running in a stubdomain, a -guest would have been able to introduce a use-after-free because there -is two parents for a guest connection. - -This is XSA-325. - -Reported-by: Pawel Wieczorkiewicz -Signed-off-by: Harsha Shamsundara Havanur -Signed-off-by: Julien Grall -Reviewed-by: Juergen Gross -Reviewed-by: Paul Durrant - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index af3d17004b3f..27d8f15b6b76 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1355,6 +1355,32 @@ static struct { - [XS_DIRECTORY_PART] = { "DIRECTORY_PART", send_directory_part }, - }; - -+/* -+ * Keep the connection alive but stop processing any new request or sending -+ * reponse. This is to allow sending @releaseDomain watch event at the correct -+ * moment and/or to allow the connection to restart (not yet implemented). -+ * -+ * All watches, transactions, buffers will be freed. -+ */ -+static 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); -+ } -+ -+ talloc_free(conn->in); -+ conn->in = NULL; -+} -+ - static const char *sockmsg_string(enum xsd_sockmsg_type type) - { - if ((unsigned int)type < ARRAY_SIZE(wire_funcs) && wire_funcs[type].str) -@@ -1413,8 +1439,10 @@ static void consider_message(struct connection *conn) - assert(conn->in == NULL); - } - --/* Errors in reading or allocating here mean we get out of sync, so we -- * drop the whole client connection. */ -+/* -+ * Errors in reading or allocating here means we get out of sync, so we mark -+ * the connection as ignored. -+ */ - static void handle_input(struct connection *conn) - { - int bytes; -@@ -1471,14 +1499,14 @@ static void handle_input(struct connection *conn) - return; - - bad_client: -- /* Kill it. */ -- talloc_free(conn); -+ ignore_connection(conn); - } - - static void handle_output(struct connection *conn) - { -+ /* Ignore the connection if an error occured */ - if (!write_messages(conn)) -- talloc_free(conn); -+ ignore_connection(conn); - } - - struct connection *new_connection(connwritefn_t *write, connreadfn_t *read) -@@ -1494,6 +1522,7 @@ struct connection *new_connection(connwritefn_t *write, connreadfn_t *read) - new->write = write; - new->read = read; - new->can_write = true; -+ new->is_ignored = false; - new->transaction_started = 0; - INIT_LIST_HEAD(&new->out_list); - INIT_LIST_HEAD(&new->watches); -@@ -2186,8 +2215,9 @@ int main(int argc, char *argv[]) - if (fds[conn->pollfd_idx].revents - & ~(POLLIN|POLLOUT)) - talloc_free(conn); -- else if (fds[conn->pollfd_idx].revents -- & POLLIN) -+ else if ((fds[conn->pollfd_idx].revents -+ & POLLIN) && -+ !conn->is_ignored) - handle_input(conn); - } - if (talloc_free(conn) == 0) -@@ -2199,8 +2229,9 @@ int main(int argc, char *argv[]) - if (fds[conn->pollfd_idx].revents - & ~(POLLIN|POLLOUT)) - talloc_free(conn); -- else if (fds[conn->pollfd_idx].revents -- & POLLOUT) -+ else if ((fds[conn->pollfd_idx].revents -+ & POLLOUT) && -+ !conn->is_ignored) - handle_output(conn); - } - if (talloc_free(conn) == 0) -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index eb19b71f5f46..196a6fd2b0be 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -80,6 +80,9 @@ struct connection - /* Is this a read-only connection? */ - bool can_write; - -+ /* Is this connection ignored? */ -+ bool is_ignored; -+ - /* Buffered incoming data. */ - struct buffered_data *in; - -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index dc635e9be30c..d5e1e3e9d42d 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -286,6 +286,10 @@ bool domain_can_read(struct connection *conn) - - if (domain_is_unprivileged(conn) && conn->domain->wrl_credit < 0) - return false; -+ -+ if (conn->is_ignored) -+ return false; -+ - return (intf->req_cons != intf->req_prod); - } - -@@ -303,6 +307,10 @@ bool domain_is_unprivileged(struct connection *conn) - bool domain_can_write(struct connection *conn) - { - struct xenstore_domain_interface *intf = conn->domain->interface; -+ -+ if (conn->is_ignored) -+ return false; -+ - return ((intf->rsp_prod - intf->rsp_cons) != XENSTORE_RING_SIZE); - } - --- -2.17.1 - diff --git a/xsa330.patch b/xsa330.patch deleted file mode 100644 index c834516..0000000 --- a/xsa330.patch +++ /dev/null @@ -1,66 +0,0 @@ -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Subject: tools/ocaml/xenstored: delete watch from trie too when resetting - watches -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -c/s f8c72b526129 "oxenstored: implement XS_RESET_WATCHES" from Xen 4.6 -introduced reset watches support in oxenstored by mirroring the change -in cxenstored. - -However the OCaml version has some additional data structures to -optimize watch firing, and just resetting the watches in one of the data -structures creates a security bug where a malicious guest kernel can -exceed its watch quota, driving oxenstored into OOM: - * create watches - * reset watches (this still keeps the watches lingering in another data - structure, using memory) - * create some more watches - * loop until oxenstored dies - -The guest kernel doesn't necessarily have to be malicious to trigger -this: - * if control/platform-feature-xs_reset_watches is set - * the guest kexecs (e.g. because it crashes) - * on boot more watches are set up - * this will slowly "leak" memory for watches in oxenstored, driving it - towards OOM. - -This is XSA-330. - -Fixes: f8c72b526129 ("oxenstored: implement XS_RESET_WATCHES") -Signed-off-by: Edwin Török -Acked-by: Christian Lindig -Reviewed-by: Andrew Cooper - -diff --git a/tools/ocaml/xenstored/connections.ml b/tools/ocaml/xenstored/connections.ml -index 9f9f7ee2f0..6ee3552ec2 100644 ---- a/tools/ocaml/xenstored/connections.ml -+++ b/tools/ocaml/xenstored/connections.ml -@@ -134,6 +134,10 @@ 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 key = key_of_path path in -diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml -index 73e04cc18b..437d2dcf9e 100644 ---- a/tools/ocaml/xenstored/process.ml -+++ b/tools/ocaml/xenstored/process.ml -@@ -179,8 +179,8 @@ let do_isintroduced con _t domains _cons data = - if domid = Define.domid_self || Domains.exist domains domid then "T\000" else "F\000" - - (* only in xen >= 4.2 *) --let do_reset_watches con _t _domains _cons _data = -- Connection.del_watches con; -+let do_reset_watches con _t _domains cons _data = -+ Connections.del_watches cons con; - Connection.del_transactions con - - (* only in >= xen3.3 *) diff --git a/xsa333.patch b/xsa333.patch deleted file mode 100644 index 6b86c94..0000000 --- a/xsa333.patch +++ /dev/null @@ -1,39 +0,0 @@ -From: Andrew Cooper -Subject: x86/pv: Handle the Intel-specific MSR_MISC_ENABLE correctly - -This MSR doesn't exist on AMD hardware, and switching away from the safe -functions in the common MSR path was an erroneous change. - -Partially revert the change. - -This is XSA-333. - -Fixes: 4fdc932b3cc ("x86/Intel: drop another 32-bit leftover") -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -Reviewed-by: Wei Liu - -diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c -index efeb2a727e..6332c74b80 100644 ---- a/xen/arch/x86/pv/emul-priv-op.c -+++ b/xen/arch/x86/pv/emul-priv-op.c -@@ -924,7 +924,8 @@ static int read_msr(unsigned int reg, uint64_t *val, - return X86EMUL_OKAY; - - case MSR_IA32_MISC_ENABLE: -- rdmsrl(reg, *val); -+ if ( rdmsr_safe(reg, *val) ) -+ break; - *val = guest_misc_enable(*val); - return X86EMUL_OKAY; - -@@ -1059,7 +1060,8 @@ static int write_msr(unsigned int reg, uint64_t val, - break; - - case MSR_IA32_MISC_ENABLE: -- rdmsrl(reg, temp); -+ if ( rdmsr_safe(reg, temp) ) -+ break; - if ( val != guest_misc_enable(temp) ) - goto invalid; - return X86EMUL_OKAY; diff --git a/xsa334.patch b/xsa334.patch deleted file mode 100644 index 4260cdb..0000000 --- a/xsa334.patch +++ /dev/null @@ -1,51 +0,0 @@ -From: Andrew Cooper -Subject: xen/memory: Don't skip the RCU unlock path in acquire_resource() - -In the case that an HVM Stubdomain makes an XENMEM_acquire_resource hypercall, -the FIXME path will bypass rcu_unlock_domain() on the way out of the function. - -Move the check to the start of the function. This does change the behaviour -of the get-size path for HVM Stubdomains, but that functionality is currently -broken and unused anyway, as well as being quite useless to entities which -can't actually map the resource anyway. - -This is XSA-334. - -Fixes: 83fa6552ce ("common: add a new mappable resource type: XENMEM_resource_grant_table") -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/common/memory.c b/xen/common/memory.c -index 1a3c9ffb30..29741d8904 100644 ---- a/xen/common/memory.c -+++ b/xen/common/memory.c -@@ -1058,6 +1058,14 @@ static int acquire_resource( - xen_pfn_t mfn_list[32]; - int rc; - -+ /* -+ * FIXME: Until foreign pages inserted into the P2M are properly -+ * reference counted, it is unsafe to allow mapping of -+ * resource pages unless the caller is the hardware domain. -+ */ -+ if ( paging_mode_translate(currd) && !is_hardware_domain(currd) ) -+ return -EACCES; -+ - if ( copy_from_guest(&xmar, arg, 1) ) - return -EFAULT; - -@@ -1114,14 +1122,6 @@ static int acquire_resource( - xen_pfn_t gfn_list[ARRAY_SIZE(mfn_list)]; - unsigned int i; - -- /* -- * FIXME: Until foreign pages inserted into the P2M are properly -- * reference counted, it is unsafe to allow mapping of -- * resource pages unless the caller is the hardware domain. -- */ -- if ( !is_hardware_domain(currd) ) -- return -EACCES; -- - if ( copy_from_guest(gfn_list, xmar.frame_list, xmar.nr_frames) ) - rc = -EFAULT; - diff --git a/xsa335-qemu.patch b/xsa335-qemu.patch deleted file mode 100644 index 5617502..0000000 --- a/xsa335-qemu.patch +++ /dev/null @@ -1,84 +0,0 @@ -From c5bd2924c6d6a5bcbffb8b5e7798a88970131c07 Mon Sep 17 00:00:00 2001 -From: Gerd Hoffmann -Date: Mon, 17 Aug 2020 08:34:22 +0200 -Subject: [PATCH] usb: fix setup_len init (CVE-2020-14364) - -Store calculated setup_len in a local variable, verify it, and only -write it to the struct (USBDevice->setup_len) in case it passed the -sanity checks. - -This prevents other code (do_token_{in,out} functions specifically) -from working with invalid USBDevice->setup_len values and overrunning -the USBDevice->setup_buf[] buffer. - -Fixes: CVE-2020-14364 -Signed-off-by: Gerd Hoffmann ---- - hw/usb/core.c | 16 ++++++++++------ - 1 file changed, 10 insertions(+), 6 deletions(-) - -diff --git a/hw/usb/core.c b/hw/usb/core.c -index 5abd128b6bc5..5234dcc73fea 100644 ---- a/hw/usb/core.c -+++ b/hw/usb/core.c -@@ -129,6 +129,7 @@ void usb_wakeup(USBEndpoint *ep, unsigned int stream) - static void do_token_setup(USBDevice *s, USBPacket *p) - { - int request, value, index; -+ unsigned int setup_len; - - if (p->iov.size != 8) { - p->status = USB_RET_STALL; -@@ -138,14 +139,15 @@ static void do_token_setup(USBDevice *s, USBPacket *p) - usb_packet_copy(p, s->setup_buf, p->iov.size); - s->setup_index = 0; - p->actual_length = 0; -- s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; -- if (s->setup_len > sizeof(s->data_buf)) { -+ setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; -+ if (setup_len > sizeof(s->data_buf)) { - fprintf(stderr, - "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", -- s->setup_len, sizeof(s->data_buf)); -+ setup_len, sizeof(s->data_buf)); - p->status = USB_RET_STALL; - return; - } -+ s->setup_len = setup_len; - - request = (s->setup_buf[0] << 8) | s->setup_buf[1]; - value = (s->setup_buf[3] << 8) | s->setup_buf[2]; -@@ -259,26 +261,28 @@ static void do_token_out(USBDevice *s, USBPacket *p) - static void do_parameter(USBDevice *s, USBPacket *p) - { - int i, request, value, index; -+ unsigned int setup_len; - - for (i = 0; i < 8; i++) { - s->setup_buf[i] = p->parameter >> (i*8); - } - - s->setup_state = SETUP_STATE_PARAM; -- s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; - s->setup_index = 0; - - request = (s->setup_buf[0] << 8) | s->setup_buf[1]; - value = (s->setup_buf[3] << 8) | s->setup_buf[2]; - index = (s->setup_buf[5] << 8) | s->setup_buf[4]; - -- if (s->setup_len > sizeof(s->data_buf)) { -+ setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; -+ if (setup_len > sizeof(s->data_buf)) { - fprintf(stderr, - "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", -- s->setup_len, sizeof(s->data_buf)); -+ setup_len, sizeof(s->data_buf)); - p->status = USB_RET_STALL; - return; - } -+ s->setup_len = setup_len; - - if (p->pid == USB_TOKEN_OUT) { - usb_packet_copy(p, s->data_buf, s->setup_len); --- -2.18.4 diff --git a/xsa335-trad.patch b/xsa335-trad.patch deleted file mode 100644 index 1310b84..0000000 --- a/xsa335-trad.patch +++ /dev/null @@ -1,45 +0,0 @@ -From a62cdd675bc6a8053f6797b6add29b2853b081e3 Mon Sep 17 00:00:00 2001 -From: Ian Jackson -Date: Wed, 19 Aug 2020 18:31:45 +0100 -Subject: [PATCH] SUPPORT.md: Desupport qemu trad except stub dm - -While investigating XSA-335 we discovered that many upstream security -fixes were missing. It is not practical to backport them. There is -no good reason to be running this very ancient version of qemu, except -that it is the only way to run a stub dm which is currently supported -by upstream. - -Signed-off-by: Ian Jackson ---- - SUPPORT.md | 15 +++++++++++++++ - 1 file changed, 15 insertions(+) - -diff --git a/SUPPORT.md b/SUPPORT.md -index 1479055c45..b0939052e2 100644 ---- a/SUPPORT.md -+++ b/SUPPORT.md -@@ -758,6 +758,21 @@ See the section **Blkback** for image formats supported by QEMU. - - Status: Supported, not security supported - -+### qemu-xen-traditional ### -+ -+The Xen Project provides an old version of qemu with modifications -+which enable use as a device model stub domain. The old version is -+normally selected by default only in a stub dm configuration, but it -+can be requested explicitly in other configurations, for example in -+`xl` with `device_model_version="QEMU_XEN_TRADITIONAL"`. -+ -+ Status, Device Model Stub Domains: Supported, with caveats -+ Status, as host process device model: No security support, not recommended -+ -+qemu-xen-traditional is security supported only for those available -+devices which are supported for mainstream QEMU (see above), with -+trusted driver domains (see Device Model Stub Domains). -+ - ## Virtual Firmware - - ### x86/HVM iPXE --- -2.20.1 - diff --git a/xsa336.patch b/xsa336.patch deleted file mode 100644 index b44c298..0000000 --- a/xsa336.patch +++ /dev/null @@ -1,283 +0,0 @@ -From: Roger Pau Monné -Subject: x86/vpt: fix race when migrating timers between vCPUs - -The current vPT code will migrate the emulated timers between vCPUs -(change the pt->vcpu field) while just holding the destination lock, -either from create_periodic_time or pt_adjust_global_vcpu_target if -the global target is adjusted. Changing the periodic_timer vCPU field -in this way creates a race where a third party could grab the lock in -the unlocked region of pt_adjust_global_vcpu_target (or before -create_periodic_time performs the vcpu change) and then release the -lock from a different vCPU, creating a locking imbalance. - -Introduce a per-domain rwlock in order to protect periodic_time -migration between vCPU lists. Taking the lock in read mode prevents -any timer from being migrated to a different vCPU, while taking it in -write mode allows performing migration of timers across vCPUs. The -per-vcpu locks are still used to protect all the other fields from the -periodic_timer struct. - -Note that such migration shouldn't happen frequently, and hence -there's no performance drop as a result of such locking. - -This is XSA-336. - -Reported-by: Igor Druzhinin -Tested-by: Igor Druzhinin -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich ---- -Changes since v2: - - Re-order pt_adjust_vcpu to remove one if. - - Fix pt_lock to not call pt_vcpu_lock, as we might end up using a - stale value of pt->vcpu when taking the per-vcpu lock. - -Changes since v1: - - Use a per-domain rwlock to protect timer vCPU migration. - ---- a/xen/arch/x86/hvm/hvm.c -+++ b/xen/arch/x86/hvm/hvm.c -@@ -658,6 +658,8 @@ int hvm_domain_initialise(struct domain - /* need link to containing domain */ - d->arch.hvm.pl_time->domain = d; - -+ rwlock_init(&d->arch.hvm.pl_time->pt_migrate); -+ - /* Set the default IO Bitmap. */ - if ( is_hardware_domain(d) ) - { ---- a/xen/arch/x86/hvm/vpt.c -+++ b/xen/arch/x86/hvm/vpt.c -@@ -153,23 +153,32 @@ static int pt_irq_masked(struct periodic - return 1; - } - --static void pt_lock(struct periodic_time *pt) -+static void pt_vcpu_lock(struct vcpu *v) - { -- struct vcpu *v; -+ read_lock(&v->domain->arch.hvm.pl_time->pt_migrate); -+ spin_lock(&v->arch.hvm.tm_lock); -+} - -- for ( ; ; ) -- { -- v = pt->vcpu; -- spin_lock(&v->arch.hvm.tm_lock); -- if ( likely(pt->vcpu == v) ) -- break; -- spin_unlock(&v->arch.hvm.tm_lock); -- } -+static void pt_vcpu_unlock(struct vcpu *v) -+{ -+ spin_unlock(&v->arch.hvm.tm_lock); -+ read_unlock(&v->domain->arch.hvm.pl_time->pt_migrate); -+} -+ -+static void pt_lock(struct periodic_time *pt) -+{ -+ /* -+ * We cannot use pt_vcpu_lock here, because we need to acquire the -+ * per-domain lock first and then (re-)fetch the value of pt->vcpu, or -+ * else we might be using a stale value of pt->vcpu. -+ */ -+ read_lock(&pt->vcpu->domain->arch.hvm.pl_time->pt_migrate); -+ spin_lock(&pt->vcpu->arch.hvm.tm_lock); - } - - static void pt_unlock(struct periodic_time *pt) - { -- spin_unlock(&pt->vcpu->arch.hvm.tm_lock); -+ pt_vcpu_unlock(pt->vcpu); - } - - static void pt_process_missed_ticks(struct periodic_time *pt) -@@ -219,7 +228,7 @@ void pt_save_timer(struct vcpu *v) - if ( v->pause_flags & VPF_blocked ) - return; - -- spin_lock(&v->arch.hvm.tm_lock); -+ pt_vcpu_lock(v); - - list_for_each_entry ( pt, head, list ) - if ( !pt->do_not_freeze ) -@@ -227,7 +236,7 @@ void pt_save_timer(struct vcpu *v) - - pt_freeze_time(v); - -- spin_unlock(&v->arch.hvm.tm_lock); -+ pt_vcpu_unlock(v); - } - - void pt_restore_timer(struct vcpu *v) -@@ -235,7 +244,7 @@ void pt_restore_timer(struct vcpu *v) - struct list_head *head = &v->arch.hvm.tm_list; - struct periodic_time *pt; - -- spin_lock(&v->arch.hvm.tm_lock); -+ pt_vcpu_lock(v); - - list_for_each_entry ( pt, head, list ) - { -@@ -248,7 +257,7 @@ void pt_restore_timer(struct vcpu *v) - - pt_thaw_time(v); - -- spin_unlock(&v->arch.hvm.tm_lock); -+ pt_vcpu_unlock(v); - } - - static void pt_timer_fn(void *data) -@@ -309,7 +318,7 @@ int pt_update_irq(struct vcpu *v) - int irq, pt_vector = -1; - bool level; - -- spin_lock(&v->arch.hvm.tm_lock); -+ pt_vcpu_lock(v); - - earliest_pt = NULL; - max_lag = -1ULL; -@@ -339,7 +348,7 @@ int pt_update_irq(struct vcpu *v) - - if ( earliest_pt == NULL ) - { -- spin_unlock(&v->arch.hvm.tm_lock); -+ pt_vcpu_unlock(v); - return -1; - } - -@@ -347,7 +356,7 @@ int pt_update_irq(struct vcpu *v) - irq = earliest_pt->irq; - level = earliest_pt->level; - -- spin_unlock(&v->arch.hvm.tm_lock); -+ pt_vcpu_unlock(v); - - switch ( earliest_pt->source ) - { -@@ -394,7 +403,7 @@ int pt_update_irq(struct vcpu *v) - time_cb *cb = NULL; - void *cb_priv; - -- spin_lock(&v->arch.hvm.tm_lock); -+ pt_vcpu_lock(v); - /* Make sure the timer is still on the list. */ - list_for_each_entry ( pt, &v->arch.hvm.tm_list, list ) - if ( pt == earliest_pt ) -@@ -404,7 +413,7 @@ int pt_update_irq(struct vcpu *v) - cb_priv = pt->priv; - break; - } -- spin_unlock(&v->arch.hvm.tm_lock); -+ pt_vcpu_unlock(v); - - if ( cb != NULL ) - cb(v, cb_priv); -@@ -441,12 +450,12 @@ void pt_intr_post(struct vcpu *v, struct - if ( intack.source == hvm_intsrc_vector ) - return; - -- spin_lock(&v->arch.hvm.tm_lock); -+ pt_vcpu_lock(v); - - pt = is_pt_irq(v, intack); - if ( pt == NULL ) - { -- spin_unlock(&v->arch.hvm.tm_lock); -+ pt_vcpu_unlock(v); - return; - } - -@@ -455,7 +464,7 @@ void pt_intr_post(struct vcpu *v, struct - cb = pt->cb; - cb_priv = pt->priv; - -- spin_unlock(&v->arch.hvm.tm_lock); -+ pt_vcpu_unlock(v); - - if ( cb != NULL ) - cb(v, cb_priv); -@@ -466,12 +475,12 @@ void pt_migrate(struct vcpu *v) - struct list_head *head = &v->arch.hvm.tm_list; - struct periodic_time *pt; - -- spin_lock(&v->arch.hvm.tm_lock); -+ pt_vcpu_lock(v); - - list_for_each_entry ( pt, head, list ) - migrate_timer(&pt->timer, v->processor); - -- spin_unlock(&v->arch.hvm.tm_lock); -+ pt_vcpu_unlock(v); - } - - void create_periodic_time( -@@ -490,7 +499,7 @@ void create_periodic_time( - - destroy_periodic_time(pt); - -- spin_lock(&v->arch.hvm.tm_lock); -+ write_lock(&v->domain->arch.hvm.pl_time->pt_migrate); - - pt->pending_intr_nr = 0; - pt->do_not_freeze = 0; -@@ -540,7 +549,7 @@ void create_periodic_time( - init_timer(&pt->timer, pt_timer_fn, pt, v->processor); - set_timer(&pt->timer, pt->scheduled); - -- spin_unlock(&v->arch.hvm.tm_lock); -+ write_unlock(&v->domain->arch.hvm.pl_time->pt_migrate); - } - - void destroy_periodic_time(struct periodic_time *pt) -@@ -565,30 +574,20 @@ void destroy_periodic_time(struct period - - static void pt_adjust_vcpu(struct periodic_time *pt, struct vcpu *v) - { -- int on_list; -- - ASSERT(pt->source == PTSRC_isa || pt->source == PTSRC_ioapic); - - if ( pt->vcpu == NULL ) - return; - -- pt_lock(pt); -- on_list = pt->on_list; -- if ( pt->on_list ) -- list_del(&pt->list); -- pt->on_list = 0; -- pt_unlock(pt); -- -- spin_lock(&v->arch.hvm.tm_lock); -+ write_lock(&pt->vcpu->domain->arch.hvm.pl_time->pt_migrate); - pt->vcpu = v; -- if ( on_list ) -+ if ( pt->on_list ) - { -- pt->on_list = 1; -+ list_del(&pt->list); - list_add(&pt->list, &v->arch.hvm.tm_list); -- - migrate_timer(&pt->timer, v->processor); - } -- spin_unlock(&v->arch.hvm.tm_lock); -+ write_unlock(&pt->vcpu->domain->arch.hvm.pl_time->pt_migrate); - } - - void pt_adjust_global_vcpu_target(struct vcpu *v) ---- a/xen/include/asm-x86/hvm/vpt.h -+++ b/xen/include/asm-x86/hvm/vpt.h -@@ -128,6 +128,13 @@ struct pl_time { /* platform time */ - struct RTCState vrtc; - struct HPETState vhpet; - struct PMTState vpmt; -+ /* -+ * rwlock to prevent periodic_time vCPU migration. Take the lock in read -+ * mode in order to prevent the vcpu field of periodic_time from changing. -+ * Lock must be taken in write mode when changes to the vcpu field are -+ * performed, as it allows exclusive access to all the timers of a domain. -+ */ -+ rwlock_t pt_migrate; - /* guest_time = Xen sys time + stime_offset */ - int64_t stime_offset; - /* Ensures monotonicity in appropriate timer modes. */ diff --git a/xsa337-1.patch b/xsa337-1.patch deleted file mode 100644 index eae4185..0000000 --- a/xsa337-1.patch +++ /dev/null @@ -1,87 +0,0 @@ -From: Roger Pau Monné -Subject: x86/msi: get rid of read_msi_msg - -It's safer and faster to just use the cached last written -(untranslated) MSI message stored in msi_desc for the single user that -calls read_msi_msg. - -This also prevents relying on the data read from the device MSI -registers in order to figure out the index into the IOMMU interrupt -remapping table, which is not safe. - -This is part of XSA-337. - -Reported-by: Andrew Cooper -Requested-by: Andrew Cooper -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich - ---- a/xen/arch/x86/msi.c -+++ b/xen/arch/x86/msi.c -@@ -185,54 +185,6 @@ void msi_compose_msg(unsigned vector, co - MSI_DATA_VECTOR(vector); - } - --static bool read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) --{ -- switch ( entry->msi_attrib.type ) -- { -- case PCI_CAP_ID_MSI: -- { -- struct pci_dev *dev = entry->dev; -- int pos = entry->msi_attrib.pos; -- uint16_t data; -- -- msg->address_lo = pci_conf_read32(dev->sbdf, -- msi_lower_address_reg(pos)); -- if ( entry->msi_attrib.is_64 ) -- { -- msg->address_hi = pci_conf_read32(dev->sbdf, -- msi_upper_address_reg(pos)); -- data = pci_conf_read16(dev->sbdf, msi_data_reg(pos, 1)); -- } -- else -- { -- msg->address_hi = 0; -- data = pci_conf_read16(dev->sbdf, msi_data_reg(pos, 0)); -- } -- msg->data = data; -- break; -- } -- case PCI_CAP_ID_MSIX: -- { -- void __iomem *base = entry->mask_base; -- -- if ( unlikely(!msix_memory_decoded(entry->dev, -- entry->msi_attrib.pos)) ) -- return false; -- msg->address_lo = readl(base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); -- msg->address_hi = readl(base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); -- msg->data = readl(base + PCI_MSIX_ENTRY_DATA_OFFSET); -- break; -- } -- default: -- BUG(); -- } -- -- if ( iommu_intremap ) -- iommu_read_msi_from_ire(entry, msg); -- -- return true; --} -- - static int write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) - { - entry->msg = *msg; -@@ -304,10 +256,7 @@ void set_msi_affinity(struct irq_desc *d - - ASSERT(spin_is_locked(&desc->lock)); - -- memset(&msg, 0, sizeof(msg)); -- if ( !read_msi_msg(msi_desc, &msg) ) -- return; -- -+ msg = msi_desc->msg; - msg.data &= ~MSI_DATA_VECTOR_MASK; - msg.data |= MSI_DATA_VECTOR(desc->arch.vector); - msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK; diff --git a/xsa337-2.patch b/xsa337-2.patch deleted file mode 100644 index 8089e68..0000000 --- a/xsa337-2.patch +++ /dev/null @@ -1,184 +0,0 @@ -From: Jan Beulich -Subject: x86/MSI-X: restrict reading of table/PBA bases from BARs - -When assigned to less trusted or un-trusted guests, devices may change -state behind our backs (they may e.g. get reset by means we may not know -about). Therefore we should avoid reading BARs from hardware once a -device is no longer owned by Dom0. Furthermore when we can't read a BAR, -or when we read zero, we shouldn't instead use the caller provided -address unless that caller can be trusted. - -Re-arrange the logic in msix_capability_init() such that only Dom0 (and -only if the device isn't DomU-owned yet) or calls through -PHYSDEVOP_prepare_msix will actually result in the reading of the -respective BAR register(s). Additionally do so only as long as in-use -table entries are known (note that invocation of PHYSDEVOP_prepare_msix -counts as a "pseudo" entry). In all other uses the value already -recorded will get used instead. - -Clear the recorded values in _pci_cleanup_msix() as well as on the one -affected error path. (Adjust this error path to also avoid blindly -disabling MSI-X when it was enabled on entry to the function.) - -While moving around variable declarations (in many cases to reduce their -scopes), also adjust some of their types. - -This is part of XSA-337. - -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné ---- -v2: Use "unsigned int" for moved bir, pbus, etc. Further restrict under - what conditions to read the BAR(s). - ---- a/xen/arch/x86/msi.c -+++ b/xen/arch/x86/msi.c -@@ -771,16 +771,14 @@ static int msix_capability_init(struct p - { - struct arch_msix *msix = dev->msix; - struct msi_desc *entry = NULL; -- int vf; - u16 control; - u64 table_paddr; - u32 table_offset; -- u8 bir, pbus, pslot, pfunc; - u16 seg = dev->seg; - u8 bus = dev->bus; - u8 slot = PCI_SLOT(dev->devfn); - u8 func = PCI_FUNC(dev->devfn); -- bool maskall = msix->host_maskall; -+ bool maskall = msix->host_maskall, zap_on_error = false; - unsigned int pos = pci_find_cap_offset(seg, bus, slot, func, - PCI_CAP_ID_MSIX); - -@@ -822,43 +820,45 @@ static int msix_capability_init(struct p - - /* Locate MSI-X table region */ - table_offset = pci_conf_read32(dev->sbdf, msix_table_offset_reg(pos)); -- bir = (u8)(table_offset & PCI_MSIX_BIRMASK); -- table_offset &= ~PCI_MSIX_BIRMASK; -+ if ( !msix->used_entries && -+ (!msi || -+ (is_hardware_domain(current->domain) && -+ (dev->domain == current->domain || dev->domain == dom_io))) ) -+ { -+ unsigned int bir = table_offset & PCI_MSIX_BIRMASK, pbus, pslot, pfunc; -+ int vf; -+ paddr_t pba_paddr; -+ unsigned int pba_offset; - -- if ( !dev->info.is_virtfn ) -- { -- pbus = bus; -- pslot = slot; -- pfunc = func; -- vf = -1; -- } -- else -- { -- pbus = dev->info.physfn.bus; -- pslot = PCI_SLOT(dev->info.physfn.devfn); -- pfunc = PCI_FUNC(dev->info.physfn.devfn); -- vf = PCI_BDF2(dev->bus, dev->devfn); -- } -- -- table_paddr = read_pci_mem_bar(seg, pbus, pslot, pfunc, bir, vf); -- WARN_ON(msi && msi->table_base != table_paddr); -- if ( !table_paddr ) -- { -- if ( !msi || !msi->table_base ) -+ if ( !dev->info.is_virtfn ) - { -- pci_conf_write16(dev->sbdf, msix_control_reg(pos), -- control & ~PCI_MSIX_FLAGS_ENABLE); -- xfree(entry); -- return -ENXIO; -+ pbus = bus; -+ pslot = slot; -+ pfunc = func; -+ vf = -1; -+ } -+ else -+ { -+ pbus = dev->info.physfn.bus; -+ pslot = PCI_SLOT(dev->info.physfn.devfn); -+ pfunc = PCI_FUNC(dev->info.physfn.devfn); -+ vf = PCI_BDF2(dev->bus, dev->devfn); - } -- table_paddr = msi->table_base; -- } -- table_paddr += table_offset; - -- if ( !msix->used_entries ) -- { -- u64 pba_paddr; -- u32 pba_offset; -+ table_paddr = read_pci_mem_bar(seg, pbus, pslot, pfunc, bir, vf); -+ WARN_ON(msi && msi->table_base != table_paddr); -+ if ( !table_paddr ) -+ { -+ if ( !msi || !msi->table_base ) -+ { -+ pci_conf_write16(dev->sbdf, msix_control_reg(pos), -+ control & ~PCI_MSIX_FLAGS_ENABLE); -+ xfree(entry); -+ return -ENXIO; -+ } -+ table_paddr = msi->table_base; -+ } -+ table_paddr += table_offset & ~PCI_MSIX_BIRMASK; - - msix->table.first = PFN_DOWN(table_paddr); - msix->table.last = PFN_DOWN(table_paddr + -@@ -877,7 +877,18 @@ static int msix_capability_init(struct p - BITS_TO_LONGS(msix->nr_entries) - 1); - WARN_ON(rangeset_overlaps_range(mmio_ro_ranges, msix->pba.first, - msix->pba.last)); -+ -+ zap_on_error = true; -+ } -+ else if ( !msix->table.first ) -+ { -+ pci_conf_write16(dev->sbdf, msix_control_reg(pos), control); -+ xfree(entry); -+ return -ENODATA; - } -+ else -+ table_paddr = (msix->table.first << PAGE_SHIFT) + -+ PAGE_OFFSET(table_offset & ~PCI_MSIX_BIRMASK); - - if ( entry ) - { -@@ -888,8 +899,15 @@ static int msix_capability_init(struct p - - if ( idx < 0 ) - { -- pci_conf_write16(dev->sbdf, msix_control_reg(pos), -- control & ~PCI_MSIX_FLAGS_ENABLE); -+ if ( zap_on_error ) -+ { -+ msix->table.first = 0; -+ msix->pba.first = 0; -+ -+ control &= ~PCI_MSIX_FLAGS_ENABLE; -+ } -+ -+ pci_conf_write16(dev->sbdf, msix_control_reg(pos), control); - xfree(entry); - return idx; - } -@@ -1072,9 +1090,14 @@ static void _pci_cleanup_msix(struct arc - if ( rangeset_remove_range(mmio_ro_ranges, msix->table.first, - msix->table.last) ) - WARN(); -+ msix->table.first = 0; -+ msix->table.last = 0; -+ - if ( rangeset_remove_range(mmio_ro_ranges, msix->pba.first, - msix->pba.last) ) - WARN(); -+ msix->pba.first = 0; -+ msix->pba.last = 0; - } - } - diff --git a/xsa338.patch b/xsa338.patch deleted file mode 100644 index 7765219..0000000 --- a/xsa338.patch +++ /dev/null @@ -1,42 +0,0 @@ -From: Jan Beulich -Subject: evtchn: relax port_is_valid() - -To avoid ports potentially becoming invalid behind the back of certain -other functions (due to ->max_evtchn shrinking) because of -- a guest invoking evtchn_reset() and from a 2nd vCPU opening new - channels in parallel (see also XSA-343), -- alloc_unbound_xen_event_channel() produced channels living above the - 2-level range (see also XSA-342), -drop the max_evtchns check from port_is_valid(). For a port for which -the function once returned "true", the returned value may not turn into -"false" later on. The function's result may only depend on bounds which -can only ever grow (which is the case for d->valid_evtchns). - -This also eliminates a false sense of safety, utilized by some of the -users (see again XSA-343): Without a suitable lock held, d->max_evtchns -may change at any time, and hence deducing that certain other operations -are safe when port_is_valid() returned true is not legitimate. The -opportunities to abuse this may get widened by the change here -(depending on guest and host configuration), but will be taken care of -by the other XSA. - -This is XSA-338. - -Fixes: 48974e6ce52e ("evtchn: use a per-domain variable for the max number of event channels") -Signed-off-by: Jan Beulich -Reviewed-by: Stefano Stabellini -Reviewed-by: Julien Grall ---- -v5: New, split from larger patch. - ---- a/xen/include/xen/event.h -+++ b/xen/include/xen/event.h -@@ -107,8 +107,6 @@ void notify_via_xen_event_channel(struct - - static inline bool_t port_is_valid(struct domain *d, unsigned int p) - { -- if ( p >= d->max_evtchns ) -- return 0; - return p < read_atomic(&d->valid_evtchns); - } - diff --git a/xsa339.patch b/xsa339.patch deleted file mode 100644 index 3311ae0..0000000 --- a/xsa339.patch +++ /dev/null @@ -1,76 +0,0 @@ -From: Andrew Cooper -Subject: x86/pv: Avoid double exception injection - -There is at least one path (SYSENTER with NT set, Xen converts to #GP) which -ends up injecting the #GP fault twice, first in compat_sysenter(), and then a -second time in compat_test_all_events(), due to the stale TBF_EXCEPTION left -in TRAPBOUNCE_flags. - -The guest kernel sees the second fault first, which is a kernel level #GP -pointing at the head of the #GP handler, and is therefore a userspace -trigger-able DoS. - -This particular bug has bitten us several times before, so rearrange -{compat_,}create_bounce_frame() to clobber TRAPBOUNCE on success, rather than -leaving this task to one area of code which isn't used uniformly. - -Other scenarios which might result in a double injection (e.g. two calls -directly to compat_create_bounce_frame) will now crash the guest, which is far -more obvious than letting the kernel run with corrupt state. - -This is XSA-339 - -Fixes: fdac9515607b ("x86: clear EFLAGS.NT in SYSENTER entry path") -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/x86_64/compat/entry.S b/xen/arch/x86/x86_64/compat/entry.S -index c3e62f8734..73619f57ca 100644 ---- a/xen/arch/x86/x86_64/compat/entry.S -+++ b/xen/arch/x86/x86_64/compat/entry.S -@@ -78,7 +78,6 @@ compat_process_softirqs: - sti - .Lcompat_bounce_exception: - call compat_create_bounce_frame -- movb $0, TRAPBOUNCE_flags(%rdx) - jmp compat_test_all_events - - ALIGN -@@ -352,7 +351,13 @@ __UNLIKELY_END(compat_bounce_null_selector) - movl %eax,UREGS_cs+8(%rsp) - movl TRAPBOUNCE_eip(%rdx),%eax - movl %eax,UREGS_rip+8(%rsp) -+ -+ /* Trapbounce complete. Clobber state to avoid an erroneous second injection. */ -+ xor %eax, %eax -+ mov %ax, TRAPBOUNCE_cs(%rdx) -+ mov %al, TRAPBOUNCE_flags(%rdx) - ret -+ - .section .fixup,"ax" - .Lfx13: - xorl %edi,%edi -diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S -index 1e880eb9f6..71a00e846b 100644 ---- a/xen/arch/x86/x86_64/entry.S -+++ b/xen/arch/x86/x86_64/entry.S -@@ -90,7 +90,6 @@ process_softirqs: - sti - .Lbounce_exception: - call create_bounce_frame -- movb $0, TRAPBOUNCE_flags(%rdx) - jmp test_all_events - - ALIGN -@@ -512,6 +511,11 @@ UNLIKELY_START(z, create_bounce_frame_bad_bounce_ip) - jmp asm_domain_crash_synchronous /* Does not return */ - __UNLIKELY_END(create_bounce_frame_bad_bounce_ip) - movq %rax,UREGS_rip+8(%rsp) -+ -+ /* Trapbounce complete. Clobber state to avoid an erroneous second injection. */ -+ xor %eax, %eax -+ mov %rax, TRAPBOUNCE_eip(%rdx) -+ mov %al, TRAPBOUNCE_flags(%rdx) - ret - - .pushsection .fixup, "ax", @progbits diff --git a/xsa340.patch b/xsa340.patch deleted file mode 100644 index 38d04da..0000000 --- a/xsa340.patch +++ /dev/null @@ -1,65 +0,0 @@ -From: Julien Grall -Subject: xen/evtchn: Add missing barriers when accessing/allocating an event channel - -While the allocation of a bucket is always performed with the per-domain -lock, the bucket may be accessed without the lock taken (for instance, see -evtchn_send()). - -Instead such sites relies on port_is_valid() to return a non-zero value -when the port has a struct evtchn associated to it. The function will -mostly check whether the port is less than d->valid_evtchns as all the -buckets/event channels should be allocated up to that point. - -Unfortunately a compiler is free to re-order the assignment in -evtchn_allocate_port() so it would be possible to have d->valid_evtchns -updated before the new bucket has finish to allocate. - -Additionally on Arm, even if this was compiled "correctly", the -processor can still re-order the memory access. - -Add a write memory barrier in the allocation side and a read memory -barrier when the port is valid to prevent any re-ordering issue. - -This is XSA-340. - -Reported-by: Julien Grall -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini - ---- a/xen/common/event_channel.c -+++ b/xen/common/event_channel.c -@@ -178,6 +178,13 @@ int evtchn_allocate_port(struct domain * - return -ENOMEM; - bucket_from_port(d, port) = chn; - -+ /* -+ * d->valid_evtchns is used to check whether the bucket can be -+ * accessed without the per-domain lock. Therefore, -+ * d->valid_evtchns should be seen *after* the new bucket has -+ * been setup. -+ */ -+ smp_wmb(); - write_atomic(&d->valid_evtchns, d->valid_evtchns + EVTCHNS_PER_BUCKET); - } - ---- a/xen/include/xen/event.h -+++ b/xen/include/xen/event.h -@@ -107,7 +107,17 @@ void notify_via_xen_event_channel(struct - - static inline bool_t port_is_valid(struct domain *d, unsigned int p) - { -- return p < read_atomic(&d->valid_evtchns); -+ if ( p >= read_atomic(&d->valid_evtchns) ) -+ return false; -+ -+ /* -+ * The caller will usually access the event channel afterwards and -+ * may be done without taking the per-domain lock. The barrier is -+ * going in pair the smp_wmb() barrier in evtchn_allocate_port(). -+ */ -+ smp_rmb(); -+ -+ return true; - } - - static inline struct evtchn *evtchn_from_port(struct domain *d, unsigned int p) diff --git a/xsa342.patch b/xsa342.patch deleted file mode 100644 index c878c07..0000000 --- a/xsa342.patch +++ /dev/null @@ -1,152 +0,0 @@ -From: Jan Beulich -Subject: evtchn/x86: enforce correct upper limit for 32-bit guests - -The recording of d->max_evtchns in evtchn_2l_init(), in particular with -the limited set of callers of the function, is insufficient. Neither for -PV nor for HVM guests the bitness is known at domain_create() time, yet -the upper bound in 2-level mode depends upon guest bitness. Recording -too high a limit "allows" x86 32-bit domains to open not properly usable -event channels, management of which (inside Xen) would then result in -corruption of the shared info and vCPU info structures. - -Keep the upper limit dynamic for the 2-level case, introducing a helper -function to retrieve the effective limit. This helper is now supposed to -be private to the event channel code. The used in do_poll() and -domain_dump_evtchn_info() weren't consistent with port uses elsewhere -and hence get switched to port_is_valid(). - -Furthermore FIFO mode's setup_ports() gets adjusted to loop only up to -the prior ABI limit, rather than all the way up to the new one. - -Finally a word on the change to do_poll(): Accessing ->max_evtchns -without holding a suitable lock was never safe, as it as well as -->evtchn_port_ops may change behind do_poll()'s back. Using -port_is_valid() instead widens some the window for potential abuse, -until we've dealt with the race altogether (see XSA-343). - -This is XSA-342. - -Reported-by: Julien Grall -Fixes: 48974e6ce52e ("evtchn: use a per-domain variable for the max number of event channels") -Signed-off-by: Jan Beulich -Reviewed-by: Stefano Stabellini -Reviewed-by: Julien Grall ---- -v6: Drop redundant port_is_valid() check from domain_dump_evtchn_info(). - Add comment in evtchn_fifo_init_control(). -v5: Split off port_is_valid() change. Drop max_evtchns field. -v4: Also mention the alloc_unbound_xen_event_channel() case that gets - fixed here. -v2: Remove max_evtchns check from port_is_valid(). - ---- a/xen/common/event_2l.c -+++ b/xen/common/event_2l.c -@@ -103,7 +103,6 @@ static const struct evtchn_port_ops evtc - void evtchn_2l_init(struct domain *d) - { - d->evtchn_port_ops = &evtchn_port_ops_2l; -- d->max_evtchns = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); - } - - /* ---- a/xen/common/event_channel.c -+++ b/xen/common/event_channel.c -@@ -151,7 +151,7 @@ static void free_evtchn_bucket(struct do - - int evtchn_allocate_port(struct domain *d, evtchn_port_t port) - { -- if ( port > d->max_evtchn_port || port >= d->max_evtchns ) -+ if ( port > d->max_evtchn_port || port >= max_evtchns(d) ) - return -ENOSPC; - - if ( port_is_valid(d, port) ) -@@ -1396,13 +1396,11 @@ static void domain_dump_evtchn_info(stru - - spin_lock(&d->event_lock); - -- for ( port = 1; port < d->max_evtchns; ++port ) -+ for ( port = 1; port_is_valid(d, port); ++port ) - { - const struct evtchn *chn; - char *ssid; - -- if ( !port_is_valid(d, port) ) -- continue; - chn = evtchn_from_port(d, port); - if ( chn->state == ECS_FREE ) - continue; ---- a/xen/common/event_fifo.c -+++ b/xen/common/event_fifo.c -@@ -478,7 +478,7 @@ static void cleanup_event_array(struct d - d->evtchn_fifo = NULL; - } - --static void setup_ports(struct domain *d) -+static void setup_ports(struct domain *d, unsigned int prev_evtchns) - { - unsigned int port; - -@@ -488,7 +488,7 @@ static void setup_ports(struct domain *d - * - save its pending state. - * - set default priority. - */ -- for ( port = 1; port < d->max_evtchns; port++ ) -+ for ( port = 1; port < prev_evtchns; port++ ) - { - struct evtchn *evtchn; - -@@ -546,6 +546,8 @@ int evtchn_fifo_init_control(struct evtc - if ( !d->evtchn_fifo ) - { - struct vcpu *vcb; -+ /* Latch the value before it changes during setup_event_array(). */ -+ unsigned int prev_evtchns = max_evtchns(d); - - for_each_vcpu ( d, vcb ) { - rc = setup_control_block(vcb); -@@ -562,8 +564,7 @@ int evtchn_fifo_init_control(struct evtc - goto error; - - d->evtchn_port_ops = &evtchn_port_ops_fifo; -- d->max_evtchns = EVTCHN_FIFO_NR_CHANNELS; -- setup_ports(d); -+ setup_ports(d, prev_evtchns); - } - else - rc = map_control_block(v, gfn, offset); ---- a/xen/common/sched/core.c -+++ b/xen/common/sched/core.c -@@ -1428,7 +1428,7 @@ static long do_poll(struct sched_poll *s - goto out; - - rc = -EINVAL; -- if ( port >= d->max_evtchns ) -+ if ( !port_is_valid(d, port) ) - goto out; - - rc = 0; ---- a/xen/include/xen/event.h -+++ b/xen/include/xen/event.h -@@ -105,6 +105,12 @@ void notify_via_xen_event_channel(struct - #define bucket_from_port(d, p) \ - ((group_from_port(d, p))[((p) % EVTCHNS_PER_GROUP) / EVTCHNS_PER_BUCKET]) - -+static inline unsigned int max_evtchns(const struct domain *d) -+{ -+ return d->evtchn_fifo ? EVTCHN_FIFO_NR_CHANNELS -+ : BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); -+} -+ - static inline bool_t port_is_valid(struct domain *d, unsigned int p) - { - if ( p >= read_atomic(&d->valid_evtchns) ) ---- a/xen/include/xen/sched.h -+++ b/xen/include/xen/sched.h -@@ -359,7 +359,6 @@ struct domain - /* Event channel information. */ - struct evtchn *evtchn; /* first bucket only */ - struct evtchn **evtchn_group[NR_EVTCHN_GROUPS]; /* all other buckets */ -- unsigned int max_evtchns; /* number supported by ABI */ - unsigned int max_evtchn_port; /* max permitted port number */ - unsigned int valid_evtchns; /* number of allocated event channels */ - spinlock_t event_lock; diff --git a/xsa343-1.patch b/xsa343-1.patch deleted file mode 100644 index 0abbc03..0000000 --- a/xsa343-1.patch +++ /dev/null @@ -1,199 +0,0 @@ -From: Jan Beulich -Subject: evtchn: evtchn_reset() shouldn't succeed with still-open ports - -While the function closes all ports, it does so without holding any -lock, and hence racing requests may be issued causing new ports to get -opened. This would have been problematic in particular if such a newly -opened port had a port number above the new implementation limit (i.e. -when switching from FIFO to 2-level) after the reset, as prior to -"evtchn: relax port_is_valid()" this could have led to e.g. -evtchn_close()'s "BUG_ON(!port_is_valid(d2, port2))" to trigger. - -Introduce a counter of active ports and check that it's (still) no -larger then the number of Xen internally used ones after obtaining the -necessary lock in evtchn_reset(). - -As to the access model of the new {active,xen}_evtchns fields - while -all writes get done using write_atomic(), reads ought to use -read_atomic() only when outside of a suitably locked region. - -Note that as of now evtchn_bind_virq() and evtchn_bind_ipi() don't have -a need to call check_free_port(). - -This is part of XSA-343. - -Signed-off-by: Jan Beulich -Reviewed-by: Stefano Stabellini -Reviewed-by: Julien Grall ---- -v7: Drop optimization from evtchn_reset(). -v6: Fix loop exit condition in evtchn_reset(). Use {read,write}_atomic() - also for xen_evtchns. -v5: Move increment in alloc_unbound_xen_event_channel() out of the inner - locked region. -v4: Account for Xen internal ports. -v3: Document intended access next to new struct field. -v2: Add comment to check_free_port(). Drop commented out calls. - ---- a/xen/common/event_channel.c -+++ b/xen/common/event_channel.c -@@ -188,6 +188,8 @@ int evtchn_allocate_port(struct domain * - write_atomic(&d->valid_evtchns, d->valid_evtchns + EVTCHNS_PER_BUCKET); - } - -+ write_atomic(&d->active_evtchns, d->active_evtchns + 1); -+ - return 0; - } - -@@ -211,11 +213,26 @@ static int get_free_port(struct domain * - return -ENOSPC; - } - -+/* -+ * Check whether a port is still marked free, and if so update the domain -+ * counter accordingly. To be used on function exit paths. -+ */ -+static void check_free_port(struct domain *d, evtchn_port_t port) -+{ -+ if ( port_is_valid(d, port) && -+ evtchn_from_port(d, port)->state == ECS_FREE ) -+ write_atomic(&d->active_evtchns, d->active_evtchns - 1); -+} -+ - void evtchn_free(struct domain *d, struct evtchn *chn) - { - /* Clear pending event to avoid unexpected behavior on re-bind. */ - evtchn_port_clear_pending(d, chn); - -+ if ( consumer_is_xen(chn) ) -+ write_atomic(&d->xen_evtchns, d->xen_evtchns - 1); -+ write_atomic(&d->active_evtchns, d->active_evtchns - 1); -+ - /* Reset binding to vcpu0 when the channel is freed. */ - chn->state = ECS_FREE; - chn->notify_vcpu_id = 0; -@@ -258,6 +275,7 @@ static long evtchn_alloc_unbound(evtchn_ - alloc->port = port; - - out: -+ check_free_port(d, port); - spin_unlock(&d->event_lock); - rcu_unlock_domain(d); - -@@ -351,6 +369,7 @@ static long evtchn_bind_interdomain(evtc - bind->local_port = lport; - - out: -+ check_free_port(ld, lport); - spin_unlock(&ld->event_lock); - if ( ld != rd ) - spin_unlock(&rd->event_lock); -@@ -488,7 +507,7 @@ static long evtchn_bind_pirq(evtchn_bind - struct domain *d = current->domain; - struct vcpu *v = d->vcpu[0]; - struct pirq *info; -- int port, pirq = bind->pirq; -+ int port = 0, pirq = bind->pirq; - long rc; - - if ( (pirq < 0) || (pirq >= d->nr_pirqs) ) -@@ -536,6 +555,7 @@ static long evtchn_bind_pirq(evtchn_bind - arch_evtchn_bind_pirq(d, pirq); - - out: -+ check_free_port(d, port); - spin_unlock(&d->event_lock); - - return rc; -@@ -1011,10 +1031,10 @@ int evtchn_unmask(unsigned int port) - return 0; - } - -- - int evtchn_reset(struct domain *d) - { - unsigned int i; -+ int rc = 0; - - if ( d != current->domain && !d->controller_pause_count ) - return -EINVAL; -@@ -1024,7 +1044,9 @@ int evtchn_reset(struct domain *d) - - spin_lock(&d->event_lock); - -- if ( d->evtchn_fifo ) -+ if ( d->active_evtchns > d->xen_evtchns ) -+ rc = -EAGAIN; -+ else if ( d->evtchn_fifo ) - { - /* Switching back to 2-level ABI. */ - evtchn_fifo_destroy(d); -@@ -1033,7 +1055,7 @@ int evtchn_reset(struct domain *d) - - spin_unlock(&d->event_lock); - -- return 0; -+ return rc; - } - - static long evtchn_set_priority(const struct evtchn_set_priority *set_priority) -@@ -1219,10 +1241,9 @@ int alloc_unbound_xen_event_channel( - - spin_lock(&ld->event_lock); - -- rc = get_free_port(ld); -+ port = rc = get_free_port(ld); - if ( rc < 0 ) - goto out; -- port = rc; - chn = evtchn_from_port(ld, port); - - rc = xsm_evtchn_unbound(XSM_TARGET, ld, chn, remote_domid); -@@ -1238,7 +1259,10 @@ int alloc_unbound_xen_event_channel( - - spin_unlock(&chn->lock); - -+ write_atomic(&ld->xen_evtchns, ld->xen_evtchns + 1); -+ - out: -+ check_free_port(ld, port); - spin_unlock(&ld->event_lock); - - return rc < 0 ? rc : port; -@@ -1314,6 +1338,7 @@ int evtchn_init(struct domain *d, unsign - return -EINVAL; - } - evtchn_from_port(d, 0)->state = ECS_RESERVED; -+ write_atomic(&d->active_evtchns, 0); - - #if MAX_VIRT_CPUS > BITS_PER_LONG - d->poll_mask = xzalloc_array(unsigned long, BITS_TO_LONGS(d->max_vcpus)); -@@ -1340,6 +1365,8 @@ void evtchn_destroy(struct domain *d) - for ( i = 0; port_is_valid(d, i); i++ ) - evtchn_close(d, i, 0); - -+ ASSERT(!d->active_evtchns); -+ - clear_global_virq_handlers(d); - - evtchn_fifo_destroy(d); ---- a/xen/include/xen/sched.h -+++ b/xen/include/xen/sched.h -@@ -361,6 +361,16 @@ struct domain - struct evtchn **evtchn_group[NR_EVTCHN_GROUPS]; /* all other buckets */ - unsigned int max_evtchn_port; /* max permitted port number */ - unsigned int valid_evtchns; /* number of allocated event channels */ -+ /* -+ * Number of in-use event channels. Writers should use write_atomic(). -+ * Readers need to use read_atomic() only when not holding event_lock. -+ */ -+ unsigned int active_evtchns; -+ /* -+ * Number of event channels used internally by Xen (not subject to -+ * EVTCHNOP_reset). Read/write access like for active_evtchns. -+ */ -+ unsigned int xen_evtchns; - spinlock_t event_lock; - const struct evtchn_port_ops *evtchn_port_ops; - struct evtchn_fifo_domain *evtchn_fifo; diff --git a/xsa343-2.patch b/xsa343-2.patch deleted file mode 100644 index b8eb499..0000000 --- a/xsa343-2.patch +++ /dev/null @@ -1,295 +0,0 @@ -From: Jan Beulich -Subject: evtchn: convert per-channel lock to be IRQ-safe - -... in order for send_guest_{global,vcpu}_virq() to be able to make use -of it. - -This is part of XSA-343. - -Signed-off-by: Jan Beulich -Acked-by: Julien Grall ---- -v6: New. ---- -TBD: This is the "dumb" conversion variant. In a couple of cases the - slightly simpler spin_{,un}lock_irq() could apparently be used. - ---- a/xen/common/event_channel.c -+++ b/xen/common/event_channel.c -@@ -248,6 +248,7 @@ static long evtchn_alloc_unbound(evtchn_ - int port; - domid_t dom = alloc->dom; - long rc; -+ unsigned long flags; - - d = rcu_lock_domain_by_any_id(dom); - if ( d == NULL ) -@@ -263,14 +264,14 @@ static long evtchn_alloc_unbound(evtchn_ - if ( rc ) - goto out; - -- spin_lock(&chn->lock); -+ spin_lock_irqsave(&chn->lock, flags); - - chn->state = ECS_UNBOUND; - if ( (chn->u.unbound.remote_domid = alloc->remote_dom) == DOMID_SELF ) - chn->u.unbound.remote_domid = current->domain->domain_id; - evtchn_port_init(d, chn); - -- spin_unlock(&chn->lock); -+ spin_unlock_irqrestore(&chn->lock, flags); - - alloc->port = port; - -@@ -283,26 +284,32 @@ static long evtchn_alloc_unbound(evtchn_ - } - - --static void double_evtchn_lock(struct evtchn *lchn, struct evtchn *rchn) -+static unsigned long double_evtchn_lock(struct evtchn *lchn, -+ struct evtchn *rchn) - { -- if ( lchn < rchn ) -+ unsigned long flags; -+ -+ if ( lchn <= rchn ) - { -- spin_lock(&lchn->lock); -- spin_lock(&rchn->lock); -+ spin_lock_irqsave(&lchn->lock, flags); -+ if ( lchn != rchn ) -+ spin_lock(&rchn->lock); - } - else - { -- if ( lchn != rchn ) -- spin_lock(&rchn->lock); -+ spin_lock_irqsave(&rchn->lock, flags); - spin_lock(&lchn->lock); - } -+ -+ return flags; - } - --static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn) -+static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn, -+ unsigned long flags) - { -- spin_unlock(&lchn->lock); - if ( lchn != rchn ) -- spin_unlock(&rchn->lock); -+ spin_unlock(&lchn->lock); -+ spin_unlock_irqrestore(&rchn->lock, flags); - } - - static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) -@@ -312,6 +319,7 @@ static long evtchn_bind_interdomain(evtc - int lport, rport = bind->remote_port; - domid_t rdom = bind->remote_dom; - long rc; -+ unsigned long flags; - - if ( rdom == DOMID_SELF ) - rdom = current->domain->domain_id; -@@ -347,7 +355,7 @@ static long evtchn_bind_interdomain(evtc - if ( rc ) - goto out; - -- double_evtchn_lock(lchn, rchn); -+ flags = double_evtchn_lock(lchn, rchn); - - lchn->u.interdomain.remote_dom = rd; - lchn->u.interdomain.remote_port = rport; -@@ -364,7 +372,7 @@ static long evtchn_bind_interdomain(evtc - */ - evtchn_port_set_pending(ld, lchn->notify_vcpu_id, lchn); - -- double_evtchn_unlock(lchn, rchn); -+ double_evtchn_unlock(lchn, rchn, flags); - - bind->local_port = lport; - -@@ -387,6 +395,7 @@ int evtchn_bind_virq(evtchn_bind_virq_t - struct domain *d = current->domain; - int virq = bind->virq, vcpu = bind->vcpu; - int rc = 0; -+ unsigned long flags; - - if ( (virq < 0) || (virq >= ARRAY_SIZE(v->virq_to_evtchn)) ) - return -EINVAL; -@@ -424,14 +433,14 @@ int evtchn_bind_virq(evtchn_bind_virq_t - - chn = evtchn_from_port(d, port); - -- spin_lock(&chn->lock); -+ spin_lock_irqsave(&chn->lock, flags); - - chn->state = ECS_VIRQ; - chn->notify_vcpu_id = vcpu; - chn->u.virq = virq; - evtchn_port_init(d, chn); - -- spin_unlock(&chn->lock); -+ spin_unlock_irqrestore(&chn->lock, flags); - - v->virq_to_evtchn[virq] = bind->port = port; - -@@ -448,6 +457,7 @@ static long evtchn_bind_ipi(evtchn_bind_ - struct domain *d = current->domain; - int port, vcpu = bind->vcpu; - long rc = 0; -+ unsigned long flags; - - if ( domain_vcpu(d, vcpu) == NULL ) - return -ENOENT; -@@ -459,13 +469,13 @@ static long evtchn_bind_ipi(evtchn_bind_ - - chn = evtchn_from_port(d, port); - -- spin_lock(&chn->lock); -+ spin_lock_irqsave(&chn->lock, flags); - - chn->state = ECS_IPI; - chn->notify_vcpu_id = vcpu; - evtchn_port_init(d, chn); - -- spin_unlock(&chn->lock); -+ spin_unlock_irqrestore(&chn->lock, flags); - - bind->port = port; - -@@ -509,6 +519,7 @@ static long evtchn_bind_pirq(evtchn_bind - struct pirq *info; - int port = 0, pirq = bind->pirq; - long rc; -+ unsigned long flags; - - if ( (pirq < 0) || (pirq >= d->nr_pirqs) ) - return -EINVAL; -@@ -541,14 +552,14 @@ static long evtchn_bind_pirq(evtchn_bind - goto out; - } - -- spin_lock(&chn->lock); -+ spin_lock_irqsave(&chn->lock, flags); - - chn->state = ECS_PIRQ; - chn->u.pirq.irq = pirq; - link_pirq_port(port, chn, v); - evtchn_port_init(d, chn); - -- spin_unlock(&chn->lock); -+ spin_unlock_irqrestore(&chn->lock, flags); - - bind->port = port; - -@@ -569,6 +580,7 @@ int evtchn_close(struct domain *d1, int - struct evtchn *chn1, *chn2; - int port2; - long rc = 0; -+ unsigned long flags; - - again: - spin_lock(&d1->event_lock); -@@ -668,14 +680,14 @@ int evtchn_close(struct domain *d1, int - BUG_ON(chn2->state != ECS_INTERDOMAIN); - BUG_ON(chn2->u.interdomain.remote_dom != d1); - -- double_evtchn_lock(chn1, chn2); -+ flags = double_evtchn_lock(chn1, chn2); - - evtchn_free(d1, chn1); - - chn2->state = ECS_UNBOUND; - chn2->u.unbound.remote_domid = d1->domain_id; - -- double_evtchn_unlock(chn1, chn2); -+ double_evtchn_unlock(chn1, chn2, flags); - - goto out; - -@@ -683,9 +695,9 @@ int evtchn_close(struct domain *d1, int - BUG(); - } - -- spin_lock(&chn1->lock); -+ spin_lock_irqsave(&chn1->lock, flags); - evtchn_free(d1, chn1); -- spin_unlock(&chn1->lock); -+ spin_unlock_irqrestore(&chn1->lock, flags); - - out: - if ( d2 != NULL ) -@@ -705,13 +717,14 @@ int evtchn_send(struct domain *ld, unsig - struct evtchn *lchn, *rchn; - struct domain *rd; - int rport, ret = 0; -+ unsigned long flags; - - if ( !port_is_valid(ld, lport) ) - return -EINVAL; - - lchn = evtchn_from_port(ld, lport); - -- spin_lock(&lchn->lock); -+ spin_lock_irqsave(&lchn->lock, flags); - - /* Guest cannot send via a Xen-attached event channel. */ - if ( unlikely(consumer_is_xen(lchn)) ) -@@ -746,7 +759,7 @@ int evtchn_send(struct domain *ld, unsig - } - - out: -- spin_unlock(&lchn->lock); -+ spin_unlock_irqrestore(&lchn->lock, flags); - - return ret; - } -@@ -1238,6 +1251,7 @@ int alloc_unbound_xen_event_channel( - { - struct evtchn *chn; - int port, rc; -+ unsigned long flags; - - spin_lock(&ld->event_lock); - -@@ -1250,14 +1264,14 @@ int alloc_unbound_xen_event_channel( - if ( rc ) - goto out; - -- spin_lock(&chn->lock); -+ spin_lock_irqsave(&chn->lock, flags); - - chn->state = ECS_UNBOUND; - chn->xen_consumer = get_xen_consumer(notification_fn); - chn->notify_vcpu_id = lvcpu; - chn->u.unbound.remote_domid = remote_domid; - -- spin_unlock(&chn->lock); -+ spin_unlock_irqrestore(&chn->lock, flags); - - write_atomic(&ld->xen_evtchns, ld->xen_evtchns + 1); - -@@ -1280,11 +1294,12 @@ void notify_via_xen_event_channel(struct - { - struct evtchn *lchn, *rchn; - struct domain *rd; -+ unsigned long flags; - - ASSERT(port_is_valid(ld, lport)); - lchn = evtchn_from_port(ld, lport); - -- spin_lock(&lchn->lock); -+ spin_lock_irqsave(&lchn->lock, flags); - - if ( likely(lchn->state == ECS_INTERDOMAIN) ) - { -@@ -1294,7 +1309,7 @@ void notify_via_xen_event_channel(struct - evtchn_port_set_pending(rd, rchn->notify_vcpu_id, rchn); - } - -- spin_unlock(&lchn->lock); -+ spin_unlock_irqrestore(&lchn->lock, flags); - } - - void evtchn_check_pollers(struct domain *d, unsigned int port) diff --git a/xsa343-3.patch b/xsa343-3.patch deleted file mode 100644 index e513e30..0000000 --- a/xsa343-3.patch +++ /dev/null @@ -1,392 +0,0 @@ -From: Jan Beulich -Subject: evtchn: address races with evtchn_reset() - -Neither d->evtchn_port_ops nor max_evtchns(d) may be used in an entirely -lock-less manner, as both may change by a racing evtchn_reset(). In the -common case, at least one of the domain's event lock or the per-channel -lock needs to be held. In the specific case of the inter-domain sending -by evtchn_send() and notify_via_xen_event_channel() holding the other -side's per-channel lock is sufficient, as the channel can't change state -without both per-channel locks held. Without such a channel changing -state, evtchn_reset() can't complete successfully. - -Lock-free accesses continue to be permitted for the shim (calling some -otherwise internal event channel functions), as this happens while the -domain is in effectively single-threaded mode. Special care also needs -taking for the shim's marking of in-use ports as ECS_RESERVED (allowing -use of such ports in the shim case is okay because switching into and -hence also out of FIFO mode is impossihble there). - -As a side effect, certain operations on Xen bound event channels which -were mistakenly permitted so far (e.g. unmask or poll) will be refused -now. - -This is part of XSA-343. - -Reported-by: Julien Grall -Signed-off-by: Jan Beulich -Acked-by: Julien Grall ---- -v9: Add arch_evtchn_is_special() to fix PV shim. -v8: Add BUILD_BUG_ON() in evtchn_usable(). -v7: Add locking related comment ahead of struct evtchn_port_ops. -v6: New. ---- -TBD: I've been considering to move some of the wrappers from xen/event.h - into event_channel.c (or even drop them altogether), when they - require external locking (e.g. evtchn_port_init() or - evtchn_port_set_priority()). Does anyone have a strong opinion - either way? - ---- a/xen/arch/x86/irq.c -+++ b/xen/arch/x86/irq.c -@@ -2488,14 +2488,24 @@ static void dump_irqs(unsigned char key) - - for ( i = 0; i < action->nr_guests; ) - { -+ struct evtchn *evtchn; -+ unsigned int pending = 2, masked = 2; -+ - d = action->guest[i++]; - pirq = domain_irq_to_pirq(d, irq); - info = pirq_info(d, pirq); -+ evtchn = evtchn_from_port(d, info->evtchn); -+ local_irq_disable(); -+ if ( spin_trylock(&evtchn->lock) ) -+ { -+ pending = evtchn_is_pending(d, evtchn); -+ masked = evtchn_is_masked(d, evtchn); -+ spin_unlock(&evtchn->lock); -+ } -+ local_irq_enable(); - printk("d%d:%3d(%c%c%c)%c", -- d->domain_id, pirq, -- evtchn_port_is_pending(d, info->evtchn) ? 'P' : '-', -- evtchn_port_is_masked(d, info->evtchn) ? 'M' : '-', -- info->masked ? 'M' : '-', -+ d->domain_id, pirq, "-P?"[pending], -+ "-M?"[masked], info->masked ? 'M' : '-', - i < action->nr_guests ? ',' : '\n'); - } - } ---- a/xen/arch/x86/pv/shim.c -+++ b/xen/arch/x86/pv/shim.c -@@ -660,8 +660,11 @@ void pv_shim_inject_evtchn(unsigned int - if ( port_is_valid(guest, port) ) - { - struct evtchn *chn = evtchn_from_port(guest, port); -+ unsigned long flags; - -+ spin_lock_irqsave(&chn->lock, flags); - evtchn_port_set_pending(guest, chn->notify_vcpu_id, chn); -+ spin_unlock_irqrestore(&chn->lock, flags); - } - } - ---- a/xen/common/event_2l.c -+++ b/xen/common/event_2l.c -@@ -63,8 +63,10 @@ static void evtchn_2l_unmask(struct doma - } - } - --static bool evtchn_2l_is_pending(const struct domain *d, evtchn_port_t port) -+static bool evtchn_2l_is_pending(const struct domain *d, -+ const struct evtchn *evtchn) - { -+ evtchn_port_t port = evtchn->port; - unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); - - ASSERT(port < max_ports); -@@ -72,8 +74,10 @@ static bool evtchn_2l_is_pending(const s - guest_test_bit(d, port, &shared_info(d, evtchn_pending))); - } - --static bool evtchn_2l_is_masked(const struct domain *d, evtchn_port_t port) -+static bool evtchn_2l_is_masked(const struct domain *d, -+ const struct evtchn *evtchn) - { -+ evtchn_port_t port = evtchn->port; - unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); - - ASSERT(port < max_ports); ---- a/xen/common/event_channel.c -+++ b/xen/common/event_channel.c -@@ -156,8 +156,9 @@ int evtchn_allocate_port(struct domain * - - if ( port_is_valid(d, port) ) - { -- if ( evtchn_from_port(d, port)->state != ECS_FREE || -- evtchn_port_is_busy(d, port) ) -+ const struct evtchn *chn = evtchn_from_port(d, port); -+ -+ if ( chn->state != ECS_FREE || evtchn_is_busy(d, chn) ) - return -EBUSY; - } - else -@@ -774,6 +775,7 @@ void send_guest_vcpu_virq(struct vcpu *v - unsigned long flags; - int port; - struct domain *d; -+ struct evtchn *chn; - - ASSERT(!virq_is_global(virq)); - -@@ -784,7 +786,10 @@ void send_guest_vcpu_virq(struct vcpu *v - goto out; - - d = v->domain; -- evtchn_port_set_pending(d, v->vcpu_id, evtchn_from_port(d, port)); -+ chn = evtchn_from_port(d, port); -+ spin_lock(&chn->lock); -+ evtchn_port_set_pending(d, v->vcpu_id, chn); -+ spin_unlock(&chn->lock); - - out: - spin_unlock_irqrestore(&v->virq_lock, flags); -@@ -813,7 +818,9 @@ void send_guest_global_virq(struct domai - goto out; - - chn = evtchn_from_port(d, port); -+ spin_lock(&chn->lock); - evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); -+ spin_unlock(&chn->lock); - - out: - spin_unlock_irqrestore(&v->virq_lock, flags); -@@ -823,6 +830,7 @@ void send_guest_pirq(struct domain *d, c - { - int port; - struct evtchn *chn; -+ unsigned long flags; - - /* - * PV guests: It should not be possible to race with __evtchn_close(). The -@@ -837,7 +845,9 @@ void send_guest_pirq(struct domain *d, c - } - - chn = evtchn_from_port(d, port); -+ spin_lock_irqsave(&chn->lock, flags); - evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); -+ spin_unlock_irqrestore(&chn->lock, flags); - } - - static struct domain *global_virq_handlers[NR_VIRQS] __read_mostly; -@@ -1034,12 +1044,15 @@ int evtchn_unmask(unsigned int port) - { - struct domain *d = current->domain; - struct evtchn *evtchn; -+ unsigned long flags; - - if ( unlikely(!port_is_valid(d, port)) ) - return -EINVAL; - - evtchn = evtchn_from_port(d, port); -+ spin_lock_irqsave(&evtchn->lock, flags); - evtchn_port_unmask(d, evtchn); -+ spin_unlock_irqrestore(&evtchn->lock, flags); - - return 0; - } -@@ -1449,8 +1462,8 @@ static void domain_dump_evtchn_info(stru - - printk(" %4u [%d/%d/", - port, -- evtchn_port_is_pending(d, port), -- evtchn_port_is_masked(d, port)); -+ evtchn_is_pending(d, chn), -+ evtchn_is_masked(d, chn)); - evtchn_port_print_state(d, chn); - printk("]: s=%d n=%d x=%d", - chn->state, chn->notify_vcpu_id, chn->xen_consumer); ---- a/xen/common/event_fifo.c -+++ b/xen/common/event_fifo.c -@@ -296,23 +296,26 @@ static void evtchn_fifo_unmask(struct do - evtchn_fifo_set_pending(v, evtchn); - } - --static bool evtchn_fifo_is_pending(const struct domain *d, evtchn_port_t port) -+static bool evtchn_fifo_is_pending(const struct domain *d, -+ const struct evtchn *evtchn) - { -- const event_word_t *word = evtchn_fifo_word_from_port(d, port); -+ const event_word_t *word = evtchn_fifo_word_from_port(d, evtchn->port); - - return word && guest_test_bit(d, EVTCHN_FIFO_PENDING, word); - } - --static bool_t evtchn_fifo_is_masked(const struct domain *d, evtchn_port_t port) -+static bool_t evtchn_fifo_is_masked(const struct domain *d, -+ const struct evtchn *evtchn) - { -- const event_word_t *word = evtchn_fifo_word_from_port(d, port); -+ const event_word_t *word = evtchn_fifo_word_from_port(d, evtchn->port); - - return !word || guest_test_bit(d, EVTCHN_FIFO_MASKED, word); - } - --static bool_t evtchn_fifo_is_busy(const struct domain *d, evtchn_port_t port) -+static bool_t evtchn_fifo_is_busy(const struct domain *d, -+ const struct evtchn *evtchn) - { -- const event_word_t *word = evtchn_fifo_word_from_port(d, port); -+ const event_word_t *word = evtchn_fifo_word_from_port(d, evtchn->port); - - return word && guest_test_bit(d, EVTCHN_FIFO_LINKED, word); - } ---- a/xen/include/asm-x86/event.h -+++ b/xen/include/asm-x86/event.h -@@ -47,4 +47,10 @@ static inline bool arch_virq_is_global(u - return true; - } - -+#ifdef CONFIG_PV_SHIM -+# include -+# define arch_evtchn_is_special(chn) \ -+ (pv_shim && (chn)->port && (chn)->state == ECS_RESERVED) -+#endif -+ - #endif ---- a/xen/include/xen/event.h -+++ b/xen/include/xen/event.h -@@ -133,6 +133,24 @@ static inline struct evtchn *evtchn_from - return bucket_from_port(d, p) + (p % EVTCHNS_PER_BUCKET); - } - -+/* -+ * "usable" as in "by a guest", i.e. Xen consumed channels are assumed to be -+ * taken care of separately where used for Xen's internal purposes. -+ */ -+static bool evtchn_usable(const struct evtchn *evtchn) -+{ -+ if ( evtchn->xen_consumer ) -+ return false; -+ -+#ifdef arch_evtchn_is_special -+ if ( arch_evtchn_is_special(evtchn) ) -+ return true; -+#endif -+ -+ BUILD_BUG_ON(ECS_FREE > ECS_RESERVED); -+ return evtchn->state > ECS_RESERVED; -+} -+ - /* Wait on a Xen-attached event channel. */ - #define wait_on_xen_event_channel(port, condition) \ - do { \ -@@ -165,19 +183,24 @@ int evtchn_reset(struct domain *d); - - /* - * Low-level event channel port ops. -+ * -+ * All hooks have to be called with a lock held which prevents the channel -+ * from changing state. This may be the domain event lock, the per-channel -+ * lock, or in the case of sending interdomain events also the other side's -+ * per-channel lock. Exceptions apply in certain cases for the PV shim. - */ - struct evtchn_port_ops { - void (*init)(struct domain *d, struct evtchn *evtchn); - void (*set_pending)(struct vcpu *v, struct evtchn *evtchn); - void (*clear_pending)(struct domain *d, struct evtchn *evtchn); - void (*unmask)(struct domain *d, struct evtchn *evtchn); -- bool (*is_pending)(const struct domain *d, evtchn_port_t port); -- bool (*is_masked)(const struct domain *d, evtchn_port_t port); -+ bool (*is_pending)(const struct domain *d, const struct evtchn *evtchn); -+ bool (*is_masked)(const struct domain *d, const struct evtchn *evtchn); - /* - * Is the port unavailable because it's still being cleaned up - * after being closed? - */ -- bool (*is_busy)(const struct domain *d, evtchn_port_t port); -+ bool (*is_busy)(const struct domain *d, const struct evtchn *evtchn); - int (*set_priority)(struct domain *d, struct evtchn *evtchn, - unsigned int priority); - void (*print_state)(struct domain *d, const struct evtchn *evtchn); -@@ -193,38 +216,67 @@ static inline void evtchn_port_set_pendi - unsigned int vcpu_id, - struct evtchn *evtchn) - { -- d->evtchn_port_ops->set_pending(d->vcpu[vcpu_id], evtchn); -+ if ( evtchn_usable(evtchn) ) -+ d->evtchn_port_ops->set_pending(d->vcpu[vcpu_id], evtchn); - } - - static inline void evtchn_port_clear_pending(struct domain *d, - struct evtchn *evtchn) - { -- d->evtchn_port_ops->clear_pending(d, evtchn); -+ if ( evtchn_usable(evtchn) ) -+ d->evtchn_port_ops->clear_pending(d, evtchn); - } - - static inline void evtchn_port_unmask(struct domain *d, - struct evtchn *evtchn) - { -- d->evtchn_port_ops->unmask(d, evtchn); -+ if ( evtchn_usable(evtchn) ) -+ d->evtchn_port_ops->unmask(d, evtchn); - } - --static inline bool evtchn_port_is_pending(const struct domain *d, -- evtchn_port_t port) -+static inline bool evtchn_is_pending(const struct domain *d, -+ const struct evtchn *evtchn) - { -- return d->evtchn_port_ops->is_pending(d, port); -+ return evtchn_usable(evtchn) && d->evtchn_port_ops->is_pending(d, evtchn); - } - --static inline bool evtchn_port_is_masked(const struct domain *d, -- evtchn_port_t port) -+static inline bool evtchn_port_is_pending(struct domain *d, evtchn_port_t port) - { -- return d->evtchn_port_ops->is_masked(d, port); -+ struct evtchn *evtchn = evtchn_from_port(d, port); -+ bool rc; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&evtchn->lock, flags); -+ rc = evtchn_is_pending(d, evtchn); -+ spin_unlock_irqrestore(&evtchn->lock, flags); -+ -+ return rc; -+} -+ -+static inline bool evtchn_is_masked(const struct domain *d, -+ const struct evtchn *evtchn) -+{ -+ return !evtchn_usable(evtchn) || d->evtchn_port_ops->is_masked(d, evtchn); -+} -+ -+static inline bool evtchn_port_is_masked(struct domain *d, evtchn_port_t port) -+{ -+ struct evtchn *evtchn = evtchn_from_port(d, port); -+ bool rc; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&evtchn->lock, flags); -+ rc = evtchn_is_masked(d, evtchn); -+ spin_unlock_irqrestore(&evtchn->lock, flags); -+ -+ return rc; - } - --static inline bool evtchn_port_is_busy(const struct domain *d, -- evtchn_port_t port) -+static inline bool evtchn_is_busy(const struct domain *d, -+ const struct evtchn *evtchn) - { - return d->evtchn_port_ops->is_busy && -- d->evtchn_port_ops->is_busy(d, port); -+ d->evtchn_port_ops->is_busy(d, evtchn); - } - - static inline int evtchn_port_set_priority(struct domain *d, -@@ -233,6 +285,8 @@ static inline int evtchn_port_set_priori - { - if ( !d->evtchn_port_ops->set_priority ) - return -ENOSYS; -+ if ( !evtchn_usable(evtchn) ) -+ return -EACCES; - return d->evtchn_port_ops->set_priority(d, evtchn, priority); - } - diff --git a/xsa344-1.patch b/xsa344-1.patch deleted file mode 100644 index 3834075..0000000 --- a/xsa344-1.patch +++ /dev/null @@ -1,137 +0,0 @@ -From: Jan Beulich -Subject: evtchn: arrange for preemption in evtchn_destroy() - -Especially closing of fully established interdomain channels can take -quite some time, due to the locking involved. Therefore we shouldn't -assume we can clean up still active ports all in one go. Besides adding -the necessary preemption check, also avoid pointlessly starting from -(or now really ending at) 0; 1 is the lowest numbered port which may -need closing. - -Since we're now reducing ->valid_evtchns, free_xen_event_channel(), -and (at least to be on the safe side) notify_via_xen_event_channel() -need to cope with attempts to close / unbind from / send through already -closed (and no longer valid, as per port_is_valid()) ports. - -This is part of XSA-344. - -Signed-off-by: Jan Beulich -Acked-by: Julien Grall -Reviewed-by: Stefano Stabellini ---- -v8: Drop ->active->evtchns part of main loop condition. Drop BUG_ON() - conversion in evtchn_close(). -v7: Comment the barriers added in v6. -v6: Add barriers ahead of new ->is_dying checks. -v5: Also adjust BUG_ON() in evtchn_close(). -v4: New. - ---- a/xen/common/domain.c -+++ b/xen/common/domain.c -@@ -715,12 +715,14 @@ int domain_kill(struct domain *d) - return domain_kill(d); - d->is_dying = DOMDYING_dying; - argo_destroy(d); -- evtchn_destroy(d); - gnttab_release_mappings(d); - vnuma_destroy(d->vnuma); - domain_set_outstanding_pages(d, 0); - /* fallthrough */ - case DOMDYING_dying: -+ rc = evtchn_destroy(d); -+ if ( rc ) -+ break; - rc = domain_relinquish_resources(d); - if ( rc != 0 ) - break; ---- a/xen/common/event_channel.c -+++ b/xen/common/event_channel.c -@@ -1297,7 +1297,16 @@ int alloc_unbound_xen_event_channel( - - void free_xen_event_channel(struct domain *d, int port) - { -- BUG_ON(!port_is_valid(d, port)); -+ if ( !port_is_valid(d, port) ) -+ { -+ /* -+ * Make sure ->is_dying is read /after/ ->valid_evtchns, pairing -+ * with the spin_barrier() and BUG_ON() in evtchn_destroy(). -+ */ -+ smp_rmb(); -+ BUG_ON(!d->is_dying); -+ return; -+ } - - evtchn_close(d, port, 0); - } -@@ -1309,7 +1318,17 @@ void notify_via_xen_event_channel(struct - struct domain *rd; - unsigned long flags; - -- ASSERT(port_is_valid(ld, lport)); -+ if ( !port_is_valid(ld, lport) ) -+ { -+ /* -+ * Make sure ->is_dying is read /after/ ->valid_evtchns, pairing -+ * with the spin_barrier() and BUG_ON() in evtchn_destroy(). -+ */ -+ smp_rmb(); -+ ASSERT(ld->is_dying); -+ return; -+ } -+ - lchn = evtchn_from_port(ld, lport); - - spin_lock_irqsave(&lchn->lock, flags); -@@ -1380,8 +1399,7 @@ int evtchn_init(struct domain *d, unsign - return 0; - } - -- --void evtchn_destroy(struct domain *d) -+int evtchn_destroy(struct domain *d) - { - unsigned int i; - -@@ -1390,14 +1408,29 @@ void evtchn_destroy(struct domain *d) - spin_barrier(&d->event_lock); - - /* Close all existing event channels. */ -- for ( i = 0; port_is_valid(d, i); i++ ) -+ for ( i = d->valid_evtchns; --i; ) -+ { - evtchn_close(d, i, 0); - -+ /* -+ * Avoid preempting when called from domain_create()'s error path, -+ * and don't check too often (choice of frequency is arbitrary). -+ */ -+ if ( i && !(i & 0x3f) && d->is_dying != DOMDYING_dead && -+ hypercall_preempt_check() ) -+ { -+ write_atomic(&d->valid_evtchns, i); -+ return -ERESTART; -+ } -+ } -+ - ASSERT(!d->active_evtchns); - - clear_global_virq_handlers(d); - - evtchn_fifo_destroy(d); -+ -+ return 0; - } - - ---- a/xen/include/xen/sched.h -+++ b/xen/include/xen/sched.h -@@ -138,7 +138,7 @@ struct evtchn - } __attribute__((aligned(64))); - - int evtchn_init(struct domain *d, unsigned int max_port); --void evtchn_destroy(struct domain *d); /* from domain_kill */ -+int evtchn_destroy(struct domain *d); /* from domain_kill */ - void evtchn_destroy_final(struct domain *d); /* from complete_domain_destroy */ - - struct waitqueue_vcpu; diff --git a/xsa344-2.patch b/xsa344-2.patch deleted file mode 100644 index c03344b..0000000 --- a/xsa344-2.patch +++ /dev/null @@ -1,210 +0,0 @@ -From: Jan Beulich -Subject: evtchn: arrange for preemption in evtchn_reset() - -Like for evtchn_destroy() looping over all possible event channels to -close them can take a significant amount of time. Unlike done there, we -can't alter domain properties (i.e. d->valid_evtchns) here. Borrow, in a -lightweight form, the paging domctl continuation concept, redirecting -the continuations to different sub-ops. Just like there this is to be -able to allow for predictable overall results of the involved sub-ops: -Racing requests should either complete or be refused. - -Note that a domain can't interfere with an already started (by a remote -domain) reset, due to being paused. It can prevent a remote reset from -happening by leaving a reset unfinished, but that's only going to affect -itself. - -This is part of XSA-344. - -Signed-off-by: Jan Beulich -Acked-by: Julien Grall -Reviewed-by: Stefano Stabellini ---- -v9: Undo v7 changes. -v7: Move extension of loop condition in evtchn_reset() here, to match - the earlier patch'es change to evtchn_destroy(). -v6: Also protect the last write of d->next_evtchn. Re-base over changes - to earlier patches. -v4: New. - ---- a/xen/common/domain.c -+++ b/xen/common/domain.c -@@ -1145,7 +1145,7 @@ void domain_unpause_except_self(struct d - domain_unpause(d); - } - --int domain_soft_reset(struct domain *d) -+int domain_soft_reset(struct domain *d, bool resuming) - { - struct vcpu *v; - int rc; -@@ -1159,7 +1159,7 @@ int domain_soft_reset(struct domain *d) - } - spin_unlock(&d->shutdown_lock); - -- rc = evtchn_reset(d); -+ rc = evtchn_reset(d, resuming); - if ( rc ) - return rc; - ---- a/xen/common/domctl.c -+++ b/xen/common/domctl.c -@@ -477,12 +477,22 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - } - - case XEN_DOMCTL_soft_reset: -+ case XEN_DOMCTL_soft_reset_cont: - if ( d == current->domain ) /* no domain_pause() */ - { - ret = -EINVAL; - break; - } -- ret = domain_soft_reset(d); -+ ret = domain_soft_reset(d, op->cmd == XEN_DOMCTL_soft_reset_cont); -+ if ( ret == -ERESTART ) -+ { -+ op->cmd = XEN_DOMCTL_soft_reset_cont; -+ if ( !__copy_field_to_guest(u_domctl, op, cmd) ) -+ ret = hypercall_create_continuation(__HYPERVISOR_domctl, -+ "h", u_domctl); -+ else -+ ret = -EFAULT; -+ } - break; - - case XEN_DOMCTL_destroydomain: ---- a/xen/common/event_channel.c -+++ b/xen/common/event_channel.c -@@ -1057,7 +1057,7 @@ int evtchn_unmask(unsigned int port) - return 0; - } - --int evtchn_reset(struct domain *d) -+int evtchn_reset(struct domain *d, bool resuming) - { - unsigned int i; - int rc = 0; -@@ -1065,11 +1065,40 @@ int evtchn_reset(struct domain *d) - if ( d != current->domain && !d->controller_pause_count ) - return -EINVAL; - -- for ( i = 0; port_is_valid(d, i); i++ ) -+ spin_lock(&d->event_lock); -+ -+ /* -+ * If we are resuming, then start where we stopped. Otherwise, check -+ * that a reset operation is not already in progress, and if none is, -+ * record that this is now the case. -+ */ -+ i = resuming ? d->next_evtchn : !d->next_evtchn; -+ if ( i > d->next_evtchn ) -+ d->next_evtchn = i; -+ -+ spin_unlock(&d->event_lock); -+ -+ if ( !i ) -+ return -EBUSY; -+ -+ for ( ; port_is_valid(d, i); i++ ) -+ { - evtchn_close(d, i, 1); - -+ /* NB: Choice of frequency is arbitrary. */ -+ if ( !(i & 0x3f) && hypercall_preempt_check() ) -+ { -+ spin_lock(&d->event_lock); -+ d->next_evtchn = i; -+ spin_unlock(&d->event_lock); -+ return -ERESTART; -+ } -+ } -+ - spin_lock(&d->event_lock); - -+ d->next_evtchn = 0; -+ - if ( d->active_evtchns > d->xen_evtchns ) - rc = -EAGAIN; - else if ( d->evtchn_fifo ) -@@ -1204,7 +1233,8 @@ long do_event_channel_op(int cmd, XEN_GU - break; - } - -- case EVTCHNOP_reset: { -+ case EVTCHNOP_reset: -+ case EVTCHNOP_reset_cont: { - struct evtchn_reset reset; - struct domain *d; - -@@ -1217,9 +1247,13 @@ long do_event_channel_op(int cmd, XEN_GU - - rc = xsm_evtchn_reset(XSM_TARGET, current->domain, d); - if ( !rc ) -- rc = evtchn_reset(d); -+ rc = evtchn_reset(d, cmd == EVTCHNOP_reset_cont); - - rcu_unlock_domain(d); -+ -+ if ( rc == -ERESTART ) -+ rc = hypercall_create_continuation(__HYPERVISOR_event_channel_op, -+ "ih", EVTCHNOP_reset_cont, arg); - break; - } - ---- a/xen/include/public/domctl.h -+++ b/xen/include/public/domctl.h -@@ -1159,7 +1159,10 @@ struct xen_domctl { - #define XEN_DOMCTL_iomem_permission 20 - #define XEN_DOMCTL_ioport_permission 21 - #define XEN_DOMCTL_hypercall_init 22 --#define XEN_DOMCTL_arch_setup 23 /* Obsolete IA64 only */ -+#ifdef __XEN__ -+/* #define XEN_DOMCTL_arch_setup 23 Obsolete IA64 only */ -+#define XEN_DOMCTL_soft_reset_cont 23 -+#endif - #define XEN_DOMCTL_settimeoffset 24 - #define XEN_DOMCTL_getvcpuaffinity 25 - #define XEN_DOMCTL_real_mode_area 26 /* Obsolete PPC only */ ---- a/xen/include/public/event_channel.h -+++ b/xen/include/public/event_channel.h -@@ -74,6 +74,9 @@ - #define EVTCHNOP_init_control 11 - #define EVTCHNOP_expand_array 12 - #define EVTCHNOP_set_priority 13 -+#ifdef __XEN__ -+#define EVTCHNOP_reset_cont 14 -+#endif - /* ` } */ - - typedef uint32_t evtchn_port_t; ---- a/xen/include/xen/event.h -+++ b/xen/include/xen/event.h -@@ -171,7 +171,7 @@ void evtchn_check_pollers(struct domain - void evtchn_2l_init(struct domain *d); - - /* Close all event channels and reset to 2-level ABI. */ --int evtchn_reset(struct domain *d); -+int evtchn_reset(struct domain *d, bool resuming); - - /* - * Low-level event channel port ops. ---- a/xen/include/xen/sched.h -+++ b/xen/include/xen/sched.h -@@ -371,6 +371,8 @@ struct domain - * EVTCHNOP_reset). Read/write access like for active_evtchns. - */ - unsigned int xen_evtchns; -+ /* Port to resume from in evtchn_reset(), when in a continuation. */ -+ unsigned int next_evtchn; - spinlock_t event_lock; - const struct evtchn_port_ops *evtchn_port_ops; - struct evtchn_fifo_domain *evtchn_fifo; -@@ -663,7 +665,7 @@ int domain_kill(struct domain *d); - int domain_shutdown(struct domain *d, u8 reason); - void domain_resume(struct domain *d); - --int domain_soft_reset(struct domain *d); -+int domain_soft_reset(struct domain *d, bool resuming); - - int vcpu_start_shutdown_deferral(struct vcpu *v); - void vcpu_end_shutdown_deferral(struct vcpu *v); diff --git a/xsa345-4.14-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch b/xsa345-4.14-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch deleted file mode 100644 index 847c34a..0000000 --- a/xsa345-4.14-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch +++ /dev/null @@ -1,94 +0,0 @@ -From e9c5a9ee5e2e888f8bb05cf0a353ed635300abe3 Mon Sep 17 00:00:00 2001 -From: Wei Liu -Date: Sat, 11 Jan 2020 21:57:41 +0000 -Subject: [PATCH 1/3] x86/mm: Refactor map_pages_to_xen to have only a single - exit path - -We will soon need to perform clean-ups before returning. - -No functional change. - -This is part of XSA-345. - -Reported-by: Hongyan Xia -Signed-off-by: Wei Liu -Signed-off-by: Hongyan Xia -Signed-off-by: George Dunlap -Acked-by: Jan Beulich ---- - xen/arch/x86/mm.c | 17 +++++++++++------ - 1 file changed, 11 insertions(+), 6 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 82bc676553..03f6e6aa62 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -5088,6 +5088,7 @@ int map_pages_to_xen( - l2_pgentry_t *pl2e, ol2e; - l1_pgentry_t *pl1e, ol1e; - unsigned int i; -+ int rc = -ENOMEM; - - #define flush_flags(oldf) do { \ - unsigned int o_ = (oldf); \ -@@ -5108,7 +5109,8 @@ int map_pages_to_xen( - l3_pgentry_t ol3e, *pl3e = virt_to_xen_l3e(virt); - - if ( !pl3e ) -- return -ENOMEM; -+ goto out; -+ - ol3e = *pl3e; - - if ( cpu_has_page1gb && -@@ -5198,7 +5200,7 @@ int map_pages_to_xen( - - l2t = alloc_xen_pagetable(); - if ( l2t == NULL ) -- return -ENOMEM; -+ goto out; - - for ( i = 0; i < L2_PAGETABLE_ENTRIES; i++ ) - l2e_write(l2t + i, -@@ -5227,7 +5229,7 @@ int map_pages_to_xen( - - pl2e = virt_to_xen_l2e(virt); - if ( !pl2e ) -- return -ENOMEM; -+ goto out; - - if ( ((((virt >> PAGE_SHIFT) | mfn_x(mfn)) & - ((1u << PAGETABLE_ORDER) - 1)) == 0) && -@@ -5271,7 +5273,7 @@ int map_pages_to_xen( - { - pl1e = virt_to_xen_l1e(virt); - if ( pl1e == NULL ) -- return -ENOMEM; -+ goto out; - } - else if ( l2e_get_flags(*pl2e) & _PAGE_PSE ) - { -@@ -5299,7 +5301,7 @@ int map_pages_to_xen( - - l1t = alloc_xen_pagetable(); - if ( l1t == NULL ) -- return -ENOMEM; -+ goto out; - - for ( i = 0; i < L1_PAGETABLE_ENTRIES; i++ ) - l1e_write(&l1t[i], -@@ -5445,7 +5447,10 @@ int map_pages_to_xen( - - #undef flush_flags - -- return 0; -+ rc = 0; -+ -+ out: -+ return rc; - } - - int populate_pt_range(unsigned long virt, unsigned long nr_mfns) --- -2.25.1 - diff --git a/xsa345-4.14-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch b/xsa345-4.14-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch deleted file mode 100644 index d31af1c..0000000 --- a/xsa345-4.14-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 8645adb7ac679e5ddc5c39e0c5c918e4a2ba5391 Mon Sep 17 00:00:00 2001 -From: Wei Liu -Date: Sat, 11 Jan 2020 21:57:42 +0000 -Subject: [PATCH 2/3] x86/mm: Refactor modify_xen_mappings to have one exit - path - -We will soon need to perform clean-ups before returning. - -No functional change. - -This is part of XSA-345. - -Reported-by: Hongyan Xia -Signed-off-by: Wei Liu -Signed-off-by: Hongyan Xia -Signed-off-by: George Dunlap -Acked-by: Jan Beulich ---- - xen/arch/x86/mm.c | 12 +++++++++--- - 1 file changed, 9 insertions(+), 3 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 03f6e6aa62..2468347a45 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -5477,6 +5477,7 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) - l1_pgentry_t *pl1e; - unsigned int i; - unsigned long v = s; -+ int rc = -ENOMEM; - - /* Set of valid PTE bits which may be altered. */ - #define FLAGS_MASK (_PAGE_NX|_PAGE_DIRTY|_PAGE_ACCESSED|_PAGE_RW|_PAGE_PRESENT) -@@ -5520,7 +5521,8 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) - /* PAGE1GB: shatter the superpage and fall through. */ - l2t = alloc_xen_pagetable(); - if ( !l2t ) -- return -ENOMEM; -+ goto out; -+ - for ( i = 0; i < L2_PAGETABLE_ENTRIES; i++ ) - l2e_write(l2t + i, - l2e_from_pfn(l3e_get_pfn(*pl3e) + -@@ -5577,7 +5579,8 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) - /* PSE: shatter the superpage and try again. */ - l1t = alloc_xen_pagetable(); - if ( !l1t ) -- return -ENOMEM; -+ goto out; -+ - for ( i = 0; i < L1_PAGETABLE_ENTRIES; i++ ) - l1e_write(&l1t[i], - l1e_from_pfn(l2e_get_pfn(*pl2e) + i, -@@ -5710,7 +5713,10 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) - flush_area(NULL, FLUSH_TLB_GLOBAL); - - #undef FLAGS_MASK -- return 0; -+ rc = 0; -+ -+ out: -+ return rc; - } - - #undef flush_area --- -2.25.1 - diff --git a/xsa345-4.14-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch b/xsa345-4.14-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch deleted file mode 100644 index 2e4dd6f..0000000 --- a/xsa345-4.14-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch +++ /dev/null @@ -1,249 +0,0 @@ -From 6b020418d0554d9ec6eb201f50776a72db67739b Mon Sep 17 00:00:00 2001 -From: Hongyan Xia -Date: Sat, 11 Jan 2020 21:57:43 +0000 -Subject: [PATCH 3/3] x86/mm: Prevent some races in hypervisor mapping updates - -map_pages_to_xen will attempt to coalesce mappings into 2MiB and 1GiB -superpages if possible, to maximize TLB efficiency. This means both -replacing superpage entries with smaller entries, and replacing -smaller entries with superpages. - -Unfortunately, while some potential races are handled correctly, -others are not. These include: - -1. When one processor modifies a sub-superpage mapping while another -processor replaces the entire range with a superpage. - -Take the following example: - -Suppose L3[N] points to L2. And suppose we have two processors, A and -B. - -* A walks the pagetables, get a pointer to L2. -* B replaces L3[N] with a 1GiB mapping. -* B Frees L2 -* A writes L2[M] # - -This is race exacerbated by the fact that virt_to_xen_l[21]e doesn't -handle higher-level superpages properly: If you call virt_xen_to_l2e -on a virtual address within an L3 superpage, you'll either hit a BUG() -(most likely), or get a pointer into the middle of a data page; same -with virt_xen_to_l1 on a virtual address within either an L3 or L2 -superpage. - -So take the following example: - -* A reads pl3e and discovers it to point to an L2. -* B replaces L3[N] with a 1GiB mapping -* A calls virt_to_xen_l2e() and hits the BUG_ON() # - -2. When two processors simultaneously try to replace a sub-superpage -mapping with a superpage mapping. - -Take the following example: - -Suppose L3[N] points to L2. And suppose we have two processors, A and B, -both trying to replace L3[N] with a superpage. - -* A walks the pagetables, get a pointer to pl3e, and takes a copy ol3e pointing to L2. -* B walks the pagetables, gets a pointre to pl3e, and takes a copy ol3e pointing to L2. -* A writes the new value into L3[N] -* B writes the new value into L3[N] -* A recursively frees all the L1's under L2, then frees L2 -* B recursively double-frees all the L1's under L2, then double-frees L2 # - -Fix this by grabbing a lock for the entirety of the mapping update -operation. - -Rather than grabbing map_pgdir_lock for the entire operation, however, -repurpose the PGT_locked bit from L3's page->type_info as a lock. -This means that rather than locking the entire address space, we -"only" lock a single 512GiB chunk of hypervisor address space at a -time. - -There was a proposal for a lock-and-reverify approach, where we walk -the pagetables to the point where we decide what to do; then grab the -map_pgdir_lock, re-verify the information we collected without the -lock, and finally make the change (starting over again if anything had -changed). Without being able to guarantee that the L2 table wasn't -freed, however, that means every read would need to be considered -potentially unsafe. Thinking carefully about that is probably -something that wants to be done on public, not under time pressure. - -This is part of XSA-345. - -Reported-by: Hongyan Xia -Signed-off-by: Hongyan Xia -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ---- - xen/arch/x86/mm.c | 92 +++++++++++++++++++++++++++++++++++++++++++++-- - 1 file changed, 89 insertions(+), 3 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 2468347a45..9c55b2b9e3 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -2088,6 +2088,50 @@ void page_unlock(struct page_info *page) - current_locked_page_set(NULL); - } - -+/* -+ * L3 table locks: -+ * -+ * Used for serialization in map_pages_to_xen() and modify_xen_mappings(). -+ * -+ * For Xen PT pages, the page->u.inuse.type_info is unused and it is safe to -+ * reuse the PGT_locked flag. This lock is taken only when we move down to L3 -+ * tables and below, since L4 (and above, for 5-level paging) is still globally -+ * protected by map_pgdir_lock. -+ * -+ * PV MMU update hypercalls call map_pages_to_xen while holding a page's page_lock(). -+ * This has two implications: -+ * - We cannot reuse reuse current_locked_page_* for debugging -+ * - To avoid the chance of deadlock, even for different pages, we -+ * must never grab page_lock() after grabbing l3t_lock(). This -+ * includes any page_lock()-based locks, such as -+ * mem_sharing_page_lock(). -+ * -+ * Also note that we grab the map_pgdir_lock while holding the -+ * l3t_lock(), so to avoid deadlock we must avoid grabbing them in -+ * reverse order. -+ */ -+static void l3t_lock(struct page_info *page) -+{ -+ unsigned long x, nx; -+ -+ do { -+ while ( (x = page->u.inuse.type_info) & PGT_locked ) -+ cpu_relax(); -+ nx = x | PGT_locked; -+ } while ( cmpxchg(&page->u.inuse.type_info, x, nx) != x ); -+} -+ -+static void l3t_unlock(struct page_info *page) -+{ -+ unsigned long x, nx, y = page->u.inuse.type_info; -+ -+ do { -+ x = y; -+ BUG_ON(!(x & PGT_locked)); -+ nx = x & ~PGT_locked; -+ } while ( (y = cmpxchg(&page->u.inuse.type_info, x, nx)) != x ); -+} -+ - #ifdef CONFIG_PV - /* - * PTE flags that a guest may change without re-validating the PTE. -@@ -5078,6 +5122,23 @@ l1_pgentry_t *virt_to_xen_l1e(unsigned long v) - flush_area_local((const void *)v, f) : \ - flush_area_all((const void *)v, f)) - -+#define L3T_INIT(page) (page) = ZERO_BLOCK_PTR -+ -+#define L3T_LOCK(page) \ -+ do { \ -+ if ( locking ) \ -+ l3t_lock(page); \ -+ } while ( false ) -+ -+#define L3T_UNLOCK(page) \ -+ do { \ -+ if ( locking && (page) != ZERO_BLOCK_PTR ) \ -+ { \ -+ l3t_unlock(page); \ -+ (page) = ZERO_BLOCK_PTR; \ -+ } \ -+ } while ( false ) -+ - int map_pages_to_xen( - unsigned long virt, - mfn_t mfn, -@@ -5089,6 +5150,7 @@ int map_pages_to_xen( - l1_pgentry_t *pl1e, ol1e; - unsigned int i; - int rc = -ENOMEM; -+ struct page_info *current_l3page; - - #define flush_flags(oldf) do { \ - unsigned int o_ = (oldf); \ -@@ -5104,13 +5166,20 @@ int map_pages_to_xen( - } \ - } while (0) - -+ L3T_INIT(current_l3page); -+ - while ( nr_mfns != 0 ) - { -- l3_pgentry_t ol3e, *pl3e = virt_to_xen_l3e(virt); -+ l3_pgentry_t *pl3e, ol3e; - -+ L3T_UNLOCK(current_l3page); -+ -+ pl3e = virt_to_xen_l3e(virt); - if ( !pl3e ) - goto out; - -+ current_l3page = virt_to_page(pl3e); -+ L3T_LOCK(current_l3page); - ol3e = *pl3e; - - if ( cpu_has_page1gb && -@@ -5450,6 +5519,7 @@ int map_pages_to_xen( - rc = 0; - - out: -+ L3T_UNLOCK(current_l3page); - return rc; - } - -@@ -5478,6 +5548,7 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) - unsigned int i; - unsigned long v = s; - int rc = -ENOMEM; -+ struct page_info *current_l3page; - - /* Set of valid PTE bits which may be altered. */ - #define FLAGS_MASK (_PAGE_NX|_PAGE_DIRTY|_PAGE_ACCESSED|_PAGE_RW|_PAGE_PRESENT) -@@ -5486,11 +5557,22 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) - ASSERT(IS_ALIGNED(s, PAGE_SIZE)); - ASSERT(IS_ALIGNED(e, PAGE_SIZE)); - -+ L3T_INIT(current_l3page); -+ - while ( v < e ) - { -- l3_pgentry_t *pl3e = virt_to_xen_l3e(v); -+ l3_pgentry_t *pl3e; -+ -+ L3T_UNLOCK(current_l3page); - -- if ( !pl3e || !(l3e_get_flags(*pl3e) & _PAGE_PRESENT) ) -+ pl3e = virt_to_xen_l3e(v); -+ if ( !pl3e ) -+ goto out; -+ -+ current_l3page = virt_to_page(pl3e); -+ L3T_LOCK(current_l3page); -+ -+ if ( !(l3e_get_flags(*pl3e) & _PAGE_PRESENT) ) - { - /* Confirm the caller isn't trying to create new mappings. */ - ASSERT(!(nf & _PAGE_PRESENT)); -@@ -5716,9 +5798,13 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) - rc = 0; - - out: -+ L3T_UNLOCK(current_l3page); - return rc; - } - -+#undef L3T_LOCK -+#undef L3T_UNLOCK -+ - #undef flush_area - - int destroy_xen_mappings(unsigned long s, unsigned long e) --- -2.25.1 - diff --git a/xsa346-1.patch b/xsa346-1.patch deleted file mode 100644 index 3ffeb65..0000000 --- a/xsa346-1.patch +++ /dev/null @@ -1,50 +0,0 @@ -From: Jan Beulich -Subject: IOMMU: suppress "iommu_dont_flush_iotlb" when about to free a page - -Deferring flushes to a single, wide range one - as is done when -handling XENMAPSPACE_gmfn_range - is okay only as long as -pages don't get freed ahead of the eventual flush. While the only -function setting the flag (xenmem_add_to_physmap()) suggests by its name -that it's only mapping new entries, in reality the way -xenmem_add_to_physmap_one() works means an unmap would happen not only -for the page being moved (but not freed) but, if the destination GFN is -populated, also for the page being displaced from that GFN. Collapsing -the two flushes for this GFN into just one (end even more so deferring -it to a batched invocation) is not correct. - -This is part of XSA-346. - -Fixes: cf95b2a9fd5a ("iommu: Introduce per cpu flag (iommu_dont_flush_iotlb) to avoid unnecessary iotlb... ") -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant -Acked-by: Julien Grall - ---- a/xen/common/memory.c -+++ b/xen/common/memory.c -@@ -293,6 +293,7 @@ int guest_remove_page(struct domain *d, - p2m_type_t p2mt; - #endif - mfn_t mfn; -+ bool *dont_flush_p, dont_flush; - int rc; - - #ifdef CONFIG_X86 -@@ -379,8 +380,18 @@ int guest_remove_page(struct domain *d, - return -ENXIO; - } - -+ /* -+ * Since we're likely to free the page below, we need to suspend -+ * xenmem_add_to_physmap()'s suppressing of IOMMU TLB flushes. -+ */ -+ dont_flush_p = &this_cpu(iommu_dont_flush_iotlb); -+ dont_flush = *dont_flush_p; -+ *dont_flush_p = false; -+ - rc = guest_physmap_remove_page(d, _gfn(gmfn), mfn, 0); - -+ *dont_flush_p = dont_flush; -+ - /* - * With the lack of an IOMMU on some platforms, domains with DMA-capable - * device must retrieve the same pfn when the hypercall populate_physmap diff --git a/xsa346-2.patch b/xsa346-2.patch deleted file mode 100644 index 630496a..0000000 --- a/xsa346-2.patch +++ /dev/null @@ -1,204 +0,0 @@ -From: Jan Beulich -Subject: IOMMU: hold page ref until after deferred TLB flush - -When moving around a page via XENMAPSPACE_gmfn_range, deferring the TLB -flush for the "from" GFN range requires that the page remains allocated -to the guest until the TLB flush has actually occurred. Otherwise a -parallel hypercall to remove the page would only flush the TLB for the -GFN it has been moved to, but not the one is was mapped at originally. - -This is part of XSA-346. - -Fixes: cf95b2a9fd5a ("iommu: Introduce per cpu flag (iommu_dont_flush_iotlb) to avoid unnecessary iotlb... ") -Reported-by: Julien Grall -Signed-off-by: Jan Beulich -Acked-by: Julien Grall - ---- a/xen/arch/arm/mm.c -+++ b/xen/arch/arm/mm.c -@@ -1407,7 +1407,7 @@ void share_xen_page_with_guest(struct pa - int xenmem_add_to_physmap_one( - struct domain *d, - unsigned int space, -- union xen_add_to_physmap_batch_extra extra, -+ union add_to_physmap_extra extra, - unsigned long idx, - gfn_t gfn) - { -@@ -1480,10 +1480,6 @@ int xenmem_add_to_physmap_one( - break; - } - case XENMAPSPACE_dev_mmio: -- /* extra should be 0. Reserved for future use. */ -- if ( extra.res0 ) -- return -EOPNOTSUPP; -- - rc = map_dev_mmio_region(d, gfn, 1, _mfn(idx)); - return rc; - ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -4497,7 +4497,7 @@ static int handle_iomem_range(unsigned l - int xenmem_add_to_physmap_one( - struct domain *d, - unsigned int space, -- union xen_add_to_physmap_batch_extra extra, -+ union add_to_physmap_extra extra, - unsigned long idx, - gfn_t gpfn) - { -@@ -4581,9 +4581,20 @@ int xenmem_add_to_physmap_one( - rc = guest_physmap_add_page(d, gpfn, mfn, PAGE_ORDER_4K); - - put_both: -- /* In the XENMAPSPACE_gmfn case, we took a ref of the gfn at the top. */ -+ /* -+ * 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 ) -+ { - put_gfn(d, gfn); -+ if ( !rc && extra.ppage ) -+ { -+ *extra.ppage = page; -+ page = NULL; -+ } -+ } - - if ( page ) - put_page(page); ---- a/xen/common/memory.c -+++ b/xen/common/memory.c -@@ -815,13 +815,12 @@ int xenmem_add_to_physmap(struct domain - { - unsigned int done = 0; - long rc = 0; -- union xen_add_to_physmap_batch_extra extra; -+ union add_to_physmap_extra extra = {}; -+ struct page_info *pages[16]; - - ASSERT(paging_mode_translate(d)); - -- if ( xatp->space != XENMAPSPACE_gmfn_foreign ) -- extra.res0 = 0; -- else -+ if ( xatp->space == XENMAPSPACE_gmfn_foreign ) - extra.foreign_domid = DOMID_INVALID; - - if ( xatp->space != XENMAPSPACE_gmfn_range ) -@@ -836,7 +835,10 @@ int xenmem_add_to_physmap(struct domain - xatp->size -= start; - - if ( is_iommu_enabled(d) ) -+ { - this_cpu(iommu_dont_flush_iotlb) = 1; -+ extra.ppage = &pages[0]; -+ } - - while ( xatp->size > done ) - { -@@ -848,8 +850,12 @@ int xenmem_add_to_physmap(struct domain - xatp->idx++; - xatp->gpfn++; - -+ if ( extra.ppage ) -+ ++extra.ppage; -+ - /* Check for continuation if it's not the last iteration. */ -- if ( xatp->size > ++done && hypercall_preempt_check() ) -+ if ( (++done > ARRAY_SIZE(pages) && extra.ppage) || -+ (xatp->size > done && hypercall_preempt_check()) ) - { - rc = start + done; - break; -@@ -859,6 +865,7 @@ int xenmem_add_to_physmap(struct domain - if ( is_iommu_enabled(d) ) - { - int ret; -+ unsigned int i; - - this_cpu(iommu_dont_flush_iotlb) = 0; - -@@ -867,6 +874,15 @@ int xenmem_add_to_physmap(struct domain - if ( unlikely(ret) && rc >= 0 ) - rc = ret; - -+ /* -+ * Now that the IOMMU TLB flush was done for the original GFN, drop -+ * the page references. The 2nd flush below is fine to make later, as -+ * whoever removes the page again from its new GFN will have to do -+ * another flush anyway. -+ */ -+ for ( i = 0; i < done; ++i ) -+ put_page(pages[i]); -+ - ret = iommu_iotlb_flush(d, _dfn(xatp->gpfn - done), done, - IOMMU_FLUSHF_added | IOMMU_FLUSHF_modified); - if ( unlikely(ret) && rc >= 0 ) -@@ -880,6 +896,8 @@ static int xenmem_add_to_physmap_batch(s - struct xen_add_to_physmap_batch *xatpb, - unsigned int extent) - { -+ union add_to_physmap_extra extra = {}; -+ - if ( unlikely(xatpb->size < extent) ) - return -EILSEQ; - -@@ -891,6 +909,19 @@ static int xenmem_add_to_physmap_batch(s - !guest_handle_subrange_okay(xatpb->errs, extent, xatpb->size - 1) ) - return -EFAULT; - -+ switch ( xatpb->space ) -+ { -+ case XENMAPSPACE_dev_mmio: -+ /* res0 is reserved for future use. */ -+ if ( xatpb->u.res0 ) -+ return -EOPNOTSUPP; -+ break; -+ -+ case XENMAPSPACE_gmfn_foreign: -+ extra.foreign_domid = xatpb->u.foreign_domid; -+ break; -+ } -+ - while ( xatpb->size > extent ) - { - xen_ulong_t idx; -@@ -903,8 +934,7 @@ static int xenmem_add_to_physmap_batch(s - extent, 1)) ) - return -EFAULT; - -- rc = xenmem_add_to_physmap_one(d, xatpb->space, -- xatpb->u, -+ rc = xenmem_add_to_physmap_one(d, xatpb->space, extra, - idx, _gfn(gpfn)); - - if ( unlikely(__copy_to_guest_offset(xatpb->errs, extent, &rc, 1)) ) ---- a/xen/include/xen/mm.h -+++ b/xen/include/xen/mm.h -@@ -592,8 +592,22 @@ void scrub_one_page(struct page_info *); - page_list_del(pg, page_to_list(d, pg)) - #endif - -+union add_to_physmap_extra { -+ /* -+ * XENMAPSPACE_gmfn: When deferring TLB flushes, a page reference needs -+ * to be kept until after the flush, so the page can't get removed from -+ * the domain (and re-used for another purpose) beforehand. By passing -+ * non-NULL, the caller of xenmem_add_to_physmap_one() indicates it wants -+ * to have ownership of such a reference transferred in the success case. -+ */ -+ struct page_info **ppage; -+ -+ /* XENMAPSPACE_gmfn_foreign */ -+ domid_t foreign_domid; -+}; -+ - int xenmem_add_to_physmap_one(struct domain *d, unsigned int space, -- union xen_add_to_physmap_batch_extra extra, -+ union add_to_physmap_extra extra, - unsigned long idx, gfn_t gfn); - - int xenmem_add_to_physmap(struct domain *d, struct xen_add_to_physmap *xatp, diff --git a/xsa347-4.14-1.patch b/xsa347-4.14-1.patch deleted file mode 100644 index c110ee6..0000000 --- a/xsa347-4.14-1.patch +++ /dev/null @@ -1,149 +0,0 @@ -From: Jan Beulich -Subject: AMD/IOMMU: convert amd_iommu_pte from struct to union - -This is to add a "raw" counterpart to the bitfield equivalent. Take the -opportunity and - - convert fields to bool / unsigned int, - - drop the naming of the reserved field, - - shorten the names of the ignored ones. - -This is part of XSA-347. - -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/amd/iommu-defs.h -+++ b/xen/drivers/passthrough/amd/iommu-defs.h -@@ -451,20 +451,23 @@ union amd_iommu_x2apic_control { - #define IOMMU_PAGE_TABLE_U32_PER_ENTRY (IOMMU_PAGE_TABLE_ENTRY_SIZE / 4) - #define IOMMU_PAGE_TABLE_ALIGNMENT 4096 - --struct amd_iommu_pte { -- uint64_t pr:1; -- uint64_t ignored0:4; -- uint64_t a:1; -- uint64_t d:1; -- uint64_t ignored1:2; -- uint64_t next_level:3; -- uint64_t mfn:40; -- uint64_t reserved:7; -- uint64_t u:1; -- uint64_t fc:1; -- uint64_t ir:1; -- uint64_t iw:1; -- uint64_t ignored2:1; -+union amd_iommu_pte { -+ uint64_t raw; -+ struct { -+ bool pr:1; -+ unsigned int ign0:4; -+ bool a:1; -+ bool d:1; -+ unsigned int ign1:2; -+ unsigned int next_level:3; -+ uint64_t mfn:40; -+ unsigned int :7; -+ bool u:1; -+ bool fc:1; -+ bool ir:1; -+ bool iw:1; -+ unsigned int ign2:1; -+ }; - }; - - /* Paging modes */ ---- a/xen/drivers/passthrough/amd/iommu_map.c -+++ b/xen/drivers/passthrough/amd/iommu_map.c -@@ -34,7 +34,7 @@ static unsigned int pfn_to_pde_idx(unsig - static unsigned int clear_iommu_pte_present(unsigned long l1_mfn, - unsigned long dfn) - { -- struct amd_iommu_pte *table, *pte; -+ union amd_iommu_pte *table, *pte; - unsigned int flush_flags; - - table = map_domain_page(_mfn(l1_mfn)); -@@ -48,7 +48,7 @@ static unsigned int clear_iommu_pte_pres - return flush_flags; - } - --static unsigned int set_iommu_pde_present(struct amd_iommu_pte *pte, -+static unsigned int set_iommu_pde_present(union amd_iommu_pte *pte, - unsigned long next_mfn, - unsigned int next_level, bool iw, - bool ir) -@@ -83,7 +83,7 @@ static unsigned int set_iommu_pte_presen - int pde_level, - bool iw, bool ir) - { -- struct amd_iommu_pte *table, *pde; -+ union amd_iommu_pte *table, *pde; - unsigned int flush_flags; - - table = map_domain_page(_mfn(pt_mfn)); -@@ -174,7 +174,7 @@ void iommu_dte_set_guest_cr3(struct amd_ - static int iommu_pde_from_dfn(struct domain *d, unsigned long dfn, - unsigned long pt_mfn[], bool map) - { -- struct amd_iommu_pte *pde, *next_table_vaddr; -+ union amd_iommu_pte *pde, *next_table_vaddr; - unsigned long next_table_mfn; - unsigned int level; - struct page_info *table; -@@ -448,7 +448,7 @@ int __init amd_iommu_quarantine_init(str - unsigned long end_gfn = - 1ul << (DEFAULT_DOMAIN_ADDRESS_WIDTH - PAGE_SHIFT); - unsigned int level = amd_iommu_get_paging_mode(end_gfn); -- struct amd_iommu_pte *table; -+ union amd_iommu_pte *table; - - if ( hd->arch.root_table ) - { -@@ -479,7 +479,7 @@ int __init amd_iommu_quarantine_init(str - - for ( i = 0; i < PTE_PER_TABLE_SIZE; i++ ) - { -- struct amd_iommu_pte *pde = &table[i]; -+ union amd_iommu_pte *pde = &table[i]; - - /* - * PDEs are essentially a subset of PTEs, so this function ---- a/xen/drivers/passthrough/amd/pci_amd_iommu.c -+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c -@@ -387,7 +387,7 @@ static void deallocate_next_page_table(s - - static void deallocate_page_table(struct page_info *pg) - { -- struct amd_iommu_pte *table_vaddr; -+ union amd_iommu_pte *table_vaddr; - unsigned int index, level = PFN_ORDER(pg); - - PFN_ORDER(pg) = 0; -@@ -402,7 +402,7 @@ static void deallocate_page_table(struct - - for ( index = 0; index < PTE_PER_TABLE_SIZE; index++ ) - { -- struct amd_iommu_pte *pde = &table_vaddr[index]; -+ union amd_iommu_pte *pde = &table_vaddr[index]; - - if ( pde->mfn && pde->next_level && pde->pr ) - { -@@ -554,7 +554,7 @@ static void amd_dump_p2m_table_level(str - paddr_t gpa, int indent) - { - paddr_t address; -- struct amd_iommu_pte *table_vaddr; -+ const union amd_iommu_pte *table_vaddr; - int index; - - if ( level < 1 ) -@@ -570,7 +570,7 @@ static void amd_dump_p2m_table_level(str - - for ( index = 0; index < PTE_PER_TABLE_SIZE; index++ ) - { -- struct amd_iommu_pte *pde = &table_vaddr[index]; -+ const union amd_iommu_pte *pde = &table_vaddr[index]; - - if ( !(index % 2) ) - process_pending_softirqs(); diff --git a/xsa347-4.14-2.patch b/xsa347-4.14-2.patch deleted file mode 100644 index a8f4776..0000000 --- a/xsa347-4.14-2.patch +++ /dev/null @@ -1,72 +0,0 @@ -From: Jan Beulich -Subject: AMD/IOMMU: update live PTEs atomically - -Updating a live PTE bitfield by bitfield risks the compiler re-ordering -the individual updates as well as splitting individual updates into -multiple memory writes. Construct the new entry fully in a local -variable, do the check to determine the flushing needs on the thus -established new entry, and then write the new entry by a single insn. - -Similarly using memset() to clear a PTE is unsafe, as the order of -writes the function does is, at least in principle, undefined. - -This is part of XSA-347. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/amd/iommu_map.c -+++ b/xen/drivers/passthrough/amd/iommu_map.c -@@ -41,7 +41,7 @@ static unsigned int clear_iommu_pte_pres - pte = &table[pfn_to_pde_idx(dfn, 1)]; - - flush_flags = pte->pr ? IOMMU_FLUSHF_modified : 0; -- memset(pte, 0, sizeof(*pte)); -+ write_atomic(&pte->raw, 0); - - unmap_domain_page(table); - -@@ -53,26 +53,30 @@ static unsigned int set_iommu_pde_presen - unsigned int next_level, bool iw, - bool ir) - { -+ union amd_iommu_pte new = {}, old; - unsigned int flush_flags = IOMMU_FLUSHF_added; - -- if ( pte->pr && -- (pte->mfn != next_mfn || -- pte->iw != iw || -- pte->ir != ir || -- pte->next_level != next_level) ) -- flush_flags |= IOMMU_FLUSHF_modified; -- - /* - * FC bit should be enabled in PTE, this helps to solve potential - * issues with ATS devices - */ -- pte->fc = !next_level; -+ new.fc = !next_level; -+ -+ new.mfn = next_mfn; -+ new.iw = iw; -+ new.ir = ir; -+ new.next_level = next_level; -+ new.pr = true; -+ -+ old.raw = read_atomic(&pte->raw); -+ old.ign0 = 0; -+ old.ign1 = 0; -+ old.ign2 = 0; -+ -+ if ( old.pr && old.raw != new.raw ) -+ flush_flags |= IOMMU_FLUSHF_modified; - -- pte->mfn = next_mfn; -- pte->iw = iw; -- pte->ir = ir; -- pte->next_level = next_level; -- pte->pr = 1; -+ write_atomic(&pte->raw, new.raw); - - return flush_flags; - } diff --git a/xsa347-4.14-3.patch b/xsa347-4.14-3.patch deleted file mode 100644 index 1fb8c60..0000000 --- a/xsa347-4.14-3.patch +++ /dev/null @@ -1,59 +0,0 @@ -From: Jan Beulich -Subject: AMD/IOMMU: ensure suitable ordering of DTE modifications - -DMA and interrupt translation should be enabled only after other -applicable DTE fields have been written. Similarly when disabling -translation or when moving a device between domains, translation should -first be disabled, before other entry fields get modified. Note however -that the "moving" aspect doesn't apply to the interrupt remapping side, -as domain specifics are maintained in the IRTEs here, not the DTE. We -also never disable interrupt remapping once it got enabled for a device -(the respective argument passed is always the immutable iommu_intremap). - -This is part of XSA-347. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/amd/iommu_map.c -+++ b/xen/drivers/passthrough/amd/iommu_map.c -@@ -103,11 +103,18 @@ void amd_iommu_set_root_page_table(struc - uint64_t root_ptr, uint16_t domain_id, - uint8_t paging_mode, bool valid) - { -+ if ( valid || dte->v ) -+ { -+ dte->tv = false; -+ dte->v = true; -+ smp_wmb(); -+ } - dte->domain_id = domain_id; - dte->pt_root = paddr_to_pfn(root_ptr); - dte->iw = true; - dte->ir = true; - dte->paging_mode = paging_mode; -+ smp_wmb(); - dte->tv = true; - dte->v = valid; - } -@@ -130,6 +137,7 @@ void amd_iommu_set_intremap_table( - } - - dte->ig = false; /* unmapped interrupts result in i/o page faults */ -+ smp_wmb(); - dte->iv = valid; - } - ---- a/xen/drivers/passthrough/amd/pci_amd_iommu.c -+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c -@@ -117,7 +117,10 @@ static void amd_iommu_setup_domain_devic - /* Undo what amd_iommu_disable_domain_device() may have done. */ - ivrs_dev = &get_ivrs_mappings(iommu->seg)[req_id]; - if ( dte->it_root ) -+ { - dte->int_ctl = IOMMU_DEV_TABLE_INT_CONTROL_TRANSLATED; -+ smp_wmb(); -+ } - dte->iv = iommu_intremap; - dte->ex = ivrs_dev->dte_allow_exclusion; - dte->sys_mgt = MASK_EXTR(ivrs_dev->device_flags, ACPI_IVHD_SYSTEM_MGMT); diff --git a/xsa348-1.patch b/xsa348-1.patch deleted file mode 100644 index 721c047..0000000 --- a/xsa348-1.patch +++ /dev/null @@ -1,113 +0,0 @@ -From: Jan Beulich -Subject: x86: replace reset_stack_and_jump_nolp() - -Move the necessary check into check_for_livepatch_work(), rather than -mostly duplicating reset_stack_and_jump() for this purpose. This is to -prevent an inflation of reset_stack_and_jump() flavors. - -Signed-off-by: Jan Beulich -Reviewed-by: Juergen Gross ---- -Of course instead of adding the check right into -check_for_livepatch_work(), a wrapper could be introduced. - ---- a/xen/arch/x86/domain.c -+++ b/xen/arch/x86/domain.c -@@ -192,7 +192,7 @@ static void noreturn continue_idle_domai - { - /* Idle vcpus might be attached to non-idle units! */ - if ( !is_idle_domain(v->sched_unit->domain) ) -- reset_stack_and_jump_nolp(guest_idle_loop); -+ reset_stack_and_jump(guest_idle_loop); - - reset_stack_and_jump(idle_loop); - } ---- a/xen/arch/x86/hvm/svm/svm.c -+++ b/xen/arch/x86/hvm/svm/svm.c -@@ -1036,7 +1036,7 @@ static void noreturn svm_do_resume(struc - - hvm_do_resume(v); - -- reset_stack_and_jump_nolp(svm_asm_do_resume); -+ reset_stack_and_jump(svm_asm_do_resume); - } - - void svm_vmenter_helper(const struct cpu_user_regs *regs) ---- a/xen/arch/x86/hvm/vmx/vmcs.c -+++ b/xen/arch/x86/hvm/vmx/vmcs.c -@@ -1909,7 +1909,7 @@ void vmx_do_resume(struct vcpu *v) - if ( host_cr4 != read_cr4() ) - __vmwrite(HOST_CR4, read_cr4()); - -- reset_stack_and_jump_nolp(vmx_asm_do_vmentry); -+ reset_stack_and_jump(vmx_asm_do_vmentry); - } - - static inline unsigned long vmr(unsigned long field) ---- a/xen/arch/x86/pv/domain.c -+++ b/xen/arch/x86/pv/domain.c -@@ -113,7 +113,7 @@ static int parse_pcid(const char *s) - static void noreturn continue_nonidle_domain(struct vcpu *v) - { - check_wakeup_from_wait(); -- reset_stack_and_jump_nolp(ret_from_intr); -+ reset_stack_and_jump(ret_from_intr); - } - - static int setup_compat_l4(struct vcpu *v) ---- a/xen/arch/x86/setup.c -+++ b/xen/arch/x86/setup.c -@@ -676,7 +676,7 @@ static void __init noreturn reinit_bsp_s - asm volatile ("setssbsy" ::: "memory"); - } - -- reset_stack_and_jump_nolp(init_done); -+ reset_stack_and_jump(init_done); - } - - /* ---- a/xen/common/livepatch.c -+++ b/xen/common/livepatch.c -@@ -1635,6 +1635,11 @@ void check_for_livepatch_work(void) - s_time_t timeout; - unsigned long flags; - -+ /* Only do any work when invoked in truly idle state. */ -+ if ( system_state != SYS_STATE_active || -+ !is_idle_domain(current->sched_unit->domain) ) -+ return; -+ - /* Fast path: no work to do. */ - if ( !per_cpu(work_to_do, cpu ) ) - return; ---- a/xen/include/asm-x86/current.h -+++ b/xen/include/asm-x86/current.h -@@ -155,13 +155,13 @@ unsigned long get_stack_dump_bottom (uns - # define SHADOW_STACK_WORK "" - #endif - --#define switch_stack_and_jump(fn, instr) \ -+#define reset_stack_and_jump(fn) \ - ({ \ - unsigned int tmp; \ - __asm__ __volatile__ ( \ - SHADOW_STACK_WORK \ - "mov %[stk], %%rsp;" \ -- instr \ -+ CHECK_FOR_LIVEPATCH_WORK \ - "jmp %c[fun];" \ - : [val] "=&r" (tmp), \ - [ssp] "=&r" (tmp) \ -@@ -176,12 +176,6 @@ unsigned long get_stack_dump_bottom (uns - unreachable(); \ - }) - --#define reset_stack_and_jump(fn) \ -- switch_stack_and_jump(fn, CHECK_FOR_LIVEPATCH_WORK) -- --#define reset_stack_and_jump_nolp(fn) \ -- switch_stack_and_jump(fn, "") -- - /* - * Which VCPU's state is currently running on each CPU? - * This is not necesasrily the same as 'current' as a CPU may be diff --git a/xsa348-2.patch b/xsa348-2.patch deleted file mode 100644 index a8106bf..0000000 --- a/xsa348-2.patch +++ /dev/null @@ -1,85 +0,0 @@ -From: Jan Beulich -Subject: x86: fold guest_idle_loop() into idle_loop() - -The latter can easily be made cover both cases. This is in preparation -of using idle_loop directly for populating idle_csw.tail. - -Take the liberty and also adjust indentation / spacing in involved code. - -Signed-off-by: Jan Beulich -Reviewed-by: Juergen Gross - ---- a/xen/arch/x86/domain.c -+++ b/xen/arch/x86/domain.c -@@ -133,14 +133,22 @@ void play_dead(void) - static void idle_loop(void) - { - unsigned int cpu = smp_processor_id(); -+ /* -+ * Idle vcpus might be attached to non-idle units! We don't do any -+ * standard idle work like tasklets or livepatching in this case. -+ */ -+ bool guest = !is_idle_domain(current->sched_unit->domain); - - for ( ; ; ) - { - if ( cpu_is_offline(cpu) ) -+ { -+ ASSERT(!guest); - play_dead(); -+ } - - /* Are we here for running vcpu context tasklets, or for idling? */ -- if ( unlikely(tasklet_work_to_do(cpu)) ) -+ if ( !guest && unlikely(tasklet_work_to_do(cpu)) ) - { - do_tasklet(); - /* Livepatch work is always kicked off via a tasklet. */ -@@ -151,28 +159,14 @@ static void idle_loop(void) - * and then, after it is done, whether softirqs became pending - * while we were scrubbing. - */ -- else if ( !softirq_pending(cpu) && !scrub_free_pages() && -- !softirq_pending(cpu) ) -- pm_idle(); -- do_softirq(); -- } --} -- --/* -- * Idle loop for siblings in active schedule units. -- * We don't do any standard idle work like tasklets or livepatching. -- */ --static void guest_idle_loop(void) --{ -- unsigned int cpu = smp_processor_id(); -- -- for ( ; ; ) -- { -- ASSERT(!cpu_is_offline(cpu)); -- -- if ( !softirq_pending(cpu) && !scrub_free_pages() && -- !softirq_pending(cpu)) -- sched_guest_idle(pm_idle, cpu); -+ else if ( !softirq_pending(cpu) && !scrub_free_pages() && -+ !softirq_pending(cpu) ) -+ { -+ if ( guest ) -+ sched_guest_idle(pm_idle, cpu); -+ else -+ pm_idle(); -+ } - do_softirq(); - } - } -@@ -190,10 +184,6 @@ void startup_cpu_idle_loop(void) - - static void noreturn continue_idle_domain(struct vcpu *v) - { -- /* Idle vcpus might be attached to non-idle units! */ -- if ( !is_idle_domain(v->sched_unit->domain) ) -- reset_stack_and_jump(guest_idle_loop); -- - reset_stack_and_jump(idle_loop); - } - diff --git a/xsa348-3.patch b/xsa348-3.patch deleted file mode 100644 index 43b8bbc..0000000 --- a/xsa348-3.patch +++ /dev/null @@ -1,174 +0,0 @@ -From: Jan Beulich -Subject: x86: avoid calling {svm,vmx}_do_resume() - -These functions follow the following path: hvm_do_resume() -> -handle_hvm_io_completion() -> hvm_wait_for_io() -> -wait_on_xen_event_channel() -> do_softirq() -> schedule() -> -sched_context_switch() -> continue_running() and hence may -recursively invoke themselves. If this ends up happening a couple of -times, a stack overflow would result. - -Prevent this by also resetting the stack at the -->arch.ctxt_switch->tail() invocations (in both places for consistency) -and thus jumping to the functions instead of calling them. - -This is XSA-348 / CVE-2020-29566. - -Reported-by: Julien Grall -Signed-off-by: Jan Beulich -Reviewed-by: Juergen Gross ---- -v2: Fix LIVEPATCH builds crashing. - ---- a/xen/arch/x86/domain.c -+++ b/xen/arch/x86/domain.c -@@ -130,7 +130,7 @@ void play_dead(void) - dead_idle(); - } - --static void idle_loop(void) -+static void noreturn idle_loop(void) - { - unsigned int cpu = smp_processor_id(); - /* -@@ -182,11 +182,6 @@ void startup_cpu_idle_loop(void) - reset_stack_and_jump(idle_loop); - } - --static void noreturn continue_idle_domain(struct vcpu *v) --{ -- reset_stack_and_jump(idle_loop); --} -- - void init_hypercall_page(struct domain *d, void *ptr) - { - memset(ptr, 0xcc, PAGE_SIZE); -@@ -710,7 +705,7 @@ int arch_domain_create(struct domain *d, - static const struct arch_csw idle_csw = { - .from = paravirt_ctxt_switch_from, - .to = paravirt_ctxt_switch_to, -- .tail = continue_idle_domain, -+ .tail = idle_loop, - }; - - d->arch.ctxt_switch = &idle_csw; -@@ -2047,20 +2042,12 @@ void context_switch(struct vcpu *prev, s - /* Ensure that the vcpu has an up-to-date time base. */ - update_vcpu_system_time(next); - -- /* -- * Schedule tail *should* be a terminal function pointer, but leave a -- * bug frame around just in case it returns, to save going back into the -- * context switching code and leaving a far more subtle crash to diagnose. -- */ -- nextd->arch.ctxt_switch->tail(next); -- BUG(); -+ reset_stack_and_jump_ind(nextd->arch.ctxt_switch->tail); - } - - void continue_running(struct vcpu *same) - { -- /* See the comment above. */ -- same->domain->arch.ctxt_switch->tail(same); -- BUG(); -+ reset_stack_and_jump_ind(same->domain->arch.ctxt_switch->tail); - } - - int __sync_local_execstate(void) ---- a/xen/arch/x86/hvm/svm/svm.c -+++ b/xen/arch/x86/hvm/svm/svm.c -@@ -991,8 +991,9 @@ static void svm_ctxt_switch_to(struct vc - wrmsr_tsc_aux(v->arch.msrs->tsc_aux); - } - --static void noreturn svm_do_resume(struct vcpu *v) -+static void noreturn svm_do_resume(void) - { -+ struct vcpu *v = current; - struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb; - bool debug_state = (v->domain->debugger_attached || - v->domain->arch.monitor.software_breakpoint_enabled || ---- a/xen/arch/x86/hvm/vmx/vmcs.c -+++ b/xen/arch/x86/hvm/vmx/vmcs.c -@@ -1850,8 +1850,9 @@ void vmx_vmentry_failure(void) - domain_crash(curr->domain); - } - --void vmx_do_resume(struct vcpu *v) -+void vmx_do_resume(void) - { -+ struct vcpu *v = current; - bool_t debug_state; - unsigned long host_cr4; - ---- a/xen/arch/x86/pv/domain.c -+++ b/xen/arch/x86/pv/domain.c -@@ -110,7 +110,7 @@ static int parse_pcid(const char *s) - return rc; - } - --static void noreturn continue_nonidle_domain(struct vcpu *v) -+static void noreturn continue_nonidle_domain(void) - { - check_wakeup_from_wait(); - reset_stack_and_jump(ret_from_intr); ---- a/xen/include/asm-x86/current.h -+++ b/xen/include/asm-x86/current.h -@@ -155,18 +155,18 @@ unsigned long get_stack_dump_bottom (uns - # define SHADOW_STACK_WORK "" - #endif - --#define reset_stack_and_jump(fn) \ -+#define switch_stack_and_jump(fn, instr, constr) \ - ({ \ - unsigned int tmp; \ - __asm__ __volatile__ ( \ - SHADOW_STACK_WORK \ - "mov %[stk], %%rsp;" \ - CHECK_FOR_LIVEPATCH_WORK \ -- "jmp %c[fun];" \ -+ instr "[fun]" \ - : [val] "=&r" (tmp), \ - [ssp] "=&r" (tmp) \ - : [stk] "r" (guest_cpu_user_regs()), \ -- [fun] "i" (fn), \ -+ [fun] constr (fn), \ - [skstk_base] "i" \ - ((PRIMARY_SHSTK_SLOT + 1) * PAGE_SIZE - 8), \ - [stack_mask] "i" (STACK_SIZE - 1), \ -@@ -176,6 +176,13 @@ unsigned long get_stack_dump_bottom (uns - unreachable(); \ - }) - -+#define reset_stack_and_jump(fn) \ -+ switch_stack_and_jump(fn, "jmp %c", "i") -+ -+/* The constraint may only specify non-call-clobbered registers. */ -+#define reset_stack_and_jump_ind(fn) \ -+ switch_stack_and_jump(fn, "INDIRECT_JMP %", "b") -+ - /* - * Which VCPU's state is currently running on each CPU? - * This is not necesasrily the same as 'current' as a CPU may be ---- a/xen/include/asm-x86/domain.h -+++ b/xen/include/asm-x86/domain.h -@@ -337,7 +337,7 @@ struct arch_domain - const struct arch_csw { - void (*from)(struct vcpu *); - void (*to)(struct vcpu *); -- void (*tail)(struct vcpu *); -+ void noreturn (*tail)(void); - } *ctxt_switch; - - #ifdef CONFIG_HVM ---- a/xen/include/asm-x86/hvm/vmx/vmx.h -+++ b/xen/include/asm-x86/hvm/vmx/vmx.h -@@ -95,7 +95,7 @@ typedef enum { - void vmx_asm_vmexit_handler(struct cpu_user_regs); - void vmx_asm_do_vmentry(void); - void vmx_intr_assist(void); --void noreturn vmx_do_resume(struct vcpu *); -+void noreturn vmx_do_resume(void); - void vmx_vlapic_msr_changed(struct vcpu *v); - struct hvm_emulate_ctxt; - void vmx_realmode_emulate_one(struct hvm_emulate_ctxt *hvmemul_ctxt); diff --git a/xsa351-arm.patch b/xsa351-arm.patch deleted file mode 100644 index d0d1941..0000000 --- a/xsa351-arm.patch +++ /dev/null @@ -1,58 +0,0 @@ -From: Julien Grall -Subject: xen/arm: Always trap AMU system registers - -The Activity Monitors Unit (AMU) has been introduced by ARMv8.4. It is -considered to be unsafe to be expose to guests as they might expose -information about code executed by other guests or the host. - -Arm provided a way to trap all the AMU system registers by setting -CPTR_EL2.TAM to 1. - -Unfortunately, on older revision of the specification, the bit 30 (now -CPTR_EL1.TAM) was RES0. Because of that, Xen is setting it to 0 and -therefore the system registers would be exposed to the guest when it is -run on processors with AMU. - -As the bit is mark as UNKNOWN at boot in Armv8.4, the only safe solution -for us is to always set CPTR_EL1.TAM to 1. - -Guest trying to access the AMU system registers will now receive an -undefined instruction. Unfortunately, this means that even well-behaved -guest may fail to boot because we don't sanitize the ID registers. - -This is a known issues with other Armv8.0+ features (e.g. SVE, Pointer -Auth). This will taken care separately. - -This is part of XSA-351 (or XSA-93 re-born). - -Signed-off-by: Julien Grall -Reviewed-by: Andre Przywara -Reviewed-by: Stefano Stabellini -Reviewed-by: Bertrand Marquis - -diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c -index a36f145e67..22bd1bd4c6 100644 ---- a/xen/arch/arm/traps.c -+++ b/xen/arch/arm/traps.c -@@ -151,7 +151,8 @@ void init_traps(void) - * On ARM64 the TCPx bits which we set here (0..9,12,13) are all - * RES1, i.e. they would trap whether we did this write or not. - */ -- WRITE_SYSREG((HCPTR_CP_MASK & ~(HCPTR_CP(10) | HCPTR_CP(11))) | HCPTR_TTA, -+ WRITE_SYSREG((HCPTR_CP_MASK & ~(HCPTR_CP(10) | HCPTR_CP(11))) | -+ HCPTR_TTA | HCPTR_TAM, - CPTR_EL2); - - /* -diff --git a/xen/include/asm-arm/processor.h b/xen/include/asm-arm/processor.h -index 3ca67f8157..d3d12a9d19 100644 ---- a/xen/include/asm-arm/processor.h -+++ b/xen/include/asm-arm/processor.h -@@ -351,6 +351,7 @@ - #define VTCR_RES1 (_AC(1,UL)<<31) - - /* HCPTR Hyp. Coprocessor Trap Register */ -+#define HCPTR_TAM ((_AC(1,U)<<30)) - #define HCPTR_TTA ((_AC(1,U)<<20)) /* Trap trace registers */ - #define HCPTR_CP(x) ((_AC(1,U)<<(x))) /* Trap Coprocessor x */ - #define HCPTR_CP_MASK ((_AC(1,U)<<14)-1) diff --git a/xsa351-x86-4.14-1.patch b/xsa351-x86-4.14-1.patch deleted file mode 100644 index 1be14fb..0000000 --- a/xsa351-x86-4.14-1.patch +++ /dev/null @@ -1,155 +0,0 @@ -From: =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= -Subject: x86/msr: fix handling of MSR_IA32_PERF_{STATUS/CTL} -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Currently a PV hardware domain can also be given control over the CPU -frequency, and such guest is allowed to write to MSR_IA32_PERF_CTL. -However since commit 322ec7c89f6 the default behavior has been changed -to reject accesses to not explicitly handled MSRs, preventing PV -guests that manage CPU frequency from reading -MSR_IA32_PERF_{STATUS/CTL}. - -Additionally some HVM guests (Windows at least) will attempt to read -MSR_IA32_PERF_CTL and will panic if given back a #GP fault: - - vmx.c:3035:d8v0 RDMSR 0x00000199 unimplemented - d8v0 VIRIDIAN CRASH: 3b c0000096 fffff806871c1651 ffffda0253683720 0 - -Move the handling of MSR_IA32_PERF_{STATUS/CTL} to the common MSR -handling shared between HVM and PV guests, and add an explicit case -for reads to MSR_IA32_PERF_{STATUS/CTL}. - -Restore previous behavior and allow PV guests with the required -permissions to read the contents of the mentioned MSRs. Non privileged -guests will get 0 when trying to read those registers, as writes to -MSR_IA32_PERF_CTL by such guest will already be silently dropped. - -Fixes: 322ec7c89f6 ('x86/pv: disallow access to unknown MSRs') -Fixes: 84e848fd7a1 ('x86/hvm: disallow access to unknown MSRs') -Signed-off-by: Roger Pau Monné -Signed-off-by: Andrew Cooper -Reviewed-by: Roger Pau Monné -Reviewed-by: Jan Beulich -(cherry picked from commit 3059178798a23ba870ff86ff54d442a07e6651fc) - -diff --git a/xen/arch/x86/msr.c b/xen/arch/x86/msr.c -index d72ab0fa1f..3db26faf08 100644 ---- a/xen/arch/x86/msr.c -+++ b/xen/arch/x86/msr.c -@@ -245,6 +245,25 @@ int guest_rdmsr(struct vcpu *v, uint32_t msr, uint64_t *val) - *val = msrs->misc_features_enables.raw; - break; - -+ /* -+ * These MSRs are not enumerated in CPUID. They have been around -+ * since the Pentium 4, and implemented by other vendors. -+ * -+ * Some versions of Windows try reading these before setting up a #GP -+ * handler, and Linux has several unguarded reads as well. Provide -+ * RAZ semantics, in general, but permit a cpufreq controller dom0 to -+ * have full access. -+ */ -+ case MSR_IA32_PERF_STATUS: -+ case MSR_IA32_PERF_CTL: -+ if ( !(cp->x86_vendor & (X86_VENDOR_INTEL | X86_VENDOR_CENTAUR)) ) -+ goto gp_fault; -+ -+ *val = 0; -+ if ( likely(!is_cpufreq_controller(d)) || rdmsr_safe(msr, *val) == 0 ) -+ break; -+ goto gp_fault; -+ - case MSR_X2APIC_FIRST ... MSR_X2APIC_LAST: - if ( !is_hvm_domain(d) || v != curr ) - goto gp_fault; -@@ -343,6 +362,7 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) - case MSR_INTEL_CORE_THREAD_COUNT: - case MSR_INTEL_PLATFORM_INFO: - case MSR_ARCH_CAPABILITIES: -+ case MSR_IA32_PERF_STATUS: - /* Read-only */ - case MSR_TEST_CTRL: - case MSR_TSX_FORCE_ABORT: -@@ -454,6 +474,21 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) - break; - } - -+ /* -+ * This MSR is not enumerated in CPUID. It has been around since the -+ * Pentium 4, and implemented by other vendors. -+ * -+ * To match the RAZ semantics, implement as write-discard, except for -+ * a cpufreq controller dom0 which has full access. -+ */ -+ case MSR_IA32_PERF_CTL: -+ if ( !(cp->x86_vendor & (X86_VENDOR_INTEL | X86_VENDOR_CENTAUR)) ) -+ goto gp_fault; -+ -+ if ( likely(!is_cpufreq_controller(d)) || wrmsr_safe(msr, val) == 0 ) -+ break; -+ goto gp_fault; -+ - case MSR_X2APIC_FIRST ... MSR_X2APIC_LAST: - if ( !is_hvm_domain(d) || v != curr ) - goto gp_fault; -diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c -index 85a9fd4767..5c7b9117ae 100644 ---- a/xen/arch/x86/pv/emul-priv-op.c -+++ b/xen/arch/x86/pv/emul-priv-op.c -@@ -820,12 +820,6 @@ static inline uint64_t guest_misc_enable(uint64_t val) - return val; - } - --static inline bool is_cpufreq_controller(const struct domain *d) --{ -- return ((cpufreq_controller == FREQCTL_dom0_kernel) && -- is_hardware_domain(d)); --} -- - static int read_msr(unsigned int reg, uint64_t *val, - struct x86_emulate_ctxt *ctxt) - { -@@ -1070,14 +1064,6 @@ static int write_msr(unsigned int reg, uint64_t val, - return X86EMUL_OKAY; - break; - -- case MSR_IA32_PERF_CTL: -- if ( boot_cpu_data.x86_vendor != X86_VENDOR_INTEL ) -- break; -- if ( likely(!is_cpufreq_controller(currd)) || -- wrmsr_safe(reg, val) == 0 ) -- return X86EMUL_OKAY; -- break; -- - case MSR_IA32_THERM_CONTROL: - case MSR_IA32_ENERGY_PERF_BIAS: - if ( boot_cpu_data.x86_vendor != X86_VENDOR_INTEL ) -diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h -index a0d87ef9d0..97ba8e0795 100644 ---- a/xen/include/xen/sched.h -+++ b/xen/include/xen/sched.h -@@ -1071,6 +1071,22 @@ extern enum cpufreq_controller { - FREQCTL_none, FREQCTL_dom0_kernel, FREQCTL_xen - } cpufreq_controller; - -+static always_inline bool is_cpufreq_controller(const struct domain *d) -+{ -+ /* -+ * A PV dom0 can be nominated as the cpufreq controller, instead of using -+ * Xen's cpufreq driver, at which point dom0 gets direct access to certain -+ * MSRs. -+ * -+ * This interface only works when dom0 is identity pinned and has the same -+ * number of vCPUs as pCPUs on the system. -+ * -+ * It would be far better to paravirtualise the interface. -+ */ -+ return (is_pv_domain(d) && is_hardware_domain(d) && -+ cpufreq_controller == FREQCTL_dom0_kernel); -+} -+ - int cpupool_move_domain(struct domain *d, struct cpupool *c); - int cpupool_do_sysctl(struct xen_sysctl_cpupool_op *op); - int cpupool_get_id(const struct domain *d); diff --git a/xsa351-x86-4.14-2.patch b/xsa351-x86-4.14-2.patch deleted file mode 100644 index 2c0f4e7..0000000 --- a/xsa351-x86-4.14-2.patch +++ /dev/null @@ -1,126 +0,0 @@ -From: Andrew Cooper -Subject: x86/msr: Disallow guest access to the RAPL MSRs - -Researchers have demonstrated using the RAPL interface to perform a -differential power analysis attack to recover AES keys used by other cores in -the system. - -Furthermore, even privileged guests cannot use this interface correctly, due -to MSR scope and vcpu scheduling issues. The interface would want to be -paravirtualised to be used sensibly. - -Disallow access to the RAPL MSRs completely, as well as other MSRs which -potentially access fine grain power information. - -This is part of XSA-351. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/msr.c b/xen/arch/x86/msr.c -index 3db26faf08..aa107823ac 100644 ---- a/xen/arch/x86/msr.c -+++ b/xen/arch/x86/msr.c -@@ -185,6 +185,13 @@ int guest_rdmsr(struct vcpu *v, uint32_t msr, uint64_t *val) - case MSR_TSX_CTRL: - case MSR_MCU_OPT_CTRL: - case MSR_RTIT_OUTPUT_BASE ... MSR_RTIT_ADDR_B(7): -+ case MSR_RAPL_POWER_UNIT: -+ case MSR_PKG_POWER_LIMIT ... MSR_PKG_POWER_INFO: -+ case MSR_DRAM_POWER_LIMIT ... MSR_DRAM_POWER_INFO: -+ case MSR_PP0_POWER_LIMIT ... MSR_PP0_POLICY: -+ case MSR_PP1_POWER_LIMIT ... MSR_PP1_POLICY: -+ case MSR_PLATFORM_ENERGY_COUNTER: -+ case MSR_PLATFORM_POWER_LIMIT: - case MSR_U_CET: - case MSR_S_CET: - case MSR_PL0_SSP ... MSR_INTERRUPT_SSP_TABLE: -@@ -192,6 +199,8 @@ int guest_rdmsr(struct vcpu *v, uint32_t msr, uint64_t *val) - case MSR_AMD64_LWP_CBADDR: - case MSR_PPIN_CTL: - case MSR_PPIN: -+ case MSR_F15H_CU_POWER ... MSR_F15H_CU_MAX_POWER: -+ case MSR_AMD_RAPL_POWER_UNIT ... MSR_AMD_PKG_ENERGY_STATUS: - case MSR_AMD_PPIN_CTL: - case MSR_AMD_PPIN: - /* Not offered to guests. */ -@@ -369,6 +378,13 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) - case MSR_TSX_CTRL: - case MSR_MCU_OPT_CTRL: - case MSR_RTIT_OUTPUT_BASE ... MSR_RTIT_ADDR_B(7): -+ case MSR_RAPL_POWER_UNIT: -+ case MSR_PKG_POWER_LIMIT ... MSR_PKG_POWER_INFO: -+ case MSR_DRAM_POWER_LIMIT ... MSR_DRAM_POWER_INFO: -+ case MSR_PP0_POWER_LIMIT ... MSR_PP0_POLICY: -+ case MSR_PP1_POWER_LIMIT ... MSR_PP1_POLICY: -+ case MSR_PLATFORM_ENERGY_COUNTER: -+ case MSR_PLATFORM_POWER_LIMIT: - case MSR_U_CET: - case MSR_S_CET: - case MSR_PL0_SSP ... MSR_INTERRUPT_SSP_TABLE: -@@ -376,6 +392,8 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) - case MSR_AMD64_LWP_CBADDR: - case MSR_PPIN_CTL: - case MSR_PPIN: -+ case MSR_F15H_CU_POWER ... MSR_F15H_CU_MAX_POWER: -+ case MSR_AMD_RAPL_POWER_UNIT ... MSR_AMD_PKG_ENERGY_STATUS: - case MSR_AMD_PPIN_CTL: - case MSR_AMD_PPIN: - /* Not offered to guests. */ -diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h -index 0fe98af923..5e64ecff91 100644 ---- a/xen/include/asm-x86/msr-index.h -+++ b/xen/include/asm-x86/msr-index.h -@@ -77,6 +77,38 @@ - #define MSR_RTIT_ADDR_A(n) (0x00000580 + (n) * 2) - #define MSR_RTIT_ADDR_B(n) (0x00000581 + (n) * 2) - -+/* -+ * Intel Runtime Average Power Limiting (RAPL) interface. Power plane base -+ * addresses (MSR_*_POWER_LIMIT) are model specific, but have so-far been -+ * consistent since their introduction in SandyBridge. -+ * -+ * Offsets of functionality from the power plane base is architectural, but -+ * not all power planes support all functionality. -+ */ -+#define MSR_RAPL_POWER_UNIT 0x00000606 -+ -+#define MSR_PKG_POWER_LIMIT 0x00000610 -+#define MSR_PKG_ENERGY_STATUS 0x00000611 -+#define MSR_PKG_PERF_STATUS 0x00000613 -+#define MSR_PKG_POWER_INFO 0x00000614 -+ -+#define MSR_DRAM_POWER_LIMIT 0x00000618 -+#define MSR_DRAM_ENERGY_STATUS 0x00000619 -+#define MSR_DRAM_PERF_STATUS 0x0000061b -+#define MSR_DRAM_POWER_INFO 0x0000061c -+ -+#define MSR_PP0_POWER_LIMIT 0x00000638 -+#define MSR_PP0_ENERGY_STATUS 0x00000639 -+#define MSR_PP0_POLICY 0x0000063a -+ -+#define MSR_PP1_POWER_LIMIT 0x00000640 -+#define MSR_PP1_ENERGY_STATUS 0x00000641 -+#define MSR_PP1_POLICY 0x00000642 -+ -+/* Intel Platform-wide power interface. */ -+#define MSR_PLATFORM_ENERGY_COUNTER 0x0000064d -+#define MSR_PLATFORM_POWER_LIMIT 0x0000065c -+ - #define MSR_U_CET 0x000006a0 - #define MSR_S_CET 0x000006a2 - #define CET_SHSTK_EN (_AC(1, ULL) << 0) -@@ -92,6 +124,13 @@ - #define PASID_PASID_MASK 0x000fffff - #define PASID_VALID (_AC(1, ULL) << 31) - -+#define MSR_F15H_CU_POWER 0xc001007a -+#define MSR_F15H_CU_MAX_POWER 0xc001007b -+ -+#define MSR_AMD_RAPL_POWER_UNIT 0xc0010299 -+#define MSR_AMD_CORE_ENERGY_STATUS 0xc001029a -+#define MSR_AMD_PKG_ENERGY_STATUS 0xc001029b -+ - /* - * Legacy MSR constants in need of cleanup. No new MSRs below this comment. - */ diff --git a/xsa352.patch b/xsa352.patch deleted file mode 100644 index e21d21a..0000000 --- a/xsa352.patch +++ /dev/null @@ -1,42 +0,0 @@ -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Subject: tools/ocaml/xenstored: only Dom0 can change node owner -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Otherwise we can give quota away to another domain, either causing it to run -out of quota, or in case of Dom0 use unbounded amounts of memory and bypass -the quota system entirely. - -This was fixed in the C version of xenstored in 2006 (c/s db34d2aaa5f5, -predating the XSA process by 5 years). - -It was also fixed in the mirage version of xenstore in 2012, with a unit test -demonstrating the vulnerability: - - https://github.com/mirage/ocaml-xenstore/commit/6b91f3ac46b885d0530a51d57a9b3a57d64923a7 - https://github.com/mirage/ocaml-xenstore/commit/22ee5417c90b8fda905c38de0d534506152eace6 - -but possibly without realising that the vulnerability still affected the -in-tree oxenstored (added c/s f44af660412 in 2010). - -This is XSA-352. - -Signed-off-by: Edwin Török -Acked-by: Christian Lindig -Reviewed-by: Andrew Cooper - -diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml -index 3b05128f1b..5f915f2bbe 100644 ---- a/tools/ocaml/xenstored/store.ml -+++ b/tools/ocaml/xenstored/store.ml -@@ -407,7 +407,8 @@ let setperms store perm path nperms = - | Some node -> - let old_owner = Node.get_owner node in - let new_owner = Perms.Node.get_owner nperms in -- if not ((old_owner = new_owner) || (Perms.Connection.is_dom0 perm)) then Quota.check store.quota new_owner 0; -+ if not ((old_owner = new_owner) || (Perms.Connection.is_dom0 perm)) then -+ raise Define.Permission_denied; - store.root <- path_setperms store perm path nperms; - Quota.del_entry store.quota old_owner; - Quota.add_entry store.quota new_owner diff --git a/xsa353.patch b/xsa353.patch deleted file mode 100644 index 764f93c..0000000 --- a/xsa353.patch +++ /dev/null @@ -1,89 +0,0 @@ -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Subject: tools/ocaml/xenstored: do permission checks on xenstore root -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -This was lacking in a disappointing number of places. - -The xenstore root node is treated differently from all other nodes, because it -doesn't have a parent, and mutation requires changing the parent. - -Unfortunately this lead to open-coding the special case for root into every -single xenstore operation, and out of all the xenstore operations only read -did a permission check when handling the root node. - -This means that an unprivileged guest can: - - * xenstore-chmod / to its liking and subsequently write new arbitrary nodes - there (subject to quota) - * xenstore-rm -r / deletes almost the entire xenstore tree (xenopsd quickly - refills some, but you are left with a broken system) - * DIRECTORY on / lists all children when called through python - bindings (xenstore-ls stops at /local because it tries to list recursively) - * get-perms on / works too, but that is just a minor information leak - -Add the missing permission checks, but this should really be refactored to do -the root handling and permission checks on the node only once from a single -function, instead of getting it wrong nearly everywhere. - -This is XSA-353. - -Signed-off-by: Edwin Török -Acked-by: Christian Lindig -Reviewed-by: Andrew Cooper - -diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml -index f299ec6461..92b6289b5e 100644 ---- a/tools/ocaml/xenstored/store.ml -+++ b/tools/ocaml/xenstored/store.ml -@@ -273,15 +273,17 @@ let path_rm store perm path = - Node.del_childname node name - with Not_found -> - raise Define.Doesnt_exist in -- if path = [] then -+ if path = [] then ( -+ Node.check_perm store.root perm Perms.WRITE; - Node.del_all_children store.root -- else -+ ) else - Path.apply_modify store.root path do_rm - - let path_setperms store perm path perms = -- if path = [] then -+ if path = [] then ( -+ Node.check_perm store.root perm Perms.WRITE; - Node.set_perms store.root perms -- else -+ ) else - let do_setperms node name = - let c = Node.find node name in - Node.check_owner c perm; -@@ -313,9 +315,10 @@ let read store perm path = - - let ls store perm path = - let children = -- if path = [] then -- (Node.get_children store.root) -- else -+ if path = [] then ( -+ Node.check_perm store.root perm Perms.READ; -+ Node.get_children store.root -+ ) else - let do_ls node name = - let cnode = Node.find node name in - Node.check_perm cnode perm Perms.READ; -@@ -324,9 +327,10 @@ let ls store perm path = - List.rev (List.map (fun n -> Symbol.to_string n.Node.name) children) - - let getperms store perm path = -- if path = [] then -- (Node.get_perms store.root) -- else -+ if path = [] then ( -+ Node.check_perm store.root perm Perms.READ; -+ Node.get_perms store.root -+ ) else - let fct n name = - let c = Node.find n name in - Node.check_perm c perm Perms.READ; diff --git a/xsa355.patch b/xsa355.patch deleted file mode 100644 index 491dd05..0000000 --- a/xsa355.patch +++ /dev/null @@ -1,23 +0,0 @@ -From: Jan Beulich -Subject: memory: fix off-by-one in XSA-346 change - -The comparison against ARRAY_SIZE() needs to be >= in order to avoid -overrunning the pages[] array. - -This is XSA-355. - -Fixes: 5777a3742d88 ("IOMMU: hold page ref until after deferred TLB flush") -Signed-off-by: Jan Beulich -Reviewed-by: Julien Grall - ---- a/xen/common/memory.c -+++ b/xen/common/memory.c -@@ -854,7 +854,7 @@ int xenmem_add_to_physmap(struct domain - ++extra.ppage; - - /* Check for continuation if it's not the last iteration. */ -- if ( (++done > ARRAY_SIZE(pages) && extra.ppage) || -+ if ( (++done >= ARRAY_SIZE(pages) && extra.ppage) || - (xatp->size > done && hypercall_preempt_check()) ) - { - rc = start + done; diff --git a/xsa356.patch b/xsa356.patch deleted file mode 100644 index f18d220..0000000 --- a/xsa356.patch +++ /dev/null @@ -1,65 +0,0 @@ -From: Roger Pau Monné -Subject: x86/irq: fix infinite loop in irq_move_cleanup_interrupt - -If Xen enters irq_move_cleanup_interrupt with a dynamic vector below -IRQ_MOVE_CLEANUP_VECTOR pending in IRR (0x20 or 0x21) that's also -designated for a cleanup it will enter a loop where -irq_move_cleanup_interrupt continuously sends a cleanup IPI (vector -0x22) to itself while waiting for the vector with lower priority to be -injected - which will never happen because IRQ_MOVE_CLEANUP_VECTOR -takes precedence and it's always injected first. - -Fix this by making sure vectors below IRQ_MOVE_CLEANUP_VECTOR are -marked as used and thus not available for APs. Also add some logic to -assert and prevent irq_move_cleanup_interrupt from entering such an -infinite loop, albeit that should never happen given the current code. - -This is XSA-356 / CVE-2020-29567. - -Fixes: 3fba06ba9f8 ('x86/IRQ: re-use legacy vector ranges on APs') -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich - ---- a/xen/arch/x86/irq.c -+++ b/xen/arch/x86/irq.c -@@ -441,8 +441,15 @@ int __init init_irq_data(void) - set_bit(HYPERCALL_VECTOR, used_vectors); - #endif - -- /* IRQ_MOVE_CLEANUP_VECTOR used for clean up vectors */ -- set_bit(IRQ_MOVE_CLEANUP_VECTOR, used_vectors); -+ /* -+ * Mark vectors up to the cleanup one as used, to prevent an infinite loop -+ * invoking irq_move_cleanup_interrupt. -+ */ -+ BUILD_BUG_ON(IRQ_MOVE_CLEANUP_VECTOR < FIRST_DYNAMIC_VECTOR); -+ for ( vector = FIRST_DYNAMIC_VECTOR; -+ vector <= IRQ_MOVE_CLEANUP_VECTOR; -+ vector++ ) -+ __set_bit(vector, used_vectors); - - return 0; - } -@@ -727,10 +734,6 @@ void irq_move_cleanup_interrupt(struct cpu_user_regs *regs) - { - unsigned vector, me; - -- /* This interrupt should not nest inside others. */ -- BUILD_BUG_ON(APIC_PRIO_CLASS(IRQ_MOVE_CLEANUP_VECTOR) != -- APIC_PRIO_CLASS(FIRST_DYNAMIC_VECTOR)); -- - ack_APIC_irq(); - - me = smp_processor_id(); -@@ -774,6 +777,11 @@ void irq_move_cleanup_interrupt(struct cpu_user_regs *regs) - */ - if ( irr & (1u << (vector % 32)) ) - { -+ if ( vector < IRQ_MOVE_CLEANUP_VECTOR ) -+ { -+ ASSERT_UNREACHABLE(); -+ goto unlock; -+ } - send_IPI_self(IRQ_MOVE_CLEANUP_VECTOR); - TRACE_3D(TRC_HW_IRQ_MOVE_CLEANUP_DELAY, - irq, vector, smp_processor_id()); diff --git a/xsa358.patch b/xsa358.patch deleted file mode 100644 index a9ff89a..0000000 --- a/xsa358.patch +++ /dev/null @@ -1,57 +0,0 @@ -From: Jan Beulich -Subject: evtchn/FIFO: re-order and synchronize (with) map_control_block() - -For evtchn_fifo_set_pending()'s check of the control block having been -set to be effective, ordering of respective reads and writes needs to be -ensured: The control block pointer needs to be recorded strictly after -the setting of all the queue heads, and it needs checking strictly -before any uses of them (this latter aspect was already guaranteed). - -This is XSA-358 / CVE-2020-29570. - -Reported-by: Julien Grall -Signed-off-by: Jan Beulich -Acked-by: Julien Grall ---- -v3: Drop read-side barrier again, leveraging guest_test_and_set_bit(). -v2: Re-base over queue locking re-work. - ---- a/xen/common/event_fifo.c -+++ b/xen/common/event_fifo.c -@@ -249,6 +249,10 @@ static void evtchn_fifo_set_pending(stru - goto unlock; - } - -+ /* -+ * This also acts as the read counterpart of the smp_wmb() in -+ * map_control_block(). -+ */ - if ( guest_test_and_set_bit(d, EVTCHN_FIFO_LINKED, word) ) - goto unlock; - -@@ -474,6 +478,7 @@ static int setup_control_block(struct vc - static int map_control_block(struct vcpu *v, uint64_t gfn, uint32_t offset) - { - void *virt; -+ struct evtchn_fifo_control_block *control_block; - unsigned int i; - int rc; - -@@ -484,10 +489,15 @@ static int map_control_block(struct vcpu - if ( rc < 0 ) - return rc; - -- v->evtchn_fifo->control_block = virt + offset; -+ control_block = virt + offset; - - for ( i = 0; i <= EVTCHN_FIFO_PRIORITY_MIN; i++ ) -- v->evtchn_fifo->queue[i].head = &v->evtchn_fifo->control_block->head[i]; -+ v->evtchn_fifo->queue[i].head = &control_block->head[i]; -+ -+ /* All queue heads must have been set before setting the control block. */ -+ smp_wmb(); -+ -+ v->evtchn_fifo->control_block = control_block; - - return 0; - } diff --git a/xsa359.patch b/xsa359.patch deleted file mode 100644 index 231810b..0000000 --- a/xsa359.patch +++ /dev/null @@ -1,40 +0,0 @@ -From: Jan Beulich -Subject: evtchn/FIFO: add 2nd smp_rmb() to evtchn_fifo_word_from_port() - -Besides with add_page_to_event_array() the function also needs to -synchronize with evtchn_fifo_init_control() setting both d->evtchn_fifo -and (subsequently) d->evtchn_port_ops. - -This is XSA-359 / CVE-2020-29571. - -Reported-by: Julien Grall -Signed-off-by: Jan Beulich -Reviewed-by: Julien Grall - ---- a/xen/common/event_fifo.c -+++ b/xen/common/event_fifo.c -@@ -55,6 +55,13 @@ static inline event_word_t *evtchn_fifo_ - { - unsigned int p, w; - -+ /* -+ * Callers aren't required to hold d->event_lock, so we need to synchronize -+ * with evtchn_fifo_init_control() setting d->evtchn_port_ops /after/ -+ * d->evtchn_fifo. -+ */ -+ smp_rmb(); -+ - if ( unlikely(port >= d->evtchn_fifo->num_evtchns) ) - return NULL; - -@@ -606,6 +613,10 @@ int evtchn_fifo_init_control(struct evtc - if ( rc < 0 ) - goto error; - -+ /* -+ * This call, as a side effect, synchronizes with -+ * evtchn_fifo_word_from_port(). -+ */ - rc = map_control_block(v, gfn, offset); - if ( rc < 0 ) - goto error; From e30580581de86bd6eb8377709843898d5b8105c4 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Sun, 20 Dec 2020 19:02:45 +0000 Subject: [PATCH 062/194] renumber patches --- xen.spec | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/xen.spec b/xen.spec index 40f2d33..bf6a1b9 100644 --- a/xen.spec +++ b/xen.spec @@ -109,10 +109,10 @@ Patch37: droplibvirtconflict.patch Patch38: qemu.trad.CVE-2017-8309.patch Patch39: qemu.trad.CVE-2017-9330.patch Patch40: xen.drop.brctl.patch -Patch42: xen.gcc9.fixes.patch -Patch45: xen.gcc10.fixes.patch -Patch70: xen.gcc11.fixes.patch -Patch76: zstd-dom0.patch +Patch41: xen.gcc9.fixes.patch +Patch42: xen.gcc10.fixes.patch +Patch43: xen.gcc11.fixes.patch +Patch44: zstd-dom0.patch %if %build_qemutrad @@ -312,10 +312,10 @@ manage Xen virtual machines. %patch37 -p1 %patch3 -p1 %patch40 -p1 +%patch41 -p1 %patch42 -p1 -%patch45 -p1 -%patch70 -p1 -%patch76 -p1 +%patch43 -p1 +%patch44 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -912,6 +912,7 @@ fi - update to 4.14.1 adjust xen.canonicalize.patch remove or adjust patches now included or superceded upstream + renumber patches * Tue Dec 15 2020 Michael Young - 4.14.0-14 - xenstore watch notifications lacking permission checks [XSA-115, From 5f1904ebd4b6885ad3dd3860d627292ac3c96db5 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 21 Jan 2021 20:10:24 +0000 Subject: [PATCH 063/194] IRQ vector leak on x86 [XSA-360] --- xen.spec | 7 +++- xsa360-4.14.patch | 97 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 xsa360-4.14.patch diff --git a/xen.spec b/xen.spec index bf6a1b9..0074f59 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: 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: zstd-dom0.patch +Patch45: xsa360-4.14.patch %if %build_qemutrad @@ -316,6 +317,7 @@ manage Xen virtual machines. %patch42 -p1 %patch43 -p1 %patch44 -p1 +%patch45 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -908,6 +910,9 @@ fi %endif %changelog +* Thu Jan 21 2021 Michael Young - 4.14.1-2 +- IRQ vector leak on x86 [XSA-360] + * Sun Dec 20 2020 Michael Young - 4.14.1-1 - update to 4.14.1 adjust xen.canonicalize.patch diff --git a/xsa360-4.14.patch b/xsa360-4.14.patch new file mode 100644 index 0000000..1bc185b --- /dev/null +++ b/xsa360-4.14.patch @@ -0,0 +1,97 @@ +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. */ From 7794fedff39012c39ab4ba2c191f3e186fefc4eb Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Wed, 27 Jan 2021 23:55:36 +0000 Subject: [PATCH 064/194] - Rebuilt for https://fedoraproject.org/wiki/Fedora_34_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 0074f59..dc23fb6 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: 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 @@ -910,6 +910,9 @@ fi %endif %changelog +* Wed Jan 27 2021 Fedora Release Engineering - 4.14.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + * Thu Jan 21 2021 Michael Young - 4.14.1-2 - IRQ vector leak on x86 [XSA-360] From ab9408e4e3d33a030fc974c3f6ee55d6521b7b6a Mon Sep 17 00:00:00 2001 From: Michael Young Date: Mon, 1 Feb 2021 20:01:40 +0000 Subject: [PATCH 065/194] backport upstream zstd dom0 and guest patches add libzstd-devel BuildRequires add weak dependency on grub modules to improve initial boot setup --- ...960ae65f28106fdc5c2130f5f08fadca0e4c.patch | 865 ++++ ...81687ad611515474b1c17afc2f79f19faef5.patch | 280 ++ ...f82049efb5b2044b33aa482ba3a136b7804d.patch | 821 ++++ ...7cf1b63ce57a6a7e2c1800dbc50eed742c32.patch | 3487 ++--------------- ...9d94dfaa3573bd86ebfc457cbc8f70a3ecda.patch | 105 + xen.spec | 27 +- 6 files changed, 2430 insertions(+), 3155 deletions(-) create mode 100644 xen.git-35d2960ae65f28106fdc5c2130f5f08fadca0e4c.patch create mode 100644 xen.git-7c9f81687ad611515474b1c17afc2f79f19faef5.patch create mode 100644 xen.git-8169f82049efb5b2044b33aa482ba3a136b7804d.patch rename zstd-dom0.patch => xen.git-d6627cf1b63ce57a6a7e2c1800dbc50eed742c32.patch (66%) create mode 100644 xen.git-d8099d94dfaa3573bd86ebfc457cbc8f70a3ecda.patch diff --git a/xen.git-35d2960ae65f28106fdc5c2130f5f08fadca0e4c.patch b/xen.git-35d2960ae65f28106fdc5c2130f5f08fadca0e4c.patch new file mode 100644 index 0000000..181a9ba --- /dev/null +++ b/xen.git-35d2960ae65f28106fdc5c2130f5f08fadca0e4c.patch @@ -0,0 +1,865 @@ +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 new file mode 100644 index 0000000..42ee5bd --- /dev/null +++ b/xen.git-7c9f81687ad611515474b1c17afc2f79f19faef5.patch @@ -0,0 +1,280 @@ +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 new file mode 100644 index 0000000..4d4f5b3 --- /dev/null +++ b/xen.git-8169f82049efb5b2044b33aa482ba3a136b7804d.patch @@ -0,0 +1,821 @@ +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/zstd-dom0.patch b/xen.git-d6627cf1b63ce57a6a7e2c1800dbc50eed742c32.patch similarity index 66% rename from zstd-dom0.patch rename to xen.git-d6627cf1b63ce57a6a7e2c1800dbc50eed742c32.patch index 57b7f76..c8643cf 100644 --- a/zstd-dom0.patch +++ b/xen.git-d6627cf1b63ce57a6a7e2c1800dbc50eed742c32.patch @@ -1,8 +1,54 @@ +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 d109f279a4..5ba09f04ac 100644 +index 7a4e652b57..d751315a87 100644 --- a/xen/common/Makefile +++ b/xen/common/Makefile -@@ -59,7 +59,7 @@ obj-bin-y += warning.init.o +@@ -55,7 +55,7 @@ obj-bin-y += warning.init.o obj-$(CONFIG_XENOPROF) += xenoprof.o obj-y += xmalloc_tlsf.o @@ -12,24 +58,26 @@ index d109f279a4..5ba09f04ac 100644 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..0da27b0ab6 100644 +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, "\050\265\057\375", 4) ) ++ 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..a2c382fddc +index 0000000000..a107616427 --- /dev/null +++ b/xen/common/unzstd.c -@@ -0,0 +1,332 @@ +@@ -0,0 +1,308 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ +/* + * Important notes about in-place decompression + * @@ -81,35 +129,16 @@ index 0000000000..a2c382fddc + * + * safety_margin = 22 + uncompressed_size * 3 / 131072 + 131072 + * <= 22 + (uncompressed_size >> 15) + 131072 -+ * -+ * 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. -+ */ -+ -+/* -+ * Preboot environments #include "path/to/decompress_unzstd.c". -+ * All of the source files we depend on must be #included. -+ * zstd's only source dependeny is xxhash, which has no source -+ * dependencies. -+ * -+ * When UNZSTD_PREBOOT is defined we declare __decompress(), which is -+ * used for kernel decompression, instead of unzstd(). -+ * -+ * Define __DISABLE_EXPORTS in preboot environments to prevent symbols -+ * from xxhash and zstd from being exported by the EXPORT_SYMBOL macro. + */ + +#include "decompress.h" -+#include "xxhash.c" ++ +#include "zstd/entropy_common.c" +#include "zstd/fse_decompress.c" +#include "zstd/huf_decompress.c" +#include "zstd/zstd_common.c" +#include "zstd/decompress.c" + -+#include -+ +/* 128MB is the maximum window size supported by zstd. */ +#define ZSTD_WINDOWSIZE_MAX (1 << ZSTD_WINDOWLOG_MAX) +/* @@ -151,7 +180,7 @@ index 0000000000..a2c382fddc + * 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, unsigned int in_len, u8 *out_buf, ++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)) +{ @@ -191,12 +220,12 @@ index 0000000000..a2c382fddc + 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, long out_len, -+ unsigned int *in_pos, -+ void (*error)(const char *x)) ++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; @@ -208,9 +237,12 @@ index 0000000000..a2c382fddc + ZSTD_DStream *dstream; + int err; + size_t ret; -+ -+ if (out_len == 0) -+ out_len = INT_MAX; /* no limit */ ++ /* ++ * 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) + /* @@ -238,7 +270,7 @@ index 0000000000..a2c382fddc + /* Read the first chunk, since we need to decode the frame header. */ + if (fill != NULL) + in_len = fill(in_buf, ZSTD_IOBUF_SIZE); -+ if (in_len < 0) { ++ if ((int)in_len < 0) { + error("ZSTD-compressed data is truncated"); + err = -1; + goto out; @@ -315,7 +347,7 @@ index 0000000000..a2c382fddc + if (in_pos != NULL) + *in_pos += in.pos; + in_len = fill ? fill(in_buf, ZSTD_IOBUF_SIZE) : -1; -+ if (in_len < 0) { ++ if ((int)in_len < 0) { + error("ZSTD-compressed data is truncated"); + err = -1; + goto out; @@ -352,512 +384,12 @@ index 0000000000..a2c382fddc + large_free(wksp); + return err; +} -+ -+STATIC int INIT unzstd(unsigned char *buf, unsigned int len, -+ int (*fill)(void*, unsigned int), -+ int (*flush)(void*, unsigned int), -+ unsigned char *out_buf, -+ unsigned int *pos, -+ void (*error)(const char *x)) -+{ -+ return __unzstd(buf, len, fill, flush, out_buf, 0, pos, error); -+} -diff --git a/xen/common/xxhash.c b/xen/common/xxhash.c -new file mode 100644 -index 0000000000..3ab3e01859 ---- /dev/null -+++ b/xen/common/xxhash.c -@@ -0,0 +1,484 @@ -+/* -+ * 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 "zstd/private.h" -+ -+/*-************************************* -+ * Macros -+ **************************************/ -+#define xxh_rotl32(x, r) ((x << r) | (x >> (32 - r))) -+#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 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; -+ -+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 INIT xxh32_copy_state(struct xxh32_state *dst, const struct xxh32_state *src) -+{ -+ memcpy(dst, src, sizeof(*dst)); -+} -+ -+void INIT xxh64_copy_state(struct xxh64_state *dst, const struct xxh64_state *src) -+{ -+ memcpy(dst, src, sizeof(*dst)); -+} -+ -+/*-*************************** -+ * Simple Hash Functions -+ ****************************/ -+static uint32_t INIT 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 INIT 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; -+} -+ -+static uint64_t INIT 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 INIT 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 INIT 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 INIT 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)); -+} -+ -+void INIT 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 INIT 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 INIT 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; -+} -+ -+int INIT 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 INIT 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; -+} diff --git a/xen/common/zstd/bitstream.h b/xen/common/zstd/bitstream.h new file mode 100644 -index 0000000000..3a49784d5c +index 0000000000..2b06d4551f --- /dev/null +++ b/xen/common/zstd/bitstream.h -@@ -0,0 +1,379 @@ +@@ -0,0 +1,380 @@ +/* + * bitstream + * Part of FSE library @@ -1119,16 +651,17 @@ index 0000000000..3a49784d5c + bitD->bitContainer = *(const BYTE *)(bitD->start); + switch (srcSize) { + case 7: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[6]) << (sizeof(bitD->bitContainer) * 8 - 16); -+ /* fall through */ ++ /* fallthrough */ + case 6: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[5]) << (sizeof(bitD->bitContainer) * 8 - 24); -+ /* fall through */ ++ /* fallthrough */ + case 5: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[4]) << (sizeof(bitD->bitContainer) * 8 - 32); -+ /* fall through */ ++ /* fallthrough */ + case 4: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[3]) << 24; -+ /* fall through */ ++ /* fallthrough */ + case 3: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[2]) << 16; -+ /* fall through */ ++ /* fallthrough */ + case 2: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[1]) << 8; ++ /* fallthrough */ + default:; + } + { @@ -1239,10 +772,10 @@ index 0000000000..3a49784d5c +#endif /* BITSTREAM_H_MODULE */ diff --git a/xen/common/zstd/decompress.c b/xen/common/zstd/decompress.c new file mode 100644 -index 0000000000..8e627d881a +index 0000000000..3d3ef136e5 --- /dev/null +++ b/xen/common/zstd/decompress.c -@@ -0,0 +1,2489 @@ +@@ -0,0 +1,2496 @@ +/** + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. + * All rights reserved. @@ -1366,7 +899,7 @@ index 0000000000..8e627d881a + return 0; +} + -+ZSTD_DCtx INIT *ZSTD_createDCtx_advanced(ZSTD_customMem customMem) ++ZSTD_DCtx *INIT ZSTD_createDCtx_advanced(ZSTD_customMem customMem) +{ + ZSTD_DCtx *dctx; + @@ -1381,7 +914,7 @@ index 0000000000..8e627d881a + return dctx; +} + -+ZSTD_DCtx INIT *ZSTD_initDCtx(void *workspace, size_t workspaceSize) ++ZSTD_DCtx *INIT ZSTD_initDCtx(void *workspace, size_t workspaceSize) +{ + ZSTD_customMem const stackMem = ZSTD_initStack(workspace, workspaceSize); + return ZSTD_createDCtx_advanced(stackMem); @@ -1401,7 +934,11 @@ index 0000000000..8e627d881a + memcpy(dstDCtx, srcDCtx, sizeof(ZSTD_DCtx) - workSpaceSize); /* no need to copy workspace */ +} + -+static void INIT ZSTD_refDDict(ZSTD_DCtx *dstDCtx, const ZSTD_DDict *ddict); ++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 @@ -1685,7 +1222,7 @@ index 0000000000..8e627d881a + case set_repeat: + if (dctx->litEntropy == 0) + return ERROR(dictionary_corrupted); -+ /* fall through */ ++ /* 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 */ @@ -1992,8 +1529,10 @@ index 0000000000..8e627d881a + @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) ++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) { @@ -2120,7 +1659,7 @@ index 0000000000..8e627d881a +} seqState_t; + +FORCE_NOINLINE -+size_t INIT ZSTD_execSequenceLast7(BYTE *op, BYTE *const oend, seq_t sequence, const BYTE **litPtr, const BYTE *const litLimit, const BYTE *const base, ++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; @@ -2403,7 +1942,7 @@ index 0000000000..8e627d881a + return op - ostart; +} + -+FORCE_INLINE seq_t INIT ZSTD_decodeSequenceLong_generic(seqState_t *seqState, int const longOffsets) ++FORCE_INLINE seq_t ZSTD_decodeSequenceLong_generic(seqState_t *seqState, int const longOffsets) +{ + seq_t seq; + @@ -2503,8 +2042,9 @@ index 0000000000..8e627d881a +} + +FORCE_INLINE -+size_t 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) ++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; @@ -2880,8 +2420,8 @@ index 0000000000..8e627d881a + return op - ostart; +} + -+static const void INIT *ZSTD_DDictDictContent(const ZSTD_DDict *ddict); -+static size_t INIT ZSTD_DDictDictSize(const ZSTD_DDict *ddict); ++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) @@ -3011,7 +2551,7 @@ index 0000000000..8e627d881a + return 0; + } + dctx->expected = 0; /* not necessary to copy more */ -+ /* fall through */ ++ /* fallthrough */ + + case ZSTDds_decodeFrameHeader: + memcpy(dctx->headerBuffer + ZSTD_frameHeaderSize_prefix, src, dctx->expected); @@ -3231,7 +2771,7 @@ index 0000000000..8e627d881a + +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 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; } + @@ -3280,7 +2820,7 @@ index 0000000000..8e627d881a + return 0; +} + -+static ZSTD_DDict INIT *ZSTD_createDDict_advanced(const void *dict, size_t dictSize, unsigned byReference, ZSTD_customMem customMem) ++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; @@ -3323,7 +2863,7 @@ index 0000000000..8e627d881a +* 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_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); @@ -3432,7 +2972,7 @@ index 0000000000..8e627d881a + return ZSTD_DCtxWorkspaceBound() + ZSTD_ALIGN(sizeof(ZSTD_DStream)) + ZSTD_ALIGN(inBuffSize) + ZSTD_ALIGN(outBuffSize); +} + -+static ZSTD_DStream INIT *ZSTD_createDStream_advanced(ZSTD_customMem customMem) ++static ZSTD_DStream *INIT ZSTD_createDStream_advanced(ZSTD_customMem customMem) +{ + ZSTD_DStream *zds; + @@ -3454,7 +2994,7 @@ index 0000000000..8e627d881a + return zds; +} + -+ZSTD_DStream INIT *ZSTD_initDStream(size_t maxWindowSize, void *workspace, size_t workspaceSize) ++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); @@ -3487,7 +3027,7 @@ index 0000000000..8e627d881a + return zds; +} + -+ZSTD_DStream INIT *ZSTD_initDStream_usingDDict(size_t maxWindowSize, const ZSTD_DDict *ddict, void *workspace, size_t workspaceSize) ++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) { @@ -3552,7 +3092,7 @@ index 0000000000..8e627d881a + switch (zds->stage) { + case zdss_init: + ZSTD_resetDStream(zds); /* transparent reset on starting decoding a new frame */ -+ /* fall through */ ++ /* fallthrough */ + + case zdss_loadHeader: { + size_t const hSize = ZSTD_getFrameParams(&zds->fParams, zds->headerBuffer, zds->lhSize); @@ -3619,7 +3159,7 @@ index 0000000000..8e627d881a + } + zds->stage = zdss_read; + } -+ /* fall through */ ++ /* fallthrough */ + + case zdss_read: { + size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zds->dctx); @@ -3648,7 +3188,7 @@ index 0000000000..8e627d881a + zds->stage = zdss_load; + /* pass-through */ + } -+ /* fall through */ ++ /* fallthrough */ + + case zdss_load: { + size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zds->dctx); @@ -3681,7 +3221,7 @@ index 0000000000..8e627d881a + /* pass-through */ + } + } -+ /* fall through */ ++ /* fallthrough */ + + case zdss_flush: { + size_t const toFlushSize = zds->outEnd - zds->outStart; @@ -3983,10 +3523,10 @@ index 0000000000..bcdb57982b +} diff --git a/xen/common/zstd/error_private.h b/xen/common/zstd/error_private.h new file mode 100644 -index 0000000000..ecbfe51dfb +index 0000000000..d07bf3cb9b --- /dev/null +++ b/xen/common/zstd/error_private.h -@@ -0,0 +1,53 @@ +@@ -0,0 +1,110 @@ +/** + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. + * All rights reserved. @@ -4012,7 +3552,38 @@ index 0000000000..ecbfe51dfb +* Dependencies +******************************************/ +#include /* size_t */ -+#include /* enum list */ ++ ++/** ++ * 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 @@ -4030,15 +3601,41 @@ index 0000000000..ecbfe51dfb +******************************************/ +#define ERROR(name) ((size_t)-PREFIX(name)) + -+ERR_STATIC unsigned ERR_isError(size_t code) { return (code > ERROR(maxCode)); } ++ERR_STATIC unsigned INIT ERR_isError(size_t code) { return (code > ERROR(maxCode)); } + -+ERR_STATIC ERR_enum ERR_getErrorCode(size_t code) ++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 @@ -4623,10 +4220,10 @@ index 0000000000..b86717c34d +#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..041a5a1f0a +index 0000000000..cc51206df6 --- /dev/null +++ b/xen/common/zstd/fse_decompress.c -@@ -0,0 +1,323 @@ +@@ -0,0 +1,324 @@ +/* + * FSE : Finite State Entropy decoder + * Copyright (C) 2013-2015, Yann Collet. @@ -4677,6 +4274,7 @@ index 0000000000..041a5a1f0a +#include "bitstream.h" +#include "fse.h" +#include "zstd_internal.h" ++#include +#include /* memcpy, memset */ + +/* ************************************************************** @@ -5170,10 +4768,10 @@ index 0000000000..a9d522c7bb +#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..f79603a12f +index 0000000000..341619e642 --- /dev/null +++ b/xen/common/zstd/huf_decompress.c -@@ -0,0 +1,958 @@ +@@ -0,0 +1,960 @@ +/* + * Huffman decoder, part of New Generation Entropy library + * Copyright (C) 2013-2016, Yann Collet. @@ -5224,6 +4822,7 @@ index 0000000000..f79603a12f +#include "bitstream.h" /* BIT_* */ +#include "fse.h" /* header compression */ +#include "huf.h" ++#include +#include /* memcpy, memset */ + +/* ************************************************************** @@ -5566,7 +5165,7 @@ index 0000000000..f79603a12f +/* 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) ++ const sortedSymbol_t *sortedSymbols, const U32 sortedListSize, U32 nbBitsBaseline, U16 baseSeq) +{ + HUF_DEltX4 DElt; + U32 rankVal[HUF_TABLELOG_MAX + 1]; @@ -5611,8 +5210,9 @@ index 0000000000..f79603a12f +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) ++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 */ @@ -6134,7 +5734,7 @@ index 0000000000..f79603a12f +} diff --git a/xen/common/zstd/mem.h b/xen/common/zstd/mem.h new file mode 100644 -index 0000000000..d2fa444687 +index 0000000000..2883200696 --- /dev/null +++ b/xen/common/zstd/mem.h @@ -0,0 +1,151 @@ @@ -6162,7 +5762,7 @@ index 0000000000..d2fa444687 +******************************************/ +#include /* memcpy */ +#include /* size_t, ptrdiff_t */ -+#include "private.h" ++#include + +/*-**************************************** +* Compiler specifics @@ -6289,120 +5889,9 @@ index 0000000000..d2fa444687 +} + +#endif /* MEM_H_MODULE */ -diff --git a/xen/common/zstd/private.h b/xen/common/zstd/private.h -new file mode 100644 -index 0000000000..fac4d3c095 ---- /dev/null -+++ b/xen/common/zstd/private.h -@@ -0,0 +1,105 @@ -+#ifndef ZSTD_PRIVATE_H -+#define ZSTD_PRIVATE_H -+ -+#include -+#include -+#include -+ -+typedef ssize_t __attribute__((__mode__(__pointer__))) ptrdiff_t; -+ -+/* from kernel include/linux/unaligned/access_ok.h */ -+ -+static always_inline u16 get_unaligned_le16(const void *p) -+{ -+ return le16_to_cpup((__le16 *)p); -+} -+ -+static always_inline u32 get_unaligned_le32(const void *p) -+{ -+ return le32_to_cpup((__le32 *)p); -+} -+ -+static always_inline u64 get_unaligned_le64(const void *p) -+{ -+ return le64_to_cpup((__le64 *)p); -+} -+ -+static always_inline u32 get_unaligned_be32(const void *p) -+{ -+ return be32_to_cpup((__be32 *)p); -+} -+ -+static always_inline u64 get_unaligned_be64(const void *p) -+{ -+ return be64_to_cpup((__be64 *)p); -+} -+ -+static always_inline void put_unaligned_le16(u16 val, void *p) -+{ -+ *((__le16 *)p) = cpu_to_le16(val); -+} -+ -+static always_inline void put_unaligned_le32(u32 val, void *p) -+{ -+ *((__le32 *)p) = cpu_to_le32(val); -+} -+ -+static always_inline void put_unaligned_le64(u64 val, void *p) -+{ -+ *((__le64 *)p) = cpu_to_le64(val); -+} -+ -+static always_inline void put_unaligned_be32(u32 val, void *p) -+{ -+ *((__be32 *)p) = cpu_to_be32(val); -+} -+ -+static always_inline void put_unaligned_be64(u64 val, void *p) -+{ -+ *((__be64 *)p) = cpu_to_be64(val); -+} -+ -+ -+/* from kernel include/asm-generic/unaligned.h with linux/unaligned/generic.h -+ assuming little endian */ -+ -+extern void __bad_unaligned_access_size(void); -+ -+#define get_unaligned(ptr) ((__force typeof(*(ptr)))({ \ -+ __builtin_choose_expr(sizeof(*(ptr)) == 1, *(ptr), \ -+ __builtin_choose_expr(sizeof(*(ptr)) == 2, get_unaligned_le16((ptr)), \ -+ __builtin_choose_expr(sizeof(*(ptr)) == 4, get_unaligned_le32((ptr)), \ -+ __builtin_choose_expr(sizeof(*(ptr)) == 8, get_unaligned_le64((ptr)), \ -+ __bad_unaligned_access_size())))); \ -+ })) -+ -+#define put_unaligned(val, ptr) ({ \ -+ void *__gu_p = (ptr); \ -+ switch (sizeof(*(ptr))) { \ -+ case 1: \ -+ *(u8 *)__gu_p = (__force u8)(val); \ -+ break; \ -+ case 2: \ -+ put_unaligned_le16((__force u16)(val), __gu_p); \ -+ break; \ -+ case 4: \ -+ put_unaligned_le32((__force u32)(val), __gu_p); \ -+ break; \ -+ case 8: \ -+ put_unaligned_le64((__force u64)(val), __gu_p); \ -+ break; \ -+ default: \ -+ __bad_unaligned_access_size(); \ -+ break; \ -+ } \ -+ (void)0; }) -+ -+ -+/* from kernel linux/kernel.h and uapi/linux/kernel.h */ -+ -+#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1) -+#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask)) -+#define ALIGN(x, a) __ALIGN_KERNEL((x), (a)) -+#define PTR_ALIGN(p, a) ((typeof(p))ALIGN((unsigned long)(p), (a))) -+ -+#endif /* ZSTD_PRIVATE_H */ diff --git a/xen/common/zstd/zstd_common.c b/xen/common/zstd/zstd_common.c new file mode 100644 -index 0000000000..1b13903538 +index 0000000000..a35c4a5f14 --- /dev/null +++ b/xen/common/zstd/zstd_common.c @@ -0,0 +1,74 @@ @@ -6455,14 +5944,14 @@ index 0000000000..1b13903538 + return stackMem; +} + -+void INIT *ZSTD_stackAllocAll(void *opaque, size_t *size) ++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) ++void *INIT ZSTD_stackAlloc(void *opaque, size_t size) +{ + ZSTD_stack *stack = (ZSTD_stack *)opaque; + return stack_push(stack, size); @@ -6473,7 +5962,7 @@ index 0000000000..1b13903538 + (void)address; +} + -+void INIT *ZSTD_malloc(size_t size, ZSTD_customMem customMem) { return customMem.customAlloc(customMem.opaque, size); } ++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) +{ @@ -6482,10 +5971,10 @@ index 0000000000..1b13903538 +} diff --git a/xen/common/zstd/zstd_internal.h b/xen/common/zstd/zstd_internal.h new file mode 100644 -index 0000000000..1b13840c44 +index 0000000000..7f8e5529eb --- /dev/null +++ b/xen/common/zstd/zstd_internal.h -@@ -0,0 +1,265 @@ +@@ -0,0 +1,372 @@ +/** + * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. + * All rights reserved. @@ -6509,19 +5998,82 @@ index 0000000000..1b13840c44 +* Compiler specifics +*********************************************************/ +#define FORCE_INLINE static always_inline -+#define FORCE_NOINLINE static noinline ++#define FORCE_NOINLINE static noinline INIT + +/*-************************************* +* Dependencies +***************************************/ +#include "error_private.h" +#include "mem.h" ++#include +#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; \ @@ -6542,15 +6094,59 @@ index 0000000000..1b13840c44 +/*-************************************* +* 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 @@ -6751,1026 +6347,40 @@ index 0000000000..1b13840c44 +size_t ZSTD_freeDStream(ZSTD_DStream *zds); + +#endif /* ZSTD_CCOMMON_H_MODULE */ -diff --git a/xen/common/zstd/zstd_opt.h b/xen/common/zstd/zstd_opt.h -new file mode 100644 -index 0000000000..55e1b4cba8 ---- /dev/null -+++ b/xen/common/zstd/zstd_opt.h -@@ -0,0 +1,1014 @@ -+/** -+ * Copyright (c) 2016-present, Przemyslaw Skibinski, 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 file is intended to be included within zstd_compress.c */ -+ -+#ifndef ZSTD_OPT_H_91842398743 -+#define ZSTD_OPT_H_91842398743 -+ -+#define ZSTD_LITFREQ_ADD 2 -+#define ZSTD_FREQ_DIV 4 -+#define ZSTD_MAX_PRICE (1 << 30) -+ -+/*-************************************* -+* Price functions for optimal parser -+***************************************/ -+FORCE_INLINE void ZSTD_setLog2Prices(seqStore_t *ssPtr) -+{ -+ ssPtr->log2matchLengthSum = ZSTD_highbit32(ssPtr->matchLengthSum + 1); -+ ssPtr->log2litLengthSum = ZSTD_highbit32(ssPtr->litLengthSum + 1); -+ ssPtr->log2litSum = ZSTD_highbit32(ssPtr->litSum + 1); -+ ssPtr->log2offCodeSum = ZSTD_highbit32(ssPtr->offCodeSum + 1); -+ ssPtr->factor = 1 + ((ssPtr->litSum >> 5) / ssPtr->litLengthSum) + ((ssPtr->litSum << 1) / (ssPtr->litSum + ssPtr->matchSum)); -+} -+ -+ZSTD_STATIC void ZSTD_rescaleFreqs(seqStore_t *ssPtr, const BYTE *src, size_t srcSize) -+{ -+ unsigned u; -+ -+ ssPtr->cachedLiterals = NULL; -+ ssPtr->cachedPrice = ssPtr->cachedLitLength = 0; -+ ssPtr->staticPrices = 0; -+ -+ if (ssPtr->litLengthSum == 0) { -+ if (srcSize <= 1024) -+ ssPtr->staticPrices = 1; -+ -+ for (u = 0; u <= MaxLit; u++) -+ ssPtr->litFreq[u] = 0; -+ for (u = 0; u < srcSize; u++) -+ ssPtr->litFreq[src[u]]++; -+ -+ ssPtr->litSum = 0; -+ ssPtr->litLengthSum = MaxLL + 1; -+ ssPtr->matchLengthSum = MaxML + 1; -+ ssPtr->offCodeSum = (MaxOff + 1); -+ ssPtr->matchSum = (ZSTD_LITFREQ_ADD << Litbits); -+ -+ for (u = 0; u <= MaxLit; u++) { -+ ssPtr->litFreq[u] = 1 + (ssPtr->litFreq[u] >> ZSTD_FREQ_DIV); -+ ssPtr->litSum += ssPtr->litFreq[u]; -+ } -+ for (u = 0; u <= MaxLL; u++) -+ ssPtr->litLengthFreq[u] = 1; -+ for (u = 0; u <= MaxML; u++) -+ ssPtr->matchLengthFreq[u] = 1; -+ for (u = 0; u <= MaxOff; u++) -+ ssPtr->offCodeFreq[u] = 1; -+ } else { -+ ssPtr->matchLengthSum = 0; -+ ssPtr->litLengthSum = 0; -+ ssPtr->offCodeSum = 0; -+ ssPtr->matchSum = 0; -+ ssPtr->litSum = 0; -+ -+ for (u = 0; u <= MaxLit; u++) { -+ ssPtr->litFreq[u] = 1 + (ssPtr->litFreq[u] >> (ZSTD_FREQ_DIV + 1)); -+ ssPtr->litSum += ssPtr->litFreq[u]; -+ } -+ for (u = 0; u <= MaxLL; u++) { -+ ssPtr->litLengthFreq[u] = 1 + (ssPtr->litLengthFreq[u] >> (ZSTD_FREQ_DIV + 1)); -+ ssPtr->litLengthSum += ssPtr->litLengthFreq[u]; -+ } -+ for (u = 0; u <= MaxML; u++) { -+ ssPtr->matchLengthFreq[u] = 1 + (ssPtr->matchLengthFreq[u] >> ZSTD_FREQ_DIV); -+ ssPtr->matchLengthSum += ssPtr->matchLengthFreq[u]; -+ ssPtr->matchSum += ssPtr->matchLengthFreq[u] * (u + 3); -+ } -+ ssPtr->matchSum *= ZSTD_LITFREQ_ADD; -+ for (u = 0; u <= MaxOff; u++) { -+ ssPtr->offCodeFreq[u] = 1 + (ssPtr->offCodeFreq[u] >> ZSTD_FREQ_DIV); -+ ssPtr->offCodeSum += ssPtr->offCodeFreq[u]; -+ } -+ } -+ -+ ZSTD_setLog2Prices(ssPtr); -+} -+ -+FORCE_INLINE U32 ZSTD_getLiteralPrice(seqStore_t *ssPtr, U32 litLength, const BYTE *literals) -+{ -+ U32 price, u; -+ -+ if (ssPtr->staticPrices) -+ return ZSTD_highbit32((U32)litLength + 1) + (litLength * 6); -+ -+ if (litLength == 0) -+ return ssPtr->log2litLengthSum - ZSTD_highbit32(ssPtr->litLengthFreq[0] + 1); -+ -+ /* literals */ -+ if (ssPtr->cachedLiterals == literals) { -+ U32 const additional = litLength - ssPtr->cachedLitLength; -+ const BYTE *literals2 = ssPtr->cachedLiterals + ssPtr->cachedLitLength; -+ price = ssPtr->cachedPrice + additional * ssPtr->log2litSum; -+ for (u = 0; u < additional; u++) -+ price -= ZSTD_highbit32(ssPtr->litFreq[literals2[u]] + 1); -+ ssPtr->cachedPrice = price; -+ ssPtr->cachedLitLength = litLength; -+ } else { -+ price = litLength * ssPtr->log2litSum; -+ for (u = 0; u < litLength; u++) -+ price -= ZSTD_highbit32(ssPtr->litFreq[literals[u]] + 1); -+ -+ if (litLength >= 12) { -+ ssPtr->cachedLiterals = literals; -+ ssPtr->cachedPrice = price; -+ ssPtr->cachedLitLength = litLength; -+ } -+ } -+ -+ /* literal Length */ -+ { -+ const BYTE LL_deltaCode = 19; -+ const BYTE llCode = (litLength > 63) ? (BYTE)ZSTD_highbit32(litLength) + LL_deltaCode : LL_Code[litLength]; -+ price += LL_bits[llCode] + ssPtr->log2litLengthSum - ZSTD_highbit32(ssPtr->litLengthFreq[llCode] + 1); -+ } -+ -+ return price; -+} -+ -+FORCE_INLINE U32 ZSTD_getPrice(seqStore_t *seqStorePtr, U32 litLength, const BYTE *literals, U32 offset, U32 matchLength, const int ultra) -+{ -+ /* offset */ -+ U32 price; -+ BYTE const offCode = (BYTE)ZSTD_highbit32(offset + 1); -+ -+ if (seqStorePtr->staticPrices) -+ return ZSTD_getLiteralPrice(seqStorePtr, litLength, literals) + ZSTD_highbit32((U32)matchLength + 1) + 16 + offCode; -+ -+ price = offCode + seqStorePtr->log2offCodeSum - ZSTD_highbit32(seqStorePtr->offCodeFreq[offCode] + 1); -+ if (!ultra && offCode >= 20) -+ price += (offCode - 19) * 2; -+ -+ /* match Length */ -+ { -+ const BYTE ML_deltaCode = 36; -+ const BYTE mlCode = (matchLength > 127) ? (BYTE)ZSTD_highbit32(matchLength) + ML_deltaCode : ML_Code[matchLength]; -+ price += ML_bits[mlCode] + seqStorePtr->log2matchLengthSum - ZSTD_highbit32(seqStorePtr->matchLengthFreq[mlCode] + 1); -+ } -+ -+ return price + ZSTD_getLiteralPrice(seqStorePtr, litLength, literals) + seqStorePtr->factor; -+} -+ -+ZSTD_STATIC void ZSTD_updatePrice(seqStore_t *seqStorePtr, U32 litLength, const BYTE *literals, U32 offset, U32 matchLength) -+{ -+ U32 u; -+ -+ /* literals */ -+ seqStorePtr->litSum += litLength * ZSTD_LITFREQ_ADD; -+ for (u = 0; u < litLength; u++) -+ seqStorePtr->litFreq[literals[u]] += ZSTD_LITFREQ_ADD; -+ -+ /* literal Length */ -+ { -+ const BYTE LL_deltaCode = 19; -+ const BYTE llCode = (litLength > 63) ? (BYTE)ZSTD_highbit32(litLength) + LL_deltaCode : LL_Code[litLength]; -+ seqStorePtr->litLengthFreq[llCode]++; -+ seqStorePtr->litLengthSum++; -+ } -+ -+ /* match offset */ -+ { -+ BYTE const offCode = (BYTE)ZSTD_highbit32(offset + 1); -+ seqStorePtr->offCodeSum++; -+ seqStorePtr->offCodeFreq[offCode]++; -+ } -+ -+ /* match Length */ -+ { -+ const BYTE ML_deltaCode = 36; -+ const BYTE mlCode = (matchLength > 127) ? (BYTE)ZSTD_highbit32(matchLength) + ML_deltaCode : ML_Code[matchLength]; -+ seqStorePtr->matchLengthFreq[mlCode]++; -+ seqStorePtr->matchLengthSum++; -+ } -+ -+ ZSTD_setLog2Prices(seqStorePtr); -+} -+ -+#define SET_PRICE(pos, mlen_, offset_, litlen_, price_) \ -+ { \ -+ while (last_pos < pos) { \ -+ opt[last_pos + 1].price = ZSTD_MAX_PRICE; \ -+ last_pos++; \ -+ } \ -+ opt[pos].mlen = mlen_; \ -+ opt[pos].off = offset_; \ -+ opt[pos].litlen = litlen_; \ -+ opt[pos].price = price_; \ -+ } -+ -+/* Update hashTable3 up to ip (excluded) -+ Assumption : always within prefix (i.e. not within extDict) */ -+FORCE_INLINE -+U32 ZSTD_insertAndFindFirstIndexHash3(ZSTD_CCtx *zc, const BYTE *ip) -+{ -+ U32 *const hashTable3 = zc->hashTable3; -+ U32 const hashLog3 = zc->hashLog3; -+ const BYTE *const base = zc->base; -+ U32 idx = zc->nextToUpdate3; -+ const U32 target = zc->nextToUpdate3 = (U32)(ip - base); -+ const size_t hash3 = ZSTD_hash3Ptr(ip, hashLog3); -+ -+ while (idx < target) { -+ hashTable3[ZSTD_hash3Ptr(base + idx, hashLog3)] = idx; -+ idx++; -+ } -+ -+ return hashTable3[hash3]; -+} -+ -+/*-************************************* -+* Binary Tree search -+***************************************/ -+static U32 ZSTD_insertBtAndGetAllMatches(ZSTD_CCtx *zc, const BYTE *const ip, const BYTE *const iLimit, U32 nbCompares, const U32 mls, U32 extDict, -+ ZSTD_match_t *matches, const U32 minMatchLen) -+{ -+ const BYTE *const base = zc->base; -+ const U32 curr = (U32)(ip - base); -+ const U32 hashLog = zc->params.cParams.hashLog; -+ const size_t h = ZSTD_hashPtr(ip, hashLog, mls); -+ U32 *const hashTable = zc->hashTable; -+ U32 matchIndex = hashTable[h]; -+ U32 *const bt = zc->chainTable; -+ const U32 btLog = zc->params.cParams.chainLog - 1; -+ const U32 btMask = (1U << btLog) - 1; -+ size_t commonLengthSmaller = 0, commonLengthLarger = 0; -+ const BYTE *const dictBase = zc->dictBase; -+ const U32 dictLimit = zc->dictLimit; -+ const BYTE *const dictEnd = dictBase + dictLimit; -+ const BYTE *const prefixStart = base + dictLimit; -+ const U32 btLow = btMask >= curr ? 0 : curr - btMask; -+ const U32 windowLow = zc->lowLimit; -+ U32 *smallerPtr = bt + 2 * (curr & btMask); -+ U32 *largerPtr = bt + 2 * (curr & btMask) + 1; -+ U32 matchEndIdx = curr + 8; -+ U32 dummy32; /* to be nullified at the end */ -+ U32 mnum = 0; -+ -+ const U32 minMatch = (mls == 3) ? 3 : 4; -+ size_t bestLength = minMatchLen - 1; -+ -+ if (minMatch == 3) { /* HC3 match finder */ -+ U32 const matchIndex3 = ZSTD_insertAndFindFirstIndexHash3(zc, ip); -+ if (matchIndex3 > windowLow && (curr - matchIndex3 < (1 << 18))) { -+ const BYTE *match; -+ size_t currMl = 0; -+ if ((!extDict) || matchIndex3 >= dictLimit) { -+ match = base + matchIndex3; -+ if (match[bestLength] == ip[bestLength]) -+ currMl = ZSTD_count(ip, match, iLimit); -+ } else { -+ match = dictBase + matchIndex3; -+ if (ZSTD_readMINMATCH(match, MINMATCH) == -+ ZSTD_readMINMATCH(ip, MINMATCH)) /* assumption : matchIndex3 <= dictLimit-4 (by table construction) */ -+ currMl = ZSTD_count_2segments(ip + MINMATCH, match + MINMATCH, iLimit, dictEnd, prefixStart) + MINMATCH; -+ } -+ -+ /* save best solution */ -+ if (currMl > bestLength) { -+ bestLength = currMl; -+ matches[mnum].off = ZSTD_REP_MOVE_OPT + curr - matchIndex3; -+ matches[mnum].len = (U32)currMl; -+ mnum++; -+ if (currMl > ZSTD_OPT_NUM) -+ goto update; -+ if (ip + currMl == iLimit) -+ goto update; /* best possible, and avoid read overflow*/ -+ } -+ } -+ } -+ -+ hashTable[h] = curr; /* Update Hash Table */ -+ -+ while (nbCompares-- && (matchIndex > windowLow)) { -+ U32 *nextPtr = bt + 2 * (matchIndex & btMask); -+ size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ -+ const BYTE *match; -+ -+ if ((!extDict) || (matchIndex + matchLength >= dictLimit)) { -+ match = base + matchIndex; -+ if (match[matchLength] == ip[matchLength]) { -+ matchLength += ZSTD_count(ip + matchLength + 1, match + matchLength + 1, iLimit) + 1; -+ } -+ } else { -+ match = dictBase + matchIndex; -+ matchLength += ZSTD_count_2segments(ip + matchLength, match + matchLength, iLimit, dictEnd, prefixStart); -+ if (matchIndex + matchLength >= dictLimit) -+ match = base + matchIndex; /* to prepare for next usage of match[matchLength] */ -+ } -+ -+ if (matchLength > bestLength) { -+ if (matchLength > matchEndIdx - matchIndex) -+ matchEndIdx = matchIndex + (U32)matchLength; -+ bestLength = matchLength; -+ matches[mnum].off = ZSTD_REP_MOVE_OPT + curr - matchIndex; -+ matches[mnum].len = (U32)matchLength; -+ mnum++; -+ if (matchLength > ZSTD_OPT_NUM) -+ break; -+ if (ip + matchLength == iLimit) /* equal : no way to know if inf or sup */ -+ break; /* drop, to guarantee consistency (miss a little bit of compression) */ -+ } -+ -+ if (match[matchLength] < ip[matchLength]) { -+ /* match is smaller than curr */ -+ *smallerPtr = matchIndex; /* update smaller idx */ -+ commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */ -+ if (matchIndex <= btLow) { -+ smallerPtr = &dummy32; -+ break; -+ } /* beyond tree size, stop the search */ -+ smallerPtr = nextPtr + 1; /* new "smaller" => larger of match */ -+ matchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to curr) */ -+ } else { -+ /* match is larger than curr */ -+ *largerPtr = matchIndex; -+ commonLengthLarger = matchLength; -+ if (matchIndex <= btLow) { -+ largerPtr = &dummy32; -+ break; -+ } /* beyond tree size, stop the search */ -+ largerPtr = nextPtr; -+ matchIndex = nextPtr[0]; -+ } -+ } -+ -+ *smallerPtr = *largerPtr = 0; -+ -+update: -+ zc->nextToUpdate = (matchEndIdx > curr + 8) ? matchEndIdx - 8 : curr + 1; -+ return mnum; -+} -+ -+/** Tree updater, providing best match */ -+static U32 ZSTD_BtGetAllMatches(ZSTD_CCtx *zc, const BYTE *const ip, const BYTE *const iLimit, const U32 maxNbAttempts, const U32 mls, ZSTD_match_t *matches, -+ const U32 minMatchLen) -+{ -+ if (ip < zc->base + zc->nextToUpdate) -+ return 0; /* skipped area */ -+ ZSTD_updateTree(zc, ip, iLimit, maxNbAttempts, mls); -+ return ZSTD_insertBtAndGetAllMatches(zc, ip, iLimit, maxNbAttempts, mls, 0, matches, minMatchLen); -+} -+ -+static U32 ZSTD_BtGetAllMatches_selectMLS(ZSTD_CCtx *zc, /* Index table will be updated */ -+ const BYTE *ip, const BYTE *const iHighLimit, const U32 maxNbAttempts, const U32 matchLengthSearch, -+ ZSTD_match_t *matches, const U32 minMatchLen) -+{ -+ switch (matchLengthSearch) { -+ case 3: return ZSTD_BtGetAllMatches(zc, ip, iHighLimit, maxNbAttempts, 3, matches, minMatchLen); -+ default: -+ case 4: return ZSTD_BtGetAllMatches(zc, ip, iHighLimit, maxNbAttempts, 4, matches, minMatchLen); -+ case 5: return ZSTD_BtGetAllMatches(zc, ip, iHighLimit, maxNbAttempts, 5, matches, minMatchLen); -+ case 7: -+ case 6: return ZSTD_BtGetAllMatches(zc, ip, iHighLimit, maxNbAttempts, 6, matches, minMatchLen); -+ } -+} -+ -+/** Tree updater, providing best match */ -+static U32 ZSTD_BtGetAllMatches_extDict(ZSTD_CCtx *zc, const BYTE *const ip, const BYTE *const iLimit, const U32 maxNbAttempts, const U32 mls, -+ ZSTD_match_t *matches, const U32 minMatchLen) -+{ -+ if (ip < zc->base + zc->nextToUpdate) -+ return 0; /* skipped area */ -+ ZSTD_updateTree_extDict(zc, ip, iLimit, maxNbAttempts, mls); -+ return ZSTD_insertBtAndGetAllMatches(zc, ip, iLimit, maxNbAttempts, mls, 1, matches, minMatchLen); -+} -+ -+static U32 ZSTD_BtGetAllMatches_selectMLS_extDict(ZSTD_CCtx *zc, /* Index table will be updated */ -+ const BYTE *ip, const BYTE *const iHighLimit, const U32 maxNbAttempts, const U32 matchLengthSearch, -+ ZSTD_match_t *matches, const U32 minMatchLen) -+{ -+ switch (matchLengthSearch) { -+ case 3: return ZSTD_BtGetAllMatches_extDict(zc, ip, iHighLimit, maxNbAttempts, 3, matches, minMatchLen); -+ default: -+ case 4: return ZSTD_BtGetAllMatches_extDict(zc, ip, iHighLimit, maxNbAttempts, 4, matches, minMatchLen); -+ case 5: return ZSTD_BtGetAllMatches_extDict(zc, ip, iHighLimit, maxNbAttempts, 5, matches, minMatchLen); -+ case 7: -+ case 6: return ZSTD_BtGetAllMatches_extDict(zc, ip, iHighLimit, maxNbAttempts, 6, matches, minMatchLen); -+ } -+} -+ -+/*-******************************* -+* Optimal parser -+*********************************/ -+FORCE_INLINE -+void ZSTD_compressBlock_opt_generic(ZSTD_CCtx *ctx, const void *src, size_t srcSize, const int ultra) -+{ -+ seqStore_t *seqStorePtr = &(ctx->seqStore); -+ const BYTE *const istart = (const BYTE *)src; -+ const BYTE *ip = istart; -+ const BYTE *anchor = istart; -+ const BYTE *const iend = istart + srcSize; -+ const BYTE *const ilimit = iend - 8; -+ const BYTE *const base = ctx->base; -+ const BYTE *const prefixStart = base + ctx->dictLimit; -+ -+ const U32 maxSearches = 1U << ctx->params.cParams.searchLog; -+ const U32 sufficient_len = ctx->params.cParams.targetLength; -+ const U32 mls = ctx->params.cParams.searchLength; -+ const U32 minMatch = (ctx->params.cParams.searchLength == 3) ? 3 : 4; -+ -+ ZSTD_optimal_t *opt = seqStorePtr->priceTable; -+ ZSTD_match_t *matches = seqStorePtr->matchTable; -+ const BYTE *inr; -+ U32 offset, rep[ZSTD_REP_NUM]; -+ -+ /* init */ -+ ctx->nextToUpdate3 = ctx->nextToUpdate; -+ ZSTD_rescaleFreqs(seqStorePtr, (const BYTE *)src, srcSize); -+ ip += (ip == prefixStart); -+ { -+ U32 i; -+ for (i = 0; i < ZSTD_REP_NUM; i++) -+ rep[i] = ctx->rep[i]; -+ } -+ -+ /* Match Loop */ -+ while (ip < ilimit) { -+ U32 cur, match_num, last_pos, litlen, price; -+ U32 u, mlen, best_mlen, best_off, litLength; -+ memset(opt, 0, sizeof(ZSTD_optimal_t)); -+ last_pos = 0; -+ litlen = (U32)(ip - anchor); -+ -+ /* check repCode */ -+ { -+ U32 i, last_i = ZSTD_REP_CHECK + (ip == anchor); -+ for (i = (ip == anchor); i < last_i; i++) { -+ const S32 repCur = (i == ZSTD_REP_MOVE_OPT) ? (rep[0] - 1) : rep[i]; -+ if ((repCur > 0) && (repCur < (S32)(ip - prefixStart)) && -+ (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(ip - repCur, minMatch))) { -+ mlen = (U32)ZSTD_count(ip + minMatch, ip + minMatch - repCur, iend) + minMatch; -+ if (mlen > sufficient_len || mlen >= ZSTD_OPT_NUM) { -+ best_mlen = mlen; -+ best_off = i; -+ cur = 0; -+ last_pos = 1; -+ goto _storeSequence; -+ } -+ best_off = i - (ip == anchor); -+ do { -+ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra); -+ if (mlen > last_pos || price < opt[mlen].price) -+ SET_PRICE(mlen, mlen, i, litlen, price); /* note : macro modifies last_pos */ -+ mlen--; -+ } while (mlen >= minMatch); -+ } -+ } -+ } -+ -+ match_num = ZSTD_BtGetAllMatches_selectMLS(ctx, ip, iend, maxSearches, mls, matches, minMatch); -+ -+ if (!last_pos && !match_num) { -+ ip++; -+ continue; -+ } -+ -+ if (match_num && (matches[match_num - 1].len > sufficient_len || matches[match_num - 1].len >= ZSTD_OPT_NUM)) { -+ best_mlen = matches[match_num - 1].len; -+ best_off = matches[match_num - 1].off; -+ cur = 0; -+ last_pos = 1; -+ goto _storeSequence; -+ } -+ -+ /* set prices using matches at position = 0 */ -+ best_mlen = (last_pos) ? last_pos : minMatch; -+ for (u = 0; u < match_num; u++) { -+ mlen = (u > 0) ? matches[u - 1].len + 1 : best_mlen; -+ best_mlen = matches[u].len; -+ while (mlen <= best_mlen) { -+ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, matches[u].off - 1, mlen - MINMATCH, ultra); -+ if (mlen > last_pos || price < opt[mlen].price) -+ SET_PRICE(mlen, mlen, matches[u].off, litlen, price); /* note : macro modifies last_pos */ -+ mlen++; -+ } -+ } -+ -+ if (last_pos < minMatch) { -+ ip++; -+ continue; -+ } -+ -+ /* initialize opt[0] */ -+ { -+ U32 i; -+ for (i = 0; i < ZSTD_REP_NUM; i++) -+ opt[0].rep[i] = rep[i]; -+ } -+ opt[0].mlen = 1; -+ opt[0].litlen = litlen; -+ -+ /* check further positions */ -+ for (cur = 1; cur <= last_pos; cur++) { -+ inr = ip + cur; -+ -+ if (opt[cur - 1].mlen == 1) { -+ litlen = opt[cur - 1].litlen + 1; -+ if (cur > litlen) { -+ price = opt[cur - litlen].price + ZSTD_getLiteralPrice(seqStorePtr, litlen, inr - litlen); -+ } else -+ price = ZSTD_getLiteralPrice(seqStorePtr, litlen, anchor); -+ } else { -+ litlen = 1; -+ price = opt[cur - 1].price + ZSTD_getLiteralPrice(seqStorePtr, litlen, inr - 1); -+ } -+ -+ if (cur > last_pos || price <= opt[cur].price) -+ SET_PRICE(cur, 1, 0, litlen, price); -+ -+ if (cur == last_pos) -+ break; -+ -+ if (inr > ilimit) /* last match must start at a minimum distance of 8 from oend */ -+ continue; -+ -+ mlen = opt[cur].mlen; -+ if (opt[cur].off > ZSTD_REP_MOVE_OPT) { -+ opt[cur].rep[2] = opt[cur - mlen].rep[1]; -+ opt[cur].rep[1] = opt[cur - mlen].rep[0]; -+ opt[cur].rep[0] = opt[cur].off - ZSTD_REP_MOVE_OPT; -+ } else { -+ opt[cur].rep[2] = (opt[cur].off > 1) ? opt[cur - mlen].rep[1] : opt[cur - mlen].rep[2]; -+ opt[cur].rep[1] = (opt[cur].off > 0) ? opt[cur - mlen].rep[0] : opt[cur - mlen].rep[1]; -+ opt[cur].rep[0] = -+ ((opt[cur].off == ZSTD_REP_MOVE_OPT) && (mlen != 1)) ? (opt[cur - mlen].rep[0] - 1) : (opt[cur - mlen].rep[opt[cur].off]); -+ } -+ -+ best_mlen = minMatch; -+ { -+ U32 i, last_i = ZSTD_REP_CHECK + (mlen != 1); -+ for (i = (opt[cur].mlen != 1); i < last_i; i++) { /* check rep */ -+ const S32 repCur = (i == ZSTD_REP_MOVE_OPT) ? (opt[cur].rep[0] - 1) : opt[cur].rep[i]; -+ if ((repCur > 0) && (repCur < (S32)(inr - prefixStart)) && -+ (ZSTD_readMINMATCH(inr, minMatch) == ZSTD_readMINMATCH(inr - repCur, minMatch))) { -+ mlen = (U32)ZSTD_count(inr + minMatch, inr + minMatch - repCur, iend) + minMatch; -+ -+ if (mlen > sufficient_len || cur + mlen >= ZSTD_OPT_NUM) { -+ best_mlen = mlen; -+ best_off = i; -+ last_pos = cur + 1; -+ goto _storeSequence; -+ } -+ -+ best_off = i - (opt[cur].mlen != 1); -+ if (mlen > best_mlen) -+ best_mlen = mlen; -+ -+ do { -+ if (opt[cur].mlen == 1) { -+ litlen = opt[cur].litlen; -+ if (cur > litlen) { -+ price = opt[cur - litlen].price + ZSTD_getPrice(seqStorePtr, litlen, inr - litlen, -+ best_off, mlen - MINMATCH, ultra); -+ } else -+ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra); -+ } else { -+ litlen = 0; -+ price = opt[cur].price + ZSTD_getPrice(seqStorePtr, 0, NULL, best_off, mlen - MINMATCH, ultra); -+ } -+ -+ if (cur + mlen > last_pos || price <= opt[cur + mlen].price) -+ SET_PRICE(cur + mlen, mlen, i, litlen, price); -+ mlen--; -+ } while (mlen >= minMatch); -+ } -+ } -+ } -+ -+ match_num = ZSTD_BtGetAllMatches_selectMLS(ctx, inr, iend, maxSearches, mls, matches, best_mlen); -+ -+ if (match_num > 0 && (matches[match_num - 1].len > sufficient_len || cur + matches[match_num - 1].len >= ZSTD_OPT_NUM)) { -+ best_mlen = matches[match_num - 1].len; -+ best_off = matches[match_num - 1].off; -+ last_pos = cur + 1; -+ goto _storeSequence; -+ } -+ -+ /* set prices using matches at position = cur */ -+ for (u = 0; u < match_num; u++) { -+ mlen = (u > 0) ? matches[u - 1].len + 1 : best_mlen; -+ best_mlen = matches[u].len; -+ -+ while (mlen <= best_mlen) { -+ if (opt[cur].mlen == 1) { -+ litlen = opt[cur].litlen; -+ if (cur > litlen) -+ price = opt[cur - litlen].price + ZSTD_getPrice(seqStorePtr, litlen, ip + cur - litlen, -+ matches[u].off - 1, mlen - MINMATCH, ultra); -+ else -+ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, matches[u].off - 1, mlen - MINMATCH, ultra); -+ } else { -+ litlen = 0; -+ price = opt[cur].price + ZSTD_getPrice(seqStorePtr, 0, NULL, matches[u].off - 1, mlen - MINMATCH, ultra); -+ } -+ -+ if (cur + mlen > last_pos || (price < opt[cur + mlen].price)) -+ SET_PRICE(cur + mlen, mlen, matches[u].off, litlen, price); -+ -+ mlen++; -+ } -+ } -+ } -+ -+ best_mlen = opt[last_pos].mlen; -+ best_off = opt[last_pos].off; -+ cur = last_pos - best_mlen; -+ -+ /* store sequence */ -+_storeSequence: /* cur, last_pos, best_mlen, best_off have to be set */ -+ opt[0].mlen = 1; -+ -+ while (1) { -+ mlen = opt[cur].mlen; -+ offset = opt[cur].off; -+ opt[cur].mlen = best_mlen; -+ opt[cur].off = best_off; -+ best_mlen = mlen; -+ best_off = offset; -+ if (mlen > cur) -+ break; -+ cur -= mlen; -+ } -+ -+ for (u = 0; u <= last_pos;) { -+ u += opt[u].mlen; -+ } -+ -+ for (cur = 0; cur < last_pos;) { -+ mlen = opt[cur].mlen; -+ if (mlen == 1) { -+ ip++; -+ cur++; -+ continue; -+ } -+ offset = opt[cur].off; -+ cur += mlen; -+ litLength = (U32)(ip - anchor); -+ -+ if (offset > ZSTD_REP_MOVE_OPT) { -+ rep[2] = rep[1]; -+ rep[1] = rep[0]; -+ rep[0] = offset - ZSTD_REP_MOVE_OPT; -+ offset--; -+ } else { -+ if (offset != 0) { -+ best_off = (offset == ZSTD_REP_MOVE_OPT) ? (rep[0] - 1) : (rep[offset]); -+ if (offset != 1) -+ rep[2] = rep[1]; -+ rep[1] = rep[0]; -+ rep[0] = best_off; -+ } -+ if (litLength == 0) -+ offset--; -+ } -+ -+ ZSTD_updatePrice(seqStorePtr, litLength, anchor, offset, mlen - MINMATCH); -+ ZSTD_storeSeq(seqStorePtr, litLength, anchor, offset, mlen - MINMATCH); -+ anchor = ip = ip + mlen; -+ } -+ } /* for (cur=0; cur < last_pos; ) */ -+ -+ /* Save reps for next block */ -+ { -+ int i; -+ for (i = 0; i < ZSTD_REP_NUM; i++) -+ ctx->repToConfirm[i] = rep[i]; -+ } -+ -+ /* Last Literals */ -+ { -+ size_t const lastLLSize = iend - anchor; -+ memcpy(seqStorePtr->lit, anchor, lastLLSize); -+ seqStorePtr->lit += lastLLSize; -+ } -+} -+ -+FORCE_INLINE -+void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx *ctx, const void *src, size_t srcSize, const int ultra) -+{ -+ seqStore_t *seqStorePtr = &(ctx->seqStore); -+ const BYTE *const istart = (const BYTE *)src; -+ const BYTE *ip = istart; -+ const BYTE *anchor = istart; -+ const BYTE *const iend = istart + srcSize; -+ const BYTE *const ilimit = iend - 8; -+ const BYTE *const base = ctx->base; -+ const U32 lowestIndex = ctx->lowLimit; -+ const U32 dictLimit = ctx->dictLimit; -+ const BYTE *const prefixStart = base + dictLimit; -+ const BYTE *const dictBase = ctx->dictBase; -+ const BYTE *const dictEnd = dictBase + dictLimit; -+ -+ const U32 maxSearches = 1U << ctx->params.cParams.searchLog; -+ const U32 sufficient_len = ctx->params.cParams.targetLength; -+ const U32 mls = ctx->params.cParams.searchLength; -+ const U32 minMatch = (ctx->params.cParams.searchLength == 3) ? 3 : 4; -+ -+ ZSTD_optimal_t *opt = seqStorePtr->priceTable; -+ ZSTD_match_t *matches = seqStorePtr->matchTable; -+ const BYTE *inr; -+ -+ /* init */ -+ U32 offset, rep[ZSTD_REP_NUM]; -+ { -+ U32 i; -+ for (i = 0; i < ZSTD_REP_NUM; i++) -+ rep[i] = ctx->rep[i]; -+ } -+ -+ ctx->nextToUpdate3 = ctx->nextToUpdate; -+ ZSTD_rescaleFreqs(seqStorePtr, (const BYTE *)src, srcSize); -+ ip += (ip == prefixStart); -+ -+ /* Match Loop */ -+ while (ip < ilimit) { -+ U32 cur, match_num, last_pos, litlen, price; -+ U32 u, mlen, best_mlen, best_off, litLength; -+ U32 curr = (U32)(ip - base); -+ memset(opt, 0, sizeof(ZSTD_optimal_t)); -+ last_pos = 0; -+ opt[0].litlen = (U32)(ip - anchor); -+ -+ /* check repCode */ -+ { -+ U32 i, last_i = ZSTD_REP_CHECK + (ip == anchor); -+ for (i = (ip == anchor); i < last_i; i++) { -+ const S32 repCur = (i == ZSTD_REP_MOVE_OPT) ? (rep[0] - 1) : rep[i]; -+ const U32 repIndex = (U32)(curr - repCur); -+ const BYTE *const repBase = repIndex < dictLimit ? dictBase : base; -+ const BYTE *const repMatch = repBase + repIndex; -+ if ((repCur > 0 && repCur <= (S32)curr) && -+ (((U32)((dictLimit - 1) - repIndex) >= 3) & (repIndex > lowestIndex)) /* intentional overflow */ -+ && (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch))) { -+ /* repcode detected we should take it */ -+ const BYTE *const repEnd = repIndex < dictLimit ? dictEnd : iend; -+ mlen = (U32)ZSTD_count_2segments(ip + minMatch, repMatch + minMatch, iend, repEnd, prefixStart) + minMatch; -+ -+ if (mlen > sufficient_len || mlen >= ZSTD_OPT_NUM) { -+ best_mlen = mlen; -+ best_off = i; -+ cur = 0; -+ last_pos = 1; -+ goto _storeSequence; -+ } -+ -+ best_off = i - (ip == anchor); -+ litlen = opt[0].litlen; -+ do { -+ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra); -+ if (mlen > last_pos || price < opt[mlen].price) -+ SET_PRICE(mlen, mlen, i, litlen, price); /* note : macro modifies last_pos */ -+ mlen--; -+ } while (mlen >= minMatch); -+ } -+ } -+ } -+ -+ match_num = ZSTD_BtGetAllMatches_selectMLS_extDict(ctx, ip, iend, maxSearches, mls, matches, minMatch); /* first search (depth 0) */ -+ -+ if (!last_pos && !match_num) { -+ ip++; -+ continue; -+ } -+ -+ { -+ U32 i; -+ for (i = 0; i < ZSTD_REP_NUM; i++) -+ opt[0].rep[i] = rep[i]; -+ } -+ opt[0].mlen = 1; -+ -+ if (match_num && (matches[match_num - 1].len > sufficient_len || matches[match_num - 1].len >= ZSTD_OPT_NUM)) { -+ best_mlen = matches[match_num - 1].len; -+ best_off = matches[match_num - 1].off; -+ cur = 0; -+ last_pos = 1; -+ goto _storeSequence; -+ } -+ -+ best_mlen = (last_pos) ? last_pos : minMatch; -+ -+ /* set prices using matches at position = 0 */ -+ for (u = 0; u < match_num; u++) { -+ mlen = (u > 0) ? matches[u - 1].len + 1 : best_mlen; -+ best_mlen = matches[u].len; -+ litlen = opt[0].litlen; -+ while (mlen <= best_mlen) { -+ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, matches[u].off - 1, mlen - MINMATCH, ultra); -+ if (mlen > last_pos || price < opt[mlen].price) -+ SET_PRICE(mlen, mlen, matches[u].off, litlen, price); -+ mlen++; -+ } -+ } -+ -+ if (last_pos < minMatch) { -+ ip++; -+ continue; -+ } -+ -+ /* check further positions */ -+ for (cur = 1; cur <= last_pos; cur++) { -+ inr = ip + cur; -+ -+ if (opt[cur - 1].mlen == 1) { -+ litlen = opt[cur - 1].litlen + 1; -+ if (cur > litlen) { -+ price = opt[cur - litlen].price + ZSTD_getLiteralPrice(seqStorePtr, litlen, inr - litlen); -+ } else -+ price = ZSTD_getLiteralPrice(seqStorePtr, litlen, anchor); -+ } else { -+ litlen = 1; -+ price = opt[cur - 1].price + ZSTD_getLiteralPrice(seqStorePtr, litlen, inr - 1); -+ } -+ -+ if (cur > last_pos || price <= opt[cur].price) -+ SET_PRICE(cur, 1, 0, litlen, price); -+ -+ if (cur == last_pos) -+ break; -+ -+ if (inr > ilimit) /* last match must start at a minimum distance of 8 from oend */ -+ continue; -+ -+ mlen = opt[cur].mlen; -+ if (opt[cur].off > ZSTD_REP_MOVE_OPT) { -+ opt[cur].rep[2] = opt[cur - mlen].rep[1]; -+ opt[cur].rep[1] = opt[cur - mlen].rep[0]; -+ opt[cur].rep[0] = opt[cur].off - ZSTD_REP_MOVE_OPT; -+ } else { -+ opt[cur].rep[2] = (opt[cur].off > 1) ? opt[cur - mlen].rep[1] : opt[cur - mlen].rep[2]; -+ opt[cur].rep[1] = (opt[cur].off > 0) ? opt[cur - mlen].rep[0] : opt[cur - mlen].rep[1]; -+ opt[cur].rep[0] = -+ ((opt[cur].off == ZSTD_REP_MOVE_OPT) && (mlen != 1)) ? (opt[cur - mlen].rep[0] - 1) : (opt[cur - mlen].rep[opt[cur].off]); -+ } -+ -+ best_mlen = minMatch; -+ { -+ U32 i, last_i = ZSTD_REP_CHECK + (mlen != 1); -+ for (i = (mlen != 1); i < last_i; i++) { -+ const S32 repCur = (i == ZSTD_REP_MOVE_OPT) ? (opt[cur].rep[0] - 1) : opt[cur].rep[i]; -+ const U32 repIndex = (U32)(curr + cur - repCur); -+ const BYTE *const repBase = repIndex < dictLimit ? dictBase : base; -+ const BYTE *const repMatch = repBase + repIndex; -+ if ((repCur > 0 && repCur <= (S32)(curr + cur)) && -+ (((U32)((dictLimit - 1) - repIndex) >= 3) & (repIndex > lowestIndex)) /* intentional overflow */ -+ && (ZSTD_readMINMATCH(inr, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch))) { -+ /* repcode detected */ -+ const BYTE *const repEnd = repIndex < dictLimit ? dictEnd : iend; -+ mlen = (U32)ZSTD_count_2segments(inr + minMatch, repMatch + minMatch, iend, repEnd, prefixStart) + minMatch; -+ -+ if (mlen > sufficient_len || cur + mlen >= ZSTD_OPT_NUM) { -+ best_mlen = mlen; -+ best_off = i; -+ last_pos = cur + 1; -+ goto _storeSequence; -+ } -+ -+ best_off = i - (opt[cur].mlen != 1); -+ if (mlen > best_mlen) -+ best_mlen = mlen; -+ -+ do { -+ if (opt[cur].mlen == 1) { -+ litlen = opt[cur].litlen; -+ if (cur > litlen) { -+ price = opt[cur - litlen].price + ZSTD_getPrice(seqStorePtr, litlen, inr - litlen, -+ best_off, mlen - MINMATCH, ultra); -+ } else -+ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra); -+ } else { -+ litlen = 0; -+ price = opt[cur].price + ZSTD_getPrice(seqStorePtr, 0, NULL, best_off, mlen - MINMATCH, ultra); -+ } -+ -+ if (cur + mlen > last_pos || price <= opt[cur + mlen].price) -+ SET_PRICE(cur + mlen, mlen, i, litlen, price); -+ mlen--; -+ } while (mlen >= minMatch); -+ } -+ } -+ } -+ -+ match_num = ZSTD_BtGetAllMatches_selectMLS_extDict(ctx, inr, iend, maxSearches, mls, matches, minMatch); -+ -+ if (match_num > 0 && (matches[match_num - 1].len > sufficient_len || cur + matches[match_num - 1].len >= ZSTD_OPT_NUM)) { -+ best_mlen = matches[match_num - 1].len; -+ best_off = matches[match_num - 1].off; -+ last_pos = cur + 1; -+ goto _storeSequence; -+ } -+ -+ /* set prices using matches at position = cur */ -+ for (u = 0; u < match_num; u++) { -+ mlen = (u > 0) ? matches[u - 1].len + 1 : best_mlen; -+ best_mlen = matches[u].len; -+ -+ while (mlen <= best_mlen) { -+ if (opt[cur].mlen == 1) { -+ litlen = opt[cur].litlen; -+ if (cur > litlen) -+ price = opt[cur - litlen].price + ZSTD_getPrice(seqStorePtr, litlen, ip + cur - litlen, -+ matches[u].off - 1, mlen - MINMATCH, ultra); -+ else -+ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, matches[u].off - 1, mlen - MINMATCH, ultra); -+ } else { -+ litlen = 0; -+ price = opt[cur].price + ZSTD_getPrice(seqStorePtr, 0, NULL, matches[u].off - 1, mlen - MINMATCH, ultra); -+ } -+ -+ if (cur + mlen > last_pos || (price < opt[cur + mlen].price)) -+ SET_PRICE(cur + mlen, mlen, matches[u].off, litlen, price); -+ -+ mlen++; -+ } -+ } -+ } /* for (cur = 1; cur <= last_pos; cur++) */ -+ -+ best_mlen = opt[last_pos].mlen; -+ best_off = opt[last_pos].off; -+ cur = last_pos - best_mlen; -+ -+ /* store sequence */ -+_storeSequence: /* cur, last_pos, best_mlen, best_off have to be set */ -+ opt[0].mlen = 1; -+ -+ while (1) { -+ mlen = opt[cur].mlen; -+ offset = opt[cur].off; -+ opt[cur].mlen = best_mlen; -+ opt[cur].off = best_off; -+ best_mlen = mlen; -+ best_off = offset; -+ if (mlen > cur) -+ break; -+ cur -= mlen; -+ } -+ -+ for (u = 0; u <= last_pos;) { -+ u += opt[u].mlen; -+ } -+ -+ for (cur = 0; cur < last_pos;) { -+ mlen = opt[cur].mlen; -+ if (mlen == 1) { -+ ip++; -+ cur++; -+ continue; -+ } -+ offset = opt[cur].off; -+ cur += mlen; -+ litLength = (U32)(ip - anchor); -+ -+ if (offset > ZSTD_REP_MOVE_OPT) { -+ rep[2] = rep[1]; -+ rep[1] = rep[0]; -+ rep[0] = offset - ZSTD_REP_MOVE_OPT; -+ offset--; -+ } else { -+ if (offset != 0) { -+ best_off = (offset == ZSTD_REP_MOVE_OPT) ? (rep[0] - 1) : (rep[offset]); -+ if (offset != 1) -+ rep[2] = rep[1]; -+ rep[1] = rep[0]; -+ rep[0] = best_off; -+ } -+ -+ if (litLength == 0) -+ offset--; -+ } -+ -+ ZSTD_updatePrice(seqStorePtr, litLength, anchor, offset, mlen - MINMATCH); -+ ZSTD_storeSeq(seqStorePtr, litLength, anchor, offset, mlen - MINMATCH); -+ anchor = ip = ip + mlen; -+ } -+ } /* for (cur=0; cur < last_pos; ) */ -+ -+ /* Save reps for next block */ -+ { -+ int i; -+ for (i = 0; i < ZSTD_REP_NUM; i++) -+ ctx->repToConfirm[i] = rep[i]; -+ } -+ -+ /* Last Literals */ -+ { -+ size_t lastLLSize = iend - anchor; -+ memcpy(seqStorePtr->lit, anchor, lastLLSize); -+ seqStorePtr->lit += lastLLSize; -+ } -+} -+ -+#endif /* ZSTD_OPT_H_91842398743 */ +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 @@ -7784,1431 +6394,6 @@ index b2955faa4b..f5bc17f2b6 100644 int decompress(void *inbuf, unsigned int len, void *outbuf); -diff --git a/xen/include/xen/xxhash.h b/xen/include/xen/xxhash.h -new file mode 100644 -index 0000000000..13ddc616d1 ---- /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 XXHASH_H -+#define XXHASH_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 /* XXHASH_H */ -diff --git a/xen/include/xen/zstd.h b/xen/include/xen/zstd.h -new file mode 100644 -index 0000000000..eb33582a18 ---- /dev/null -+++ b/xen/include/xen/zstd.h -@@ -0,0 +1,1157 @@ -+/* -+ * 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_H -+#define ZSTD_H -+ -+/* ====== Dependency ======*/ -+#include /* size_t */ -+ -+ -+/*-***************************************************************************** -+ * Introduction -+ * -+ * zstd, short for Zstandard, is a fast lossless compression algorithm, -+ * targeting real-time compression scenarios at zlib-level and better -+ * compression ratios. The zstd compression library provides in-memory -+ * compression and decompression functions. The library supports compression -+ * levels from 1 up to ZSTD_maxCLevel() which is 22. Levels >= 20, labeled -+ * ultra, should be used with caution, as they require more memory. -+ * Compression can be done in: -+ * - a single step, reusing a context (described as Explicit memory management) -+ * - unbounded multiple steps (described as Streaming compression) -+ * The compression ratio achievable on small data can be highly improved using -+ * compression with a dictionary in: -+ * - a single step (described as Simple dictionary API) -+ * - a single step, reusing a dictionary (described as Fast dictionary API) -+ ******************************************************************************/ -+ -+/*====== Helper functions ======*/ -+ -+/** -+ * 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; -+ -+/** -+ * ZSTD_maxCLevel() - maximum compression level available -+ * -+ * Return: Maximum compression level available. -+ */ -+int ZSTD_maxCLevel(void); -+/** -+ * ZSTD_compressBound() - maximum compressed size in worst case scenario -+ * @srcSize: The size of the data to compress. -+ * -+ * Return: The maximum compressed size in the worst case scenario. -+ */ -+size_t ZSTD_compressBound(size_t srcSize); -+/** -+ * 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 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 ZSTD_getErrorCode( -+ size_t functionResult) -+{ -+ if (!ZSTD_isError(functionResult)) -+ return (ZSTD_ErrorCode)0; -+ return (ZSTD_ErrorCode)(0 - functionResult); -+} -+ -+/** -+ * enum ZSTD_strategy - zstd compression search strategy -+ * -+ * From faster to stronger. -+ */ -+typedef enum { -+ ZSTD_fast, -+ ZSTD_dfast, -+ ZSTD_greedy, -+ ZSTD_lazy, -+ ZSTD_lazy2, -+ ZSTD_btlazy2, -+ ZSTD_btopt, -+ ZSTD_btopt2 -+} ZSTD_strategy; -+ -+/** -+ * struct ZSTD_compressionParameters - zstd compression parameters -+ * @windowLog: Log of the largest match distance. Larger means more -+ * compression, and more memory needed during decompression. -+ * @chainLog: Fully searched segment. Larger means more compression, slower, -+ * and more memory (useless for fast). -+ * @hashLog: Dispatch table. Larger means more compression, -+ * slower, and more memory. -+ * @searchLog: Number of searches. Larger means more compression and slower. -+ * @searchLength: Match length searched. Larger means faster decompression, -+ * sometimes less compression. -+ * @targetLength: Acceptable match size for optimal parser (only). Larger means -+ * more compression, and slower. -+ * @strategy: The zstd compression strategy. -+ */ -+typedef struct { -+ unsigned int windowLog; -+ unsigned int chainLog; -+ unsigned int hashLog; -+ unsigned int searchLog; -+ unsigned int searchLength; -+ unsigned int targetLength; -+ ZSTD_strategy strategy; -+} ZSTD_compressionParameters; -+ -+/** -+ * struct ZSTD_frameParameters - zstd frame parameters -+ * @contentSizeFlag: Controls whether content size will be present in the frame -+ * header (when known). -+ * @checksumFlag: Controls whether a 32-bit checksum is generated at the end -+ * of the frame for error detection. -+ * @noDictIDFlag: Controls whether dictID will be saved into the frame header -+ * when using dictionary compression. -+ * -+ * The default value is all fields set to 0. -+ */ -+typedef struct { -+ unsigned int contentSizeFlag; -+ unsigned int checksumFlag; -+ unsigned int noDictIDFlag; -+} ZSTD_frameParameters; -+ -+/** -+ * struct ZSTD_parameters - zstd parameters -+ * @cParams: The compression parameters. -+ * @fParams: The frame parameters. -+ */ -+typedef struct { -+ ZSTD_compressionParameters cParams; -+ ZSTD_frameParameters fParams; -+} ZSTD_parameters; -+ -+/** -+ * ZSTD_getCParams() - returns ZSTD_compressionParameters for selected level -+ * @compressionLevel: The compression level from 1 to ZSTD_maxCLevel(). -+ * @estimatedSrcSize: The estimated source size to compress or 0 if unknown. -+ * @dictSize: The dictionary size or 0 if a dictionary isn't being used. -+ * -+ * Return: The selected ZSTD_compressionParameters. -+ */ -+ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, -+ unsigned long long estimatedSrcSize, size_t dictSize); -+ -+/** -+ * ZSTD_getParams() - returns ZSTD_parameters for selected level -+ * @compressionLevel: The compression level from 1 to ZSTD_maxCLevel(). -+ * @estimatedSrcSize: The estimated source size to compress or 0 if unknown. -+ * @dictSize: The dictionary size or 0 if a dictionary isn't being used. -+ * -+ * The same as ZSTD_getCParams() except also selects the default frame -+ * parameters (all zero). -+ * -+ * Return: The selected ZSTD_parameters. -+ */ -+ZSTD_parameters ZSTD_getParams(int compressionLevel, -+ unsigned long long estimatedSrcSize, size_t dictSize); -+ -+/*-************************************* -+ * Explicit memory management -+ **************************************/ -+ -+/** -+ * ZSTD_CCtxWorkspaceBound() - amount of memory needed to initialize a ZSTD_CCtx -+ * @cParams: The compression parameters to be used for compression. -+ * -+ * If multiple compression parameters might be used, the caller must call -+ * ZSTD_CCtxWorkspaceBound() for each set of parameters and use the maximum -+ * size. -+ * -+ * Return: A lower bound on the size of the workspace that is passed to -+ * ZSTD_initCCtx(). -+ */ -+size_t ZSTD_CCtxWorkspaceBound(ZSTD_compressionParameters cParams); -+ -+/** -+ * struct ZSTD_CCtx - the zstd compression context -+ * -+ * When compressing many times it is recommended to allocate a context just once -+ * and reuse it for each successive compression operation. -+ */ -+typedef struct ZSTD_CCtx_s ZSTD_CCtx; -+/** -+ * ZSTD_initCCtx() - initialize a zstd compression context -+ * @workspace: The workspace to emplace the context into. It must outlive -+ * the returned context. -+ * @workspaceSize: The size of workspace. Use ZSTD_CCtxWorkspaceBound() to -+ * determine how large the workspace must be. -+ * -+ * Return: A compression context emplaced into workspace. -+ */ -+ZSTD_CCtx *ZSTD_initCCtx(void *workspace, size_t workspaceSize); -+ -+/** -+ * ZSTD_compressCCtx() - compress src into dst -+ * @ctx: The context. Must have been initialized with a workspace at -+ * least as large as ZSTD_CCtxWorkspaceBound(params.cParams). -+ * @dst: The buffer to compress src into. -+ * @dstCapacity: The size of the destination buffer. May be any size, but -+ * ZSTD_compressBound(srcSize) is guaranteed to be large enough. -+ * @src: The data to compress. -+ * @srcSize: The size of the data to compress. -+ * @params: The parameters to use for compression. See ZSTD_getParams(). -+ * -+ * Return: The compressed size or an error, which can be checked using -+ * ZSTD_isError(). -+ */ -+size_t ZSTD_compressCCtx(ZSTD_CCtx *ctx, void *dst, size_t dstCapacity, -+ const void *src, size_t srcSize, ZSTD_parameters params); -+ -+/** -+ * ZSTD_DCtxWorkspaceBound() - amount of memory needed to initialize a ZSTD_DCtx -+ * -+ * Return: A lower bound on the size of the workspace that is passed to -+ * ZSTD_initDCtx(). -+ */ -+size_t ZSTD_DCtxWorkspaceBound(void); -+ -+/** -+ * struct ZSTD_DCtx - the zstd decompression context -+ * -+ * When decompressing many times it is recommended to allocate a context just -+ * once and reuse it for each successive decompression operation. -+ */ -+typedef struct ZSTD_DCtx_s ZSTD_DCtx; -+/** -+ * ZSTD_initDCtx() - initialize a zstd decompression context -+ * @workspace: The workspace to emplace the context into. It must outlive -+ * the returned context. -+ * @workspaceSize: The size of workspace. Use ZSTD_DCtxWorkspaceBound() to -+ * determine how large the workspace must be. -+ * -+ * Return: A decompression context emplaced into workspace. -+ */ -+ZSTD_DCtx *ZSTD_initDCtx(void *workspace, size_t workspaceSize); -+ -+/** -+ * ZSTD_decompressDCtx() - decompress zstd compressed src into dst -+ * @ctx: The decompression context. -+ * @dst: The buffer to decompress src into. -+ * @dstCapacity: The size of the destination buffer. Must be at least as large -+ * as the decompressed size. If the caller cannot upper bound the -+ * decompressed size, then it's better to use the streaming API. -+ * @src: The zstd compressed data to decompress. Multiple concatenated -+ * frames and skippable frames are allowed. -+ * @srcSize: The exact size of the data to decompress. -+ * -+ * Return: The decompressed size or an error, which can be checked using -+ * ZSTD_isError(). -+ */ -+size_t ZSTD_decompressDCtx(ZSTD_DCtx *ctx, void *dst, size_t dstCapacity, -+ const void *src, size_t srcSize); -+ -+/*-************************ -+ * Simple dictionary API -+ **************************/ -+ -+/** -+ * ZSTD_compress_usingDict() - compress src into dst using a dictionary -+ * @ctx: The context. Must have been initialized with a workspace at -+ * least as large as ZSTD_CCtxWorkspaceBound(params.cParams). -+ * @dst: The buffer to compress src into. -+ * @dstCapacity: The size of the destination buffer. May be any size, but -+ * ZSTD_compressBound(srcSize) is guaranteed to be large enough. -+ * @src: The data to compress. -+ * @srcSize: The size of the data to compress. -+ * @dict: The dictionary to use for compression. -+ * @dictSize: The size of the dictionary. -+ * @params: The parameters to use for compression. See ZSTD_getParams(). -+ * -+ * Compression using a predefined dictionary. The same dictionary must be used -+ * during decompression. -+ * -+ * Return: The compressed size or an error, which can be checked using -+ * ZSTD_isError(). -+ */ -+size_t ZSTD_compress_usingDict(ZSTD_CCtx *ctx, void *dst, size_t dstCapacity, -+ const void *src, size_t srcSize, const void *dict, size_t dictSize, -+ ZSTD_parameters params); -+ -+/** -+ * ZSTD_decompress_usingDict() - decompress src into dst using a dictionary -+ * @ctx: The decompression context. -+ * @dst: The buffer to decompress src into. -+ * @dstCapacity: The size of the destination buffer. Must be at least as large -+ * as the decompressed size. If the caller cannot upper bound the -+ * decompressed size, then it's better to use the streaming API. -+ * @src: The zstd compressed data to decompress. Multiple concatenated -+ * frames and skippable frames are allowed. -+ * @srcSize: The exact size of the data to decompress. -+ * @dict: The dictionary to use for decompression. The same dictionary -+ * must've been used to compress the data. -+ * @dictSize: The size of the dictionary. -+ * -+ * Return: The decompressed size or an error, which can be checked using -+ * ZSTD_isError(). -+ */ -+size_t ZSTD_decompress_usingDict(ZSTD_DCtx *ctx, void *dst, size_t dstCapacity, -+ const void *src, size_t srcSize, const void *dict, size_t dictSize); -+ -+/*-************************** -+ * Fast dictionary API -+ ***************************/ -+ -+/** -+ * ZSTD_CDictWorkspaceBound() - memory needed to initialize a ZSTD_CDict -+ * @cParams: The compression parameters to be used for compression. -+ * -+ * Return: A lower bound on the size of the workspace that is passed to -+ * ZSTD_initCDict(). -+ */ -+size_t ZSTD_CDictWorkspaceBound(ZSTD_compressionParameters cParams); -+ -+/** -+ * struct ZSTD_CDict - a digested dictionary to be used for compression -+ */ -+typedef struct ZSTD_CDict_s ZSTD_CDict; -+ -+/** -+ * ZSTD_initCDict() - initialize a digested dictionary for compression -+ * @dictBuffer: The dictionary to digest. The buffer is referenced by the -+ * ZSTD_CDict so it must outlive the returned ZSTD_CDict. -+ * @dictSize: The size of the dictionary. -+ * @params: The parameters to use for compression. See ZSTD_getParams(). -+ * @workspace: The workspace. It must outlive the returned ZSTD_CDict. -+ * @workspaceSize: The workspace size. Must be at least -+ * ZSTD_CDictWorkspaceBound(params.cParams). -+ * -+ * When compressing multiple messages / blocks with the same dictionary it is -+ * recommended to load it just once. The ZSTD_CDict merely references the -+ * dictBuffer, so it must outlive the returned ZSTD_CDict. -+ * -+ * Return: The digested dictionary emplaced into workspace. -+ */ -+ZSTD_CDict *ZSTD_initCDict(const void *dictBuffer, size_t dictSize, -+ ZSTD_parameters params, void *workspace, size_t workspaceSize); -+ -+/** -+ * ZSTD_compress_usingCDict() - compress src into dst using a ZSTD_CDict -+ * @ctx: The context. Must have been initialized with a workspace at -+ * least as large as ZSTD_CCtxWorkspaceBound(cParams) where -+ * cParams are the compression parameters used to initialize the -+ * cdict. -+ * @dst: The buffer to compress src into. -+ * @dstCapacity: The size of the destination buffer. May be any size, but -+ * ZSTD_compressBound(srcSize) is guaranteed to be large enough. -+ * @src: The data to compress. -+ * @srcSize: The size of the data to compress. -+ * @cdict: The digested dictionary to use for compression. -+ * @params: The parameters to use for compression. See ZSTD_getParams(). -+ * -+ * Compression using a digested dictionary. The same dictionary must be used -+ * during decompression. -+ * -+ * Return: The compressed size or an error, which can be checked using -+ * ZSTD_isError(). -+ */ -+size_t ZSTD_compress_usingCDict(ZSTD_CCtx *cctx, void *dst, size_t dstCapacity, -+ const void *src, size_t srcSize, const ZSTD_CDict *cdict); -+ -+ -+/** -+ * ZSTD_DDictWorkspaceBound() - memory needed to initialize a ZSTD_DDict -+ * -+ * Return: A lower bound on the size of the workspace that is passed to -+ * ZSTD_initDDict(). -+ */ -+size_t ZSTD_DDictWorkspaceBound(void); -+ -+/** -+ * struct ZSTD_DDict - a digested dictionary to be used for decompression -+ */ -+typedef struct ZSTD_DDict_s ZSTD_DDict; -+ -+/** -+ * ZSTD_initDDict() - initialize a digested dictionary for decompression -+ * @dictBuffer: The dictionary to digest. The buffer is referenced by the -+ * ZSTD_DDict so it must outlive the returned ZSTD_DDict. -+ * @dictSize: The size of the dictionary. -+ * @workspace: The workspace. It must outlive the returned ZSTD_DDict. -+ * @workspaceSize: The workspace size. Must be at least -+ * ZSTD_DDictWorkspaceBound(). -+ * -+ * When decompressing multiple messages / blocks with the same dictionary it is -+ * recommended to load it just once. The ZSTD_DDict merely references the -+ * dictBuffer, so it must outlive the returned ZSTD_DDict. -+ * -+ * Return: The digested dictionary emplaced into workspace. -+ */ -+ZSTD_DDict *ZSTD_initDDict(const void *dictBuffer, size_t dictSize, -+ void *workspace, size_t workspaceSize); -+ -+/** -+ * ZSTD_decompress_usingDDict() - decompress src into dst using a ZSTD_DDict -+ * @ctx: The decompression context. -+ * @dst: The buffer to decompress src into. -+ * @dstCapacity: The size of the destination buffer. Must be at least as large -+ * as the decompressed size. If the caller cannot upper bound the -+ * decompressed size, then it's better to use the streaming API. -+ * @src: The zstd compressed data to decompress. Multiple concatenated -+ * frames and skippable frames are allowed. -+ * @srcSize: The exact size of the data to decompress. -+ * @ddict: The digested dictionary to use for decompression. The same -+ * dictionary must've been used to compress the data. -+ * -+ * Return: The decompressed size or an error, which can be checked using -+ * ZSTD_isError(). -+ */ -+size_t ZSTD_decompress_usingDDict(ZSTD_DCtx *dctx, void *dst, -+ size_t dstCapacity, const void *src, size_t srcSize, -+ const ZSTD_DDict *ddict); -+ -+ -+/*-************************** -+ * Streaming -+ ***************************/ -+ -+/** -+ * 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; -+ -+ -+ -+/*-***************************************************************************** -+ * Streaming compression - HowTo -+ * -+ * A ZSTD_CStream object is required to track streaming operation. -+ * Use ZSTD_initCStream() to initialize a ZSTD_CStream object. -+ * ZSTD_CStream objects can be reused multiple times on consecutive compression -+ * operations. It is recommended to re-use ZSTD_CStream in situations where many -+ * streaming operations will be achieved consecutively. Use one separate -+ * ZSTD_CStream per thread for parallel execution. -+ * -+ * Use ZSTD_compressStream() repetitively to consume input stream. -+ * The function will automatically update both `pos` fields. -+ * Note that it may not consume the entire input, in which case `pos < size`, -+ * and it's up to the caller to present again remaining data. -+ * It returns a hint for the preferred number of bytes to use as an input for -+ * the next function call. -+ * -+ * At any moment, it's possible to flush whatever data remains within internal -+ * buffer, using ZSTD_flushStream(). `output->pos` will be updated. There might -+ * still be some content left within the internal buffer if `output->size` is -+ * too small. It returns the number of bytes left in the internal buffer and -+ * must be called until it returns 0. -+ * -+ * ZSTD_endStream() instructs to finish a frame. It will perform a flush and -+ * write frame epilogue. The epilogue is required for decoders to consider a -+ * frame completed. Similar to ZSTD_flushStream(), it may not be able to flush -+ * the full content if `output->size` is too small. In which case, call again -+ * ZSTD_endStream() to complete the flush. It returns the number of bytes left -+ * in the internal buffer and must be called until it returns 0. -+ ******************************************************************************/ -+ -+/** -+ * ZSTD_CStreamWorkspaceBound() - memory needed to initialize a ZSTD_CStream -+ * @cParams: The compression parameters to be used for compression. -+ * -+ * Return: A lower bound on the size of the workspace that is passed to -+ * ZSTD_initCStream() and ZSTD_initCStream_usingCDict(). -+ */ -+size_t ZSTD_CStreamWorkspaceBound(ZSTD_compressionParameters cParams); -+ -+/** -+ * struct ZSTD_CStream - the zstd streaming compression context -+ */ -+typedef struct ZSTD_CStream_s ZSTD_CStream; -+ -+/*===== ZSTD_CStream management functions =====*/ -+/** -+ * ZSTD_initCStream() - initialize a zstd streaming compression context -+ * @params: The zstd compression parameters. -+ * @pledgedSrcSize: If params.fParams.contentSizeFlag == 1 then the caller must -+ * pass the source size (zero means empty source). Otherwise, -+ * the caller may optionally pass the source size, or zero if -+ * unknown. -+ * @workspace: The workspace to emplace the context into. It must outlive -+ * the returned context. -+ * @workspaceSize: The size of workspace. -+ * Use ZSTD_CStreamWorkspaceBound(params.cParams) to determine -+ * how large the workspace must be. -+ * -+ * Return: The zstd streaming compression context. -+ */ -+ZSTD_CStream *ZSTD_initCStream(ZSTD_parameters params, -+ unsigned long long pledgedSrcSize, void *workspace, -+ size_t workspaceSize); -+ -+/** -+ * ZSTD_initCStream_usingCDict() - initialize a streaming compression context -+ * @cdict: The digested dictionary to use for compression. -+ * @pledgedSrcSize: Optionally the source size, or zero if unknown. -+ * @workspace: The workspace to emplace the context into. It must outlive -+ * the returned context. -+ * @workspaceSize: The size of workspace. Call ZSTD_CStreamWorkspaceBound() -+ * with the cParams used to initialize the cdict to determine -+ * how large the workspace must be. -+ * -+ * Return: The zstd streaming compression context. -+ */ -+ZSTD_CStream *ZSTD_initCStream_usingCDict(const ZSTD_CDict *cdict, -+ unsigned long long pledgedSrcSize, void *workspace, -+ size_t workspaceSize); -+ -+/*===== Streaming compression functions =====*/ -+/** -+ * ZSTD_resetCStream() - reset the context using parameters from creation -+ * @zcs: The zstd streaming compression context to reset. -+ * @pledgedSrcSize: Optionally the source size, or zero if unknown. -+ * -+ * Resets the context using the parameters from creation. Skips dictionary -+ * loading, since it can be reused. If `pledgedSrcSize` is non-zero the frame -+ * content size is always written into the frame header. -+ * -+ * Return: Zero or an error, which can be checked using ZSTD_isError(). -+ */ -+size_t ZSTD_resetCStream(ZSTD_CStream *zcs, unsigned long long pledgedSrcSize); -+/** -+ * ZSTD_compressStream() - streaming compress some of input into output -+ * @zcs: The zstd streaming compression context. -+ * @output: Destination buffer. `output->pos` is updated to indicate how much -+ * compressed data was written. -+ * @input: Source buffer. `input->pos` is updated to indicate how much data was -+ * read. Note that it may not consume the entire input, in which case -+ * `input->pos < input->size`, and it's up to the caller to present -+ * remaining data again. -+ * -+ * The `input` and `output` buffers may be any size. Guaranteed to make some -+ * forward progress if `input` and `output` are not empty. -+ * -+ * Return: A hint for the number of bytes to use as the input for the next -+ * function call or an error, which can be checked using -+ * ZSTD_isError(). -+ */ -+size_t ZSTD_compressStream(ZSTD_CStream *zcs, ZSTD_outBuffer *output, -+ ZSTD_inBuffer *input); -+/** -+ * ZSTD_flushStream() - flush internal buffers into output -+ * @zcs: The zstd streaming compression context. -+ * @output: Destination buffer. `output->pos` is updated to indicate how much -+ * compressed data was written. -+ * -+ * ZSTD_flushStream() must be called until it returns 0, meaning all the data -+ * has been flushed. Since ZSTD_flushStream() causes a block to be ended, -+ * calling it too often will degrade the compression ratio. -+ * -+ * Return: The number of bytes still present within internal buffers or an -+ * error, which can be checked using ZSTD_isError(). -+ */ -+size_t ZSTD_flushStream(ZSTD_CStream *zcs, ZSTD_outBuffer *output); -+/** -+ * ZSTD_endStream() - flush internal buffers into output and end the frame -+ * @zcs: The zstd streaming compression context. -+ * @output: Destination buffer. `output->pos` is updated to indicate how much -+ * compressed data was written. -+ * -+ * ZSTD_endStream() must be called until it returns 0, meaning all the data has -+ * been flushed and the frame epilogue has been written. -+ * -+ * Return: The number of bytes still present within internal buffers or an -+ * error, which can be checked using ZSTD_isError(). -+ */ -+size_t ZSTD_endStream(ZSTD_CStream *zcs, ZSTD_outBuffer *output); -+ -+/** -+ * ZSTD_CStreamInSize() - recommended size for the input buffer -+ * -+ * Return: The recommended size for the input buffer. -+ */ -+size_t ZSTD_CStreamInSize(void); -+/** -+ * ZSTD_CStreamOutSize() - recommended size for the output buffer -+ * -+ * When the output buffer is at least this large, it is guaranteed to be large -+ * enough to flush at least one complete compressed block. -+ * -+ * Return: The recommended size for the output buffer. -+ */ -+size_t ZSTD_CStreamOutSize(void); -+ -+ -+ -+/*-***************************************************************************** -+ * Streaming decompression - HowTo -+ * -+ * A ZSTD_DStream object is required to track streaming operations. -+ * Use ZSTD_initDStream() to initialize a ZSTD_DStream object. -+ * ZSTD_DStream objects can be re-used multiple times. -+ * -+ * Use ZSTD_decompressStream() repetitively to consume your input. -+ * The function will update both `pos` fields. -+ * If `input->pos < input->size`, some input has not been consumed. -+ * It's up to the caller to present again remaining data. -+ * If `output->pos < output->size`, decoder has flushed everything it could. -+ * Returns 0 iff a frame is completely decoded and fully flushed. -+ * Otherwise it returns a suggested next input size that will never load more -+ * than the current frame. -+ ******************************************************************************/ -+ -+/** -+ * ZSTD_DStreamWorkspaceBound() - memory needed to initialize a ZSTD_DStream -+ * @maxWindowSize: The maximum window size allowed for compressed frames. -+ * -+ * Return: A lower bound on the size of the workspace that is passed to -+ * ZSTD_initDStream() and ZSTD_initDStream_usingDDict(). -+ */ -+size_t ZSTD_DStreamWorkspaceBound(size_t maxWindowSize); -+ -+/** -+ * struct ZSTD_DStream - the zstd streaming decompression context -+ */ -+typedef struct ZSTD_DStream_s ZSTD_DStream; -+/*===== ZSTD_DStream management functions =====*/ -+/** -+ * ZSTD_initDStream() - initialize a zstd streaming decompression context -+ * @maxWindowSize: The maximum window size allowed for compressed frames. -+ * @workspace: The workspace to emplace the context into. It must outlive -+ * the returned context. -+ * @workspaceSize: The size of workspace. -+ * Use ZSTD_DStreamWorkspaceBound(maxWindowSize) to determine -+ * how large the workspace must be. -+ * -+ * Return: The zstd streaming decompression context. -+ */ -+ZSTD_DStream *ZSTD_initDStream(size_t maxWindowSize, void *workspace, -+ size_t workspaceSize); -+/** -+ * ZSTD_initDStream_usingDDict() - initialize streaming decompression context -+ * @maxWindowSize: The maximum window size allowed for compressed frames. -+ * @ddict: The digested dictionary to use for decompression. -+ * @workspace: The workspace to emplace the context into. It must outlive -+ * the returned context. -+ * @workspaceSize: The size of workspace. -+ * Use ZSTD_DStreamWorkspaceBound(maxWindowSize) to determine -+ * how large the workspace must be. -+ * -+ * Return: The zstd streaming decompression context. -+ */ -+ZSTD_DStream *ZSTD_initDStream_usingDDict(size_t maxWindowSize, -+ const ZSTD_DDict *ddict, void *workspace, size_t workspaceSize); -+ -+/*===== Streaming decompression functions =====*/ -+/** -+ * ZSTD_resetDStream() - reset the context using parameters from creation -+ * @zds: The zstd streaming decompression context to reset. -+ * -+ * Resets the context using the parameters from creation. Skips dictionary -+ * loading, since it can be reused. -+ * -+ * Return: Zero or an error, which can be checked using ZSTD_isError(). -+ */ -+size_t ZSTD_resetDStream(ZSTD_DStream *zds); -+/** -+ * ZSTD_decompressStream() - streaming decompress some of input into output -+ * @zds: The zstd streaming decompression context. -+ * @output: Destination buffer. `output.pos` is updated to indicate how much -+ * decompressed data was written. -+ * @input: Source buffer. `input.pos` is updated to indicate how much data was -+ * read. Note that it may not consume the entire input, in which case -+ * `input.pos < input.size`, and it's up to the caller to present -+ * remaining data again. -+ * -+ * The `input` and `output` buffers may be any size. Guaranteed to make some -+ * forward progress if `input` and `output` are not empty. -+ * ZSTD_decompressStream() will not consume the last byte of the frame until -+ * the entire frame is flushed. -+ * -+ * Return: Returns 0 iff a frame is completely decoded and fully flushed. -+ * Otherwise returns a hint for the number of bytes to use as the input -+ * for the next function call or an error, which can be checked using -+ * ZSTD_isError(). The size hint will never load more than the frame. -+ */ -+size_t ZSTD_decompressStream(ZSTD_DStream *zds, ZSTD_outBuffer *output, -+ ZSTD_inBuffer *input); -+ -+/** -+ * ZSTD_DStreamInSize() - recommended size for the input buffer -+ * -+ * Return: The recommended size for the input buffer. -+ */ -+size_t ZSTD_DStreamInSize(void); -+/** -+ * ZSTD_DStreamOutSize() - recommended size for the output buffer -+ * -+ * When the output buffer is at least this large, it is guaranteed to be large -+ * enough to flush at least one complete decompressed block. -+ * -+ * Return: The recommended size for the output buffer. -+ */ -+size_t ZSTD_DStreamOutSize(void); -+ -+ -+/* --- Constants ---*/ -+#define ZSTD_MAGICNUMBER 0xFD2FB528 /* >= v0.8.0 */ -+#define ZSTD_MAGIC_SKIPPABLE_START 0x184D2A50U -+ -+#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 -+ -+/* 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; -+ -+ -+/*-************************************* -+ * Compressed size functions -+ **************************************/ -+ -+/** -+ * ZSTD_findFrameCompressedSize() - returns the size of a compressed frame -+ * @src: Source buffer. It should point to the start of a zstd encoded frame -+ * or a skippable frame. -+ * @srcSize: The size of the source buffer. It must be at least as large as the -+ * size of the frame. -+ * -+ * Return: The compressed size of the frame pointed to by `src` or an error, -+ * which can be check with ZSTD_isError(). -+ * Suitable to pass to ZSTD_decompress() or similar functions. -+ */ -+size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize); -+ -+/*-************************************* -+ * Decompressed size functions -+ **************************************/ -+/** -+ * ZSTD_getFrameContentSize() - returns the content size in a zstd frame header -+ * @src: It should point to the start of a zstd encoded frame. -+ * @srcSize: The size of the source buffer. It must be at least as large as the -+ * frame header. `ZSTD_frameHeaderSize_max` is always large enough. -+ * -+ * Return: The frame content size stored in the frame header if known. -+ * `ZSTD_CONTENTSIZE_UNKNOWN` if the content size isn't stored in the -+ * frame header. `ZSTD_CONTENTSIZE_ERROR` on invalid input. -+ */ -+unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize); -+ -+/** -+ * ZSTD_findDecompressedSize() - returns decompressed size of a series of frames -+ * @src: It should point to the start of a series of zstd encoded and/or -+ * skippable frames. -+ * @srcSize: The exact size of the series of frames. -+ * -+ * If any zstd encoded frame in the series doesn't have the frame content size -+ * set, `ZSTD_CONTENTSIZE_UNKNOWN` is returned. But frame content size is always -+ * set when using ZSTD_compress(). The decompressed size can be very large. -+ * If the source is untrusted, the decompressed size could be wrong or -+ * intentionally modified. Always ensure the result fits within the -+ * application's authorized limits. ZSTD_findDecompressedSize() handles multiple -+ * frames, and so it must traverse the input to read each frame header. This is -+ * efficient as most of the data is skipped, however it does mean that all frame -+ * data must be present and valid. -+ * -+ * Return: Decompressed size of all the data contained in the frames if known. -+ * `ZSTD_CONTENTSIZE_UNKNOWN` if the decompressed size is unknown. -+ * `ZSTD_CONTENTSIZE_ERROR` if an error occurred. -+ */ -+unsigned long long ZSTD_findDecompressedSize(const void *src, size_t srcSize); -+ -+/*-************************************* -+ * Advanced compression functions -+ **************************************/ -+/** -+ * ZSTD_checkCParams() - ensure parameter values remain within authorized range -+ * @cParams: The zstd compression parameters. -+ * -+ * Return: Zero or an error, which can be checked using ZSTD_isError(). -+ */ -+size_t ZSTD_checkCParams(ZSTD_compressionParameters cParams); -+ -+/** -+ * ZSTD_adjustCParams() - optimize parameters for a given srcSize and dictSize -+ * @srcSize: Optionally the estimated source size, or zero if unknown. -+ * @dictSize: Optionally the estimated dictionary size, or zero if unknown. -+ * -+ * Return: The optimized parameters. -+ */ -+ZSTD_compressionParameters ZSTD_adjustCParams( -+ ZSTD_compressionParameters cParams, unsigned long long srcSize, -+ size_t dictSize); -+ -+/*--- Advanced decompression functions ---*/ -+ -+/** -+ * ZSTD_isFrame() - returns true iff the buffer starts with a valid frame -+ * @buffer: The source buffer to check. -+ * @size: The size of the source buffer, must be at least 4 bytes. -+ * -+ * Return: True iff the buffer starts with a zstd or skippable frame identifier. -+ */ -+unsigned int ZSTD_isFrame(const void *buffer, size_t size); -+ -+/** -+ * ZSTD_getDictID_fromDict() - returns the dictionary id stored in a dictionary -+ * @dict: The dictionary buffer. -+ * @dictSize: The size of the dictionary buffer. -+ * -+ * Return: The dictionary id stored within the dictionary or 0 if the -+ * dictionary is not a zstd dictionary. If it returns 0 the -+ * dictionary can still be loaded as a content-only dictionary. -+ */ -+unsigned int ZSTD_getDictID_fromDict(const void *dict, size_t dictSize); -+ -+/** -+ * ZSTD_getDictID_fromDDict() - returns the dictionary id stored in a ZSTD_DDict -+ * @ddict: The ddict to find the id of. -+ * -+ * Return: The dictionary id stored within `ddict` or 0 if the dictionary is not -+ * a zstd dictionary. If it returns 0 `ddict` will be loaded as a -+ * content-only dictionary. -+ */ -+unsigned int ZSTD_getDictID_fromDDict(const ZSTD_DDict *ddict); -+ -+/** -+ * ZSTD_getDictID_fromFrame() - returns the dictionary id stored in a zstd frame -+ * @src: Source buffer. It must be a zstd encoded frame. -+ * @srcSize: The size of the source buffer. It must be at least as large as the -+ * frame header. `ZSTD_frameHeaderSize_max` is always large enough. -+ * -+ * Return: The dictionary id required to decompress the frame stored within -+ * `src` or 0 if the dictionary id could not be decoded. It can return -+ * 0 if the frame does not require a dictionary, the dictionary id -+ * wasn't stored in the frame, `src` is not a zstd frame, or `srcSize` -+ * is too small. -+ */ -+unsigned int ZSTD_getDictID_fromFrame(const void *src, size_t srcSize); -+ -+/** -+ * 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; -+ -+/** -+ * ZSTD_getFrameParams() - extracts parameters from a zstd or skippable frame -+ * @fparamsPtr: On success the frame parameters are written here. -+ * @src: The source buffer. It must point to a zstd or skippable frame. -+ * @srcSize: The size of the source buffer. `ZSTD_frameHeaderSize_max` is -+ * always large enough to succeed. -+ * -+ * Return: 0 on success. If more data is required it returns how many bytes -+ * must be provided to make forward progress. Otherwise it returns -+ * an error, which can be checked using ZSTD_isError(). -+ */ -+size_t ZSTD_getFrameParams(ZSTD_frameParams *fparamsPtr, const void *src, -+ size_t srcSize); -+ -+/*-***************************************************************************** -+ * Buffer-less and synchronous inner streaming functions -+ * -+ * This is an advanced API, giving full control over buffer management, for -+ * users which need direct control over memory. -+ * But it's also a complex one, with many restrictions (documented below). -+ * Prefer using normal streaming API for an easier experience -+ ******************************************************************************/ -+ -+/*-***************************************************************************** -+ * Buffer-less streaming compression (synchronous mode) -+ * -+ * A ZSTD_CCtx object is required to track streaming operations. -+ * Use ZSTD_initCCtx() to initialize a context. -+ * ZSTD_CCtx object can be re-used multiple times within successive compression -+ * operations. -+ * -+ * Start by initializing a context. -+ * Use ZSTD_compressBegin(), or ZSTD_compressBegin_usingDict() for dictionary -+ * compression, -+ * or ZSTD_compressBegin_advanced(), for finer parameter control. -+ * It's also possible to duplicate a reference context which has already been -+ * initialized, using ZSTD_copyCCtx() -+ * -+ * Then, consume your input using ZSTD_compressContinue(). -+ * There are some important considerations to keep in mind when using this -+ * advanced function : -+ * - ZSTD_compressContinue() has no internal buffer. It uses externally provided -+ * buffer only. -+ * - Interface is synchronous : input is consumed entirely and produce 1+ -+ * (or more) compressed blocks. -+ * - Caller must ensure there is enough space in `dst` to store compressed data -+ * under worst case scenario. Worst case evaluation is provided by -+ * ZSTD_compressBound(). -+ * ZSTD_compressContinue() doesn't guarantee recover after a failed -+ * compression. -+ * - ZSTD_compressContinue() presumes prior input ***is still accessible and -+ * unmodified*** (up to maximum distance size, see WindowLog). -+ * It remembers all previous contiguous blocks, plus one separated memory -+ * segment (which can itself consists of multiple contiguous blocks) -+ * - ZSTD_compressContinue() detects that prior input has been overwritten when -+ * `src` buffer overlaps. In which case, it will "discard" the relevant memory -+ * section from its history. -+ * -+ * Finish a frame with ZSTD_compressEnd(), which will write the last block(s) -+ * and optional checksum. It's possible to use srcSize==0, in which case, it -+ * will write a final empty block to end the frame. Without last block mark, -+ * frames will be considered unfinished (corrupted) by decoders. -+ * -+ * `ZSTD_CCtx` object can be re-used (ZSTD_compressBegin()) to compress some new -+ * frame. -+ ******************************************************************************/ -+ -+/*===== Buffer-less streaming compression functions =====*/ -+size_t ZSTD_compressBegin(ZSTD_CCtx *cctx, int compressionLevel); -+size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx *cctx, const void *dict, -+ size_t dictSize, int compressionLevel); -+size_t ZSTD_compressBegin_advanced(ZSTD_CCtx *cctx, const void *dict, -+ size_t dictSize, ZSTD_parameters params, -+ unsigned long long pledgedSrcSize); -+size_t ZSTD_copyCCtx(ZSTD_CCtx *cctx, const ZSTD_CCtx *preparedCCtx, -+ unsigned long long pledgedSrcSize); -+size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx *cctx, const ZSTD_CDict *cdict, -+ unsigned long long pledgedSrcSize); -+size_t ZSTD_compressContinue(ZSTD_CCtx *cctx, void *dst, size_t dstCapacity, -+ const void *src, size_t srcSize); -+size_t ZSTD_compressEnd(ZSTD_CCtx *cctx, void *dst, size_t dstCapacity, -+ const void *src, size_t srcSize); -+ -+ -+ -+/*-***************************************************************************** -+ * Buffer-less streaming decompression (synchronous mode) -+ * -+ * A ZSTD_DCtx object is required to track streaming operations. -+ * Use ZSTD_initDCtx() to initialize a context. -+ * A ZSTD_DCtx object can be re-used multiple times. -+ * -+ * First typical operation is to retrieve frame parameters, using -+ * ZSTD_getFrameParams(). It fills a ZSTD_frameParams structure which provide -+ * important information to correctly decode the frame, such as the minimum -+ * rolling buffer size to allocate to decompress data (`windowSize`), and the -+ * dictionary ID used. -+ * Note: content size is optional, it may not be present. 0 means unknown. -+ * Note that these values could be wrong, either because of data malformation, -+ * or because an attacker is spoofing deliberate false information. As a -+ * consequence, check that values remain within valid application range, -+ * especially `windowSize`, before allocation. Each application can set its own -+ * limit, depending on local restrictions. For extended interoperability, it is -+ * recommended to support at least 8 MB. -+ * Frame parameters are extracted from the beginning of the compressed frame. -+ * Data fragment must be large enough to ensure successful decoding, typically -+ * `ZSTD_frameHeaderSize_max` bytes. -+ * Result: 0: successful decoding, the `ZSTD_frameParams` structure is filled. -+ * >0: `srcSize` is too small, provide at least this many bytes. -+ * errorCode, which can be tested using ZSTD_isError(). -+ * -+ * Start decompression, with ZSTD_decompressBegin() or -+ * ZSTD_decompressBegin_usingDict(). Alternatively, you can copy a prepared -+ * context, using ZSTD_copyDCtx(). -+ * -+ * Then use ZSTD_nextSrcSizeToDecompress() and ZSTD_decompressContinue() -+ * alternatively. -+ * ZSTD_nextSrcSizeToDecompress() tells how many bytes to provide as 'srcSize' -+ * to ZSTD_decompressContinue(). -+ * ZSTD_decompressContinue() requires this _exact_ amount of bytes, or it will -+ * fail. -+ * -+ * The result of ZSTD_decompressContinue() is the number of bytes regenerated -+ * within 'dst' (necessarily <= dstCapacity). It can be zero, which is not an -+ * error; it just means ZSTD_decompressContinue() has decoded some metadata -+ * item. It can also be an error code, which can be tested with ZSTD_isError(). -+ * -+ * ZSTD_decompressContinue() needs previous data blocks during decompression, up -+ * to `windowSize`. They should preferably be located contiguously, prior to -+ * current block. Alternatively, a round buffer of sufficient size is also -+ * possible. Sufficient size is determined by frame parameters. -+ * ZSTD_decompressContinue() is very sensitive to contiguity, if 2 blocks don't -+ * follow each other, make sure that either the compressor breaks contiguity at -+ * the same place, or that previous contiguous segment is large enough to -+ * properly handle maximum back-reference. -+ * -+ * A frame is fully decoded when ZSTD_nextSrcSizeToDecompress() returns zero. -+ * Context can then be reset to start a new decompression. -+ * -+ * Note: it's possible to know if next input to present is a header or a block, -+ * using ZSTD_nextInputType(). This information is not required to properly -+ * decode a frame. -+ * -+ * == Special case: skippable frames == -+ * -+ * Skippable frames allow integration of user-defined data into a flow of -+ * concatenated frames. Skippable frames will be ignored (skipped) by a -+ * decompressor. The format of skippable frames is as follows: -+ * a) Skippable frame ID - 4 Bytes, Little endian format, any value from -+ * 0x184D2A50 to 0x184D2A5F -+ * b) Frame Size - 4 Bytes, Little endian format, unsigned 32-bits -+ * c) Frame Content - any content (User Data) of length equal to Frame Size -+ * For skippable frames ZSTD_decompressContinue() always returns 0. -+ * For skippable frames ZSTD_getFrameParams() returns fparamsPtr->windowLog==0 -+ * what means that a frame is skippable. -+ * Note: If fparamsPtr->frameContentSize==0, it is ambiguous: the frame might -+ * actually be a zstd encoded frame with no content. For purposes of -+ * decompression, it is valid in both cases to skip the frame using -+ * ZSTD_findFrameCompressedSize() to find its size in bytes. -+ * It also returns frame size as fparamsPtr->frameContentSize. -+ ******************************************************************************/ -+ -+/*===== Buffer-less streaming decompression functions =====*/ -+size_t ZSTD_decompressBegin(ZSTD_DCtx *dctx); -+size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx *dctx, const void *dict, -+ size_t dictSize); -+void ZSTD_copyDCtx(ZSTD_DCtx *dctx, const ZSTD_DCtx *preparedDCtx); -+size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx *dctx); -+size_t ZSTD_decompressContinue(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, -+ const void *src, size_t srcSize); -+typedef enum { -+ ZSTDnit_frameHeader, -+ ZSTDnit_blockHeader, -+ ZSTDnit_block, -+ ZSTDnit_lastBlock, -+ ZSTDnit_checksum, -+ ZSTDnit_skippableFrame -+} ZSTD_nextInputType_e; -+ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx *dctx); -+ -+/*-***************************************************************************** -+ * Block functions -+ * -+ * Block functions produce and decode raw zstd blocks, without frame metadata. -+ * Frame metadata cost is typically ~18 bytes, which can be non-negligible for -+ * very small blocks (< 100 bytes). User will have to take in charge required -+ * information to regenerate data, such as compressed and content sizes. -+ * -+ * A few rules to respect: -+ * - Compressing and decompressing require a context structure -+ * + Use ZSTD_initCCtx() and ZSTD_initDCtx() -+ * - It is necessary to init context before starting -+ * + compression : ZSTD_compressBegin() -+ * + decompression : ZSTD_decompressBegin() -+ * + variants _usingDict() are also allowed -+ * + copyCCtx() and copyDCtx() work too -+ * - Block size is limited, it must be <= ZSTD_getBlockSizeMax() -+ * + If you need to compress more, cut data into multiple blocks -+ * + Consider using the regular ZSTD_compress() instead, as frame metadata -+ * costs become negligible when source size is large. -+ * - When a block is considered not compressible enough, ZSTD_compressBlock() -+ * result will be zero. In which case, nothing is produced into `dst`. -+ * + User must test for such outcome and deal directly with uncompressed data -+ * + ZSTD_decompressBlock() doesn't accept uncompressed data as input!!! -+ * + In case of multiple successive blocks, decoder must be informed of -+ * uncompressed block existence to follow proper history. Use -+ * ZSTD_insertBlock() in such a case. -+ ******************************************************************************/ -+ -+/* Define for static allocation */ -+#define ZSTD_BLOCKSIZE_ABSOLUTEMAX (128 * 1024) -+/*===== Raw zstd block functions =====*/ -+size_t ZSTD_getBlockSizeMax(ZSTD_CCtx *cctx); -+size_t ZSTD_compressBlock(ZSTD_CCtx *cctx, void *dst, size_t dstCapacity, -+ const void *src, size_t srcSize); -+size_t ZSTD_decompressBlock(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, -+ const void *src, size_t srcSize); -+size_t ZSTD_insertBlock(ZSTD_DCtx *dctx, const void *blockStart, -+ size_t blockSize); -+ -+#endif /* ZSTD_H */ +-- +2.20.1 + diff --git a/xen.git-d8099d94dfaa3573bd86ebfc457cbc8f70a3ecda.patch b/xen.git-d8099d94dfaa3573bd86ebfc457cbc8f70a3ecda.patch new file mode 100644 index 0000000..8437382 --- /dev/null +++ b/xen.git-d8099d94dfaa3573bd86ebfc457cbc8f70a3ecda.patch @@ -0,0 +1,105 @@ +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.spec b/xen.spec index dc23fb6..b4907ec 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: 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 @@ -112,8 +112,12 @@ Patch40: xen.drop.brctl.patch Patch41: xen.gcc9.fixes.patch Patch42: xen.gcc10.fixes.patch Patch43: xen.gcc11.fixes.patch -Patch44: zstd-dom0.patch -Patch45: xsa360-4.14.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 %if %build_qemutrad @@ -142,7 +146,7 @@ BuildRequires: libuuid-devel # iasl needed to build hvmloader BuildRequires: acpica-tools # modern compressed kernels -BuildRequires: bzip2-devel xz-devel +BuildRequires: bzip2-devel xz-devel libzstd-devel # libfsimage BuildRequires: e2fsprogs-devel # tools now require yajl and wget @@ -185,6 +189,12 @@ 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 @@ -318,6 +328,10 @@ manage Xen virtual machines. %patch43 -p1 %patch44 -p1 %patch45 -p1 +%patch46 -p1 +%patch47 -p1 +%patch48 -p1 +%patch49 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -910,6 +924,11 @@ fi %endif %changelog +* Mon Feb 01 2021 Michael Young - 4.14.1-4 +- backport upstream zstd dom0 and guest patches +- add libzstd-devel BuildRequires +- add weak dependency on grub modules to improve initial boot setup + * Wed Jan 27 2021 Fedora Release Engineering - 4.14.1-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild From 811d8e19d1d0d61a121cdbcccce469d9d6de091a Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 16 Feb 2021 22:17:42 +0000 Subject: [PATCH 066/194] Linux: display frontend "be-alloc" mode is unsupported (comment only) [XSA-363, CVE-2021-26934] arm: The cache may not be cleaned for newly allocated scrubbed pages [XSA-364, CVE-2021-26933] --- xen.spec | 12 ++++++++- xsa363.patch | 22 +++++++++++++++++ xsa364.patch | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 xsa363.patch create mode 100644 xsa364.patch diff --git a/xen.spec b/xen.spec index b4907ec..1f9f51a 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: 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 @@ -118,6 +118,8 @@ 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 %if %build_qemutrad @@ -332,6 +334,8 @@ manage Xen virtual machines. %patch47 -p1 %patch48 -p1 %patch49 -p1 +%patch50 -p1 +%patch51 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -924,6 +928,12 @@ fi %endif %changelog +* 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] +- arm: The cache may not be cleaned for newly allocated scrubbed pages + [XSA-364, CVE-2021-26933] + * Mon Feb 01 2021 Michael Young - 4.14.1-4 - backport upstream zstd dom0 and guest patches - add libzstd-devel BuildRequires diff --git a/xsa363.patch b/xsa363.patch new file mode 100644 index 0000000..c8a3de3 --- /dev/null +++ b/xsa363.patch @@ -0,0 +1,22 @@ +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 new file mode 100644 index 0000000..2d4b057 --- /dev/null +++ b/xsa364.patch @@ -0,0 +1,69 @@ +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 + From a8899f28f988dbab81d6e1972d9b4d3d005b302f Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 17 Feb 2021 21:08:18 +0000 Subject: [PATCH 067/194] Add bug references --- xen.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xen.spec b/xen.spec index 1f9f51a..349cb3a 100644 --- a/xen.spec +++ b/xen.spec @@ -930,9 +930,9 @@ fi %changelog * 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] + [XSA-363, CVE-2021-26934] (#1929549) - arm: The cache may not be cleaned for newly allocated scrubbed pages - [XSA-364, CVE-2021-26933] + [XSA-364, CVE-2021-26933] (#1929547) * Mon Feb 01 2021 Michael Young - 4.14.1-4 - backport upstream zstd dom0 and guest patches From 2ba51878a6fcf14df4fc78ef3f550f79df9750c6 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 2 Mar 2021 18:46:24 +0000 Subject: [PATCH 068/194] 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 520a08e99146ea6834d59b1dc46734313b67520d Mon Sep 17 00:00:00 2001 From: Michael Young Date: Fri, 19 Mar 2021 22:19:01 +0000 Subject: [PATCH 069/194] 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 0736a4dbf197a2ebfbee42fae66d0537fafaa271 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 5 May 2021 22:40:31 +0100 Subject: [PATCH 070/194] 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 071/194] 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 072/194] 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 073/194] 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 2f753f7e9bae2370d7d1f4861d7c31a67f6fcf44 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 10 Jun 2021 21:21:02 +0100 Subject: [PATCH 074/194] 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 075/194] - 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 076/194] 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 077/194] 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 078/194] 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 3b4e0888e191d8f3c8df4757da36f012338bdb44 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 8 Sep 2021 20:58:27 +0100 Subject: [PATCH 079/194] 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 84657107f472f5703c2699d1ce62b1a7192ffab0 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 9 Sep 2021 20:04:03 +0100 Subject: [PATCH 080/194] 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 7724e33e7340b2beac564efcd2629b3c7a1939f2 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Sun, 12 Sep 2021 16:19:18 +0100 Subject: [PATCH 081/194] 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 41796d118d4a85f1d66871d36ffbf798120e6e57 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 5 Oct 2021 22:11:15 +0100 Subject: [PATCH 082/194] 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 6e2824bf21b55d24ea81d221eacd44942138f70f Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 6 Oct 2021 21:29:26 +0100 Subject: [PATCH 083/194] 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 084/194] 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 8c923532c155a944ee4e9aea6196d281d847c6c4 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Mon, 10 Jan 2022 19:46:29 +0000 Subject: [PATCH 085/194] 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 086/194] - 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 087/194] 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 088/194] 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 089/194] 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 090/194] 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 091/194] 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 092/194] 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 093/194] 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 094/194] 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 095/194] 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 096/194] 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 097/194] 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 098/194] 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 099/194] 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 100/194] 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 101/194] 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 102/194] 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 103/194] 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 104/194] 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 105/194] 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 106/194] 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 107/194] 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 108/194] 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 109/194] 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 110/194] 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 111/194] 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 112/194] 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 113/194] 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 114/194] 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 115/194] 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 116/194] 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 117/194] 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 118/194] 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 119/194] 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 120/194] 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 121/194] 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 122/194] 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 123/194] 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 124/194] 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 125/194] 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 126/194] 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 127/194] 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 128/194] 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 129/194] 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 130/194] 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 131/194] 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 132/194] 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 133/194] 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 134/194] 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 135/194] 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 136/194] 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 137/194] 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 138/194] 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 139/194] 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 140/194] 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 141/194] 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 142/194] 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 143/194] 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 144/194] 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 145/194] 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 146/194] 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 147/194] 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 148/194] 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 149/194] 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 150/194] 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 151/194] 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 152/194] 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 153/194] 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 154/194] 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 155/194] 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 156/194] 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 157/194] 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 158/194] 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 159/194] 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 160/194] 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 161/194] 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 162/194] 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 163/194] 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 164/194] 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 165/194] 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 166/194] 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 167/194] 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 168/194] 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 169/194] 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 170/194] 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 171/194] 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 172/194] 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 173/194] 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 174/194] 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 175/194] 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 176/194] 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 177/194] 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 178/194] 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 179/194] 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 180/194] 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 181/194] 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 182/194] 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 183/194] 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 184/194] 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 185/194] 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 186/194] 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 187/194] 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 188/194] 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 189/194] 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 190/194] 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 191/194] 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 192/194] 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 193/194] 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 194/194] 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