From e41f4de6799bd7798f3058e6b2f294baf686eb88 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 14 May 2019 22:51:28 +0100 Subject: [PATCH 01/13] 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 780a4075dcb0c5a4335d9b1db759f9badfc39532 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Sat, 15 Jun 2019 19:13:39 +0100 Subject: [PATCH 02/13] 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 842cba0ff778261bfd137072ff10a10953650e66 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Mon, 1 Jul 2019 21:56:32 +0100 Subject: [PATCH 03/13] 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 04/13] 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 f522170871e162c6ff048e961bd241078f1f473d Mon Sep 17 00:00:00 2001 From: Michael Young Date: Fri, 1 Nov 2019 19:28:17 +0000 Subject: [PATCH 05/13] 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 5eeb52e7d5c2312b643af6b65c1b7c6db5d8d88a Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 12 Nov 2019 20:44:11 +0000 Subject: [PATCH 06/13] add CVE and bugzilla references --- xen.spec | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/xen.spec b/xen.spec index 3566ea4..704e20c 100644 --- a/xen.spec +++ b/xen.spec @@ -958,16 +958,17 @@ fi %changelog * Fri Nov 01 2019 Michael Young - 4.11.2-2 -- 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) + (#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) + [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) * Mon Jul 01 2019 Michael Young - 4.11.2-1 - update to 4.11.2 @@ -976,7 +977,8 @@ fi drop parts of xen.gcc9.fixes.patch * Sat Jun 15 2019 Michael Young - 4.11.1-6 -- Unlimited Arm Atomics Operations [XSA-295] (#1720760) +- Unlimited Arm Atomics Operations [XSA-295, CVE-2019-17349, + CVE-2019-17350] (#1720760) * Tue May 14 2019 Michael Young - 4.11.1-5 - Microarchitectural Data Sampling speculative side channel [XSA-297, @@ -986,15 +988,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 7ac942e717eadcdbefb7403f96d97b4f6ca1438a Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 12 Nov 2019 21:45:34 +0000 Subject: [PATCH 07/13] add missing XSA-299 patches and 2 more security patches 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 | 59 +++++++-- xsa304-4.11-1.patch | 71 +++++++++++ xsa304-4.11-2.patch | 298 ++++++++++++++++++++++++++++++++++++++++++++ xsa304-4.11-3.patch | 110 ++++++++++++++++ xsa305-4.11-1.patch | 288 ++++++++++++++++++++++++++++++++++++++++++ xsa305-4.11-2.patch | 192 ++++++++++++++++++++++++++++ 6 files changed, 1006 insertions(+), 12 deletions(-) create mode 100644 xsa304-4.11-1.patch create mode 100644 xsa304-4.11-2.patch create mode 100644 xsa304-4.11-3.patch create mode 100644 xsa305-4.11-1.patch create mode 100644 xsa305-4.11-2.patch diff --git a/xen.spec b/xen.spec index 704e20c..b48dc81 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: 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 @@ -129,15 +129,30 @@ 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 +Patch50: xsa299-4.11-0002-x86-mm-Don-t-re-set-PGT_pinned-on-a-partially-de-val.patch +Patch51: xsa299-4.11-0003-x86-mm-Separate-out-partial_pte-tristate-into-indivi.patch +Patch52: xsa299-4.11-0004-x86-mm-Use-flags-for-_put_page_type-rather-than-a-bo.patch +Patch53: xsa299-4.11-0005-x86-mm-Rework-get_page_and_type_from_mfn-conditional.patch +Patch54: xsa299-4.11-0006-x86-mm-Have-alloc_l-23-_table-clear-partial_flags-wh.patch +Patch55: xsa299-4.11-0007-x86-mm-Always-retain-a-general-ref-on-partial.patch +Patch56: xsa299-4.11-0008-x86-mm-Collapse-PTF_partial_set-and-PTF_partial_gene.patch +Patch57: xsa299-4.11-0009-x86-mm-Properly-handle-linear-pagetable-promotion-fa.patch +Patch58: xsa299-4.11-0010-x86-mm-Fix-nested-de-validation-on-error.patch +Patch59: xsa299-4.11-0011-x86-mm-Don-t-drop-a-type-ref-unless-you-held-a-ref-t.patch +Patch60: xsa301-4.11-1.patch +Patch61: xsa301-4.11-2.patch +Patch62: xsa301-4.11-3.patch +Patch63: xsa302-4.11-0001-IOMMU-add-missing-HVM-check.patch +Patch64: xsa302-4.11-0002-passthrough-quarantine-PCI-devices.patch +Patch65: xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch +Patch66: xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch +Patch67: xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch +Patch68: xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch +Patch69: xsa304-4.11-1.patch +Patch70: xsa304-4.11-2.patch +Patch71: xsa304-4.11-3.patch +Patch72: xsa305-4.11-1.patch +Patch73: xsa305-4.11-2.patch %if %build_qemutrad @@ -350,14 +365,29 @@ manage Xen virtual machines. %patch50 -p1 %patch51 -p1 %patch52 -p1 -%ifarch %{ix86} x86_64 %patch53 -p1 %patch54 -p1 -%endif %patch55 -p1 %patch56 -p1 %patch57 -p1 %patch58 -p1 +%patch59 -p1 +%patch60 -p1 +%patch61 -p1 +%patch62 -p1 +%ifarch %{ix86} x86_64 +%patch63 -p1 +%patch64 -p1 +%endif +%patch65 -p1 +%patch66 -p1 +%patch67 -p1 +%patch68 -p1 +%patch69 -p1 +%patch70 -p1 +%patch71 -p1 +%patch72 -p1 +%patch73 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -957,6 +987,11 @@ fi %endif %changelog +* Tue Nov 12 2019 Michael Young - 4.11.2-3 +- add missing XSA-299 patches +- 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] + * Fri Nov 01 2019 Michael Young - 4.11.2-2 - 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.11-1.patch b/xsa304-4.11-1.patch new file mode 100644 index 0000000..f78d152 --- /dev/null +++ b/xsa304-4.11-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 fb7edfaef9..d698b1d50a 100644 +--- a/xen/drivers/passthrough/vtd/extern.h ++++ b/xen/drivers/passthrough/vtd/extern.h +@@ -96,6 +96,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 f242e30caf..8712d3b4dc 100644 +--- a/xen/drivers/passthrough/vtd/iommu.c ++++ b/xen/drivers/passthrough/vtd/iommu.c +@@ -1211,6 +1211,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.11-2.patch b/xsa304-4.11-2.patch new file mode 100644 index 0000000..f22d7cc --- /dev/null +++ b/xsa304-4.11-2.patch @@ -0,0 +1,298 @@ +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.markdown b/docs/misc/xen-command-line.markdown +index c63a07d29b..684671cb7b 100644 +--- a/docs/misc/xen-command-line.markdown ++++ b/docs/misc/xen-command-line.markdown +@@ -828,7 +828,7 @@ effect the inverse meaning. + >> set as UC. + + ### ept (Intel) +-> `= List of ( {no-}pml | {no-}ad )` ++> `= List of [ {no-}pml, {no-}ad, {no-}exec-sp ]` + + Controls EPT related features. + +@@ -851,6 +851,16 @@ Controls EPT related features. + + >> Have hardware keep accessed/dirty (A/D) bits updated. + ++* 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 f4a6a37149..1924434960 100644 +--- a/xen/arch/x86/hvm/hvm.c ++++ b/xen/arch/x86/hvm/hvm.c +@@ -1706,6 +1706,7 @@ int hvm_hap_nested_page_fault(paddr_t gpa, unsigned long gla, + struct p2m_domain *p2m, *hostp2m; + int rc, fall_through = 0, paged = 0; + int sharing_enomem = 0; ++ unsigned int page_order = 0; + vm_event_request_t *req_ptr = NULL; + bool_t ap2m_active, sync = 0; + +@@ -1774,7 +1775,7 @@ int hvm_hap_nested_page_fault(paddr_t gpa, unsigned long gla, + hostp2m = p2m_get_hostp2m(currd); + mfn = get_gfn_type_access(hostp2m, gfn, &p2mt, &p2ma, + P2M_ALLOC | (npfec.write_access ? P2M_UNSHARE : 0), +- NULL); ++ &page_order); + + if ( ap2m_active ) + { +@@ -1786,7 +1787,7 @@ int hvm_hap_nested_page_fault(paddr_t gpa, unsigned long gla, + goto out; + } + +- mfn = get_gfn_type_access(p2m, gfn, &p2mt, &p2ma, 0, NULL); ++ mfn = get_gfn_type_access(p2m, gfn, &p2mt, &p2ma, 0, &page_order); + } + else + p2m = hostp2m; +@@ -1828,6 +1829,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 493986e84a..8821a3b536 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_t __read_mostly opt_pml_enabled = 1; + static s8 __read_mostly opt_ept_ad = -1; ++int8_t __read_mostly opt_ept_exec_sp = -1; + + /* + * The 'ept' parameter controls functionalities that depend on, or impact the +@@ -94,6 +95,8 @@ static int __init parse_ept_param(const char *s) + opt_pml_enabled = val; + else if ( !cmdline_strcmp(s, "ad") ) + opt_ept_ad = val; ++ else if ( !cmdline_strcmp(s, "exec-sp") ) ++ 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 840dc2b44d..a568d62643 100644 +--- a/xen/arch/x86/hvm/vmx/vmx.c ++++ b/xen/arch/x86/hvm/vmx/vmx.c +@@ -2415,6 +2415,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); +@@ -2435,6 +2531,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 ce46201d45..93e08f89a2 100644 +--- a/xen/arch/x86/mm/p2m-ept.c ++++ b/xen/arch/x86/mm/p2m-ept.c +@@ -215,6 +215,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 89619e4afd..20eb7f6082 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 b8151d2d9f..89ae3e03f1 100644 +--- a/xen/include/asm-x86/msr-index.h ++++ b/xen/include/asm-x86/msr-index.h +@@ -54,6 +54,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.11-3.patch b/xsa304-4.11-3.patch new file mode 100644 index 0000000..bf30699 --- /dev/null +++ b/xsa304-4.11-3.patch @@ -0,0 +1,110 @@ +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.markdown b/docs/misc/xen-command-line.markdown +index 684671cb7b..33ed1ffc40 100644 +--- a/docs/misc/xen-command-line.markdown ++++ b/docs/misc/xen-command-line.markdown +@@ -861,6 +861,21 @@ Controls EPT related features. + 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 8821a3b536..15376e25ba 100644 +--- a/xen/arch/x86/hvm/vmx/vmcs.c ++++ b/xen/arch/x86/hvm/vmx/vmcs.c +@@ -107,6 +107,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 2b62bc61dd..97c417fc3e 100644 +--- a/xen/arch/x86/mm/p2m.c ++++ b/xen/arch/x86/mm/p2m.c +@@ -257,17 +257,22 @@ int p2m_is_logdirty_range(struct p2m_domain *p2m, unsigned long start, + return 0; + } + ++/* ++ * 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 *p2m = p2m_get_hostp2m(d); + +- ASSERT(ot != nt); + ASSERT(p2m_is_changeable(ot) && p2m_is_changeable(nt)); + + p2m_lock(p2m); + 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); + p2m_unlock(p2m); + } + diff --git a/xsa305-4.11-1.patch b/xsa305-4.11-1.patch new file mode 100644 index 0000000..92e6ed3 --- /dev/null +++ b/xsa305-4.11-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.markdown b/docs/misc/xen-command-line.markdown +index 684671cb7b..b86d26399a 100644 +--- a/docs/misc/xen-command-line.markdown ++++ b/docs/misc/xen-command-line.markdown +@@ -1948,6 +1948,20 @@ pages) must also be specified via the tbuf\_size parameter. + ### 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 da1e4827f4..4c82d9f710 100644 +--- a/xen/arch/x86/Makefile ++++ b/xen/arch/x86/Makefile +@@ -65,6 +65,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 5e11970701..04aefa555d 100644 +--- a/xen/arch/x86/cpuid.c ++++ b/xen/arch/x86/cpuid.c +@@ -622,6 +622,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 ebc0665615..35d99a98a1 100644 +--- a/xen/arch/x86/msr.c ++++ b/xen/arch/x86/msr.c +@@ -153,6 +153,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; + +@@ -233,6 +234,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 657160549f..dc13ad6c36 100644 +--- a/xen/arch/x86/setup.c ++++ b/xen/arch/x86/setup.c +@@ -1551,6 +1551,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 fd52a10cf9..bdc118d88b 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 89ae3e03f1..5ee7a37c12 100644 +--- a/xen/include/asm-x86/msr-index.h ++++ b/xen/include/asm-x86/msr-index.h +@@ -55,6 +55,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) +@@ -62,6 +63,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 20d1ecb332..66224f23b9 100644 +--- a/xen/include/asm-x86/processor.h ++++ b/xen/include/asm-x86/processor.h +@@ -258,6 +258,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 always_inline void cpuid_count_leaf(uint32_t leaf, uint32_t subleaf, + struct cpuid_leaf *data) + { +@@ -610,6 +620,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 750f809968..be223a6950 100644 +--- a/xen/include/xen/lib.h ++++ b/xen/include/xen/lib.h +@@ -116,6 +116,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.11-2.patch b/xsa305-4.11-2.patch new file mode 100644 index 0000000..0b68ff1 --- /dev/null +++ b/xsa305-4.11-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.markdown b/docs/misc/xen-command-line.markdown +index b86d26399a..31635a473a 100644 +--- a/docs/misc/xen-command-line.markdown ++++ b/docs/misc/xen-command-line.markdown +@@ -1841,7 +1841,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 +@@ -1952,7 +1952,7 @@ pages) must also be specified via the tbuf\_size parameter. + = + + Applicability: x86 +- Default: true ++ Default: false on parts vulnerable to TAA, true otherwise + + Controls for the use of Transactional Synchronization eXtensions. + +@@ -1962,6 +1962,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 2fe16b423d..ab196b156d 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -152,6 +152,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; +@@ -362,7 +365,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" : "", +@@ -374,7 +377,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) ) +@@ -388,7 +393,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" : +@@ -397,6 +402,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" : ""); +@@ -911,6 +918,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) ) +@@ -1140,6 +1148,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 5ee7a37c12..1761a01f1f 100644 +--- a/xen/include/asm-x86/msr-index.h ++++ b/xen/include/asm-x86/msr-index.h +@@ -56,6 +56,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 45331b704417bcfbe3130a9e7d6ae07857d9c572 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 26 Nov 2019 20:31:47 +0000 Subject: [PATCH 08/13] Device quarantine for alternate pci assignment methods [XSA-306] --- xen.spec | 9 ++++- xsa306-4.11.patch | 94 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 xsa306-4.11.patch diff --git a/xen.spec b/xen.spec index b48dc81..826387e 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: 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 @@ -153,6 +153,7 @@ Patch70: xsa304-4.11-2.patch Patch71: xsa304-4.11-3.patch Patch72: xsa305-4.11-1.patch Patch73: xsa305-4.11-2.patch +Patch74: xsa306-4.11.patch %if %build_qemutrad @@ -388,6 +389,9 @@ manage Xen virtual machines. %patch71 -p1 %patch72 -p1 %patch73 -p1 +%ifarch %{ix86} x86_64 +%patch74 -p1 +%endif # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -987,6 +991,9 @@ fi %endif %changelog +* Tue Nov 26 2019 Michael Young - 4.11.2-4 +- Device quarantine for alternate pci assignment methods [XSA-306] + * Tue Nov 12 2019 Michael Young - 4.11.2-3 - add missing XSA-299 patches - x86: Machine Check Error on Page Size Change DoS [XSA-304, CVE-2018-12207] diff --git a/xsa306-4.11.patch b/xsa306-4.11.patch new file mode 100644 index 0000000..320121c --- /dev/null +++ b/xsa306-4.11.patch @@ -0,0 +1,94 @@ +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.markdown ++++ b/docs/misc/xen-command-line.markdown +@@ -1112,7 +1112,7 @@ detection of systems known to misbehave + > Default: `new` unless directed-EOI is supported + + ### iommu +-> `= List of [ | force | required | intremap | intpost | qinval | snoop | sharept | dom0-passthrough | dom0-strict | amd-iommu-perdev-intremap | workaround_bios_bug | igfx | crash-disable | verbose | debug ]` ++> `= List of [ | force | required | quarantine | intremap | intpost | qinval | snoop | sharept | dom0-passthrough | dom0-strict | amd-iommu-perdev-intremap | workaround_bios_bug | igfx | crash-disable | verbose | debug ]` + + > Sub-options: + +@@ -1132,6 +1132,15 @@ detection of systems known to misbehave + >> Don't continue booting unless IOMMU support is found and can be initialized + >> successfully. + ++> `quarantine` ++ ++> Default: `true` ++ ++>> Control Xen's behavior when de-assigning devices from guests. If enabled, ++>> 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. ++ + > `intremap` + + > Default: `true` +--- a/xen/drivers/passthrough/iommu.c ++++ b/xen/drivers/passthrough/iommu.c +@@ -52,6 +52,7 @@ custom_param("iommu", parse_iommu_param) + bool_t __initdata iommu_enable = 1; + bool_t __read_mostly iommu_enabled; + bool_t __read_mostly force_iommu; ++bool __read_mostly iommu_quarantine = true; + bool_t __hwdom_initdata iommu_dom0_strict; + bool_t __read_mostly iommu_verbose; + bool_t __read_mostly iommu_workaround_bios_bug; +@@ -99,6 +100,8 @@ static int __init parse_iommu_param(cons + else if ( !cmdline_strcmp(s, "force") || + !cmdline_strcmp(s, "required") ) + force_iommu = val; ++ else if ( !cmdline_strcmp(s, "quarantine") ) ++ iommu_quarantine = val; + else if ( !cmdline_strcmp(s, "workaround_bios_bug") ) + iommu_workaround_bios_bug = val; + else if ( !cmdline_strcmp(s, "igfx") ) +--- a/xen/drivers/passthrough/pci.c ++++ b/xen/drivers/passthrough/pci.c +@@ -1511,7 +1511,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 +@@ -29,7 +29,7 @@ + #include + + extern bool_t iommu_enable, iommu_enabled; +-extern bool_t force_iommu, iommu_verbose; ++extern bool force_iommu, iommu_quarantine, iommu_verbose; + extern bool_t iommu_workaround_bios_bug, iommu_igfx, iommu_passthrough; + extern bool_t iommu_snoop, iommu_qinval, iommu_intremap, iommu_intpost; + extern bool_t iommu_hap_pt_share; From 980b6af727164280004e4298a33ce4b9ca4aa7a9 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 3 Dec 2019 17:08:42 +0000 Subject: [PATCH 09/13] update to xen-4.11.3 --- .gitignore | 2 +- sources | 2 +- xen.spec | 69 +- xen.use.fedora.ipxe.patch | 2 +- 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 ---- xsa304-4.11-1.patch | 71 -- xsa304-4.11-2.patch | 298 --------- xsa304-4.11-3.patch | 110 ---- xsa305-4.11-1.patch | 288 --------- xsa305-4.11-2.patch | 192 ------ xsa306-4.11.patch | 94 --- 32 files changed, 10 insertions(+), 5078 deletions(-) delete mode 100644 xsa296.patch delete mode 100644 xsa298-4.11.patch delete mode 100644 xsa299-4.11-0001-x86-mm-L1TF-checks-don-t-leave-a-partial-entry.patch delete mode 100644 xsa299-4.11-0002-x86-mm-Don-t-re-set-PGT_pinned-on-a-partially-de-val.patch delete mode 100644 xsa299-4.11-0003-x86-mm-Separate-out-partial_pte-tristate-into-indivi.patch delete mode 100644 xsa299-4.11-0004-x86-mm-Use-flags-for-_put_page_type-rather-than-a-bo.patch delete mode 100644 xsa299-4.11-0005-x86-mm-Rework-get_page_and_type_from_mfn-conditional.patch delete mode 100644 xsa299-4.11-0006-x86-mm-Have-alloc_l-23-_table-clear-partial_flags-wh.patch delete mode 100644 xsa299-4.11-0007-x86-mm-Always-retain-a-general-ref-on-partial.patch delete mode 100644 xsa299-4.11-0008-x86-mm-Collapse-PTF_partial_set-and-PTF_partial_gene.patch delete mode 100644 xsa299-4.11-0009-x86-mm-Properly-handle-linear-pagetable-promotion-fa.patch delete mode 100644 xsa299-4.11-0010-x86-mm-Fix-nested-de-validation-on-error.patch delete mode 100644 xsa299-4.11-0011-x86-mm-Don-t-drop-a-type-ref-unless-you-held-a-ref-t.patch delete mode 100644 xsa301-4.11-1.patch delete mode 100644 xsa301-4.11-2.patch delete mode 100644 xsa301-4.11-3.patch delete mode 100644 xsa302-4.11-0001-IOMMU-add-missing-HVM-check.patch delete mode 100644 xsa302-4.11-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.11-1.patch delete mode 100644 xsa304-4.11-2.patch delete mode 100644 xsa304-4.11-3.patch delete mode 100644 xsa305-4.11-1.patch delete mode 100644 xsa305-4.11-2.patch delete mode 100644 xsa306-4.11.patch diff --git a/.gitignore b/.gitignore index 767ea3b..9266fed 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.2.tar.gz +/xen-4.11.3.tar.gz diff --git a/sources b/sources index ef56fa4..88770fd 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.2.tar.gz) = 48d3d926d35eb56c79c06d0abc6e6be2564fadb43367cc7f46881c669a75016707672179c2cca1c4cfb14af2cefd46e2e7f99470cddf7df2886d8435a2de814e +SHA512 (xen-4.11.3.tar.gz) = 2204e490e9fc357a05983a9bf4e7345e1d364fe00400ce473988dcb9ca7d4e2b921fe10f095cbbc64248130a92d22c6f0d154dcae250a57a7f915df32e3dc436 diff --git a/xen.spec b/xen.spec index 826387e..5016551 100644 --- a/xen.spec +++ b/xen.spec @@ -66,8 +66,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.11.2 -Release: 4%{?dist} +Version: 4.11.3 +Release: 1%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -126,34 +126,6 @@ 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: xsa299-4.11-0002-x86-mm-Don-t-re-set-PGT_pinned-on-a-partially-de-val.patch -Patch51: xsa299-4.11-0003-x86-mm-Separate-out-partial_pte-tristate-into-indivi.patch -Patch52: xsa299-4.11-0004-x86-mm-Use-flags-for-_put_page_type-rather-than-a-bo.patch -Patch53: xsa299-4.11-0005-x86-mm-Rework-get_page_and_type_from_mfn-conditional.patch -Patch54: xsa299-4.11-0006-x86-mm-Have-alloc_l-23-_table-clear-partial_flags-wh.patch -Patch55: xsa299-4.11-0007-x86-mm-Always-retain-a-general-ref-on-partial.patch -Patch56: xsa299-4.11-0008-x86-mm-Collapse-PTF_partial_set-and-PTF_partial_gene.patch -Patch57: xsa299-4.11-0009-x86-mm-Properly-handle-linear-pagetable-promotion-fa.patch -Patch58: xsa299-4.11-0010-x86-mm-Fix-nested-de-validation-on-error.patch -Patch59: xsa299-4.11-0011-x86-mm-Don-t-drop-a-type-ref-unless-you-held-a-ref-t.patch -Patch60: xsa301-4.11-1.patch -Patch61: xsa301-4.11-2.patch -Patch62: xsa301-4.11-3.patch -Patch63: xsa302-4.11-0001-IOMMU-add-missing-HVM-check.patch -Patch64: xsa302-4.11-0002-passthrough-quarantine-PCI-devices.patch -Patch65: xsa303-0001-xen-arm32-entry-Split-__DEFINE_ENTRY_TRAP-in-two.patch -Patch66: xsa303-0002-xen-arm32-entry-Fold-the-macro-SAVE_ALL-in-the-macro.patch -Patch67: xsa303-0003-xen-arm32-Don-t-blindly-unmask-interrupts-on-trap-wi.patch -Patch68: xsa303-0004-xen-arm64-Don-t-blindly-unmask-interrupts-on-trap-wi.patch -Patch69: xsa304-4.11-1.patch -Patch70: xsa304-4.11-2.patch -Patch71: xsa304-4.11-3.patch -Patch72: xsa305-4.11-1.patch -Patch73: xsa305-4.11-2.patch -Patch74: xsa306-4.11.patch %if %build_qemutrad @@ -360,38 +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 -%ifarch %{ix86} x86_64 -%patch63 -p1 -%patch64 -p1 -%endif -%patch65 -p1 -%patch66 -p1 -%patch67 -p1 -%patch68 -p1 -%patch69 -p1 -%patch70 -p1 -%patch71 -p1 -%patch72 -p1 -%patch73 -p1 -%ifarch %{ix86} x86_64 -%patch74 -p1 -%endif # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -991,6 +931,11 @@ fi %endif %changelog +* Tue Dec 03 2019 Michael Young - 4.11.3-1 +- update to 4.11.3 + remove patches now fixed upstream + adjust xen.use.fedora.ipxe.patch + * Tue Nov 26 2019 Michael Young - 4.11.2-4 - Device quarantine for alternate pci assignment methods [XSA-306] diff --git a/xen.use.fedora.ipxe.patch b/xen.use.fedora.ipxe.patch index 25ab8d7..a063763 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.2 + QEMU_TRADITIONAL_REVISION ?= xen-4.11.3 --- 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 @@ 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-4.11.patch b/xsa298-4.11.patch deleted file mode 100644 index 9f649e3..0000000 --- a/xsa298-4.11.patch +++ /dev/null @@ -1,87 +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 - ---- 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 deleted file mode 100644 index 6475328..0000000 --- a/xsa299-4.11-0001-x86-mm-L1TF-checks-don-t-leave-a-partial-entry.patch +++ /dev/null @@ -1,94 +0,0 @@ -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 deleted file mode 100644 index a369f93..0000000 --- a/xsa299-4.11-0002-x86-mm-Don-t-re-set-PGT_pinned-on-a-partially-de-val.patch +++ /dev/null @@ -1,99 +0,0 @@ -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 deleted file mode 100644 index fa6914b..0000000 --- a/xsa299-4.11-0003-x86-mm-Separate-out-partial_pte-tristate-into-indivi.patch +++ /dev/null @@ -1,609 +0,0 @@ -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 deleted file mode 100644 index 767ebc1..0000000 --- a/xsa299-4.11-0004-x86-mm-Use-flags-for-_put_page_type-rather-than-a-bo.patch +++ /dev/null @@ -1,140 +0,0 @@ -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 deleted file mode 100644 index 523ea25..0000000 --- a/xsa299-4.11-0005-x86-mm-Rework-get_page_and_type_from_mfn-conditional.patch +++ /dev/null @@ -1,79 +0,0 @@ -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 deleted file mode 100644 index c611801..0000000 --- a/xsa299-4.11-0006-x86-mm-Have-alloc_l-23-_table-clear-partial_flags-wh.patch +++ /dev/null @@ -1,101 +0,0 @@ -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 deleted file mode 100644 index 0d54cc5..0000000 --- a/xsa299-4.11-0007-x86-mm-Always-retain-a-general-ref-on-partial.patch +++ /dev/null @@ -1,374 +0,0 @@ -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 deleted file mode 100644 index dd847e8..0000000 --- a/xsa299-4.11-0008-x86-mm-Collapse-PTF_partial_set-and-PTF_partial_gene.patch +++ /dev/null @@ -1,227 +0,0 @@ -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 deleted file mode 100644 index f62d774..0000000 --- a/xsa299-4.11-0009-x86-mm-Properly-handle-linear-pagetable-promotion-fa.patch +++ /dev/null @@ -1,106 +0,0 @@ -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 deleted file mode 100644 index 643ef53..0000000 --- a/xsa299-4.11-0010-x86-mm-Fix-nested-de-validation-on-error.patch +++ /dev/null @@ -1,169 +0,0 @@ -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 deleted file mode 100644 index 24970da..0000000 --- a/xsa299-4.11-0011-x86-mm-Don-t-drop-a-type-ref-unless-you-held-a-ref-t.patch +++ /dev/null @@ -1,413 +0,0 @@ -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 deleted file mode 100644 index 4d528fe..0000000 --- a/xsa301-4.11-1.patch +++ /dev/null @@ -1,80 +0,0 @@ -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 deleted file mode 100644 index 33b6150..0000000 --- a/xsa301-4.11-2.patch +++ /dev/null @@ -1,92 +0,0 @@ -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 deleted file mode 100644 index 55a701a..0000000 --- a/xsa301-4.11-3.patch +++ /dev/null @@ -1,49 +0,0 @@ -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 deleted file mode 100644 index c3d4435..0000000 --- a/xsa302-4.11-0001-IOMMU-add-missing-HVM-check.patch +++ /dev/null @@ -1,37 +0,0 @@ -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 deleted file mode 100644 index 5204c9f..0000000 --- a/xsa302-4.11-0002-passthrough-quarantine-PCI-devices.patch +++ /dev/null @@ -1,498 +0,0 @@ -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 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.11-1.patch b/xsa304-4.11-1.patch deleted file mode 100644 index f78d152..0000000 --- a/xsa304-4.11-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 fb7edfaef9..d698b1d50a 100644 ---- a/xen/drivers/passthrough/vtd/extern.h -+++ b/xen/drivers/passthrough/vtd/extern.h -@@ -96,6 +96,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 f242e30caf..8712d3b4dc 100644 ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -1211,6 +1211,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.11-2.patch b/xsa304-4.11-2.patch deleted file mode 100644 index f22d7cc..0000000 --- a/xsa304-4.11-2.patch +++ /dev/null @@ -1,298 +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.markdown b/docs/misc/xen-command-line.markdown -index c63a07d29b..684671cb7b 100644 ---- a/docs/misc/xen-command-line.markdown -+++ b/docs/misc/xen-command-line.markdown -@@ -828,7 +828,7 @@ effect the inverse meaning. - >> set as UC. - - ### ept (Intel) --> `= List of ( {no-}pml | {no-}ad )` -+> `= List of [ {no-}pml, {no-}ad, {no-}exec-sp ]` - - Controls EPT related features. - -@@ -851,6 +851,16 @@ Controls EPT related features. - - >> Have hardware keep accessed/dirty (A/D) bits updated. - -+* 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 f4a6a37149..1924434960 100644 ---- a/xen/arch/x86/hvm/hvm.c -+++ b/xen/arch/x86/hvm/hvm.c -@@ -1706,6 +1706,7 @@ int hvm_hap_nested_page_fault(paddr_t gpa, unsigned long gla, - struct p2m_domain *p2m, *hostp2m; - int rc, fall_through = 0, paged = 0; - int sharing_enomem = 0; -+ unsigned int page_order = 0; - vm_event_request_t *req_ptr = NULL; - bool_t ap2m_active, sync = 0; - -@@ -1774,7 +1775,7 @@ int hvm_hap_nested_page_fault(paddr_t gpa, unsigned long gla, - hostp2m = p2m_get_hostp2m(currd); - mfn = get_gfn_type_access(hostp2m, gfn, &p2mt, &p2ma, - P2M_ALLOC | (npfec.write_access ? P2M_UNSHARE : 0), -- NULL); -+ &page_order); - - if ( ap2m_active ) - { -@@ -1786,7 +1787,7 @@ int hvm_hap_nested_page_fault(paddr_t gpa, unsigned long gla, - goto out; - } - -- mfn = get_gfn_type_access(p2m, gfn, &p2mt, &p2ma, 0, NULL); -+ mfn = get_gfn_type_access(p2m, gfn, &p2mt, &p2ma, 0, &page_order); - } - else - p2m = hostp2m; -@@ -1828,6 +1829,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 493986e84a..8821a3b536 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_t __read_mostly opt_pml_enabled = 1; - static s8 __read_mostly opt_ept_ad = -1; -+int8_t __read_mostly opt_ept_exec_sp = -1; - - /* - * The 'ept' parameter controls functionalities that depend on, or impact the -@@ -94,6 +95,8 @@ static int __init parse_ept_param(const char *s) - opt_pml_enabled = val; - else if ( !cmdline_strcmp(s, "ad") ) - opt_ept_ad = val; -+ else if ( !cmdline_strcmp(s, "exec-sp") ) -+ 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 840dc2b44d..a568d62643 100644 ---- a/xen/arch/x86/hvm/vmx/vmx.c -+++ b/xen/arch/x86/hvm/vmx/vmx.c -@@ -2415,6 +2415,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); -@@ -2435,6 +2531,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 ce46201d45..93e08f89a2 100644 ---- a/xen/arch/x86/mm/p2m-ept.c -+++ b/xen/arch/x86/mm/p2m-ept.c -@@ -215,6 +215,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 89619e4afd..20eb7f6082 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 b8151d2d9f..89ae3e03f1 100644 ---- a/xen/include/asm-x86/msr-index.h -+++ b/xen/include/asm-x86/msr-index.h -@@ -54,6 +54,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.11-3.patch b/xsa304-4.11-3.patch deleted file mode 100644 index bf30699..0000000 --- a/xsa304-4.11-3.patch +++ /dev/null @@ -1,110 +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.markdown b/docs/misc/xen-command-line.markdown -index 684671cb7b..33ed1ffc40 100644 ---- a/docs/misc/xen-command-line.markdown -+++ b/docs/misc/xen-command-line.markdown -@@ -861,6 +861,21 @@ Controls EPT related features. - 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 8821a3b536..15376e25ba 100644 ---- a/xen/arch/x86/hvm/vmx/vmcs.c -+++ b/xen/arch/x86/hvm/vmx/vmcs.c -@@ -107,6 +107,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 2b62bc61dd..97c417fc3e 100644 ---- a/xen/arch/x86/mm/p2m.c -+++ b/xen/arch/x86/mm/p2m.c -@@ -257,17 +257,22 @@ int p2m_is_logdirty_range(struct p2m_domain *p2m, unsigned long start, - return 0; - } - -+/* -+ * 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 *p2m = p2m_get_hostp2m(d); - -- ASSERT(ot != nt); - ASSERT(p2m_is_changeable(ot) && p2m_is_changeable(nt)); - - p2m_lock(p2m); - 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); - p2m_unlock(p2m); - } - diff --git a/xsa305-4.11-1.patch b/xsa305-4.11-1.patch deleted file mode 100644 index 92e6ed3..0000000 --- a/xsa305-4.11-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.markdown b/docs/misc/xen-command-line.markdown -index 684671cb7b..b86d26399a 100644 ---- a/docs/misc/xen-command-line.markdown -+++ b/docs/misc/xen-command-line.markdown -@@ -1948,6 +1948,20 @@ pages) must also be specified via the tbuf\_size parameter. - ### 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 da1e4827f4..4c82d9f710 100644 ---- a/xen/arch/x86/Makefile -+++ b/xen/arch/x86/Makefile -@@ -65,6 +65,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 5e11970701..04aefa555d 100644 ---- a/xen/arch/x86/cpuid.c -+++ b/xen/arch/x86/cpuid.c -@@ -622,6 +622,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 ebc0665615..35d99a98a1 100644 ---- a/xen/arch/x86/msr.c -+++ b/xen/arch/x86/msr.c -@@ -153,6 +153,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; - -@@ -233,6 +234,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 657160549f..dc13ad6c36 100644 ---- a/xen/arch/x86/setup.c -+++ b/xen/arch/x86/setup.c -@@ -1551,6 +1551,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 fd52a10cf9..bdc118d88b 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 89ae3e03f1..5ee7a37c12 100644 ---- a/xen/include/asm-x86/msr-index.h -+++ b/xen/include/asm-x86/msr-index.h -@@ -55,6 +55,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) -@@ -62,6 +63,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 20d1ecb332..66224f23b9 100644 ---- a/xen/include/asm-x86/processor.h -+++ b/xen/include/asm-x86/processor.h -@@ -258,6 +258,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 always_inline void cpuid_count_leaf(uint32_t leaf, uint32_t subleaf, - struct cpuid_leaf *data) - { -@@ -610,6 +620,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 750f809968..be223a6950 100644 ---- a/xen/include/xen/lib.h -+++ b/xen/include/xen/lib.h -@@ -116,6 +116,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.11-2.patch b/xsa305-4.11-2.patch deleted file mode 100644 index 0b68ff1..0000000 --- a/xsa305-4.11-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.markdown b/docs/misc/xen-command-line.markdown -index b86d26399a..31635a473a 100644 ---- a/docs/misc/xen-command-line.markdown -+++ b/docs/misc/xen-command-line.markdown -@@ -1841,7 +1841,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 -@@ -1952,7 +1952,7 @@ pages) must also be specified via the tbuf\_size parameter. - = - - Applicability: x86 -- Default: true -+ Default: false on parts vulnerable to TAA, true otherwise - - Controls for the use of Transactional Synchronization eXtensions. - -@@ -1962,6 +1962,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 2fe16b423d..ab196b156d 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -152,6 +152,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; -@@ -362,7 +365,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" : "", -@@ -374,7 +377,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) ) -@@ -388,7 +393,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" : -@@ -397,6 +402,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" : ""); -@@ -911,6 +918,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) ) -@@ -1140,6 +1148,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 5ee7a37c12..1761a01f1f 100644 ---- a/xen/include/asm-x86/msr-index.h -+++ b/xen/include/asm-x86/msr-index.h -@@ -56,6 +56,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.11.patch b/xsa306-4.11.patch deleted file mode 100644 index 320121c..0000000 --- a/xsa306-4.11.patch +++ /dev/null @@ -1,94 +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.markdown -+++ b/docs/misc/xen-command-line.markdown -@@ -1112,7 +1112,7 @@ detection of systems known to misbehave - > Default: `new` unless directed-EOI is supported - - ### iommu --> `= List of [ | force | required | intremap | intpost | qinval | snoop | sharept | dom0-passthrough | dom0-strict | amd-iommu-perdev-intremap | workaround_bios_bug | igfx | crash-disable | verbose | debug ]` -+> `= List of [ | force | required | quarantine | intremap | intpost | qinval | snoop | sharept | dom0-passthrough | dom0-strict | amd-iommu-perdev-intremap | workaround_bios_bug | igfx | crash-disable | verbose | debug ]` - - > Sub-options: - -@@ -1132,6 +1132,15 @@ detection of systems known to misbehave - >> Don't continue booting unless IOMMU support is found and can be initialized - >> successfully. - -+> `quarantine` -+ -+> Default: `true` -+ -+>> Control Xen's behavior when de-assigning devices from guests. If enabled, -+>> 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. -+ - > `intremap` - - > Default: `true` ---- a/xen/drivers/passthrough/iommu.c -+++ b/xen/drivers/passthrough/iommu.c -@@ -52,6 +52,7 @@ custom_param("iommu", parse_iommu_param) - bool_t __initdata iommu_enable = 1; - bool_t __read_mostly iommu_enabled; - bool_t __read_mostly force_iommu; -+bool __read_mostly iommu_quarantine = true; - bool_t __hwdom_initdata iommu_dom0_strict; - bool_t __read_mostly iommu_verbose; - bool_t __read_mostly iommu_workaround_bios_bug; -@@ -99,6 +100,8 @@ static int __init parse_iommu_param(cons - else if ( !cmdline_strcmp(s, "force") || - !cmdline_strcmp(s, "required") ) - force_iommu = val; -+ else if ( !cmdline_strcmp(s, "quarantine") ) -+ iommu_quarantine = val; - else if ( !cmdline_strcmp(s, "workaround_bios_bug") ) - iommu_workaround_bios_bug = val; - else if ( !cmdline_strcmp(s, "igfx") ) ---- a/xen/drivers/passthrough/pci.c -+++ b/xen/drivers/passthrough/pci.c -@@ -1511,7 +1511,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 -@@ -29,7 +29,7 @@ - #include - - extern bool_t iommu_enable, iommu_enabled; --extern bool_t force_iommu, iommu_verbose; -+extern bool force_iommu, iommu_quarantine, iommu_verbose; - extern bool_t iommu_workaround_bios_bug, iommu_igfx, iommu_passthrough; - extern bool_t iommu_snoop, iommu_qinval, iommu_intremap, iommu_intpost; - extern bool_t iommu_hap_pt_share; From 9fbe31b9238cd25fc44d4c18e7f9176416727756 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 12 Dec 2019 20:28:25 +0000 Subject: [PATCH 10/13] 5 security fixes denial of service in find_next_bit() [XSA-307, CVE-2019-19581, 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] (#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) --- xen.spec | 31 ++- 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.11.patch | 187 ++++++++++++++++++ 8 files changed, 793 insertions(+), 2 deletions(-) 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.11.patch diff --git a/xen.spec b/xen.spec index 5016551..b55151b 100644 --- a/xen.spec +++ b/xen.spec @@ -67,7 +67,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.11.3 -Release: 1%{?dist} +Release: 2%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -126,6 +126,13 @@ Patch42: xen.stubdom.build.patch Patch44: xen.vwprintw.fix.patch Patch45: xen.python.env.patch Patch46: xen.gcc9.fixes.patch +Patch47: xsa307.patch +Patch48: xsa308.patch +Patch49: xsa309.patch +Patch50: xsa310-0001-x86-mm-Set-old_guest_table-when-destroying-vcpu-page.patch +Patch51: xsa310-0002-x86-mm-alloc-free_lN_table-Retain-partial_flags-on-E.patch +Patch52: xsa310-0003-x86-mm-relinquish_memory-Grab-an-extra-type-ref-when.patch +Patch53: xsa311-4.11.patch %if %build_qemutrad @@ -332,6 +339,13 @@ manage Xen virtual machines. %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 @@ -931,13 +945,26 @@ fi %endif %changelog +* Thu Dec 12 2019 Michael Young - 4.11.3-2 +- denial of service in find_next_bit() [XSA-307, CVE-2019-19581, + 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] + (#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) + * Tue Dec 03 2019 Michael Young - 4.11.3-1 - update to 4.11.3 remove patches now fixed upstream adjust xen.use.fedora.ipxe.patch * Tue Nov 26 2019 Michael Young - 4.11.2-4 -- 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.11.2-3 - 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.11.patch b/xsa311-4.11.patch new file mode 100644 index 0000000..a4cc837 --- /dev/null +++ b/xsa311-4.11.patch @@ -0,0 +1,187 @@ +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 + +--- a/xen/drivers/passthrough/amd/iommu_map.c ++++ b/xen/drivers/passthrough/amd/iommu_map.c +@@ -569,97 +569,6 @@ static int iommu_pde_from_gfn(struct dom + return 0; + } + +-static int update_paging_mode(struct domain *d, unsigned long gfn) +-{ +- u16 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 ( gfn == gfn_x(INVALID_GFN) ) +- return -EADDRNOTAVAIL; +- ASSERT(!(gfn >> DEFAULT_DOMAIN_ADDRESS_WIDTH)); +- +- level = hd->arch.paging_mode; +- old_root = hd->arch.root_table; +- offset = gfn >> (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 ) +- { +- 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((u32 *)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, unsigned long gfn, unsigned long mfn, + unsigned int flags) + { +@@ -685,19 +594,6 @@ int amd_iommu_map_page(struct domain *d, + return rc; + } + +- /* Since HVM domain is initialized with 2 level IO page table, +- * we might need a deeper page table for lager gfn now */ +- if ( is_hvm_domain(d) ) +- { +- if ( update_paging_mode(d, gfn) ) +- { +- spin_unlock(&hd->arch.mapping_lock); +- AMD_IOMMU_DEBUG("Update page mode failed gfn = %lx\n", gfn); +- domain_crash(d); +- return -EFAULT; +- } +- } +- + if ( iommu_pde_from_gfn(d, gfn, pt_mfn, true) || (pt_mfn[1] == 0) ) + { + spin_unlock(&hd->arch.mapping_lock); +--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c ++++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c +@@ -242,11 +242,17 @@ static int amd_iommu_domain_init(struct + { + 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) ? +- IOMMU_PAGING_MODE_LEVEL_2 : +- 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) ++ ? IOMMU_PAGING_MODE_LEVEL_4 : get_paging_mode(get_upper_mfn_bound()); ++ + return 0; + } + From 1b63e4a37b061dee243c933ac44237942deb4cae Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 14 Jan 2020 22:19:03 +0000 Subject: [PATCH 11/13] arm: a CPU may speculate past the ERET instruction [XSA-312] --- xen.spec | 8 +++- xsa312-4.11.patch | 99 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 xsa312-4.11.patch diff --git a/xen.spec b/xen.spec index b55151b..10fc3d9 100644 --- a/xen.spec +++ b/xen.spec @@ -67,7 +67,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.11.3 -Release: 2%{?dist} +Release: 3%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -133,6 +133,7 @@ Patch50: xsa310-0001-x86-mm-Set-old_guest_table-when-destroying-vcpu-page.patch Patch51: xsa310-0002-x86-mm-alloc-free_lN_table-Retain-partial_flags-on-E.patch Patch52: xsa310-0003-x86-mm-relinquish_memory-Grab-an-extra-type-ref-when.patch Patch53: xsa311-4.11.patch +Patch54: xsa312-4.11.patch %if %build_qemutrad @@ -346,6 +347,7 @@ manage Xen virtual machines. %patch51 -p1 %patch52 -p1 %patch53 -p1 +%patch54 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -945,6 +947,10 @@ fi %endif %changelog +%changelog +* Tue Jan 14 2020 Michael Young - 4.11.3-3 +- arm: a CPU may speculate past the ERET instruction [XSA-312] + * Thu Dec 12 2019 Michael Young - 4.11.3-2 - denial of service in find_next_bit() [XSA-307, CVE-2019-19581, CVE-2019-19582] (#1782211) diff --git a/xsa312-4.11.patch b/xsa312-4.11.patch new file mode 100644 index 0000000..553578a --- /dev/null +++ b/xsa312-4.11.patch @@ -0,0 +1,99 @@ +From 35cb81a9967a061df7d0eb8c387395f1c1984454 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 | 2 ++ + xen/arch/arm/arm64/entry.S | 3 +++ + xen/include/asm-arm/macros.h | 9 +++++++++ + 3 files changed, 14 insertions(+) + +diff --git a/xen/arch/arm/arm32/entry.S b/xen/arch/arm/arm32/entry.S +index 16d9f93653..464c8b8645 100644 +--- a/xen/arch/arm/arm32/entry.S ++++ b/xen/arch/arm/arm32/entry.S +@@ -1,4 +1,5 @@ + #include ++#include + #include + #include + #include +@@ -379,6 +380,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 12df95e901..a42c51e489 100644 +--- a/xen/arch/arm/arm64/entry.S ++++ b/xen/arch/arm/arm64/entry.S +@@ -2,6 +2,7 @@ + #include + #include + #include ++#include + #include + + /* +@@ -288,6 +289,7 @@ guest_sync: + */ + mov x1, xzr + eret ++ sb + + 1: + /* +@@ -413,6 +415,7 @@ return_from_trap: + ldr lr, [sp], #(UREGS_SPSR_el1 - UREGS_LR) /* CPSR, PC, SP, LR */ + + eret ++ sb + + /* + * This function is used to check pending virtual SError in the gap of +diff --git a/xen/include/asm-arm/macros.h b/xen/include/asm-arm/macros.h +index 5d837cb38b..539f613ee5 100644 +--- a/xen/include/asm-arm/macros.h ++++ b/xen/include/asm-arm/macros.h +@@ -13,4 +13,13 @@ + # error "unknown ARM variant" + #endif + ++ /* ++ * Speculative barrier ++ * XXX: Add support for the 'sb' instruction ++ */ ++ .macro sb ++ dsb nsh ++ isb ++ .endm ++ + #endif /* __ASM_ARM_MACROS_H */ +-- +2.17.1 + From fc71df92e7578176243a19b0d3d5dc5bc1fde1d5 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 15 Apr 2020 22:13:08 +0100 Subject: [PATCH 12/13] 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 | 22 +++++++- 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(+), 2 deletions(-) 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 10fc3d9..276508a 100644 --- a/xen.spec +++ b/xen.spec @@ -67,7 +67,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.11.3 -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 @@ -134,6 +134,11 @@ Patch51: xsa310-0002-x86-mm-alloc-free_lN_table-Retain-partial_flags-on-E.patch Patch52: xsa310-0003-x86-mm-relinquish_memory-Grab-an-extra-type-ref-when.patch Patch53: xsa311-4.11.patch Patch54: xsa312-4.11.patch +Patch55: xsa313-1.patch +Patch56: xsa313-2.patch +Patch57: xsa314-4.13.patch +Patch58: xsa316-xen.patch +Patch59: xsa318.patch %if %build_qemutrad @@ -348,6 +353,11 @@ manage Xen virtual machines. %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 @@ -947,7 +957,15 @@ fi %endif %changelog -%changelog +* Wed Apr 15 2020 Michael Young - 4.11.3-4 +- 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 Jan 14 2020 Michael Young - 4.11.3-3 - arm: a CPU may speculate past the ERET instruction [XSA-312] 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 d9aa09bb1be1a7b98922eafd1cd8ff878caa2871 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Sat, 25 Apr 2020 12:22:49 +0100 Subject: [PATCH 13/13] update to xen-4.11.4 --- .gitignore | 2 +- sources | 2 +- xen.spec | 35 +--- xen.use.fedora.ipxe.patch | 2 +- 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.11.patch | 187 ------------------ xsa312-4.11.patch | 99 ---------- xsa313-1.patch | 26 --- xsa313-2.patch | 132 ------------- xsa314-4.13.patch | 121 ------------ xsa316-xen.patch | 30 --- xsa318.patch | 39 ---- 17 files changed, 10 insertions(+), 1242 deletions(-) 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.11.patch delete mode 100644 xsa312-4.11.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 9266fed..ced851d 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.3.tar.gz +/xen-4.11.4.tar.gz diff --git a/sources b/sources index 88770fd..e5d3e7c 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.3.tar.gz) = 2204e490e9fc357a05983a9bf4e7345e1d364fe00400ce473988dcb9ca7d4e2b921fe10f095cbbc64248130a92d22c6f0d154dcae250a57a7f915df32e3dc436 +SHA512 (xen-4.11.4.tar.gz) = 8383f0b369fa08c8ecfdd68f902a2aaad140146a183131c50c020fe04c2f1e829c219b9bd9923fa8f1c180e1e7c6e73d0d68b7015fc39fd3b7f59e55c680cedb diff --git a/xen.spec b/xen.spec index 276508a..6aa5317 100644 --- a/xen.spec +++ b/xen.spec @@ -66,8 +66,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.11.3 -Release: 4%{?dist} +Version: 4.11.4 +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,19 +126,6 @@ Patch42: xen.stubdom.build.patch Patch44: xen.vwprintw.fix.patch Patch45: xen.python.env.patch Patch46: xen.gcc9.fixes.patch -Patch47: xsa307.patch -Patch48: xsa308.patch -Patch49: xsa309.patch -Patch50: xsa310-0001-x86-mm-Set-old_guest_table-when-destroying-vcpu-page.patch -Patch51: xsa310-0002-x86-mm-alloc-free_lN_table-Retain-partial_flags-on-E.patch -Patch52: xsa310-0003-x86-mm-relinquish_memory-Grab-an-extra-type-ref-when.patch -Patch53: xsa311-4.11.patch -Patch54: xsa312-4.11.patch -Patch55: xsa313-1.patch -Patch56: xsa313-2.patch -Patch57: xsa314-4.13.patch -Patch58: xsa316-xen.patch -Patch59: xsa318.patch %if %build_qemutrad @@ -345,19 +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 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -957,6 +931,11 @@ fi %endif %changelog +* Sat Apr 25 2020 Michael Young - 4.11.4-1 +- update to 4.11.4 + remove patches now fixed upstream + adjust xen.use.fedora.ipxe.patch + * Wed Apr 15 2020 Michael Young - 4.11.3-4 - multiple xenoprof issues [XSA-313, CVE-2020-11740, CVE-2020-11741] (#1823912, #1823914) diff --git a/xen.use.fedora.ipxe.patch b/xen.use.fedora.ipxe.patch index a063763..bbc98cd 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.3 + QEMU_TRADITIONAL_REVISION ?= xen-4.11.4 --- 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 @@ 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.11.patch b/xsa311-4.11.patch deleted file mode 100644 index a4cc837..0000000 --- a/xsa311-4.11.patch +++ /dev/null @@ -1,187 +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 - ---- a/xen/drivers/passthrough/amd/iommu_map.c -+++ b/xen/drivers/passthrough/amd/iommu_map.c -@@ -569,97 +569,6 @@ static int iommu_pde_from_gfn(struct dom - return 0; - } - --static int update_paging_mode(struct domain *d, unsigned long gfn) --{ -- u16 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 ( gfn == gfn_x(INVALID_GFN) ) -- return -EADDRNOTAVAIL; -- ASSERT(!(gfn >> DEFAULT_DOMAIN_ADDRESS_WIDTH)); -- -- level = hd->arch.paging_mode; -- old_root = hd->arch.root_table; -- offset = gfn >> (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 ) -- { -- 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((u32 *)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, unsigned long gfn, unsigned long mfn, - unsigned int flags) - { -@@ -685,19 +594,6 @@ int amd_iommu_map_page(struct domain *d, - return rc; - } - -- /* Since HVM domain is initialized with 2 level IO page table, -- * we might need a deeper page table for lager gfn now */ -- if ( is_hvm_domain(d) ) -- { -- if ( update_paging_mode(d, gfn) ) -- { -- spin_unlock(&hd->arch.mapping_lock); -- AMD_IOMMU_DEBUG("Update page mode failed gfn = %lx\n", gfn); -- domain_crash(d); -- return -EFAULT; -- } -- } -- - if ( iommu_pde_from_gfn(d, gfn, pt_mfn, true) || (pt_mfn[1] == 0) ) - { - spin_unlock(&hd->arch.mapping_lock); ---- a/xen/drivers/passthrough/amd/pci_amd_iommu.c -+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c -@@ -242,11 +242,17 @@ static int amd_iommu_domain_init(struct - { - 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) ? -- IOMMU_PAGING_MODE_LEVEL_2 : -- 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) -+ ? IOMMU_PAGING_MODE_LEVEL_4 : get_paging_mode(get_upper_mfn_bound()); -+ - return 0; - } - diff --git a/xsa312-4.11.patch b/xsa312-4.11.patch deleted file mode 100644 index 553578a..0000000 --- a/xsa312-4.11.patch +++ /dev/null @@ -1,99 +0,0 @@ -From 35cb81a9967a061df7d0eb8c387395f1c1984454 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 | 2 ++ - xen/arch/arm/arm64/entry.S | 3 +++ - xen/include/asm-arm/macros.h | 9 +++++++++ - 3 files changed, 14 insertions(+) - -diff --git a/xen/arch/arm/arm32/entry.S b/xen/arch/arm/arm32/entry.S -index 16d9f93653..464c8b8645 100644 ---- a/xen/arch/arm/arm32/entry.S -+++ b/xen/arch/arm/arm32/entry.S -@@ -1,4 +1,5 @@ - #include -+#include - #include - #include - #include -@@ -379,6 +380,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 12df95e901..a42c51e489 100644 ---- a/xen/arch/arm/arm64/entry.S -+++ b/xen/arch/arm/arm64/entry.S -@@ -2,6 +2,7 @@ - #include - #include - #include -+#include - #include - - /* -@@ -288,6 +289,7 @@ guest_sync: - */ - mov x1, xzr - eret -+ sb - - 1: - /* -@@ -413,6 +415,7 @@ return_from_trap: - ldr lr, [sp], #(UREGS_SPSR_el1 - UREGS_LR) /* CPSR, PC, SP, LR */ - - eret -+ sb - - /* - * This function is used to check pending virtual SError in the gap of -diff --git a/xen/include/asm-arm/macros.h b/xen/include/asm-arm/macros.h -index 5d837cb38b..539f613ee5 100644 ---- a/xen/include/asm-arm/macros.h -+++ b/xen/include/asm-arm/macros.h -@@ -13,4 +13,13 @@ - # error "unknown ARM variant" - #endif - -+ /* -+ * 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",